Skip to content

Commit

Permalink
Added ScriptModuleTypeProvider (#635)
Browse files Browse the repository at this point in the history
Added ScriptModuleTypeProvider, which dynamically adds available script
languages to the ParameterOptions used in Paper UI when configuring a
ScriptAction or ScriptCondition.

Signed-off-by: Scott Rushworth <openhab@5iver.com>
  • Loading branch information
Scott Rushworth authored and maggu2810 committed Apr 11, 2019
1 parent 48a97c1 commit 5f880e1
Show file tree
Hide file tree
Showing 11 changed files with 426 additions and 313 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,47 @@
import java.util.Map;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.internal.provider.ScriptModuleTypeProvider;

/**
* This class is used by the ScriptManager to load ScriptEngines.
* This is meant as a way to allow other OSGi bundles to provide custom Script-Languages with special needs (like
* Nashorn, Groovy, etc.)
*
* @author Simon Merschjohann
* This class is used by the ScriptEngineManager to load ScriptEngines. This is meant as a way to allow other OSGi
* bundles to provide custom script-languages with special needs, e.g. Nashorn, Jython, Groovy, etc.
*
* @author Simon Merschjohann - Initial contribution
* @author Scott Rushworth - added/changed methods and parameters when implementing {@link ScriptModuleTypeProvider}
*/
@NonNullByDefault
public interface ScriptEngineFactory {

final static ScriptEngineManager engineManager = new ScriptEngineManager();

/**
* @return the list of supported language endings e.g. py, jy
* This method returns a list of file extensions and MimeTypes that are supported by the ScriptEngine, e.g. py,
* application/python, js, application/javascript, etc.
*
* @return List of supported script types
*/
List<String> getLanguages();
List<String> getScriptTypes();

/**
* "scopes" new values into the given ScriptEngine
* This method "scopes" new values into the given ScriptEngine.
*
* @param scriptEngine
* @param scopeValues
*/
void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues);

/**
* created a new ScriptEngine
*
* @param fileExtension
* @return
*/
ScriptEngine createScriptEngine(String fileExtension);

/**
* checks if the script is supported. Does not necessarily be equal to getLanguages()
* This method creates a new ScriptEngine based on the supplied file extension or MimeType.
*
* @param fileExtension
* @return
* @param scriptType a file extension (script) or MimeType (ScriptAction or ScriptCondition)
* @return ScriptEngine or null
*/
boolean isSupported(String fileExtension);
@Nullable
ScriptEngine createScriptEngine(String scriptType);

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,48 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.internal.provider.ScriptModuleTypeProvider;

/**
* The ScriptEngineManager provides the ability to load and unload scripts.
*
* @author Simon Merschjohann - Initial contribution
* @author Scott Rushworth - changed parameter names when implementing {@link ScriptModuleTypeProvider}
*/
@NonNullByDefault
public interface ScriptEngineManager {

/**
* Checks if a given fileExtension is supported
* Creates a new ScriptEngine used to execute scripts, ScriptActions or ScriptConditions
*
* @param fileExtension
* @return true if supported
* @param scriptType a file extension (script) or MimeType (ScriptAction or ScriptCondition)
* @param engineIdentifier the unique identifier for the ScriptEngine (script file path or UUID)
* @return ScriptEngineContainer or null
*/
boolean isSupported(String fileExtension);
@Nullable
ScriptEngineContainer createScriptEngine(String scriptType, String engineIdentifier);

/**
* Creates a new ScriptEngine based on the given fileExtension
* Loads a script and initializes its scope variables
*
* @param fileExtension
* @param scriptIdentifier
* @return
* @param engineIdentifier the unique identifier for the ScriptEngine (script file path or UUID)
* @param scriptData the content of the script
*/
@Nullable
ScriptEngineContainer createScriptEngine(String fileExtension, String scriptIdentifier);
void loadScript(String engineIdentifier, InputStreamReader scriptData);

/**
* Loads a script and initializes its scope variables
* Unloads the ScriptEngine loaded with the engineIdentifier
*
* @param fileExtension
* @param scriptIdentifier
* @param scriptData
* @return
* @param engineIdentifier the unique identifier for the ScriptEngine (script file path or UUID)
*/
void loadScript(String scriptIdentifier, InputStreamReader scriptData);
void removeEngine(String engineIdentifier);

/**
* Unloads the ScriptEngine loaded with the scriptIdentifer
* Checks if the supplied file extension or MimeType is supported by the existing ScriptEngineFactories
*
* @param scriptIdentifier
* @param scriptType a file extension (script) or MimeType (ScriptAction or ScriptCondition)
* @return true, if supported, else false
*/
void removeEngine(String scriptIdentifier);
boolean isSupported(String scriptType);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Copyright (c) 2010-2019 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.core.automation.module.script.internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.script.ScriptEngine;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This is an abstract class for implementing {@link ScriptEngineFactory}s.
*
* @author Scott Rushworth - initial contribution
*/
@NonNullByDefault
public abstract class AbstractScriptEngineFactory implements ScriptEngineFactory {

protected final Logger logger = LoggerFactory.getLogger(this.getClass());

@Override
public List<String> getScriptTypes() {
List<String> scriptTypes = new ArrayList<>();

for (javax.script.ScriptEngineFactory f : engineManager.getEngineFactories()) {
scriptTypes.addAll(f.getExtensions());
scriptTypes.addAll(f.getMimeTypes());
}
return Collections.unmodifiableList(scriptTypes);
}

@Override
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
for (Entry<String, Object> entry : scopeValues.entrySet()) {
scriptEngine.put(entry.getKey(), entry.getValue());
}
}

@Override
public @Nullable ScriptEngine createScriptEngine(String scriptType) {
ScriptEngine scriptEngine = engineManager.getEngineByExtension(scriptType);
if (scriptEngine == null) {
scriptEngine = engineManager.getEngineByMimeType(scriptType);
}
if (scriptEngine == null) {
scriptEngine = engineManager.getEngineByName(scriptType);
}
return scriptEngine;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,78 +12,18 @@
*/
package org.openhab.core.automation.module.script.internal;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.osgi.service.component.annotations.Component;

/**
* An implementation of {@link ScriptEngineFactory} for ScriptEngines that do not require customizations.
*
* @author Simon Merschjohann - Initial contribution
* @author Scott Rushworth - added service and removed methods now inherited from AbstractScriptEngineFactory
*/
public class GenericScriptEngineFactory implements ScriptEngineFactory {
private ScriptEngineManager engineManager = new ScriptEngineManager();
private final Logger logger = LoggerFactory.getLogger(getClass());

public GenericScriptEngineFactory() {
for (javax.script.ScriptEngineFactory f : engineManager.getEngineFactories()) {
logger.info("Activated scripting support for {}", f.getLanguageName());
logger.debug(
"Activated scripting support with engine {}({}) for {}({}) with mimetypes {} and file extensions {}",
f.getEngineName(), f.getEngineVersion(), f.getLanguageName(), f.getLanguageVersion(),
f.getMimeTypes(), f.getExtensions());
}
}

@Override
public List<String> getLanguages() {
ArrayList<String> languages = new ArrayList<>();

for (javax.script.ScriptEngineFactory f : engineManager.getEngineFactories()) {
languages.addAll(f.getExtensions());
}

return languages;
}

@Override
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
for (Entry<String, Object> entry : scopeValues.entrySet()) {
scriptEngine.put(entry.getKey(), entry.getValue());
}
}

@Override
public ScriptEngine createScriptEngine(String fileExtension) {
ScriptEngine engine = engineManager.getEngineByExtension(fileExtension);

if (engine == null) {
engine = engineManager.getEngineByName(fileExtension);
}

if (engine == null) {
engine = engineManager.getEngineByMimeType(fileExtension);
}

return engine;
}

@Override
public boolean isSupported(String fileExtension) {
for (javax.script.ScriptEngineFactory f : engineManager.getEngineFactories()) {
if (f.getExtensions().contains(fileExtension)) {
return true;
}
}

return false;
}
@NonNullByDefault
@Component(service = ScriptEngineFactory.class)
public class GenericScriptEngineFactory extends AbstractScriptEngineFactory {

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,74 +12,64 @@
*/
package org.openhab.core.automation.module.script.internal;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.automation.module.script.ScriptEngineFactory;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An implementation of {@link ScriptEngineFactory} with customizations for Nashorn ScriptEngines.
*
* @author Simon Merschjohann - Initial contribution
* @author Scott Rushworth - removed default methods provided by ScriptEngineFactory
*/
@NonNullByDefault
@Component(service = ScriptEngineFactory.class)
public class NashornScriptEngineFactory implements ScriptEngineFactory {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public class NashornScriptEngineFactory extends AbstractScriptEngineFactory {

private ScriptEngineManager engineManager = new ScriptEngineManager();
private static final String SCRIPT_TYPE = "js";

@Override
public List<String> getLanguages() {
return Arrays.asList("js", "javascript", "application/javascript");
public List<String> getScriptTypes() {
List<String> scriptTypes = new ArrayList<>();

for (javax.script.ScriptEngineFactory f : engineManager.getEngineFactories()) {
List<String> extensions = f.getExtensions();

if (extensions.contains(SCRIPT_TYPE)) {
scriptTypes.addAll(extensions);
scriptTypes.addAll(f.getMimeTypes());
}
}
return Collections.unmodifiableList(scriptTypes);
}

@Override
public void scopeValues(ScriptEngine engine, Map<String, Object> scopeValues) {
Set<String> expressions = new HashSet<String>();
public void scopeValues(ScriptEngine scriptEngine, Map<String, Object> scopeValues) {
Set<String> expressions = new HashSet<>();

for (Entry<String, Object> entry : scopeValues.entrySet()) {
engine.put(entry.getKey(), entry.getValue());

scriptEngine.put(entry.getKey(), entry.getValue());
if (entry.getValue() instanceof Class) {
expressions.add(String.format("%s = %s.static;", entry.getKey(), entry.getKey()));
expressions.add(String.format("%s = %<s.static;", entry.getKey()));
}
}
String scriptToEval = String.join("\n", expressions);
try {
engine.eval(scriptToEval);
} catch (ScriptException e) {
logger.error("ScriptException while importing scope: {}", e.getMessage());
}
}

@Override
public ScriptEngine createScriptEngine(String fileExtension) {
ScriptEngine engine = engineManager.getEngineByExtension(fileExtension);

if (engine == null) {
engine = engineManager.getEngineByName(fileExtension);
}

if (engine == null) {
engine = engineManager.getEngineByMimeType(fileExtension);
scriptEngine.eval(scriptToEval);
} catch (ScriptException ex) {
logger.error("ScriptException while importing scope: {}", ex.getMessage());
}

return engine;
}

@Override
public boolean isSupported(String fileExtension) {
return getLanguages().contains(fileExtension);
}

}
Loading

0 comments on commit 5f880e1

Please sign in to comment.