Skip to content

Commit

Permalink
[groovyscripting] Fix default preset scope not applied (#17383)
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 #17247

Signed-off-by: Wouter Born <github@maindrain.net>
  • Loading branch information
wborn authored Sep 8, 2024
1 parent 6b2462c commit d161354
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 d161354

Please sign in to comment.