package org.springframework.scripting.js;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import static org.springframework.util.StringUtils.uncapitalize;
/**
* @author Tomasz Nurkiewicz
* @since 2010-09-21, 23:05:20
*/
public class JavaScriptScriptUtils {
private static final Log log = LogFactory.getLog(JavaScriptScriptUtils.class);
public static Object createJavaScriptObject(ScriptSource script, Class[] actualInterfaces) throws IOException, ScriptException {
return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), actualInterfaces, new JavaScriptInvocationHandler(script));
}
private static class JavaScriptInvocationHandler implements InvocationHandler {
private final ScriptEngine engine;
private JavaScriptInvocationHandler(ScriptSource script) throws ScriptException, IOException {
log.debug("Creating proxy for script '" + script + "'");
engine = new ScriptEngineManager().getEngineByName("JavaScript");
if(script instanceof ResourceScriptSource)
engine.put(ScriptEngine.FILENAME, ((ResourceScriptSource) script).getResource().getURI().getPath());
// CompiledScript compiledScript = ((Compilable) engine).compile(script.getScriptAsString());
engine.eval(script.getScriptAsString());
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (ReflectionUtils.isEqualsMethod(method))
return equals(args[0]);
if (ReflectionUtils.isHashCodeMethod(method))
return this.hashCode();
if (ReflectionUtils.isToStringMethod(method))
return toString();
if (isSetter(method)) {
addBeanToScriptContext(method.getName(), args[0]);
return null;
}
return invokeScriptMethod(method, args);
}
private Object invokeScriptMethod(Method method, Object[] args) throws ScriptException, NoSuchMethodException {
try {
return ((Invocable) engine).invokeFunction(method.getName(), args);
} catch (ScriptException e) {
throw new JavaScriptExecutionException(e.getMessage(), e);
} catch (NoSuchMethodException e) {
throw new JavaScriptExecutionException("Script does not define: " + method.getName() + " function", e);
}
}
private void addBeanToScriptContext(String setterName, Object bean) {
final String beanName = uncapitalize(setterName.substring(3));
log.debug("Putting " + bean + " to the " + engine + " context with name '" + beanName + "'");
engine.put(beanName, bean);
}
private boolean isSetter(Method method) {
return method.getName().startsWith("set") && method.getParameterTypes().length == 1;
}
}
}