package org.oddjob.script;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.oddjob.FailedToStopException;
import org.oddjob.Stoppable;
import org.oddjob.arooa.ArooaValue;
import org.oddjob.framework.SerializableJob;
import org.oddjob.jmx.JMXServiceJob;
/**
*
* @oddjob.description Invoke a java method or script snippet.
* <p>
* This is a wrapper for {@link InvokeType}. The result of the
* invocation is placed in the <code>result</code> property.
* <p>
* Note that stopping this job will simply attempt to interrupt the
* thread invoking the method. The outcome of this will obviously vary.
* <p>
* Oddjob will do it's best to convert arguments to the signature of
* the method or operation. An exception will result if it can't achieve
* this.
*
* @oddjob.example
*
* Invoking a method on a bean.
*
* {@oddjob.xml.resource org/oddjob/script/InvokeJobMethod.xml}
*
* Where <code>EchoService</code> is:
*
* {@oddjob.java.resource org/oddjob/script/EchoService.java}
*
* @oddjob.example
*
* Invoking a static method. Note that this uses args instead of parameters
* for convenience.
*
* {@oddjob.xml.resource org/oddjob/script/InvokeJobStatic.xml}
*
* @oddjob.example
*
* Examples elsewhere.
* <p>
* See {@link InvokeType} for several more examples. Property configuration
* is the same for the type and the job.
* <p>
* The {@link JMXServiceJob} job has an example of
* invoking a JMX operation.
*
* @author rob
*
*/
public class InvokeJob extends SerializableJob
implements Stoppable {
private static final long serialVersionUID = 2012080600L;
/**
* @oddjob.property
* @oddjob.description The java object or script Invocable on
* which to invoke the method/function. If the method is a Java static
* method then this is the class on which to invoke the method.
* @oddjob.required Yes.
*/
private transient Invoker source;
/**
* @oddjob.property
* @oddjob.description The function/method/operation name to call. Note
* that for a Java static method the method name must be prefixed with
* the word static (see InvokeType examples).
* @oddjob.required Yes.
*/
private String function;
/**
* @oddjob.property
* @oddjob.description The values to use as arguments. Note that the
* <code>args</code> property may be more convenient for simple arguments.
* @oddjob.required Must match the expected arguments.
*/
private transient List<ArooaValue> parameters;
/**
* @oddjob.property
* @oddjob.description An alternative configuration for the values to use
* as arguments. This was added for convenience as setting up a lot
* of simple arguments can be tedious. If this property is provided then
* parameters is ignored.
* @oddjob.required Must match the expected arguments.
*/
private transient Object[] args;
/** The result of the invocation. */
private transient Object result;
private transient volatile Thread executingThread;
/**
* Constructor.
*/
public InvokeJob() {
completeConstruction();
}
private void completeConstruction() {
parameters = new ArrayList<ArooaValue>();
}
@Override
protected int execute() throws Throwable {
InvokeType delegate = new InvokeType();
delegate.setArooaSession(getArooaSession());
delegate.setFunction(function);
delegate.setSource(source);
delegate.setArgs(args);
for (int i = 0; i < parameters.size(); ++i) {
delegate.setParameters(i, parameters.get(i));
}
executingThread = Thread.currentThread();
try {
result = delegate.toValue();
}
finally {
executingThread = null;
}
return 0;
}
@Override
protected void onReset() {
result = null;
}
@Override
protected void onStop() throws FailedToStopException {
Thread thread = executingThread;
if (thread != null) {
logger().info("Interrupting Invoke operation.");
thread.interrupt();
}
}
public Invoker getSource() {
return source;
}
public void setSource(Invoker source) {
this.source = source;
}
public String getFunction() {
return function;
}
public void setFunction(String function) {
this.function = function;
}
public ArooaValue getParameters(int index) {
return parameters.get(index);
}
public void setParameters(int index, ArooaValue parameter) {
if (parameter == null) {
parameters.remove(index);
}
else {
parameters.add(index, parameter);
}
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public Object getResult() {
return result;
}
/**
* Custom serialisation.
*/
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
if (result instanceof Serializable) {
s.writeObject(result);
}
else {
s.writeObject(null);
}
}
/**
* Custom serialisation.
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
result = s.readObject();
completeConstruction();
}
}