]> git.basschouten.com Git - openhab-addons.git/commitdiff
[jsscripting] Implement `javax.script.Compilable` (#16970)
authorFlorian Hotze <florianh_dev@icloud.com>
Tue, 9 Jul 2024 18:08:30 +0000 (20:08 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Jul 2024 18:08:30 +0000 (20:08 +0200)
* [jsscripting] Restructure & Comment POM
* [jsscripting] Use OPENHAB_TRANSFORMATION_SCRIPT constant from core

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
bundles/org.openhab.automation.jsscripting/pom.xml
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/DebuggingGraalScriptEngine.java
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/OpenhabGraalJSScriptEngine.java
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndAutocloseable.java [deleted file]
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable.java [new file with mode: 0644]
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java [deleted file]
bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable.java [new file with mode: 0644]
bundles/org.openhab.automation.jsscripting/suppressions.properties

index 824527ded031c0e5687edc82f1e37ecd5548999a..9041a6a8c5b845396bda01a91224656be2fe7e11 100644 (file)
@@ -29,6 +29,7 @@
 
   <build>
     <plugins>
+      <!-- exclude META-INF/services/com.oracle.truffle.api.TruffleLanguage$Provider when unpacking dependencies -->
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-dependency-plugin</artifactId>
@@ -44,6 +45,7 @@
           </execution>
         </executions>
       </plugin>
+      <!-- bundle the openhab-js library -->
       <plugin>
         <groupId>com.github.eirslett</groupId>
         <artifactId>frontend-maven-plugin</artifactId>
           </execution>
         </executions>
       </plugin>
+      <!-- run SAT -->
       <plugin>
         <groupId>org.openhab.tools.sat</groupId>
         <artifactId>sat-plugin</artifactId>
   </build>
 
   <dependencies>
+    <dependency>
+      <groupId>org.graalvm.sdk</groupId>
+      <artifactId>graal-sdk</artifactId>
+      <version>${graal.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.graalvm.truffle</groupId>
       <artifactId>truffle-api</artifactId>
       <version>${graal.version}</version>
     </dependency>
+    <!-- Graal JavaScript ScriptEngine JSR 223 support -->
     <dependency>
       <groupId>org.graalvm.js</groupId>
       <artifactId>js-scriptengine</artifactId>
       <version>${graal.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.graalvm.sdk</groupId>
-      <artifactId>graal-sdk</artifactId>
-      <version>${graal.version}</version>
-    </dependency>
+    <!-- Graal TRegex engine (internally used by Graal JavaScript engine) -->
     <dependency>
       <groupId>org.graalvm.regex</groupId>
       <artifactId>regex</artifactId>
       <version>${graal.version}</version>
     </dependency>
-    <dependency> <!-- this must come AFTER the regex lib -->
+    <!-- Graal JavaScript engine (depends on Graal TRegex engine, must be added after it) -->
+    <dependency>
       <groupId>org.graalvm.js</groupId>
       <artifactId>js</artifactId>
       <version>${graal.version}</version>
     </dependency>
-    <!-- GraalJS changelog says that com.ibm.icu/icu4j is not required for GraalJS >= 22.0.0 as it moved to org.graalvm.truffle;
-      but GraalJS >= 22.2.0 requires it, so we'll need to add it when we upgrade -->
   </dependencies>
 </project>
index 508e9cf26aa7f6b6c489e1d338aa09f584cc97dd..76f1afb2dd54483c33b893d4589cdf4fb2783744 100644 (file)
  */
 package org.openhab.automation.jsscripting.internal;
 
+import static org.openhab.core.automation.module.script.ScriptTransformationService.OPENHAB_TRANSFORMATION_SCRIPT;
+
 import java.util.Arrays;
 import java.util.stream.Collectors;
 
+import javax.script.Compilable;
 import javax.script.Invocable;
 import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.graalvm.polyglot.PolyglotException;
-import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable;
+import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,10 +34,9 @@ import org.slf4j.LoggerFactory;
  * @author Jonathan Gilbert - Initial contribution
  * @author Florian Hotze - Improve logger name, Fix memory leak caused by exception logging
  */
-class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable & AutoCloseable>
-        extends InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<T> {
+class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable & AutoCloseable & Compilable>
+        extends InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable<T> {
 
-    private static final String SCRIPT_TRANSFORMATION_ENGINE_IDENTIFIER = "openhab-transformation-script-";
     private static final int STACK_TRACE_LENGTH = 5;
 
     private @Nullable Logger logger;
@@ -91,9 +93,8 @@ class DebuggingGraalScriptEngine<T extends ScriptEngine & Invocable & AutoClosea
         } else if (ruleUID != null) {
             identifier = ruleUID.toString();
         } else if (ohEngineIdentifier != null) {
-            if (ohEngineIdentifier.toString().startsWith(SCRIPT_TRANSFORMATION_ENGINE_IDENTIFIER)) {
-                identifier = ohEngineIdentifier.toString().replaceAll(SCRIPT_TRANSFORMATION_ENGINE_IDENTIFIER,
-                        "transformation.");
+            if (ohEngineIdentifier.toString().startsWith(OPENHAB_TRANSFORMATION_SCRIPT)) {
+                identifier = ohEngineIdentifier.toString().replaceAll(OPENHAB_TRANSFORMATION_SCRIPT, "transformation.");
             }
         }
 
index e4d324a8a72c5aa261db0b7dfa218b21a597ae55..cb4dcfd23b58b6a3f7ea1290fc201bd752f66b44 100644 (file)
@@ -50,7 +50,7 @@ import org.openhab.automation.jsscripting.internal.fs.DelegatingFileSystem;
 import org.openhab.automation.jsscripting.internal.fs.PrefixedSeekableByteChannel;
 import org.openhab.automation.jsscripting.internal.fs.ReadOnlySeekableByteArrayChannel;
 import org.openhab.automation.jsscripting.internal.fs.watch.JSDependencyTracker;
-import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable;
+import org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable;
 import org.openhab.core.automation.module.script.ScriptExtensionAccessor;
 import org.openhab.core.items.Item;
 import org.openhab.core.library.types.QuantityType;
@@ -69,7 +69,7 @@ import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
  *         {@link Lock} for multi-thread synchronization; globals and openhab-js injection code caching
  */
 public class OpenhabGraalJSScriptEngine
-        extends InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<GraalJSScriptEngine> {
+        extends InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable<GraalJSScriptEngine> {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(OpenhabGraalJSScriptEngine.class);
     private static final Source GLOBAL_SOURCE;
diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndAutocloseable.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndAutocloseable.java
deleted file mode 100644 (file)
index 7e00c6a..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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;
-
-import org.eclipse.jdt.annotation.NonNull;
-
-/**
- * {@link ScriptEngine} implementation that delegates to a supplied ScriptEngine instance. Allows overriding specific
- * methods.
- *
- * @author Jonathan Gilbert - Initial contribution
- */
-public abstract class DelegatingScriptEngineWithInvocableAndAutocloseable<T extends ScriptEngine & Invocable & AutoCloseable>
-        implements ScriptEngine, Invocable, AutoCloseable {
-    protected @NonNull T delegate;
-
-    public DelegatingScriptEngineWithInvocableAndAutocloseable(@NonNull 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, IllegalArgumentException {
-        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);
-    }
-
-    @Override
-    public void close() throws Exception {
-        delegate.close();
-    }
-}
diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable.java
new file mode 100644 (file)
index 0000000..79a93b9
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2010-2024 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.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * {@link ScriptEngine} implementation that delegates to a supplied ScriptEngine instance. Allows overriding specific
+ * methods.
+ *
+ * @author Jonathan Gilbert - Initial contribution
+ */
+public abstract class DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable<T extends ScriptEngine & Invocable & Compilable & AutoCloseable>
+        implements ScriptEngine, Invocable, Compilable, AutoCloseable {
+    protected @NonNull T delegate;
+
+    public DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable(@NonNull 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, IllegalArgumentException {
+        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);
+    }
+
+    @Override
+    public CompiledScript compile(String s) throws ScriptException {
+        return delegate.compile(s);
+    }
+
+    @Override
+    public CompiledScript compile(Reader reader) throws ScriptException {
+        return delegate.compile(reader);
+    }
+
+    @Override
+    public void close() throws Exception {
+        delegate.close();
+    }
+}
diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java
deleted file mode 100644 (file)
index 71501f3..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/**
- * Copyright (c) 2010-2024 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 java.lang.reflect.UndeclaredThrowableException;
-
-import javax.script.Bindings;
-import javax.script.Invocable;
-import javax.script.ScriptContext;
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-
-/**
- * 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
- */
-public abstract class InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable<T extends ScriptEngine & Invocable & AutoCloseable>
-        extends DelegatingScriptEngineWithInvocableAndAutocloseable<T> {
-
-    public InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable(T delegate) {
-        super(delegate);
-    }
-
-    protected void beforeInvocation() {
-    }
-
-    protected Object afterInvocation(Object obj) {
-        return obj;
-    }
-
-    protected Exception afterThrowsInvocation(Exception e) {
-        return e;
-    }
-
-    @Override
-    public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(s, scriptContext));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(reader, scriptContext));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object eval(String s) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(s));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object eval(Reader reader) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(reader));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object eval(String s, Bindings bindings) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(s, bindings));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object eval(Reader reader, Bindings bindings) throws ScriptException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.eval(reader, bindings));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object invokeMethod(Object o, String s, Object... objects)
-            throws ScriptException, NoSuchMethodException, NullPointerException, IllegalArgumentException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.invokeMethod(o, s, objects));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeMethod to avoid
-                                            // deadlocks
-            throw (NoSuchMethodException) afterThrowsInvocation(e);
-        } catch (NullPointerException e) {
-            throw (NullPointerException) afterThrowsInvocation(e);
-        } catch (IllegalArgumentException e) {
-            throw (IllegalArgumentException) afterThrowsInvocation(e);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-
-    @Override
-    public Object invokeFunction(String s, Object... objects)
-            throws ScriptException, NoSuchMethodException, NullPointerException {
-        try {
-            beforeInvocation();
-            return afterInvocation(super.invokeFunction(s, objects));
-        } catch (ScriptException se) {
-            throw (ScriptException) afterThrowsInvocation(se);
-        } catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeFunction to avoid
-                                            // deadlocks
-            throw (NoSuchMethodException) afterThrowsInvocation(e);
-        } catch (NullPointerException e) {
-            throw (NullPointerException) afterThrowsInvocation(e);
-        } catch (Exception e) {
-            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
-        }
-    }
-}
diff --git a/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable.java b/bundles/org.openhab.automation.jsscripting/src/main/java/org/openhab/automation/jsscripting/internal/scriptengine/InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable.java
new file mode 100644 (file)
index 0000000..d23a5fc
--- /dev/null
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2010-2024 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 java.lang.reflect.UndeclaredThrowableException;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+/**
+ * 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
+ */
+public abstract class InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable<T extends ScriptEngine & Invocable & Compilable & AutoCloseable>
+        extends DelegatingScriptEngineWithInvocableAndCompilableAndAutocloseable<T> {
+
+    public InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable(T delegate) {
+        super(delegate);
+    }
+
+    protected void beforeInvocation() {
+    }
+
+    protected Object afterInvocation(Object obj) {
+        return obj;
+    }
+
+    protected Exception afterThrowsInvocation(Exception e) {
+        return e;
+    }
+
+    @Override
+    public Object eval(String s, ScriptContext scriptContext) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(s, scriptContext));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(reader, scriptContext));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object eval(String s) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(s));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object eval(Reader reader) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(reader));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object eval(String s, Bindings bindings) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(s, bindings));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object eval(Reader reader, Bindings bindings) throws ScriptException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.eval(reader, bindings));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object invokeMethod(Object o, String s, Object... objects)
+            throws ScriptException, NoSuchMethodException, NullPointerException, IllegalArgumentException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.invokeMethod(o, s, objects));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeMethod to avoid
+                                            // deadlocks
+            throw (NoSuchMethodException) afterThrowsInvocation(e);
+        } catch (NullPointerException e) {
+            throw (NullPointerException) afterThrowsInvocation(e);
+        } catch (IllegalArgumentException e) {
+            throw (IllegalArgumentException) afterThrowsInvocation(e);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public Object invokeFunction(String s, Object... objects)
+            throws ScriptException, NoSuchMethodException, NullPointerException {
+        try {
+            beforeInvocation();
+            return afterInvocation(super.invokeFunction(s, objects));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (NoSuchMethodException e) { // Make sure to unlock on exceptions from Invocable.invokeFunction to avoid
+                                            // deadlocks
+            throw (NoSuchMethodException) afterThrowsInvocation(e);
+        } catch (NullPointerException e) {
+            throw (NullPointerException) afterThrowsInvocation(e);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public CompiledScript compile(String s) throws ScriptException {
+        try {
+            beforeInvocation();
+            return (CompiledScript) afterInvocation(super.compile(s));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+
+    @Override
+    public CompiledScript compile(Reader reader) throws ScriptException {
+        try {
+            beforeInvocation();
+            return (CompiledScript) afterInvocation(super.compile(reader));
+        } catch (ScriptException se) {
+            throw (ScriptException) afterThrowsInvocation(se);
+        } catch (Exception e) {
+            throw new UndeclaredThrowableException(afterThrowsInvocation(e)); // Wrap and rethrow other exceptions
+        }
+    }
+}
index 38e9aa4cf1896c82f999cde36408fdb3ae94b06f..11aad04ecbe6b46c4976e209aae81dfb98e6928c 100644 (file)
@@ -1,3 +1,3 @@
 # Please check here how to add suppressions https://maven.apache.org/plugins/maven-pmd-plugin/examples/violation-exclusions.html
 org.openhab.automation.jsscripting.internal.OpenhabGraalJSScriptEngine=UnusedPrivateField
-org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable=AvoidThrowingNullPointerException,AvoidCatchingNPE
+org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndCompilableAndAutoCloseable=AvoidThrowingNullPointerException,AvoidCatchingNPE