[groovyscripting] Fix default preset scope not applied (#17383)

This allows for removing many imports from scripts which results in less code.

Fixes #17247

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2024-09-08 14:55:31 +02:00 committed by GitHub
parent 6b2462ca22
commit d1613548df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 322 additions and 44 deletions

View File

@ -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);
}
}

View File

@ -12,22 +12,20 @@
*/ */
package org.openhab.automation.groovyscripting.internal; package org.openhab.automation.groovyscripting.internal;
import java.io.File;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.script.ScriptEngine; 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.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; 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.AbstractScriptEngineFactory;
import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import groovy.lang.GroovyClassLoader;
/** /**
* This is an implementation of a {@link ScriptEngineFactory} for Groovy. * This is an implementation of a {@link ScriptEngineFactory} for Groovy.
* *
@ -37,20 +35,11 @@ import groovy.lang.GroovyClassLoader;
@NonNullByDefault @NonNullByDefault
public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory { 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 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) // .flatMap(List::stream) //
.collect(Collectors.toUnmodifiableList()); .toList();
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);
}
@Override @Override
public List<String> getScriptTypes() { public List<String> getScriptTypes() {
@ -58,10 +47,24 @@ public class GroovyScriptEngineFactory extends AbstractScriptEngineFactory {
} }
@Override @Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) { public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
if (scriptTypes.contains(scriptType)) { ImportCustomizer importCustomizer = new ImportCustomizer();
return new org.codehaus.groovy.jsr223.GroovyScriptEngineImpl(gcl); 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;
} }
} }

View File

@ -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()));
}
}

View File

@ -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");
}
}

View File

@ -13,18 +13,11 @@
package org.openhab.automation.groovyscripting; package org.openhab.automation.groovyscripting;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import javax.script.ScriptEngine;
import javax.script.ScriptException; import javax.script.ScriptException;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; 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. * 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 * @author Wouter Born - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class SlurperOSGiTest extends JavaOSGiTest { public class SlurperOSGiTest extends AbstractGroovyScriptingOSGiTest {
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()));
}
@Test @Test
public void jsonSlurper() throws ScriptException, IOException { public void jsonSlurper() throws ScriptException, IOException {

View File

@ -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"))