From 1cdcbe6d3dff4c9549d4bbc50158570f36a71cde Mon Sep 17 00:00:00 2001 From: Scott Rushworth Date: Sun, 1 Dec 2019 02:34:18 -0500 Subject: [PATCH] Prepare automation for ScriptEngineFactory projects These changes prepare for custom ScriptEngineFactories and does not include any breaking changes. * Moved AbstractScriptEngineFactory out of internal so that it can be used by custom factories * Modified ScriptEngineModuleTypeProvider to include engines from custom factories * Fully qualified some names to eliminate confusion with javax.script classes * Cleaned up some logging Signed-off-by: Scott Rushworth --- .../AbstractScriptEngineFactory.java | 9 +++-- .../internal/GenericScriptEngineFactory.java | 3 +- .../internal/NashornScriptEngineFactory.java | 3 +- .../internal/ScriptEngineManagerImpl.java | 31 +++++++++------- .../provider/ScriptModuleTypeProvider.java | 36 ++++++++++++------- 5 files changed, 50 insertions(+), 32 deletions(-) rename bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/{internal => }/AbstractScriptEngineFactory.java (87%) diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/AbstractScriptEngineFactory.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/AbstractScriptEngineFactory.java similarity index 87% rename from bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/AbstractScriptEngineFactory.java rename to bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/AbstractScriptEngineFactory.java index acec24d910b..d8dc72fcad2 100755 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/AbstractScriptEngineFactory.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/AbstractScriptEngineFactory.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.core.automation.module.script.internal; +package org.openhab.core.automation.module.script; import java.util.ArrayList; import java.util.Collections; @@ -22,7 +22,6 @@ 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; @@ -32,9 +31,10 @@ * @author Scott Rushworth - Initial contribution */ @NonNullByDefault -public abstract class AbstractScriptEngineFactory implements ScriptEngineFactory { +public abstract class AbstractScriptEngineFactory + implements org.openhab.core.automation.module.script.ScriptEngineFactory { - protected final Logger logger = LoggerFactory.getLogger(AbstractScriptEngineFactory.class); + protected final static Logger logger = LoggerFactory.getLogger(AbstractScriptEngineFactory.class); @Override public List getScriptTypes() { @@ -65,5 +65,4 @@ public void scopeValues(ScriptEngine scriptEngine, Map scopeValu } return scriptEngine; } - } diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/GenericScriptEngineFactory.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/GenericScriptEngineFactory.java index 7cfb0844527..b6dd598ea20 100644 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/GenericScriptEngineFactory.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/GenericScriptEngineFactory.java @@ -13,6 +13,7 @@ package org.openhab.core.automation.module.script.internal; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.automation.module.script.AbstractScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.osgi.service.component.annotations.Component; @@ -23,7 +24,7 @@ * @author Scott Rushworth - added service and removed methods now inherited from AbstractScriptEngineFactory */ @NonNullByDefault -@Component(service = ScriptEngineFactory.class) +@Component(service = org.openhab.core.automation.module.script.ScriptEngineFactory.class) public class GenericScriptEngineFactory extends AbstractScriptEngineFactory { } diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/NashornScriptEngineFactory.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/NashornScriptEngineFactory.java index 034e139f08c..2e3de48a818 100644 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/NashornScriptEngineFactory.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/NashornScriptEngineFactory.java @@ -24,6 +24,7 @@ import javax.script.ScriptException; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.automation.module.script.AbstractScriptEngineFactory; import org.openhab.core.automation.module.script.ScriptEngineFactory; import org.osgi.service.component.annotations.Component; @@ -34,7 +35,7 @@ * @author Scott Rushworth - removed default methods provided by ScriptEngineFactory */ @NonNullByDefault -@Component(service = ScriptEngineFactory.class) +@Component(service = org.openhab.core.automation.module.script.ScriptEngineFactory.class) public class NashornScriptEngineFactory extends AbstractScriptEngineFactory { private static final String SCRIPT_TYPE = "js"; diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/ScriptEngineManagerImpl.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/ScriptEngineManagerImpl.java index 629d69a60e4..c7616e68b3c 100644 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/ScriptEngineManagerImpl.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/ScriptEngineManagerImpl.java @@ -26,7 +26,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.automation.module.script.ScriptEngineContainer; import org.openhab.core.automation.module.script.ScriptEngineFactory; -import org.openhab.core.automation.module.script.ScriptEngineManager; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; @@ -40,10 +39,10 @@ * @author Scott Rushworth - replaced GenericScriptEngineFactory with a service and cleaned up logging */ @NonNullByDefault -@Component(service = ScriptEngineManager.class) -public class ScriptEngineManagerImpl implements ScriptEngineManager { +@Component(service = org.openhab.core.automation.module.script.ScriptEngineManager.class) +public class ScriptEngineManagerImpl implements org.openhab.core.automation.module.script.ScriptEngineManager { - private final Logger logger = LoggerFactory.getLogger(ScriptEngineManagerImpl.class); + private static final Logger logger = LoggerFactory.getLogger(ScriptEngineManagerImpl.class); private final Map loadedScriptEngineInstances = new HashMap<>(); private final Map customSupport = new HashMap<>(); private final Map genericSupport = new HashMap<>(); @@ -70,12 +69,20 @@ public void addScriptEngineFactory(ScriptEngineFactory engineFactory) { this.genericSupport.put(scriptType, engineFactory); } } - logger.debug("Added {}", engineFactory.getClass().getSimpleName()); - for (javax.script.ScriptEngineFactory f : ScriptEngineFactory.ENGINE_MANAGER.getEngineFactories()) { - logger.debug( - "ScriptEngineFactory details for {} ({}): supports {} ({}) with file extensions {}, names {}, and mimetypes {}", - f.getEngineName(), f.getEngineVersion(), f.getLanguageName(), f.getLanguageVersion(), - f.getExtensions(), f.getNames(), f.getMimeTypes()); + if (!engineFactory.getScriptTypes().isEmpty()) { + ScriptEngine scriptEngine = engineFactory.createScriptEngine(engineFactory.getScriptTypes().get(0)); + if (scriptEngine != null) { + javax.script.ScriptEngineFactory factory = scriptEngine.getFactory(); + logger.debug( + "Initialized a {} ScriptEngineFactory for {} ({}): supports {} ({}) with file extensions {}, names {}, and mimetypes {}", + (isCustomFactory(engineFactory)) ? "custom" : "generic", factory.getEngineName(), + factory.getEngineVersion(), factory.getLanguageName(), factory.getLanguageVersion(), + factory.getExtensions(), factory.getNames(), factory.getMimeTypes()); + } else { + logger.trace("addScriptEngineFactory: engine was null"); + } + } else { + logger.trace("addScriptEngineFactory: scriptTypes was empty"); } } @@ -126,7 +133,7 @@ private boolean isCustomFactory(ScriptEngineFactory engineFactory) { logger.debug("Added ScriptEngine for language '{}' with identifier: {}", scriptType, engineIdentifier); } else { - logger.error("ScriptEngine for language '{}' could not be found for identifier: {}", scriptType, + logger.error("ScriptEngine for language '{}' could not be created for identifier: {}", scriptType, engineIdentifier); } } catch (Exception ex) { @@ -193,7 +200,7 @@ private void removeScriptExtensions(String pathIdentifier) { /** * This method will find and return a {@link ScriptEngineFactory} capable of executing a script of the given type, - * if one exists. + * if one exists. Custom ScriptEngineFactories are preferred over generic. * * @param scriptType a file extension (script) or MimeType (ScriptAction or ScriptCondition) * @return {@link ScriptEngineFactory} or null diff --git a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider/ScriptModuleTypeProvider.java b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider/ScriptModuleTypeProvider.java index 54f4a8eeca7..93ef3671ec8 100755 --- a/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider/ScriptModuleTypeProvider.java +++ b/bundles/org.openhab.core.automation.module.script/src/main/java/org/openhab/core/automation/module/script/internal/provider/ScriptModuleTypeProvider.java @@ -16,11 +16,13 @@ import java.util.Collection; import java.util.List; import java.util.Locale; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Map; +import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; +import javax.script.ScriptEngine; + import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; import org.eclipse.smarthome.config.core.ConfigDescriptionParameterBuilder; @@ -53,7 +55,7 @@ public class ScriptModuleTypeProvider implements ModuleTypeProvider { private final Logger logger = LoggerFactory.getLogger(ScriptModuleTypeProvider.class); - private final List parameterOptions = new CopyOnWriteArrayList<>(); + private final TreeMap parameterOptions = new TreeMap<>(); @SuppressWarnings("unchecked") @Override @@ -97,10 +99,14 @@ private ModuleType getScriptConditionType(Locale locale) { * @return a list of {#link ConfigurationDescriptionParameter}s */ private List getConfigDescriptions(Locale locale) { + List parameterOptionsList = new ArrayList(); + for (Map.Entry entry : parameterOptions.entrySet()) { + parameterOptionsList.add(new ParameterOption(entry.getKey(), entry.getValue())); + } final ConfigDescriptionParameter scriptType = ConfigDescriptionParameterBuilder.create("type", Type.TEXT) .withRequired(true).withReadOnly(true).withMultiple(false).withLabel("Script Type") - .withDescription("the scripting language used").withOptions(parameterOptions).withLimitToOptions(true) - .build(); + .withDescription("the scripting language used").withOptions(parameterOptionsList) + .withLimitToOptions(true).build(); final ConfigDescriptionParameter script = ConfigDescriptionParameterBuilder.create("script", Type.TEXT) .withRequired(true).withReadOnly(false).withMultiple(false).withLabel("Script").withContext("script") .withDescription("the script to execute").build(); @@ -128,26 +134,30 @@ public void removeProviderChangeListener(ProviderChangeListener list } /** - * As {@link ScriptEngineFactory}s are added/changed, this method will create the {@link ParameterOption}s + * As {@link ScriptEngineFactory}s are added/removed, this method will create the {@link ParameterOption}s * that are available when selecting a script type in a ScriptActionType or ScriptConditionType. */ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) public void setScriptEngineFactory(ScriptEngineFactory engineFactory) { - parameterOptions.clear(); - for (javax.script.ScriptEngineFactory f : ScriptEngineFactory.ENGINE_MANAGER.getEngineFactories()) { - String languageName = String.format("%s (%s)", StringUtils.capitalize(f.getLanguageName()), - f.getLanguageVersion()); + ScriptEngine scriptEngine = engineFactory.createScriptEngine(engineFactory.getScriptTypes().get(0)); + if (scriptEngine != null) { List mimeTypes = new ArrayList<>(); - mimeTypes.addAll(f.getMimeTypes()); + javax.script.ScriptEngineFactory factory = scriptEngine.getFactory(); + String languageName = String.format("%s (%s)", + factory.getLanguageName().substring(0, 1).toUpperCase() + factory.getLanguageName().substring(1), + factory.getLanguageVersion()); + mimeTypes.addAll(factory.getMimeTypes()); String preferredMimeType = mimeTypes.get(0); mimeTypes.removeIf(mimeType -> !mimeType.contains("application") || mimeType.contains("x-")); if (!mimeTypes.isEmpty()) { preferredMimeType = mimeTypes.get(0); } - parameterOptions.add(new ParameterOption(preferredMimeType, languageName)); + parameterOptions.put(preferredMimeType, languageName); + logger.trace("ParameterOptions: {}", parameterOptions); + } else { + logger.trace("setScriptEngineFactory: engine was null"); } - logger.trace("ParameterOptions: {}", parameterOptions); } public void unsetScriptEngineFactory(ScriptEngineFactory scriptEngineFactory) {