/** * Copyright (c) 2015-2017 Simon Merschjohann and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.automation.module.script.internal; import java.io.InputStreamReader; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptException; import org.eclipse.smarthome.automation.module.script.ScriptEngineContainer; import org.eclipse.smarthome.automation.module.script.ScriptEngineFactory; import org.eclipse.smarthome.automation.module.script.ScriptEngineManager; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The ScriptManager allows to load and unloading of script files using a script engines script type * * @author Simon Merschjohann * */ public class ScriptEngineManagerImpl implements ScriptEngineManager { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private Set<ScriptEngineFactory> scriptEngineFactories = new HashSet<>(); private HashMap<String, ScriptEngineContainer> loadedScriptEngineInstances = new HashMap<>(); private HashMap<String, ScriptEngineFactory> supportedLanguages = new HashMap<>(); private GenericScriptEngineFactory genericScriptEngineFactory = new GenericScriptEngineFactory(); public ScriptEngineManagerImpl() { logger.debug("ScriptManager loading..."); } public void activate(BundleContext bundleContext) { } public void addScriptEngineFactory(ScriptEngineFactory provider) { this.scriptEngineFactories.add(provider); for (String language : provider.getLanguages()) { this.supportedLanguages.put(language, provider); } } public void removeScriptEngineFactory(ScriptEngineFactory provider) { this.scriptEngineFactories.remove(provider); for (String language : provider.getLanguages()) { this.supportedLanguages.remove(language, provider); } } @Override public boolean isSupported(String fileExtension) { return findEngineFactory(fileExtension) != null; } @Override public ScriptEngineContainer createScriptEngine(String fileExtension, String scriptIdentifier) { ScriptEngineContainer result = null; ScriptEngineFactory engineProvider = findEngineFactory(fileExtension); if (engineProvider == null) { logger.error("loadScript(): scriptengine for language '{}' could not be found for identifier: {}", fileExtension, scriptIdentifier); } else { try { ScriptEngine engine = engineProvider.createScriptEngine(fileExtension); HashMap<String, Object> scriptExManager = new HashMap<>(); result = new ScriptEngineContainer(engine, engineProvider, scriptIdentifier); ScriptExtensionManagerWrapper wrapper = new ScriptExtensionManagerWrapper(result); scriptExManager.put("scriptExtension", wrapper); scriptExManager.put("se", wrapper); engineProvider.scopeValues(engine, scriptExManager); ScriptExtensionManager.importDefaultPresets(engineProvider, engine, scriptIdentifier); loadedScriptEngineInstances.put(scriptIdentifier, result); } catch (Exception ex) { logger.error("Error while creating ScriptEngine", ex); removeScriptExtensions(scriptIdentifier); } } return result; } @Override public void loadScript(String scriptIdentifier, InputStreamReader scriptData) { ScriptEngineContainer container = loadedScriptEngineInstances.get(scriptIdentifier); if (container == null) { logger.error("could not load script as no engine is created"); } else { ScriptEngine engine = container.getScriptEngine(); try { engine.eval(scriptData); if (engine instanceof Invocable) { Invocable inv = (Invocable) engine; try { inv.invokeFunction("scriptLoaded", scriptIdentifier); } catch (NoSuchMethodException e) { logger.trace("scriptLoaded() not defined in script: {}", scriptIdentifier); } } else { logger.trace("engine does not support Invocable interface"); } } catch (Exception ex) { logger.error("Error during evaluation of script '{}': {}", scriptIdentifier, ex.getMessage()); } } } @Override public void removeEngine(String scriptIdentifier) { ScriptEngineContainer container = loadedScriptEngineInstances.get(scriptIdentifier); if (container != null) { if (container.getScriptEngine() instanceof Invocable) { Invocable inv = (Invocable) container.getScriptEngine(); try { inv.invokeFunction("scriptUnloaded"); } catch (NoSuchMethodException e) { logger.trace("scriptUnloaded() not defined in script"); } catch (ScriptException e) { logger.error("Error while executing script", e); } } else { logger.trace("engine does not support Invocable interface"); } removeScriptExtensions(scriptIdentifier); } } private void removeScriptExtensions(String pathIdentifier) { try { ScriptExtensionManager.dispose(pathIdentifier); } catch (Exception ex) { logger.error("error removing engine", ex); } } private ScriptEngineFactory findEngineFactory(String fileExtension) { ScriptEngineFactory engineProvider = supportedLanguages.get(fileExtension); if (engineProvider != null) { return engineProvider; } for (ScriptEngineFactory provider : supportedLanguages.values()) { if (provider.isSupported(fileExtension)) { return provider; } } if (genericScriptEngineFactory.isSupported(fileExtension)) { return genericScriptEngineFactory; } return null; } }