/*
* 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 .
*
* 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 ,
* 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