package org.oddjob.jobs.tasks;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.oddjob.FailedToStopException;
import org.oddjob.Stateful;
import org.oddjob.Stoppable;
import org.oddjob.Structural;
import org.oddjob.arooa.ArooaSession;
import org.oddjob.arooa.convert.ArooaConversionException;
import org.oddjob.arooa.deploy.annotations.ArooaAttribute;
import org.oddjob.arooa.deploy.annotations.ArooaComponent;
import org.oddjob.arooa.deploy.annotations.ArooaInterceptor;
import org.oddjob.arooa.runtime.ExpressionParser;
import org.oddjob.arooa.runtime.ParsedExpression;
import org.oddjob.arooa.runtime.PropertyLookup;
import org.oddjob.arooa.runtime.PropertySource;
import org.oddjob.arooa.utils.ListSetterHelper;
import org.oddjob.framework.SimpleService;
import org.oddjob.input.InputRequest;
import org.oddjob.jobs.job.ResetAction;
import org.oddjob.jobs.job.ResetActions;
import org.oddjob.structural.ChildHelper;
import org.oddjob.structural.StructuralListener;
import org.oddjob.values.properties.PropertiesConfigurationSession;
/**
* @oddjob.description Provide a very simple task execution service.
* <p>
* The task to be executed is defined by the nested jobs which may use the properties.
* which will be defined when executing the tasks.
* <p>
* This implementation only supports the single execution of a task at one time. If
* the task is running additional requests to execute the task will be ignored.
* <p>
* Future version will support multiple parallel executions of tasks.
*
* @oddjob.example
*
* {@oddjob.xml.resource org/oddjob/jobs/job/TaskRequestSimple.xml}
*
* @author Rob Gordon
*/
@ArooaInterceptor("org.oddjob.values.properties.PropertiesInterceptor")
public class TaskExecutionService extends SimpleService
implements TaskExecutor, Structural {
/** Track changes to children an notify listeners. */
protected final ChildHelper<Object> childHelper =
new ChildHelper<Object>(this);
private final Properties properties = new Properties();
private final List<InputRequest> requests =
new ArrayList<InputRequest>();
private volatile InputRequest[] requestArray;
private volatile TaskView taskView;
private volatile ResetAction reset;
private volatile String response;
@Override
public void setArooaSession(ArooaSession session) {
super.setArooaSession(session);
if (! (session instanceof PropertiesConfigurationSession)) {
throw new IllegalStateException();
}
((PropertiesConfigurationSession) session).getPropertyManager(
).addPropertyOverride(new TaskPropertyLookup());
}
public InputRequest getRequests(int index) {
return requests.get(index);
}
public void setRequests(int index, InputRequest request) {
new ListSetterHelper<InputRequest>(requests).set(index, request);
}
@Override
public InputRequest[] getParameterInfo() {
return requestArray;
}
@Override
public TaskView execute(Task task)
throws TaskException {
if (requestArray == null) {
throw new TaskException("Task Execution Service not Started.");
}
final ArooaSession session = getArooaSession();
if (session == null) {
throw new NullPointerException("No session.");
}
final Object job = childHelper.getChild();
if (!(job instanceof Runnable)) {
throw new TaskException("No Job to Execute the Task.");
}
if (taskView != null &&
taskView.lastStateEvent().getState().isStoppable()) {
throw new TaskException("Task Execution in progress.");
}
Properties properties = task.getProperties();
if (properties != null) {
this.properties.clear();
this.properties.putAll(properties);
}
ResetAction reset = this.reset;
if (reset == null) {
reset = ResetActions.AUTO;
}
reset.doWith(job);
((Runnable) job).run();
taskView = new JobTaskView((Stateful) job) {
@Override
protected Object onDone() {
String responseExpression = response;
if (responseExpression != null) {
ExpressionParser parser = session.getTools().getExpressionParser();
try {
ParsedExpression expression = parser.parse(responseExpression);
return expression.evaluate(session, String.class);
}
catch (ArooaConversionException e) {
return "Failed to evaluate response" + e.toString();
}
}
return "OK";
}
@Override
public String toString() {
return "TaskView for " + TaskExecutionService.this.toString();
}
};
return taskView;
}
@Override
protected void onStart() throws Throwable {
requestArray = requests.toArray(
new InputRequest[requests.size()]);
}
@Override
protected void onStop() throws FailedToStopException {
requestArray = null;
Object job = childHelper.getChild();
if (job != null && job instanceof Stoppable) {
((Stoppable) job).stop();
}
}
/**
* Add a listener. The listener will immediately receive add
* notifications for all existing children.
*
* @param listener The listener.
*/
public void addStructuralListener(StructuralListener listener) {
stateHandler().assertAlive();
childHelper.addStructuralListener(listener);
}
/**
* Remove a listener.
*
* @param listener The listener.
*/
public void removeStructuralListener(StructuralListener listener) {
childHelper.removeStructuralListener(listener);
}
/**
* @oddjob.property job
* @oddjob.description The job to pass resets on to.
* @oddjob.required Yes.
*/
@ArooaComponent
public synchronized void setJob(Object job) {
if (job == null) {
childHelper.removeChildAt(0);
}
else {
childHelper.insertChild(0, job);
}
}
public ResetAction getReset() {
return reset;
}
@ArooaAttribute
public void setReset(ResetAction resetAction) {
this.reset = resetAction;
}
public Properties getProperties() {
return properties;
}
class TaskPropertyLookup implements PropertyLookup {
private final PropertySource source = new PropertySource() {
@Override
public String toString() {
return TaskExecutionService.this.toString();
}
};
@Override
public String lookup(String propertyName) {
return properties.getProperty(propertyName);
}
@Override
public PropertySource sourceFor(String propertyName) {
if (properties.containsKey(propertyName)) {
return source;
}
else {
return null;
}
}
@Override
public Set<String> propertyNames() {
return properties.stringPropertyNames();
}
}
public String getResponse() {
return response;
}
public void setResponse(String responseExpression) {
this.response = responseExpression;
}
}