/**
* Copyright (c) 1997, 2015 by ProSyst Software GmbH 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.handler;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import org.eclipse.smarthome.automation.Module;
import org.eclipse.smarthome.automation.handler.BaseModuleHandler;
import org.eclipse.smarthome.automation.module.script.ScriptEngineContainer;
import org.eclipse.smarthome.automation.module.script.ScriptEngineManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is an abstract class that can be used when implementing any module handler that handles scripts.
*
* @author Kai Kreuzer - Initial contribution
* @author Simon Merschjohann
*
* @param <T> the type of module the concrete handler can handle
*/
abstract public class AbstractScriptModuleHandler<T extends Module> extends BaseModuleHandler<T> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/** Constant defining the configuration parameter of modules that specifies the mime type of a script */
protected static final String SCRIPT_TYPE = "type";
/** Constant defining the configuration parameter of modules that specifies the script itself */
protected static final String SCRIPT = "script";
protected ScriptEngineManager scriptEngineManager;
private String engineIdentifier;
private Optional<ScriptEngine> scriptEngine = Optional.empty();
private String type;
protected String script;
private String ruleUID;
public AbstractScriptModuleHandler(T module, String ruleUID, ScriptEngineManager scriptEngineManager) {
super(module);
this.scriptEngineManager = scriptEngineManager;
this.ruleUID = ruleUID;
engineIdentifier = UUID.randomUUID().toString();
loadConfig();
}
@Override
public void dispose() {
if (scriptEngine != null) {
scriptEngineManager.removeEngine(engineIdentifier);
}
}
protected Optional<ScriptEngine> getScriptEngine() {
return scriptEngine.isPresent() ? scriptEngine : createScriptEngine();
}
private Optional<ScriptEngine> createScriptEngine() {
ScriptEngineContainer container = scriptEngineManager.createScriptEngine(type, engineIdentifier);
if (container != null) {
scriptEngine = Optional.ofNullable(container.getScriptEngine());
return scriptEngine;
} else {
logger.debug("No engine available for script type '{}' in action '{}'.", type, module.getId());
return Optional.empty();
}
}
private void loadConfig() {
Object type = module.getConfiguration().get(SCRIPT_TYPE);
Object script = module.getConfiguration().get(SCRIPT);
if (type == null || !(type instanceof String) || ((String) type).trim().isEmpty()) {
throw new RuntimeException(
String.format("Type is missing in the configuration of module '%s'.", module.getId()));
} else if (script == null || !(script instanceof String) || ((String) script).trim().isEmpty()) {
throw new RuntimeException(
String.format("Script is missing in the configuration of module '%s'.", module.getId()));
} else {
this.type = (String) type;
this.script = (String) script;
}
}
/**
* Adds the passed context variables of the rule engine to the context scope of the ScriptEngine, this should be
* updated each time the module is executed
*
* @param engine the scriptengine that is used
* @param context the variables and types to put into the execution context
*/
protected void setExecutionContext(ScriptEngine engine, Map<String, ?> context) {
ScriptContext executionContext = engine.getContext();
// make the whole context available as "ctx"
// Note: We don't use "context" here as it doesn't work on all JVM versions!
executionContext.setAttribute("ctx", context, ScriptContext.ENGINE_SCOPE);
executionContext.setAttribute("ruleUID", this.ruleUID, ScriptContext.ENGINE_SCOPE);
// add the single context entries without their prefix to the scope
for (Entry<String, ?> entry : context.entrySet()) {
Object value = entry.getValue();
String key = entry.getKey();
int dotIndex = key.indexOf('.');
if (dotIndex != -1) {
key = key.substring(dotIndex + 1);
}
executionContext.setAttribute(key, value, ScriptContext.ENGINE_SCOPE);
}
}
}