/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* 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.model.script.actions;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import org.apache.commons.lang.StringUtils;
import org.eclipse.smarthome.model.core.ModelRepository;
import org.eclipse.smarthome.model.script.ScriptServiceUtil;
import org.eclipse.smarthome.model.script.engine.Script;
import org.eclipse.smarthome.model.script.engine.ScriptEngine;
import org.eclipse.smarthome.model.script.engine.ScriptExecutionException;
import org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob;
import org.eclipse.smarthome.model.script.internal.actions.TimerImpl;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure0;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.joda.time.base.AbstractInstant;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The static methods of this class are made available as functions in the scripts.
* This allows a script to call another script, which is available as a file.
*
* @author Kai Kreuzer - Initial contribution and API
*
*/
public class ScriptExecution {
/**
* Calls a script which must be located in the configurations/scripts folder.
*
* @param scriptName the name of the script (if the name does not end with
* the .script file extension it is added)
*
* @return the return value of the script
* @throws ScriptExecutionException if an error occurs during the execution
*/
public static Object callScript(String scriptName) throws ScriptExecutionException {
ModelRepository repo = ScriptServiceUtil.getModelRepository();
if (repo != null) {
String scriptNameWithExt = scriptName;
if (!StringUtils.endsWith(scriptName, Script.SCRIPT_FILEEXT)) {
scriptNameWithExt = scriptName + "." + Script.SCRIPT_FILEEXT;
}
XExpression expr = (XExpression) repo.getModel(scriptNameWithExt);
if (expr != null) {
ScriptEngine scriptEngine = ScriptServiceUtil.getScriptEngine();
if (scriptEngine != null) {
Script script = scriptEngine.newScriptFromXExpression(expr);
return script.execute();
} else {
throw new ScriptExecutionException("Script engine is not available.");
}
} else {
throw new ScriptExecutionException("Script '" + scriptName + "' cannot be found.");
}
} else {
throw new ScriptExecutionException("Model repository is not available.");
}
}
/**
* Schedules a block of code for later execution.
*
* @param instant the point in time when the code should be executed
* @param closure the code block to execute
*
* @return a handle to the created timer, so that it can be canceled or rescheduled
* @throws ScriptExecutionException if an error occurs during the execution
*/
public static Timer createTimer(AbstractInstant instant, Procedure0 closure) {
JobDataMap dataMap = new JobDataMap();
dataMap.put("procedure", closure);
return makeTimer(instant, closure.toString(), dataMap);
}
/**
* Schedules a block of code (with argument) for later execution
*
* @param instant the point in time when the code should be executed
* @param arg1 the argument to pass to the code block
* @param closure the code block to execute
*
* @return a handle to the created timer, so that it can be canceled or rescheduled
* @throws ScriptExecutionException if an error occurs during the execution
*/
public static Timer createTimerWithArgument(AbstractInstant instant, Object arg1, Procedure1<Object> closure) {
JobDataMap dataMap = new JobDataMap();
dataMap.put("procedure1", closure);
dataMap.put("argument1", arg1);
return makeTimer(instant, closure.toString(), dataMap);
}
/**
* helper function to create the timer
*
* @param instant the point in time when the code should be executed
* @param closure string for job id
* @param dataMap job data map, preconfigured with arguments
* @return
*/
private static Timer makeTimer(AbstractInstant instant, String closure, JobDataMap dataMap) {
Logger logger = LoggerFactory.getLogger(ScriptExecution.class);
JobKey jobKey = new JobKey(instant.toString() + ": " + closure.toString());
Trigger trigger = newTrigger().startAt(instant.toDate()).build();
Timer timer = new TimerImpl(jobKey, trigger.getKey(), instant);
dataMap.put("timer", timer);
try {
JobDetail job = newJob(TimerExecutionJob.class).withIdentity(jobKey).usingJobData(dataMap).build();
if (TimerImpl.scheduler.checkExists(job.getKey())) {
TimerImpl.scheduler.deleteJob(job.getKey());
logger.debug("Deleted existing Job {}", job.getKey().toString());
}
TimerImpl.scheduler.scheduleJob(job, trigger);
logger.debug("Scheduled code for execution at {}", instant.toString());
return timer;
} catch (SchedulerException e) {
logger.error("Failed to schedule code for execution.", e);
return null;
}
}
}