Skip to content

Commit

Permalink
[groovyscripting] Fix default preset scope not applied
Browse files Browse the repository at this point in the history
This allows for removing many imports from scripts which results in less code.

Fixes openhab#17247

Signed-off-by: Wouter Born <github@maindrain.net>
  • Loading branch information
wborn committed Sep 7, 2024
1 parent 247d2d3 commit 4178b0a
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,20 @@
*/
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.
*
Expand All @@ -37,31 +35,36 @@
@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() {
return scriptTypes;
}

@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;
}
}
Original file line number Diff line number Diff line change
@@ -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()));
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,19 @@
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.
*
* @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 {
Expand Down
Loading

0 comments on commit 4178b0a

Please sign in to comment.