394 lines
12 KiB
Java
394 lines
12 KiB
Java
/*
|
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
|
* (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.mozilla.org/MPL/>.
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
|
|
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Venice Web Communities System.
|
|
*
|
|
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
|
|
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
|
|
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
package com.silverwrist.venice.ui.script;
|
|
|
|
import java.lang.reflect.*;
|
|
import java.io.*;
|
|
import java.util.*;
|
|
import com.ibm.bsf.*;
|
|
import org.apache.log4j.*;
|
|
import com.silverwrist.util.*;
|
|
import com.silverwrist.venice.ui.*;
|
|
import com.silverwrist.venice.ui.helpers.ThrowableContent;
|
|
|
|
public class ScriptManager
|
|
{
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal class for script utility
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public class MyScriptLibrary extends ScriptLibrary
|
|
{
|
|
public Object exec(String script) throws ScriptingException, ThrowableContent
|
|
{
|
|
RequestInput ri = (RequestInput)(ScriptManager.this.lookupObject("request"));
|
|
String script_file = ri.getScriptName(script);
|
|
String logger_name = ri.getScriptLoggerName(script);
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("EXECUTING " + script_file);
|
|
ScriptReturn sro = new ScriptReturn();
|
|
ScriptManager.this.exec(new File(script_file),logger_name,sro);
|
|
return sro.get();
|
|
|
|
} // end exec
|
|
|
|
public void output(Object o)
|
|
{
|
|
if (return_stack.size()>0)
|
|
{ // set the return value
|
|
ScriptReturn sro = (ScriptReturn)(return_stack.getFirst());
|
|
sro.set(o);
|
|
|
|
} // end if
|
|
|
|
} // end output
|
|
|
|
} // end class MyScriptLibrary
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal class for doing logging
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public class ScriptLogger
|
|
{
|
|
public void fatal(Object message)
|
|
{
|
|
if (logger_stack.size()>0)
|
|
((Category)(logger_stack.getFirst())).fatal(message);
|
|
|
|
} // end fatal
|
|
|
|
public void error(Object message)
|
|
{
|
|
if (logger_stack.size()>0)
|
|
((Category)(logger_stack.getFirst())).error(message);
|
|
|
|
} // end error
|
|
|
|
public void warn(Object message)
|
|
{
|
|
if (logger_stack.size()>0)
|
|
((Category)(logger_stack.getFirst())).warn(message);
|
|
|
|
} // end warn
|
|
|
|
public void info(Object message)
|
|
{
|
|
if (logger_stack.size()>0)
|
|
{ // check to make sure info is enabled first
|
|
Category cat = (Category)(logger_stack.getFirst());
|
|
if (cat.isInfoEnabled())
|
|
cat.info(message);
|
|
|
|
} // end if
|
|
|
|
} // end info
|
|
|
|
public void debug(Object message)
|
|
{
|
|
if (logger_stack.size()>0)
|
|
{ // check to make sure debug is enabled first
|
|
Category cat = (Category)(logger_stack.getFirst());
|
|
if (cat.isDebugEnabled())
|
|
cat.debug(message);
|
|
|
|
} // end if
|
|
|
|
} // end debug
|
|
|
|
} // end class ScriptLogger
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Static data members
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static Category logger = Category.getInstance(ScriptManager.class);
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Attributes
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private BSFManager mgr; // Bean Scripting Framework manager
|
|
private LinkedList registered_names; // stack of lists of registered names
|
|
private LinkedList logger_stack; // stack of loggers for nested evaluation
|
|
private LinkedList return_stack; // stack of return values
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Constructor
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public ScriptManager(String temp_dir) throws ScriptingException
|
|
{
|
|
// initialize the BSFManager
|
|
mgr = new BSFManager();
|
|
mgr.setTempDir(temp_dir);
|
|
|
|
if (BSFManager.isLanguageRegistered("javascript"))
|
|
{ // we need to so some fixing up to make Rhino work
|
|
try
|
|
{ // ensure the engine is loaded, and do our fixup routine
|
|
mgr.loadScriptingEngine("javascript");
|
|
fixupRhino();
|
|
|
|
} // end if
|
|
catch (BSFException e)
|
|
{ // do nothing - we don't have JavaScript loaded
|
|
} // end catch
|
|
|
|
} // end if
|
|
|
|
try
|
|
{ // declare beans used by the scripting engines
|
|
mgr.declareBean("vlib",new MyScriptLibrary(),MyScriptLibrary.class);
|
|
mgr.declareBean("logger",new ScriptLogger(),ScriptLogger.class);
|
|
|
|
} // end try
|
|
catch (BSFException e)
|
|
{ // translate the exception
|
|
logger.error("bean declarations threw a BSFException",e);
|
|
throw new ScriptingException("initialization error in ScriptManager",e);
|
|
|
|
} // end catch
|
|
|
|
// initialize the rest of the object
|
|
registered_names = new LinkedList();
|
|
registered_names.addFirst(new HashSet());
|
|
logger_stack = new LinkedList();
|
|
return_stack = new LinkedList();
|
|
|
|
} // end constructor
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static void fixupRhino()
|
|
{
|
|
// What fixupRhino does is to turn off the fancy caching in the JavaScript engine. This is because
|
|
// caching involves dynamic creation of classes on the fly, and, for some reason (probably because
|
|
// of differing ClassLoader implementations), that doesn't work, whether Rhino is loaded in the
|
|
// Web application or in the JRE extensions. When caching is turned off, Rhino can be loaded as
|
|
// part of the web application, and it can see all the standard classes and all the Web application
|
|
// classes. But we have to turn it off by manipulating it via the Reflection API, as, for all we know,
|
|
// Rhino isn't even being loaded right now. (Though we have BSF load Rhino right away if it can,
|
|
// to try and get this straightened out.)
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Entered fixupRhino");
|
|
|
|
try
|
|
{ // get a pointer to the Context class
|
|
Class klass = Class.forName("org.mozilla.javascript.Context");
|
|
|
|
// get a pointer to its setCachingEnabled method
|
|
final Class[] parmtypes = { Boolean.TYPE };
|
|
Method m = klass.getMethod("setCachingEnabled",parmtypes);
|
|
|
|
// invoke it to turn caching off!
|
|
final Object[] parms = { Boolean.FALSE };
|
|
m.invoke(null,parms);
|
|
|
|
// all done!
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("fixupRhino COMPLETED SUCCESSFULLY");
|
|
|
|
} // end try
|
|
catch (Exception e)
|
|
{ // just log the exception and proceed
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("fixupRhino NOT COMPLETED",e);
|
|
|
|
} // end catch
|
|
|
|
} // end fixupRhino
|
|
|
|
private static void translateReturnedObject(ScriptReturn sro) throws ScriptingException
|
|
{
|
|
Object obj = sro.get();
|
|
if (obj==null)
|
|
return; // nothing to do
|
|
|
|
Class klass = obj.getClass();
|
|
final String klassname = klass.getName();
|
|
|
|
// Special processing for JavaScript NativeError objects
|
|
if (klassname.equals("org.mozilla.javascript.NativeError"))
|
|
throw new ScriptingException("JavaScript runtime error: " + obj.toString());
|
|
|
|
} // end translateReturnedObject
|
|
|
|
private void translateScriptException(String source, String lang, BSFException e)
|
|
throws ScriptingException, ThrowableContent
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("translateScriptException for " + source + " [" + lang + "]");
|
|
|
|
Throwable t = e.getTargetException();
|
|
if (t instanceof ScriptExit)
|
|
{ // that was a deliberate exit from the script - discard it and continue
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("ScriptExit - normal condition");
|
|
return;
|
|
|
|
} // end if
|
|
|
|
if (t instanceof ThrowableContent)
|
|
{ // treat ThrowableContent specially
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("ThrowableContent (" + t.getClass().getName() + ") - no need to process");
|
|
throw (ThrowableContent)t;
|
|
|
|
} // end if
|
|
|
|
if (e.getReason()==BSFException.REASON_EXECUTION_ERROR)
|
|
{ // throw a ScriptingException
|
|
logger.error("Execution exception while running script",t);
|
|
if (t!=null)
|
|
throw new ScriptingException(source,lang,e.getMessage(),t);
|
|
else
|
|
throw new ScriptingException(source,lang,e.getMessage(),e);
|
|
|
|
} // end if
|
|
else if (e.getReason()==BSFException.REASON_OTHER_ERROR)
|
|
{ // usually indicates a compiler error in the scripting engine
|
|
logger.error("Script compilation error",e);
|
|
if (t!=null)
|
|
logger.error("Root cause:",t);
|
|
throw new ScriptingException(source,lang,e.getMessage());
|
|
|
|
} // end else if
|
|
else
|
|
{ // unknown exception type here!
|
|
logger.error("Unknown BSFException in script execution (reason " + e.getReason() + ")",e);
|
|
if (t!=null)
|
|
logger.error("Root cause:",t);
|
|
throw new ScriptingException("unexpected exception while executing " + source + " [" + lang + "]",e);
|
|
|
|
} // end else
|
|
|
|
} // end translateScriptException
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* External operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public void shutdown()
|
|
{
|
|
mgr.terminate();
|
|
registered_names.clear();
|
|
logger_stack.clear();
|
|
|
|
} // end shutdown
|
|
|
|
public void exec(File file, String logger_name, ScriptReturn sro) throws ScriptingException, ThrowableContent
|
|
{
|
|
String filename = file.getAbsolutePath();
|
|
String lang;
|
|
StringBuffer code;
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Exec'ing " + filename + ", logger " + logger_name);
|
|
|
|
try
|
|
{ // look up the scripting language for this file
|
|
lang = mgr.getLangFromFilename(filename);
|
|
code = IOUtil.loadText(file);
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Code language is " + lang);
|
|
|
|
} // end try
|
|
catch (BSFException e)
|
|
{ // this may be thrown by getLangFromFilename
|
|
throw new ScriptingException("unknown scripting language: file " + filename);
|
|
|
|
} // end catch
|
|
catch (IOException ie)
|
|
{ // this may be thrown by IOUtil.load
|
|
throw new ScriptingException("unable to load script file: " + filename,ie);
|
|
|
|
} // end catch
|
|
|
|
if (logger_name!=null) // push a new logger onto the stack
|
|
logger_stack.addFirst(Category.getInstance(logger_name));
|
|
if (sro!=null)
|
|
return_stack.addFirst(sro);
|
|
|
|
try
|
|
{ // execute the script!
|
|
mgr.exec(lang,filename,1,1,code);
|
|
|
|
} // end try
|
|
catch (BSFException e)
|
|
{ // translate the exception thrown by the script
|
|
translateScriptException(filename,lang,e);
|
|
|
|
} // end catch
|
|
finally
|
|
{ // pop the logger stack if necessary
|
|
if (logger_name!=null)
|
|
logger_stack.removeFirst();
|
|
if (sro!=null)
|
|
return_stack.removeFirst();
|
|
|
|
} // end finally
|
|
|
|
// if we've gotten this far, see if the returned object is actually an error that needs
|
|
// translation before it can be presented to the world
|
|
translateReturnedObject(sro);
|
|
|
|
} // end exec
|
|
|
|
public void register(String name, Object obj)
|
|
{
|
|
mgr.registerBean(name,obj);
|
|
HashSet tbl = (HashSet)(registered_names.getFirst());
|
|
tbl.add(name);
|
|
|
|
} // end register
|
|
|
|
public void pushContext()
|
|
{
|
|
registered_names.addFirst(new HashSet());
|
|
|
|
} // end pushContext
|
|
|
|
public void popContext()
|
|
{
|
|
HashSet tbl = (HashSet)(registered_names.removeFirst());
|
|
Iterator it = tbl.iterator();
|
|
while (it.hasNext())
|
|
mgr.unregisterBean((String)(it.next()));
|
|
if (registered_names.isEmpty())
|
|
registered_names.addFirst(new HashSet());
|
|
|
|
} // end popContext
|
|
|
|
public Object lookupObject(String name)
|
|
{
|
|
return mgr.lookupBean(name);
|
|
|
|
} // end lookupObject
|
|
|
|
} // end class ScriptManager
|