/*
* Copyright 2000-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.oddjob.script;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.script.Invocable;
import org.apache.log4j.Logger;
import org.oddjob.framework.SerializableJob;
import org.oddjob.util.OddjobConfigException;
/**
* @oddjob.description Execute a script. The script can be in any
* language that supports the Java Scripting Framework.
* <p>
* The named beans property allow values to be passed to and from the
* script.
* <p>
*
* @oddjob.example
*
* Hello World.
*
* {@oddjob.xml.resource org/oddjob/script/ScriptHelloWorld.xml}
*
* @oddjob.example
*
* Variables from and to Oddjob.
*
* {@oddjob.xml.resource org/oddjob/script/VariablesFromAndToOddjob.xml}
*
*
* @oddjob.example
*
* Using a script to set a property on a Job elsewhere in Oddjob.
*
* {@oddjob.xml.resource org/oddjob/script/ScriptSettingProperty.xml}
*
* @oddjob.example
*
* Invoking a script to provide a substring function.
*
* {@oddjob.xml.resource org/oddjob/script/InvokeScriptFunction.xml}
*
* @oddjob.example
*
* Setting the script job to not complete.
*
* {@oddjob.xml.resource org/oddjob/script/ScriptResult.xml}
*
* @author Rob Gordon - Based on the original from Ant.
*/
public class ScriptJob extends SerializableJob {
private static final long serialVersionUID = 2010012600;
private static final Logger logger = Logger.getLogger(ScriptJob.class);
/**
* @oddjob.property
* @oddjob.description The name of the language the script
* is in.
* @oddjob.required No. Defaults to JavaScript.
*/
private transient String language;
/**
* @oddjob.property
* @oddjob.description A named bean which is made available to
* the script.
* @oddjob.required No.
*/
private transient Map<String, Object> beans;
/**
* @oddjob.property
* @oddjob.description The script provided as input from file or buffer etc.
* @oddjob.required Yes.
*/
private transient InputStream input;
/**
* @oddjob.property
* @oddjob.description The variable in the script that will be used to
* provide the result. The property is designed for use with scripting
* languages who's execution does not produce a result. If, however
* the script does produce a result and this property is set, the variable
* will override the scripts return value.
* @oddjob.required No.
*/
private String resultVariable;
/**
* @oddjob.property
* @oddjob.description The result of executing the script or the script
* variable chosen as the result variable with the {@code resultVariable}
* property.
*/
private Object result;
/**
* @oddjob.property
* @oddjob.description If true then use the result to determine the
* completion state of the job. If the result is not a number this
* property will have no affect.
* If the result is a number and 0 the job will COMPLETE, any
* other value and the job will be INCOMPLETE.
* @oddjob.required No, defaults to false.
*/
private boolean resultForState;
/**
* @oddjob.property
* @oddjob.description Allow a scripted function to be evaluated
* from elsewhere in Oddjob.
*/
private transient Invocable invocable;
/** The thing that was compiles and/or run. */
private transient Evaluatable evaluatable;
/**
* @oddjob.property
* @oddjob.description ClassLoader to load the Script Engine.
* @oddjob.required No. Automatically set to the current Oddjob class loader.
*/
private transient ClassLoader classLoader;
/*
* (non-Javadoc)
* @see org.oddjob.framework.SimpleJob#execute()
*/
protected int execute() throws IOException {
ScriptCompiler compiler = new ScriptCompiler(language,
classLoader);
if (input == null) {
throw new OddjobConfigException("No script provided!");
}
evaluatable = compiler.compileScript(
new InputStreamReader(input));
logger.info("Script compiled.");
invocable = compiler.getInvocable();
ScriptRunner runner = new ScriptRunner(resultVariable);
if (beans != null) {
runner.addBeans(beans);
}
result = runner.executeScript(evaluatable);
logger.info("Script executed. Result " + result);
if (resultForState) {
if (this.result instanceof Number) {
return ((Number) this.result).intValue();
}
}
return 0;
}
/**
* Defines the language (required).
*
* @param language the scripting language name for the script.
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* Get the language.
*
* @return The language.
*/
public String getLanguage() {
return language;
}
/**
* Get the named bean.
*
* @param name The name of the bean
* @return The bean or null if it doesn't exist.
*/
public Object getBeans(String name) {
if (beans == null) {
return null;
}
return beans.get(name);
}
/**
* Add a named bean.
*
* @param name The name of the bean.
* @param value The bean.
*/
public void setBeans(String name, Object value) {
if (beans == null) {
beans = new HashMap<String, Object>();
}
logger().debug("Adding bean (" + name
+ ", [" + value + "]");
beans.put(name, value);
}
/**
* Get the input.
*
* @return The input.
*/
public InputStream getInput() {
return input;
}
/**
* Set the input.
*
* @param input The input.
*/
public void setInput(InputStream input) {
this.input = input;
}
public Invocable getInvocable() {
return invocable;
}
/**
* @oddjob.property variables
* @oddjob.description Provide access to variables declared within the
* script.
*/
public Object getVariables(String key) {
return evaluatable.get(key);
}
public String getResultVariable() {
return resultVariable;
}
public void setResultVariable(String resultVariable) {
this.resultVariable = resultVariable;
}
public boolean isResultForState() {
return resultForState;
}
public void setResultForState(boolean resultForState) {
this.resultForState = resultForState;
}
public Object getResult() {
return result;
}
public ClassLoader getClassLoader() {
return classLoader;
}
@Inject
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}