package org.toobsframework.biz.scriptmanager;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//import org.hibernate.Session;
//import org.hibernate.SessionFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.toobsframework.exception.PermissionException;
import org.toobsframework.exception.ValidationException;
@SuppressWarnings("unchecked")
public final class ScriptServiceImpl implements IScriptManager, BeanFactoryAware {
private Log log = LogFactory.getLog(ScriptServiceImpl.class);
private Map registry = new HashMap();
private boolean doReload = true;
private BeanFactory beanFactory;
public ScriptServiceImpl() {
super();
}
public Object runScript(String scriptName, Map params, Map outParams) throws ScriptException {
Object result = null;
// Setup script context and scope.
Context ctx = Context.enter();
Scriptable scope = new ImporterTopLevel(ctx);
// Add Spring beanFactory for use in scripts.
Object wrappedBeanFactory = Context.javaToJS(this.beanFactory, scope);
ScriptableObject.putProperty(scope, "beanFactory", wrappedBeanFactory);
Object wrappedLog = Context.javaToJS(this.log, scope);
ScriptableObject.putProperty(scope, "log", wrappedLog);
Object wrappedOutParams = Context.javaToJS(outParams, scope);
ScriptableObject.putProperty(scope, "outParams", wrappedOutParams);
// Add other parameters as passed in.
if (null != params) {
Iterator it = params.entrySet().iterator();
while (it.hasNext()) {
Entry thisEntry = (Entry) it.next();
Object wrappedParam = Context.javaToJS(thisEntry.getValue(), scope);
ScriptableObject.putProperty(scope, (String) thisEntry.getKey(),
wrappedParam);
}
}
// Get the script
Script script = this.loadScript(scriptName, ctx);
// Run script, return result.
if (null != script) {
result = script.exec(ctx, scope);
} else {
throw new ScriptException("Can't find script:" + scriptName);
}
return result;
}
public Object runScript(String action, String objectDao, String objectType, Map params, Map outParams)
throws ScriptException, ValidationException, PermissionException {
NativeJavaObject result = null;
// Setup script context and scope.
try {
Context ctx = Context.enter();
Scriptable scope = new ImporterTopLevel(ctx);
// Add Spring beanFactory for use in scripts.
Object wrappedBeanFactory = Context.javaToJS(this.beanFactory, scope);
ScriptableObject.putProperty(scope, "beanFactory", wrappedBeanFactory);
// Add log object to be used in scripts.
Object wrappedLog = Context.javaToJS(this.log, scope);
ScriptableObject.putProperty(scope, "log", wrappedLog);
// Add action and objectType as parameters.
Object wrappedAction = Context.javaToJS(action, scope);
ScriptableObject.putProperty(scope, "action", wrappedAction);
Object wrappedObjectDao = Context.javaToJS(objectDao, scope);
ScriptableObject.putProperty(scope, "objectDao", wrappedObjectDao);
if (beanFactory.containsBean(objectDao)) {
Object wrappedObjectDaoImpl = Context.javaToJS(beanFactory.getBean(objectDao), scope);
ScriptableObject.putProperty(scope, "objectDaoImpl", wrappedObjectDaoImpl);
}
if (objectType != null) {
Object wrappedObjectType = Context.javaToJS(objectType, scope);
ScriptableObject.putProperty(scope, "objectType", wrappedObjectType);
}
Object wrappedParams = Context.javaToJS(params, scope);
ScriptableObject.putProperty(scope, "params", wrappedParams);
Object wrappedOutParams = Context.javaToJS(outParams, scope);
ScriptableObject.putProperty(scope, "outParams", wrappedOutParams);
/* TODO Check there are no uses of SessionFactory in scripts
SessionFactory sessionFactory = (SessionFactory)beanFactory.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
Object wrappedSession = Context.javaToJS(session, scope);
ScriptableObject.putProperty(scope, "session", wrappedSession);
*/
// Fetch the script with action_objecttype_bizScript.js as the pattern.
// This includes using the more generic action_bizScript.js if an object
// specific
// override does not exist.
String scriptName = action + "_" + objectType;
Script script = this.loadScript(scriptName, ctx);
if (null == script) {
scriptName = action;
script = this.loadScript(scriptName, ctx);
}
// Run script, return result.
if (null != script) {
try {
result = (NativeJavaObject) script.exec(ctx, scope);
} catch (JavaScriptException e) {
Object eValue = ((JavaScriptException)e).getValue();
if (eValue instanceof NativeJavaObject) {
NativeJavaObject njo = (NativeJavaObject)((JavaScriptException)e).getValue();
Throwable t = (Throwable)njo.unwrap();
if (t instanceof ValidationException) {
throw (ValidationException)t;
} else {
log.error("Wrapped Exception: " + t.getMessage(), t);
throw e;
}
} else {
throw e;
}
} catch (WrappedException e) {
Throwable t = (Throwable)e.getWrappedException();
if (t instanceof ValidationException) {
throw (ValidationException)t;
} else if (t instanceof PermissionException) {
throw (PermissionException)t;
} else {
log.error("Wrapped Exception: " + t.getMessage(), t);
throw e;
}
}
} else {
throw new ScriptException("Can't find script:" + action + "_" + objectType);
}
} finally {
Context.exit();
}
if(result != null) {
return result.unwrap();
}
return null;
}
private Script loadScript(String scriptName, Context ctx) throws ScriptException {
Date initStart = new Date();;
if (registry.containsKey(scriptName) && !doReload) {
return (Script) registry.get(scriptName);
}
Script script = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL configFileURL = classLoader.getResource("script/" + scriptName + ".js");
// If the file exists, read it.
if (null != configFileURL) {
try {
InputStreamReader reader = new InputStreamReader(configFileURL.openStream());
script = ctx.compileReader(reader, scriptName, 1, null);
} catch (IOException e) {
throw new ScriptException("Can't load script:" + scriptName);
}
}
this.registry.put(scriptName, script);
if (log.isDebugEnabled()) {
Date initEnd = new Date();
log.debug("Script [" + scriptName + "] - Compile Time: " + (initEnd.getTime() - initStart.getTime()));
}
return script;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public boolean isDoReload() {
return doReload;
}
public void setDoReload(boolean doReload) {
this.doReload = doReload;
}
}