]> git.basschouten.com Git - openhab-addons.git/commitdiff
[groovyscripting] Fix default preset scope not applied (#17383)
authorWouter Born <github@maindrain.net>
Sun, 8 Sep 2024 12:55:31 +0000 (14:55 +0200)
committerGitHub <noreply@github.com>
Sun, 8 Sep 2024 12:55:31 +0000 (14:55 +0200)
This allows for removing many imports from scripts which results in less code.

Fixes #17247

Signed-off-by: Wouter Born <github@maindrain.net>
bundles/org.openhab.automation.groovyscripting/src/main/java/org/openhab/automation/groovyscripting/internal/CustomizableGroovyClassLoader.java [new file with mode: 0644]
bundles/org.openhab.automation.groovyscripting/src/main/java/org/openhab/automation/groovyscripting/internal/GroovyScriptEngineFactory.java
itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/AbstractGroovyScriptingOSGiTest.java [new file with mode: 0644]
itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/ScriptScopeOSGiTest.java [new file with mode: 0644]
itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/SlurperOSGiTest.java
itests/org.openhab.automation.groovyscripting.tests/src/main/resources/OH-INF/automation/jsr223/scope-working.groovy [new file with mode: 0644]

diff --git a/bundles/org.openhab.automation.groovyscripting/src/main/java/org/openhab/automation/groovyscripting/internal/CustomizableGroovyClassLoader.java b/bundles/org.openhab.automation.groovyscripting/src/main/java/org/openhab/automation/groovyscripting/internal/CustomizableGroovyClassLoader.java
new file mode 100644 (file)
index 0000000..0314f33
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * 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.groovyscripting.internal;
+
+import java.io.File;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.CompilationCustomizer;
+import org.openhab.core.OpenHAB;
+
+import groovy.lang.GroovyClassLoader;
+
+/**
+ * Customizes the {@link GroovyClassLoader} so that {@link CompilationCustomizer}s can be added which allows for
+ * importing additional classes via scopes.
+ *
+ * @author Wouter Born - Initial contribution
+ */
+public class CustomizableGroovyClassLoader extends GroovyClassLoader {
+
+    private static final String FILE_DIRECTORY = "automation" + File.separator + "groovy";
+
+    private CompilerConfiguration config;
+
+    public CustomizableGroovyClassLoader() {
+        this(CustomizableGroovyClassLoader.class.getClassLoader(), new CompilerConfiguration(), true);
+    }
+
+    public CustomizableGroovyClassLoader(ClassLoader parent, CompilerConfiguration config,
+            boolean useConfigurationClasspath) {
+        super(parent, config, useConfigurationClasspath);
+        this.config = config;
+        addClasspath(OpenHAB.getConfigFolder() + File.separator + FILE_DIRECTORY);
+    }
+
+    public void addCompilationCustomizers(CompilationCustomizer... customizers) {
+        config.addCompilationCustomizers(customizers);
+    }
+}
index f192ed0106c21a1cbdb01ec2807b7ce5beb9b011..d2dac88609dd4380bd34488e0f8f02c0c0d1a3c8 100644 (file)
  */
 package org.openhab.automation.groovyscripting.internal;
 
-import java.io.File;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Map;
 import java.util.stream.Stream;
 
 import javax.script.ScriptEngine;
 
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
+import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.OpenHAB;
 import org.openhab.core.automation.module.script.AbstractScriptEngineFactory;
 import org.openhab.core.automation.module.script.ScriptEngineFactory;
 import org.osgi.service.component.annotations.Component;
 
-import groovy.lang.GroovyClassLoader;
-
 /**
  * This is an implementation of a {@link ScriptEngineFactory} for Groovy.
  *
@@ -37,20 +35,11 @@ import groovy.lang.GroovyClassLoader;
 @NonNullByDefault
 public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory {
 
-    private static final String FILE_DIRECTORY = "automation" + File.separator + "groovy";
     private final org.codehaus.groovy.jsr223.GroovyScriptEngineFactory factory = new org.codehaus.groovy.jsr223.GroovyScriptEngineFactory();
 
-    private final List<String> scriptTypes = (List<String>) Stream.of(factory.getExtensions(), factory.getMimeTypes())
+    private final List<String> scriptTypes = Stream.of(factory.getExtensions(), factory.getMimeTypes())
             .flatMap(List::stream) //
-            .collect(Collectors.toUnmodifiableList());
-
-    private final GroovyClassLoader gcl = new GroovyClassLoader(GroovyScriptEngineFactory.class.getClassLoader());
-
-    public GroovyScriptEngineFactory() {
-        String scriptDir = OpenHAB.getConfigFolder() + File.separator + FILE_DIRECTORY;
-        logger.debug("Adding script directory {} to the GroovyScriptEngine class path.", scriptDir);
-        gcl.addClasspath(scriptDir);
-    }
+            .toList();
 
     @Override
     public List<String> getScriptTypes() {
@@ -58,10 +47,24 @@ public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory {
     }
 
     @Override
-    public @Nullable ScriptEngine createScriptEngine(String scriptType) {
-        if (scriptTypes.contains(scriptType)) {
-            return new org.codehaus.groovy.jsr223.GroovyScriptEngineImpl(gcl);
+    public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
+        ImportCustomizer importCustomizer = new ImportCustomizer();
+        for (Map.Entry<String, Object> entry : scopeValues.entrySet()) {
+            if (entry.getValue() instanceof Class<?> clazz) {
+                importCustomizer.addImport(entry.getKey(), clazz.getCanonicalName());
+            } else {
+                scriptEngine.put(entry.getKey(), entry.getValue());
+            }
         }
-        return null;
+
+        GroovyScriptEngineImpl gse = (GroovyScriptEngineImpl) scriptEngine;
+        CustomizableGroovyClassLoader cl = (CustomizableGroovyClassLoader) gse.getClassLoader();
+        cl.addCompilationCustomizers(importCustomizer);
+    }
+
+    @Override
+    public @Nullable ScriptEngine createScriptEngine(String scriptType) {
+        return scriptTypes.contains(scriptType) ? new GroovyScriptEngineImpl(new CustomizableGroovyClassLoader())
+                : null;
     }
 }
diff --git a/itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/AbstractGroovyScriptingOSGiTest.java b/itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/AbstractGroovyScriptingOSGiTest.java
new file mode 100644 (file)
index 0000000..e9a0d64
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * 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.groovyscripting;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Objects;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.openhab.core.automation.module.script.ScriptEngineContainer;
+import org.openhab.core.automation.module.script.ScriptEngineManager;
+import org.openhab.core.test.java.JavaOSGiTest;
+
+/**
+ * Provides helper methods that can be reused for testing Groovy scripts.
+ *
+ * @author Wouter Born - Initial contribution
+ */
+@NonNullByDefault
+public abstract class AbstractGroovyScriptingOSGiTest extends JavaOSGiTest {
+
+    protected @NonNullByDefault({}) ScriptEngine engine;
+
+    private final String path = "OH-INF/automation/jsr223/";
+
+    @BeforeEach
+    public void init() {
+        ScriptEngineManager scriptManager = Objects.requireNonNull(getService(ScriptEngineManager.class),
+                "Could not get ScriptEngineManager");
+        ScriptEngineContainer container = Objects.requireNonNull(
+                scriptManager.createScriptEngine("groovy", "testGroovyEngine"), "Could not create Groovy ScriptEngine");
+        engine = container.getScriptEngine();
+    }
+
+    protected void evalScript(String fileName) throws ScriptException, IOException {
+        URL url = bundleContext.getBundle().getResource(path + fileName);
+        engine.eval(new InputStreamReader(url.openStream()));
+    }
+}
diff --git a/itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/ScriptScopeOSGiTest.java b/itests/org.openhab.automation.groovyscripting.tests/src/main/java/org/openhab/automation/groovyscripting/ScriptScopeOSGiTest.java
new file mode 100644 (file)
index 0000000..6b2da61
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * 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.groovyscripting;
+
+import java.io.IOException;
+
+import javax.script.ScriptException;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This tests the script modules using the Groovy scripting engine.
+ *
+ * @author Wouter Born - Initial contribution
+ */
+public class ScriptScopeOSGiTest extends AbstractGroovyScriptingOSGiTest {
+
+    @Test
+    public void scopeWorking() throws ScriptException, IOException {
+        evalScript("scope-working.groovy");
+    }
+}
index c12e6db610a54ee15322430fd563bacdfcf79844..430268d94e3c19583e97b2fc07c12eee64438f47 100644 (file)
 package org.openhab.automation.groovyscripting;
 
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
 
-import javax.script.ScriptEngine;
 import javax.script.ScriptException;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.openhab.core.automation.module.script.ScriptEngineContainer;
-import org.openhab.core.automation.module.script.ScriptEngineManager;
-import org.openhab.core.test.java.JavaOSGiTest;
 
 /**
  * This tests the JSON, XML and YAML slurpers using the Groovy scripting engine.
@@ -32,23 +25,7 @@ import org.openhab.core.test.java.JavaOSGiTest;
  * @author Wouter Born - Initial contribution
  */
 @NonNullByDefault
-public class SlurperOSGiTest extends JavaOSGiTest {
-
-    private @NonNullByDefault({}) ScriptEngine engine;
-
-    private final String path = "OH-INF/automation/jsr223/";
-
-    @BeforeEach
-    public void init() {
-        ScriptEngineManager scriptManager = getService(ScriptEngineManager.class);
-        ScriptEngineContainer container = scriptManager.createScriptEngine("groovy", "myGroovyEngine");
-        engine = container.getScriptEngine();
-    }
-
-    private void evalScript(String fileName) throws ScriptException, IOException {
-        URL url = bundleContext.getBundle().getResource(path + fileName);
-        engine.eval(new InputStreamReader(url.openStream()));
-    }
+public class SlurperOSGiTest extends AbstractGroovyScriptingOSGiTest {
 
     @Test
     public void jsonSlurper() throws ScriptException, IOException {
diff --git a/itests/org.openhab.automation.groovyscripting.tests/src/main/resources/OH-INF/automation/jsr223/scope-working.groovy b/itests/org.openhab.automation.groovyscripting.tests/src/main/resources/OH-INF/automation/jsr223/scope-working.groovy
new file mode 100644 (file)
index 0000000..508cb06
--- /dev/null
@@ -0,0 +1,163 @@
+import static org.hamcrest.CoreMatchers.*
+import static org.hamcrest.MatcherAssert.assertThat
+
+assertThat(actions, instanceOf(Object))
+assertThat(events, instanceOf(Object))
+assertThat(ir, instanceOf(Object))
+assertThat(itemRegistry, instanceOf(Object))
+assertThat(ir, is(itemRegistry))
+assertThat(items, instanceOf(Object))
+assertThat(rules, instanceOf(Object))
+assertThat(se, instanceOf(Object))
+assertThat(scriptExtension, instanceOf(Object))
+assertThat(se, is(scriptExtension))
+assertThat(things, instanceOf(Object))
+
+assertThat(State, instanceOf(Class))
+assertThat(State.getCanonicalName(), is("org.openhab.core.types.State"))
+
+assertThat(Command, instanceOf(Class))
+assertThat(Command.getCanonicalName(), is("org.openhab.core.types.Command"))
+
+assertThat(URLEncoder, instanceOf(Class))
+assertThat(URLEncoder.getCanonicalName(), is("java.net.URLEncoder"))
+
+assertThat(File, instanceOf(Class))
+assertThat(File.getCanonicalName(), is("java.io.File"))
+
+assertThat(Files, instanceOf(Class))
+assertThat(Files.getCanonicalName(), is("java.nio.file.Files"))
+
+assertThat(Path, instanceOf(Class))
+assertThat(Path.getCanonicalName(), is("java.nio.file.Path"))
+
+assertThat(Paths, instanceOf(Class))
+assertThat(Paths.getCanonicalName(), is("java.nio.file.Paths"))
+
+assertThat(IncreaseDecreaseType, instanceOf(Class))
+assertThat(IncreaseDecreaseType.getCanonicalName(), is("org.openhab.core.library.types.IncreaseDecreaseType"))
+assertThat(DECREASE, instanceOf(IncreaseDecreaseType))
+assertThat(DECREASE, is(IncreaseDecreaseType.DECREASE))
+assertThat(INCREASE, instanceOf(IncreaseDecreaseType))
+assertThat(INCREASE, is(IncreaseDecreaseType.INCREASE))
+
+assertThat(OnOffType, instanceOf(Class))
+assertThat(OnOffType.getCanonicalName(), is("org.openhab.core.library.types.OnOffType"))
+assertThat(ON, instanceOf(OnOffType))
+assertThat(ON, is(OnOffType.ON))
+assertThat(OFF, instanceOf(OnOffType))
+assertThat(OFF, is(OnOffType.OFF))
+
+assertThat(OpenClosedType, instanceOf(Class))
+assertThat(OpenClosedType.getCanonicalName(), is("org.openhab.core.library.types.OpenClosedType"))
+assertThat(CLOSED, instanceOf(OpenClosedType))
+assertThat(CLOSED, is(OpenClosedType.CLOSED))
+assertThat(OPEN, instanceOf(OpenClosedType))
+assertThat(OPEN, is(OpenClosedType.OPEN))
+
+assertThat(StopMoveType, instanceOf(Class))
+assertThat(StopMoveType.getCanonicalName(), is("org.openhab.core.library.types.StopMoveType"))
+assertThat(MOVE, instanceOf(StopMoveType))
+assertThat(MOVE, is(StopMoveType.MOVE))
+assertThat(STOP, instanceOf(StopMoveType))
+assertThat(STOP, is(StopMoveType.STOP))
+
+assertThat(UpDownType, instanceOf(Class))
+assertThat(UpDownType.getCanonicalName(), is("org.openhab.core.library.types.UpDownType"))
+assertThat(DOWN, instanceOf(UpDownType))
+assertThat(DOWN, is(UpDownType.DOWN))
+assertThat(UP, instanceOf(UpDownType))
+assertThat(UP, is(UpDownType.UP))
+
+assertThat(UnDefType, instanceOf(Class))
+assertThat(UnDefType.getCanonicalName(), is("org.openhab.core.types.UnDefType"))
+assertThat(NULL, instanceOf(UnDefType))
+assertThat(NULL, is(UnDefType.NULL))
+assertThat(UNDEF, instanceOf(UnDefType))
+assertThat(UNDEF, is(UnDefType.UNDEF))
+
+assertThat(RefreshType, instanceOf(Class))
+assertThat(RefreshType.getCanonicalName(), is("org.openhab.core.types.RefreshType"))
+assertThat(REFRESH, instanceOf(RefreshType))
+assertThat(REFRESH, is(RefreshType.REFRESH))
+
+assertThat(NextPreviousType, instanceOf(Class))
+assertThat(NextPreviousType.getCanonicalName(), is("org.openhab.core.library.types.NextPreviousType"))
+assertThat(NEXT, instanceOf(NextPreviousType))
+assertThat(NEXT, is(NextPreviousType.NEXT))
+assertThat(PREVIOUS, instanceOf(NextPreviousType))
+assertThat(PREVIOUS, is(NextPreviousType.PREVIOUS))
+
+assertThat(PlayPauseType, instanceOf(Class))
+assertThat(PlayPauseType.getCanonicalName(), is("org.openhab.core.library.types.PlayPauseType"))
+assertThat(PLAY, instanceOf(PlayPauseType))
+assertThat(PLAY, is(PlayPauseType.PLAY))
+assertThat(PAUSE, instanceOf(PlayPauseType))
+assertThat(PAUSE, is(PlayPauseType.PAUSE))
+
+assertThat(RewindFastforwardType, instanceOf(Class))
+assertThat(RewindFastforwardType.getCanonicalName(), is("org.openhab.core.library.types.RewindFastforwardType"))
+assertThat(REWIND, instanceOf(RewindFastforwardType))
+assertThat(REWIND, is(RewindFastforwardType.REWIND))
+assertThat(FASTFORWARD, instanceOf(RewindFastforwardType))
+assertThat(FASTFORWARD, is(RewindFastforwardType.FASTFORWARD))
+
+assertThat(QuantityType, instanceOf(Class))
+assertThat(QuantityType.getCanonicalName(), is("org.openhab.core.library.types.QuantityType"))
+
+assertThat(StringListType, instanceOf(Class))
+assertThat(StringListType.getCanonicalName(), is("org.openhab.core.library.types.StringListType"))
+
+assertThat(RawType, instanceOf(Class))
+assertThat(RawType.getCanonicalName(), is("org.openhab.core.library.types.RawType"))
+
+assertThat(DateTimeType, instanceOf(Class))
+assertThat(DateTimeType.getCanonicalName(), is("org.openhab.core.library.types.DateTimeType"))
+
+assertThat(DecimalType, instanceOf(Class))
+assertThat(DecimalType.getCanonicalName(), is("org.openhab.core.library.types.DecimalType"))
+
+assertThat(HSBType, instanceOf(Class))
+assertThat(HSBType.getCanonicalName(), is("org.openhab.core.library.types.HSBType"))
+
+assertThat(PercentType, instanceOf(Class))
+assertThat(PercentType.getCanonicalName(), is("org.openhab.core.library.types.PercentType"))
+
+assertThat(PointType, instanceOf(Class))
+assertThat(PointType.getCanonicalName(), is("org.openhab.core.library.types.PointType"))
+
+assertThat(StringType, instanceOf(Class))
+assertThat(StringType.getCanonicalName(), is("org.openhab.core.library.types.StringType"))
+
+assertThat(ImperialUnits, instanceOf(Class))
+assertThat(ImperialUnits.getCanonicalName(), is("org.openhab.core.library.unit.ImperialUnits"))
+
+assertThat(MetricPrefix, instanceOf(Class))
+assertThat(MetricPrefix.getCanonicalName(), is("org.openhab.core.library.unit.MetricPrefix"))
+
+assertThat(SIUnits, instanceOf(Class))
+assertThat(SIUnits.getCanonicalName(), is("org.openhab.core.library.unit.SIUnits"))
+
+assertThat(Units, instanceOf(Class))
+assertThat(Units.getCanonicalName(), is("org.openhab.core.library.unit.Units"))
+
+assertThat(BinaryPrefix, instanceOf(Class))
+assertThat(BinaryPrefix.getCanonicalName(), is("org.openhab.core.library.unit.BinaryPrefix"))
+
+assertThat(ChronoUnit, instanceOf(Class))
+assertThat(ChronoUnit.getCanonicalName(), is("java.time.temporal.ChronoUnit"))
+
+assertThat(DayOfWeek, instanceOf(Class))
+assertThat(DayOfWeek.getCanonicalName(), is("java.time.DayOfWeek"))
+
+assertThat(Duration, instanceOf(Class))
+assertThat(Duration.getCanonicalName(), is("java.time.Duration"))
+
+assertThat(Month, instanceOf(Class))
+assertThat(Month.getCanonicalName(), is("java.time.Month"))
+
+assertThat(ZoneId, instanceOf(Class))
+assertThat(ZoneId.getCanonicalName(), is("java.time.ZoneId"))
+
+assertThat(ZonedDateTime, instanceOf(Class))
+assertThat(ZonedDateTime.getCanonicalName(), is("java.time.ZonedDateTime"))