mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[jsscripting] ES6+ Support (#8516)
Signed-off-by: Jonathan Gilbert <jpg@trillica.com>
This commit is contained in:
parent
a1ebab11c5
commit
9912e1afc2
@ -6,6 +6,7 @@
|
||||
|
||||
# Add-on maintainers:
|
||||
/bundles/org.openhab.automation.groovyscripting/ @wborn
|
||||
/bundles/org.openhab.automation.jsscripting/ @jpg0
|
||||
/bundles/org.openhab.automation.jythonscripting/ @openhab/add-ons-maintainers
|
||||
/bundles/org.openhab.automation.pidcontroller/ @fwolter
|
||||
/bundles/org.openhab.binding.adorne/ @theiding
|
||||
|
@ -31,6 +31,11 @@
|
||||
<artifactId>org.openhab.automation.pidcontroller</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.automation.jsscripting</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.adorne</artifactId>
|
||||
|
13
bundles/org.openhab.automation.jsscripting/NOTICE
Normal file
13
bundles/org.openhab.automation.jsscripting/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
43
bundles/org.openhab.automation.jsscripting/README.md
Normal file
43
bundles/org.openhab.automation.jsscripting/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# JavaScript Scripting
|
||||
|
||||
This add-on provides support for JavaScript (ECMAScript 2021+) that can be used as a scripting language within automation rules.
|
||||
|
||||
## Creating JavaScript Scripts
|
||||
|
||||
When this add-on is installed, JavaScript script actions will be run by this add-on and allow ECMAScript 2021+ features.
|
||||
|
||||
Alternatively, you can create scripts in the `automation/jsr223` configuration directory.
|
||||
If you create an empty file called `test.js`, you will see a log line with information similar to:
|
||||
|
||||
```text
|
||||
... [INFO ] [.a.m.s.r.i.l.ScriptFileWatcher:150 ] - Loading script 'test.js'
|
||||
```
|
||||
|
||||
To enable debug logging, use the [console logging]({{base}}/administration/logging.html) commands to enable debug logging for the automation functionality:
|
||||
|
||||
```text
|
||||
log:set DEBUG org.openhab.core.automation
|
||||
```
|
||||
|
||||
For more information on the available APIs in scripts see the [JSR223 Scripting]({{base}}/configuration/jsr223.html) documentation.
|
||||
|
||||
## Script Examples
|
||||
|
||||
JavaScript scripts provide access to almost all the functionality in an openHAB runtime environment.
|
||||
As a simple example, the following script logs "Hello, World!".
|
||||
Note that `console.log` will usually not work since the output has no terminal to display the text.
|
||||
The openHAB server uses the [SLF4J](https://www.slf4j.org/) library for logging.
|
||||
|
||||
```js
|
||||
const LoggerFactory = Java.type('org.slf4j.LoggerFactory');
|
||||
|
||||
LoggerFactory.getLogger("org.openhab.core.automation.examples").info("Hello world!");
|
||||
```
|
||||
|
||||
Depending on the openHAB logging configuration, you may need to prefix logger names with `org.openhab.core.automation` for them to show up in the log file (or you modify the logging configuration).
|
||||
|
||||
The script uses the [LoggerFactory](https://www.slf4j.org/apidocs/org/slf4j/Logger.html) to obtain a named logger and then logs a message like:
|
||||
|
||||
```text
|
||||
... [INFO ] [.openhab.core.automation.examples:-2 ] - Hello world!
|
||||
```
|
13
bundles/org.openhab.automation.jsscripting/bnd.bnd
Normal file
13
bundles/org.openhab.automation.jsscripting/bnd.bnd
Normal file
@ -0,0 +1,13 @@
|
||||
Bundle-SymbolicName: ${project.artifactId}
|
||||
DynamicImport-Package: *
|
||||
Import-Package: org.openhab.core.automation.module.script,javax.management,javax.script,javax.xml.datatype,javax.xml.stream;version="[1.0,2)",org.osgi.framework;version="[1.8,2)",org.slf4j;version="[1.7,2)"
|
||||
Require-Capability: osgi.extender;
|
||||
filter:="(osgi.extender=osgi.serviceloader.processor)",
|
||||
osgi.serviceloader;
|
||||
filter:="(osgi.serviceloader=org.graalvm.polyglot.impl.AbstractPolyglotImpl)";
|
||||
cardinality:=multiple
|
||||
|
||||
SPI-Provider: *
|
||||
SPI-Consumer: *
|
||||
|
||||
-fixupmessages "Classes found in the wrong directory"; restrict:=error; is:=warning
|
115
bundles/org.openhab.automation.jsscripting/pom.xml
Normal file
115
bundles/org.openhab.automation.jsscripting/pom.xml
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.automation.jsscripting</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Automation :: JSScripting</name>
|
||||
|
||||
<properties>
|
||||
<bnd.importpackage>
|
||||
!sun.misc.*,
|
||||
!sun.reflect.*,
|
||||
!com.sun.management.*,
|
||||
!jdk.internal.reflect.*,
|
||||
!jdk.vm.ci.services
|
||||
</bnd.importpackage>
|
||||
<graal.version>20.1.0</graal.version>
|
||||
<asm.version>6.2.1</asm.version>
|
||||
<oh.version>${project.version}</oh.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>embed-dependencies</id>
|
||||
<goals>
|
||||
<goal>unpack-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludes>META-INF/services/com.oracle.truffle.api.TruffleLanguage$Provider</excludes> <!-- we'll provide this -->
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.truffle</groupId>
|
||||
<artifactId>truffle-api</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js-scriptengine</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js-launcher</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.sdk</groupId>
|
||||
<artifactId>graal-sdk</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.regex</groupId>
|
||||
<artifactId>regex</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency> <!-- this must come AFTER the regex lib -->
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js</artifactId>
|
||||
<version>${graal.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ibm.icu</groupId>
|
||||
<artifactId>icu4j</artifactId>
|
||||
<version>62.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- include as version required is older than OH provides -->
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-tree</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-util</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-analysis</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<features name="org.openhab.automation.jsscripting-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-automation-jsscripting" description="JSScripting" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.automation.jsscripting/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.automation.jsscripting;
|
||||
|
||||
import com.oracle.truffle.js.runtime.java.adapter.JavaAdapterFactory;
|
||||
|
||||
/**
|
||||
* Class utility to allow creation of 'extendable' classes with a classloader of the GraalJS bundle, rather than the
|
||||
* classloader of the file being extended.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public class ClassExtender {
|
||||
private static ClassLoader classLoader = ClassExtender.class.getClassLoader();
|
||||
|
||||
public static Object extend(String className) {
|
||||
try {
|
||||
return extend(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Cannot find class " + className, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object extend(Class<?> clazz) {
|
||||
return JavaAdapterFactory.getAdapterClassFor(clazz, null, classLoader);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.graalvm.polyglot.PolyglotException;
|
||||
import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Wraps ScriptEngines provided by Graal to provide error messages and stack traces for scripts.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable>
|
||||
extends InvocationInterceptingScriptEngineWithInvocable<T> {
|
||||
|
||||
private static final Logger stackLogger = LoggerFactory.getLogger("org.openhab.automation.script.javascript.stack");
|
||||
|
||||
public DebuggingGraalScriptEngine(T delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptException afterThrowsInvocation(ScriptException se) {
|
||||
Throwable cause = se.getCause();
|
||||
if (cause instanceof PolyglotException) {
|
||||
stackLogger.error("Failed to execute script:", cause);
|
||||
}
|
||||
return se;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.automation.jsscripting.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
|
||||
import org.openhab.core.automation.module.script.ScriptEngineFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
import com.oracle.truffle.js.scriptengine.GraalJSEngineFactory;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ScriptEngineFactory} with customizations for GraalJS ScriptEngines.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
@Component(service = ScriptEngineFactory.class)
|
||||
public final class GraalJSScriptEngineFactory implements ScriptEngineFactory {
|
||||
|
||||
@Override
|
||||
public List<String> getScriptTypes() {
|
||||
List<String> scriptTypes = new ArrayList<>();
|
||||
GraalJSEngineFactory graalJSEngineFactory = new GraalJSEngineFactory();
|
||||
|
||||
scriptTypes.addAll(graalJSEngineFactory.getMimeTypes());
|
||||
scriptTypes.addAll(graalJSEngineFactory.getExtensions());
|
||||
|
||||
return Collections.unmodifiableList(scriptTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
|
||||
// noop; the are retrieved via modules, not injected
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine createScriptEngine(String scriptType) {
|
||||
OpenhabGraalJSScriptEngine engine = new OpenhabGraalJSScriptEngine();
|
||||
return new DebuggingGraalScriptEngine<>(engine);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
/**
|
||||
* Locates modules from a module name
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public interface ModuleLocator {
|
||||
Optional<Value> locateModule(String name);
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal;
|
||||
|
||||
import static org.openhab.core.automation.module.script.ScriptEngineFactory.CONTEXT_KEY_ENGINE_IDENTIFIER;
|
||||
import static org.openhab.core.automation.module.script.ScriptEngineFactory.CONTEXT_KEY_EXTENSION_ACCESSOR;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.script.ScriptContext;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.openhab.automation.jsscripting.internal.fs.DelegatingFileSystem;
|
||||
import org.openhab.automation.jsscripting.internal.fs.PrefixedSeekableByteChannel;
|
||||
import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocable;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.automation.module.script.ScriptExtensionAccessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
|
||||
|
||||
/**
|
||||
* GraalJS Script Engine implementation
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public class OpenhabGraalJSScriptEngine extends InvocationInterceptingScriptEngineWithInvocable<GraalJSScriptEngine> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenhabGraalJSScriptEngine.class);
|
||||
|
||||
private static final String REQUIRE_WRAPPER_NAME = "__wraprequire__";
|
||||
private static final String MODULE_DIR = String.join(File.separator, OpenHAB.getConfigFolder(), "automation", "lib",
|
||||
"javascript", "personal");
|
||||
|
||||
// these fields start as null because they are populated on first use
|
||||
@NonNullByDefault({})
|
||||
private String engineIdentifier;
|
||||
@NonNullByDefault({})
|
||||
private Consumer<String> scriptDependencyListener;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Creates an implementation of ScriptEngine (& Invocable), wrapping the contained engine, that tracks the script
|
||||
* lifecycle and provides hooks for scripts to do so too.
|
||||
*/
|
||||
public OpenhabGraalJSScriptEngine() {
|
||||
super(null); // delegate depends on fields not yet initialised, so we cannot set it immediately
|
||||
delegate = GraalJSScriptEngine.create(null,
|
||||
Context.newBuilder("js").allowExperimentalOptions(true).allowAllAccess(true)
|
||||
.option("js.commonjs-require-cwd", MODULE_DIR).option("js.nashorn-compat", "true") // to ease
|
||||
// migration
|
||||
.option("js.commonjs-require", "true") // enable CommonJS module support
|
||||
.fileSystem(new DelegatingFileSystem(FileSystems.getDefault().provider()) {
|
||||
@Override
|
||||
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options,
|
||||
FileAttribute<?>... attrs) throws IOException {
|
||||
if (scriptDependencyListener != null) {
|
||||
scriptDependencyListener.accept(path.toString());
|
||||
}
|
||||
|
||||
if (path.toString().endsWith(".js")) {
|
||||
return new PrefixedSeekableByteChannel(
|
||||
("require=" + REQUIRE_WRAPPER_NAME + "(require);").getBytes(),
|
||||
super.newByteChannel(path, options, attrs));
|
||||
} else {
|
||||
return super.newByteChannel(path, options, attrs);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeInvocation() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptContext ctx = delegate.getContext();
|
||||
|
||||
// these are added post-construction, so we need to fetch them late
|
||||
this.engineIdentifier = (String) ctx.getAttribute(CONTEXT_KEY_ENGINE_IDENTIFIER);
|
||||
if (this.engineIdentifier == null) {
|
||||
throw new IllegalStateException("Failed to retrieve engine identifier from engine bindings");
|
||||
}
|
||||
|
||||
ScriptExtensionAccessor scriptExtensionAccessor = (ScriptExtensionAccessor) ctx
|
||||
.getAttribute(CONTEXT_KEY_EXTENSION_ACCESSOR);
|
||||
if (scriptExtensionAccessor == null) {
|
||||
throw new IllegalStateException("Failed to retrieve script extension accessor from engine bindings");
|
||||
}
|
||||
|
||||
scriptDependencyListener = (Consumer<String>) ctx
|
||||
.getAttribute("oh.dependency-listener"/* CONTEXT_KEY_DEPENDENCY_LISTENER */);
|
||||
if (scriptDependencyListener == null) {
|
||||
logger.warn(
|
||||
"Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.");
|
||||
}
|
||||
|
||||
ScriptExtensionModuleProvider scriptExtensionModuleProvider = new ScriptExtensionModuleProvider(
|
||||
scriptExtensionAccessor);
|
||||
|
||||
Function<Function<Object[], Object>, Function<String, Object>> wrapRequireFn = originalRequireFn -> moduleName -> scriptExtensionModuleProvider
|
||||
.locatorFor(delegate.getPolyglotContext(), engineIdentifier).locateModule(moduleName)
|
||||
.map(m -> (Object) m).orElseGet(() -> originalRequireFn.apply(new Object[] { moduleName }));
|
||||
|
||||
delegate.getBindings(ScriptContext.ENGINE_SCOPE).put(REQUIRE_WRAPPER_NAME, wrapRequireFn);
|
||||
delegate.put("require", wrapRequireFn.apply((Function<Object[], Object>) delegate.get("require")));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.automation.jsscripting.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Source;
|
||||
import org.graalvm.polyglot.Value;
|
||||
import org.openhab.automation.jsscripting.internal.threading.ThreadsafeWrappingScriptedAutomationManagerDelegate;
|
||||
import org.openhab.core.automation.module.script.ScriptExtensionAccessor;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager;
|
||||
|
||||
/**
|
||||
* Class providing script extensions via CommonJS modules.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class ScriptExtensionModuleProvider {
|
||||
|
||||
private static final String RUNTIME_MODULE_PREFIX = "@runtime";
|
||||
private static final String DEFAULT_MODULE_NAME = "Defaults";
|
||||
|
||||
private final ScriptExtensionAccessor scriptExtensionAccessor;
|
||||
|
||||
public ScriptExtensionModuleProvider(ScriptExtensionAccessor scriptExtensionAccessor) {
|
||||
this.scriptExtensionAccessor = scriptExtensionAccessor;
|
||||
}
|
||||
|
||||
public ModuleLocator locatorFor(Context ctx, String engineIdentifier) {
|
||||
return name -> {
|
||||
String[] segments = name.split("/");
|
||||
if (segments[0].equals(RUNTIME_MODULE_PREFIX)) {
|
||||
if (segments.length == 1) {
|
||||
return runtimeModule(DEFAULT_MODULE_NAME, engineIdentifier, ctx);
|
||||
} else {
|
||||
return runtimeModule(segments[1], engineIdentifier, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
};
|
||||
}
|
||||
|
||||
private Optional<Value> runtimeModule(String name, String scriptIdentifier, Context ctx) {
|
||||
|
||||
Map<String, Object> symbols;
|
||||
|
||||
if (DEFAULT_MODULE_NAME.equals(name)) {
|
||||
symbols = scriptExtensionAccessor.findDefaultPresets(scriptIdentifier);
|
||||
} else {
|
||||
symbols = scriptExtensionAccessor.findPreset(name, scriptIdentifier);
|
||||
}
|
||||
|
||||
return Optional.of(symbols).map(this::processValues).map(v -> toValue(ctx, v));
|
||||
}
|
||||
|
||||
private Value toValue(Context ctx, Map<String, Object> map) {
|
||||
try {
|
||||
return ctx.eval(Source.newBuilder( // convert to Map to JS Object
|
||||
"js",
|
||||
"(function (mapOfValues) {\n" + "let rv = {};\n" + "for (var key in mapOfValues) {\n"
|
||||
+ " rv[key] = mapOfValues.get(key);\n" + "}\n" + "return rv;\n" + "})",
|
||||
"<generated>").build()).execute(map);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("Failed to generate exports", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some specific objects need wrapping when exposed to a GraalJS environment. This method does this.
|
||||
*
|
||||
* @param values the map of names to values of things to process
|
||||
* @return a map of the processed keys and values
|
||||
*/
|
||||
private Map<String, Object> processValues(Map<String, Object> values) {
|
||||
Map<String, Object> rv = new HashMap<>(values);
|
||||
|
||||
for (Map.Entry<String, Object> entry : rv.entrySet()) {
|
||||
if (entry.getValue() instanceof ScriptedAutomationManager) {
|
||||
entry.setValue(new ThreadsafeWrappingScriptedAutomationManagerDelegate(
|
||||
(ScriptedAutomationManager) entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.AccessMode;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.graalvm.polyglot.io.FileSystem;
|
||||
|
||||
/**
|
||||
* Delegate wrapping a {@link FileSystem}
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public class DelegatingFileSystem implements FileSystem {
|
||||
private FileSystemProvider delegate;
|
||||
|
||||
public DelegatingFileSystem(FileSystemProvider delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path parsePath(URI uri) {
|
||||
return Paths.get(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path parsePath(String path) {
|
||||
return Paths.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Path path, Set<? extends AccessMode> modes, LinkOption... linkOptions) throws IOException {
|
||||
delegate.checkAccess(path, modes.toArray(new AccessMode[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
delegate.createDirectory(dir, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Path path) throws IOException {
|
||||
delegate.delete(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs)
|
||||
throws IOException {
|
||||
return delegate.newByteChannel(path, options, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
|
||||
throws IOException {
|
||||
return delegate.newDirectoryStream(dir, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path toAbsolutePath(Path path) {
|
||||
return path.toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path toRealPath(Path path, LinkOption... linkOptions) throws IOException {
|
||||
return path.toRealPath(linkOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
|
||||
return delegate.readAttributes(path, attributes, options);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Wrapper for a {@link SeekableByteChannel} allowing prefixing the stream with a fixed array of bytes
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public class PrefixedSeekableByteChannel implements SeekableByteChannel {
|
||||
|
||||
private byte[] prefix;
|
||||
private SeekableByteChannel source;
|
||||
private long position;
|
||||
|
||||
public PrefixedSeekableByteChannel(byte[] prefix, SeekableByteChannel source) {
|
||||
this.prefix = prefix;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
|
||||
int read = 0;
|
||||
|
||||
if (position < prefix.length) {
|
||||
dst.put(Arrays.copyOfRange(prefix, (int) position, prefix.length));
|
||||
read += prefix.length - position;
|
||||
}
|
||||
|
||||
read += source.read(dst);
|
||||
|
||||
position += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
throw new IOException("Read only!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() throws IOException {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position(long newPosition) throws IOException {
|
||||
|
||||
this.position = newPosition;
|
||||
|
||||
if (newPosition > prefix.length) {
|
||||
source.position(newPosition - prefix.length);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return source.size() + prefix.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate(long size) throws IOException {
|
||||
throw new IOException("Read only!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return source.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
source.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal.scriptengine;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
/**
|
||||
* {@link ScriptEngine} implementation that delegates to a supplied ScriptEngine instance. Allows overriding specific
|
||||
* methods.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
public abstract class DelegatingScriptEngineWithInvocable<T extends ScriptEngine & Invocable>
|
||||
implements ScriptEngine, Invocable {
|
||||
protected T delegate;
|
||||
|
||||
public DelegatingScriptEngineWithInvocable(T delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
|
||||
return delegate.eval(s, scriptContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
|
||||
return delegate.eval(reader, scriptContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s) throws ScriptException {
|
||||
return delegate.eval(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader) throws ScriptException {
|
||||
return delegate.eval(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s, Bindings bindings) throws ScriptException {
|
||||
return delegate.eval(s, bindings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
|
||||
return delegate.eval(reader, bindings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String s, Object o) {
|
||||
delegate.put(s, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String s) {
|
||||
return delegate.get(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bindings getBindings(int i) {
|
||||
return delegate.getBindings(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBindings(Bindings bindings, int i) {
|
||||
delegate.setBindings(bindings, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bindings createBindings() {
|
||||
return delegate.createBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptContext getContext() {
|
||||
return delegate.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(ScriptContext scriptContext) {
|
||||
delegate.setContext(scriptContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngineFactory getFactory() {
|
||||
return delegate.getFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeMethod(Object o, String s, Object... objects) throws ScriptException, NoSuchMethodException {
|
||||
return delegate.invokeMethod(o, s, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeFunction(String s, Object... objects) throws ScriptException, NoSuchMethodException {
|
||||
return delegate.invokeFunction(s, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInterface(Class<T> aClass) {
|
||||
return delegate.getInterface(aClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInterface(Object o, Class<T> aClass) {
|
||||
return delegate.getInterface(o, aClass);
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal.scriptengine;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Delegate allowing AOP-style interception of calls, either before Invocation, or upon a {@link ScriptException}.
|
||||
* being thrown.
|
||||
*
|
||||
* @param <T> The delegate class
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class InvocationInterceptingScriptEngineWithInvocable<T extends ScriptEngine & Invocable>
|
||||
extends DelegatingScriptEngineWithInvocable<T> {
|
||||
|
||||
public InvocationInterceptingScriptEngineWithInvocable(T delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
protected void beforeInvocation() {
|
||||
}
|
||||
|
||||
protected ScriptException afterThrowsInvocation(ScriptException se) {
|
||||
return se;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(s, scriptContext);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(reader, scriptContext);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(s);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(reader);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String s, Bindings bindings) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(s, bindings);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader, Bindings bindings) throws ScriptException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.eval(reader, bindings);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeMethod(Object o, String s, Object... objects) throws ScriptException, NoSuchMethodException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.invokeMethod(o, s, objects);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeFunction(String s, Object... objects) throws ScriptException, NoSuchMethodException {
|
||||
try {
|
||||
beforeInvocation();
|
||||
return super.invokeFunction(s, objects);
|
||||
} catch (ScriptException se) {
|
||||
throw afterThrowsInvocation(se);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.automation.jsscripting.internal.threading;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.automation.Action;
|
||||
import org.openhab.core.automation.Condition;
|
||||
import org.openhab.core.automation.Module;
|
||||
import org.openhab.core.automation.Rule;
|
||||
import org.openhab.core.automation.Trigger;
|
||||
import org.openhab.core.automation.Visibility;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRuleActionHandler;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
|
||||
/**
|
||||
* An version of {@link SimpleRule} which controls multithreaded execution access to this specific rule. This is useful
|
||||
* for rules which wrap GraalJS Contexts, which are not multithreaded.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class ThreadsafeSimpleRuleDelegate implements Rule, SimpleRuleActionHandler {
|
||||
|
||||
private final Object lock;
|
||||
private final SimpleRule delegate;
|
||||
|
||||
/**
|
||||
* Constructor requires a lock object and delegate to forward invocations to.
|
||||
*
|
||||
* @param lock rule executions will synchronize on this object
|
||||
* @param delegate the delegate to forward invocations to
|
||||
*/
|
||||
ThreadsafeSimpleRuleDelegate(Object lock, SimpleRule delegate) {
|
||||
this.lock = lock;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNullByDefault({})
|
||||
public Object execute(Action module, Map<String, ?> inputs) {
|
||||
synchronized (lock) {
|
||||
return delegate.execute(module, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUID() {
|
||||
return delegate.getUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getTemplateUID() {
|
||||
return delegate.getTemplateUID();
|
||||
}
|
||||
|
||||
public void setTemplateUID(@Nullable String templateUID) {
|
||||
delegate.setTemplateUID(templateUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return delegate.getName();
|
||||
}
|
||||
|
||||
public void setName(@Nullable String ruleName) {
|
||||
delegate.setName(ruleName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getTags() {
|
||||
return delegate.getTags();
|
||||
}
|
||||
|
||||
public void setTags(@Nullable Set<String> ruleTags) {
|
||||
delegate.setTags(ruleTags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return delegate.getDescription();
|
||||
}
|
||||
|
||||
public void setDescription(@Nullable String ruleDescription) {
|
||||
delegate.setDescription(ruleDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Visibility getVisibility() {
|
||||
return delegate.getVisibility();
|
||||
}
|
||||
|
||||
public void setVisibility(@Nullable Visibility visibility) {
|
||||
delegate.setVisibility(visibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConfiguration() {
|
||||
return delegate.getConfiguration();
|
||||
}
|
||||
|
||||
public void setConfiguration(@Nullable Configuration ruleConfiguration) {
|
||||
delegate.setConfiguration(ruleConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigDescriptionParameter> getConfigurationDescriptions() {
|
||||
return delegate.getConfigurationDescriptions();
|
||||
}
|
||||
|
||||
public void setConfigurationDescriptions(@Nullable List<ConfigDescriptionParameter> configDescriptions) {
|
||||
delegate.setConfigurationDescriptions(configDescriptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Condition> getConditions() {
|
||||
return delegate.getConditions();
|
||||
}
|
||||
|
||||
public void setConditions(@Nullable List<Condition> conditions) {
|
||||
delegate.setConditions(conditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getActions() {
|
||||
return delegate.getActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Trigger> getTriggers() {
|
||||
return delegate.getTriggers();
|
||||
}
|
||||
|
||||
public void setActions(@Nullable List<Action> actions) {
|
||||
delegate.setActions(actions);
|
||||
}
|
||||
|
||||
public void setTriggers(@Nullable List<Trigger> triggers) {
|
||||
delegate.setTriggers(triggers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Module> getModules() {
|
||||
return delegate.getModules();
|
||||
}
|
||||
|
||||
public <T extends Module> List<T> getModules(@Nullable Class<T> moduleClazz) {
|
||||
return delegate.getModules(moduleClazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return delegate.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Module getModule(String moduleId) {
|
||||
return delegate.getModule(moduleId);
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
|
||||
package org.openhab.automation.jsscripting.internal.threading;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.automation.Rule;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedAutomationManager;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.ScriptedHandler;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleActionHandler;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleConditionHandler;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule;
|
||||
import org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleTriggerHandler;
|
||||
import org.openhab.core.automation.type.ActionType;
|
||||
import org.openhab.core.automation.type.ConditionType;
|
||||
import org.openhab.core.automation.type.TriggerType;
|
||||
|
||||
/**
|
||||
* A replacement for {@link ScriptedAutomationManager} which wraps all rule registrations in a
|
||||
* {@link ThreadsafeSimpleRuleDelegate}. This means that all rules registered via this class with be run in serial per
|
||||
* instance of this class that they are registered with.
|
||||
*
|
||||
* @author Jonathan Gilbert - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ThreadsafeWrappingScriptedAutomationManagerDelegate {
|
||||
|
||||
private ScriptedAutomationManager delegate;
|
||||
private Object lock = new Object();
|
||||
|
||||
public ThreadsafeWrappingScriptedAutomationManagerDelegate(ScriptedAutomationManager delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void removeModuleType(String UID) {
|
||||
delegate.removeModuleType(UID);
|
||||
}
|
||||
|
||||
public void removeHandler(String typeUID) {
|
||||
delegate.removeHandler(typeUID);
|
||||
}
|
||||
|
||||
public void removePrivateHandler(String privId) {
|
||||
delegate.removePrivateHandler(privId);
|
||||
}
|
||||
|
||||
public void removeAll() {
|
||||
delegate.removeAll();
|
||||
}
|
||||
|
||||
public Rule addRule(Rule element) {
|
||||
// wrap in a threadsafe version, safe per context
|
||||
if (element instanceof SimpleRule) {
|
||||
element = new ThreadsafeSimpleRuleDelegate(lock, (SimpleRule) element);
|
||||
}
|
||||
|
||||
return delegate.addRule(element);
|
||||
}
|
||||
|
||||
public void addConditionType(ConditionType condititonType) {
|
||||
delegate.addConditionType(condititonType);
|
||||
}
|
||||
|
||||
public void addConditionHandler(String uid, ScriptedHandler conditionHandler) {
|
||||
delegate.addConditionHandler(uid, conditionHandler);
|
||||
}
|
||||
|
||||
public String addPrivateConditionHandler(SimpleConditionHandler conditionHandler) {
|
||||
return delegate.addPrivateConditionHandler(conditionHandler);
|
||||
}
|
||||
|
||||
public void addActionType(ActionType actionType) {
|
||||
delegate.addActionType(actionType);
|
||||
}
|
||||
|
||||
public void addActionHandler(String uid, ScriptedHandler actionHandler) {
|
||||
delegate.addActionHandler(uid, actionHandler);
|
||||
}
|
||||
|
||||
public String addPrivateActionHandler(SimpleActionHandler actionHandler) {
|
||||
return delegate.addPrivateActionHandler(actionHandler);
|
||||
}
|
||||
|
||||
public void addTriggerType(TriggerType triggerType) {
|
||||
delegate.addTriggerType(triggerType);
|
||||
}
|
||||
|
||||
public void addTriggerHandler(String uid, ScriptedHandler triggerHandler) {
|
||||
delegate.addTriggerHandler(uid, triggerHandler);
|
||||
}
|
||||
|
||||
public String addPrivateTriggerHandler(SimpleTriggerHandler triggerHandler) {
|
||||
return delegate.addPrivateTriggerHandler(triggerHandler);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
com.oracle.truffle.regex.RegexLanguageProvider
|
||||
com.oracle.truffle.js.lang.JavaScriptLanguageProvider
|
@ -21,6 +21,7 @@
|
||||
<module>org.openhab.automation.groovyscripting</module>
|
||||
<module>org.openhab.automation.jythonscripting</module>
|
||||
<module>org.openhab.automation.pidcontroller</module>
|
||||
<module>org.openhab.automation.jsscripting</module>
|
||||
<!-- io -->
|
||||
<module>org.openhab.io.homekit</module>
|
||||
<module>org.openhab.io.hueemulation</module>
|
||||
|
Loading…
Reference in New Issue
Block a user