You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by st...@apache.org on 2004/06/24 18:48:54 UTC

cvs commit: cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript JavaScriptFlowTestCase.java JavaScriptFlowTestCase.xtest

stephan     2004/06/24 09:48:54

  Modified:    src/blocks/javaflow/java/org/apache/cocoon/components/flow/java
                        AbstractContinuable.java
                        ContinuationClassLoader.java JavaInterpreter.java
               src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/analyser
                        Subroutines.java
               src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test
                        JavaFlowTestCase.java JavaFlowTestCase.xtest
                        SimpleFlow.java
               src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript
                        JavaScriptFlowTestCase.java
                        JavaScriptFlowTestCase.xtest
  Added:       src/blocks/javaflow/java/org/apache/cocoon/components/flow/java
                        DecompilingVisitor.java
               src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript
                        JavaScriptHelper.java JavaScriptInterpreter.java
                        ScriptHelper.java
  Log:
  Using a "ScriptHelper" to encapsulate the instrumented JavaScript interpreter.
  Add a decompiler to debug the bytecode based on Torsten's work.
  The current problem is the usage of "xxx.class", which were
  translated to "Class.forName("..")".
  
  Revision  Changes    Path
  1.6       +16 -1     cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/AbstractContinuable.java
  
  Index: AbstractContinuable.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/AbstractContinuable.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- AbstractContinuable.java	23 Jun 2004 09:16:31 -0000	1.5
  +++ AbstractContinuable.java	24 Jun 2004 16:48:53 -0000	1.6
  @@ -16,11 +16,14 @@
   package org.apache.cocoon.components.flow.java;
   
   import org.apache.avalon.framework.CascadingRuntimeException;
  +import org.apache.avalon.framework.context.Context;
   import org.apache.avalon.framework.logger.Logger;
   import org.apache.avalon.framework.parameters.Parameters;
  +import org.apache.avalon.framework.service.ServiceManager;
   import org.apache.cocoon.components.ContextHelper;
   import org.apache.cocoon.components.flow.FlowHelper;
   import org.apache.cocoon.components.flow.util.PipelineUtil;
  +import org.apache.cocoon.environment.Redirector;
   import org.apache.cocoon.environment.Request;
   import org.apache.excalibur.source.SourceUtil;
   
  @@ -44,6 +47,18 @@
     
       public Logger getLogger() {
           return getContext().getLogger();
  +    }
  +    
  +    public Context getAvalonContext() {
  +        return getContext().getAvalonContext();
  +    }
  +
  +    public ServiceManager getServiceManager() {
  +        return getContext().getServiceManager();
  +    }
  + 
  +    public Redirector getRedirector() {
  +        return getContext().getRedirector();
       }
   
       public void sendPageAndWait(String uri) {
  
  
  
  1.11      +68 -13    cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/ContinuationClassLoader.java
  
  Index: ContinuationClassLoader.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/ContinuationClassLoader.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- ContinuationClassLoader.java	14 Jun 2004 15:08:35 -0000	1.10
  +++ ContinuationClassLoader.java	24 Jun 2004 16:48:53 -0000	1.11
  @@ -15,6 +15,7 @@
    */
   package org.apache.cocoon.components.flow.java;
   
  +import java.io.FileOutputStream;
   import java.util.*;
   
   import org.apache.bcel.*;
  @@ -57,6 +58,8 @@
   
       private static boolean currentMethodStatic;
       
  +    private boolean debug = false;
  +	
       private List includes = new ArrayList();
   
       public ContinuationClassLoader(ClassLoader parent) {
  @@ -64,6 +67,10 @@
           Repository.setRepository(new ClassLoaderRepository(parent));
       }
       
  +    public void setDebug(boolean debug) {
  +    	this.debug = debug;
  +    }
  +    
       public void addIncludeClass(String pattern) {
           
           StringBuffer buffer = new StringBuffer();
  @@ -106,12 +113,16 @@
       protected synchronized Class loadClass(String name, boolean resolve)
               throws ClassNotFoundException {
           // this finds also classes, which are already transformed, via findLoadedClass
  +    	if (debug)
  +    		System.out.println("load class "+name);
           Class c = super.loadClass(name, resolve);
   
           // transform class if class is continuable and not continuation capable
           if (isContinuable(c) && 
               (!ContinuationCapable.class.isAssignableFrom(c)) && 
               (!c.isInterface())) {
  +        	if (debug)
  +        		System.out.println("instrument class "+name);
               JavaClass clazz = Repository.lookupClass(c);
   
               byte data[] = transform(clazz);
  @@ -127,9 +138,21 @@
       }
   
       private byte[] transform(JavaClass javaclazz) throws ClassNotFoundException {
  +    	
           // make all methods of java class continuable
           ClassGen clazz = new ClassGen(javaclazz);
           ConstantPoolGen cp = clazz.getConstantPool();
  +        
  +        if (debug) {
  +        	try {
  +                FileOutputStream fos = new FileOutputStream(javaclazz.getClassName() + ".orig.j");
  +                DecompilingVisitor v = new DecompilingVisitor(javaclazz, fos);
  +                v.start();
  +            } catch (Exception e) {
  +                e.printStackTrace();
  +            }
  +        }
  +        
           // vistor to build the frame information
           ExecutionVisitor ev = new ExecutionVisitor();
           ev.setConstantPoolGen(cp);
  @@ -144,11 +167,13 @@
                   // information about every instruction
                   ControlFlowGraph cfg = new ControlFlowGraph(method);
                   
  -                //System.out.println("analyse " + methods[i].getName());
  +                if (debug)
  +                	System.out.println("analyse " + methods[i].getName());
                   analyse(clazz, method, cfg, ev);
                   
                   // add intercepting code
  -                //System.out.println("rewriting " + methods[i].getName());
  +                if (debug)
  +                	System.out.println("rewriting " + methods[i].getName());
                   rewrite(method, cfg);
                   
                   // make last optional check for consistency
  @@ -168,15 +193,25 @@
               }
           }
           
  -        /*byte[] changed = clazz.getJavaClass().getBytes();
  -        try {
  -            java.io.FileOutputStream out = new java.io.FileOutputStream(clazz.getClassName() + ".rewritten.class");
  -            out.write(changed);
  -            out.flush();
  -            out.close();
  -        } catch (java.io.IOException ioe) {
  -            ioe.printStackTrace();
  -        }*/
  +        if (debug) {
  +        	byte[] changed = clazz.getJavaClass().getBytes();
  +        	try {
  +        		FileOutputStream out = new FileOutputStream(clazz.getClassName() + ".rewritten.class");
  +        		out.write(changed);
  +        		out.flush();
  +        		out.close();
  +        	} catch (java.io.IOException ioe) {
  +        		ioe.printStackTrace();
  +        	}
  +        	
  +        	try {
  +                FileOutputStream fos = new FileOutputStream(clazz.getClassName() + ".rewritten.j");
  +                DecompilingVisitor v = new DecompilingVisitor(clazz.getJavaClass(), fos);
  +                v.start();
  +            } catch (Exception e) {
  +                e.printStackTrace();
  +            }
  +        }
           
           clazz.addInterface(CONTINUATIONCAPABLE_CLASS);
           return clazz.getJavaClass().getBytes();
  @@ -417,6 +452,8 @@
               insList.insert(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
   
               // test if the continuation should be restored
  +            if (debug)
  +            	insList.insert(insFactory.createPrintln("restoring invocation "+method));
               insList.insert(new IFEQ(firstIns));
               insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, RESTORING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
               insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
  @@ -541,6 +578,8 @@
           boolean skipFirst = returnType.getSize() > 0;
   
           // save stack
  +        if (debug)
  +        	insList.append(insFactory.createPrintln("save stack"));
           OperandStack os = frame.getStack();
           for (int i = skipFirst ? 1 : 0; i < os.size(); i++) {
               Type type = os.peek(i);
  @@ -572,15 +611,24 @@
                   insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
               }
           }
  +        
           // add isCapturing test
  +        if (debug)
  +        	insList.insert(insFactory.createPrintln("capturing invocation "+method));
           insList.insert(new IFEQ(handle.getNext()));
  +        
           // test if the continuation should be captured after the invocation
           insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, CAPURING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
           insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
  +        
           // test if continuation exists
           insList.insert(new IFNULL(handle.getNext()));
           insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
  +        
           // save local variables
  +        if (debug)
  +        	insList.append(insFactory.createPrintln("save local variables"));
  +        
           LocalVariables lvs = frame.getLocals();
           for (int i = 0; i < lvs.maxLocals(); i++) {
               Type type = lvs.get(i);
  @@ -619,7 +667,11 @@
       private InstructionList restoreFrame(MethodGen method, InstructionHandle handle,
               InstructionFactory insFactory, Frame frame, ObjectType objecttype) {
           InstructionList insList = new InstructionList();
  +        
           // restore local variables
  +        if (debug)
  +        	insList.append(insFactory.createPrintln("restore local variables"));
  +        
           LocalVariables lvs = frame.getLocals();
           for (int i = lvs.maxLocals() - 1; i >= 0; i--) {
               Type type = lvs.get(i);
  @@ -649,8 +701,11 @@
           InvokeInstruction inv = (InvokeInstruction) handle.getInstruction();
           Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex());
           boolean skipFirst = returnType.getSize() > 0;
  -
  +        
           // restore stack
  +        if (debug)
  +        	insList.append(insFactory.createPrintln("restore stack"));
  +        
           OperandStack os = frame.getStack();
           for (int i = os.size() - 1; i >= (skipFirst ? 1 : 0); i--) {
               Type type = os.peek(i);
  
  
  
  1.10      +2 -1      cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/JavaInterpreter.java
  
  Index: JavaInterpreter.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/JavaInterpreter.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- JavaInterpreter.java	23 Jun 2004 09:16:32 -0000	1.9
  +++ JavaInterpreter.java	24 Jun 2004 16:48:53 -0000	1.10
  @@ -77,6 +77,7 @@
               throw new ConfigurationException(e.getMessage());
           }
           continuationclassloader = new ContinuationClassLoader(javascriptclassloader);
  +        continuationclassloader.setDebug(config.getAttributeAsBoolean("debug", false));
   
           Configuration[] includes = config.getChildren("include");
           for (int i = 0; i < includes.length; i++)
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/DecompilingVisitor.java
  
  Index: DecompilingVisitor.java
  ===================================================================
  /*
   * Copyright 1999-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.apache.cocoon.components.flow.java;
  
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.Hashtable;
  
  import org.apache.bcel.Constants;
  import org.apache.bcel.classfile.Attribute;
  import org.apache.bcel.classfile.Code;
  import org.apache.bcel.classfile.ConstantValue;
  import org.apache.bcel.classfile.Deprecated;
  import org.apache.bcel.classfile.ExceptionTable;
  import org.apache.bcel.classfile.Field;
  import org.apache.bcel.classfile.JavaClass;
  import org.apache.bcel.classfile.Method;
  import org.apache.bcel.classfile.Synthetic;
  import org.apache.bcel.classfile.Utility;
  import org.apache.bcel.generic.BranchInstruction;
  import org.apache.bcel.generic.CodeExceptionGen;
  import org.apache.bcel.generic.ConstantPoolGen;
  import org.apache.bcel.generic.Instruction;
  import org.apache.bcel.generic.InstructionHandle;
  import org.apache.bcel.generic.InstructionList;
  import org.apache.bcel.generic.LocalVariableGen;
  import org.apache.bcel.generic.MethodGen;
  import org.apache.bcel.generic.ObjectType;
  import org.apache.bcel.generic.Select;
  import org.apache.bcel.generic.TABLESWITCH;
  
  public final class DecompilingVisitor extends org.apache.bcel.classfile.EmptyVisitor {
  	private JavaClass clazz;
  	private PrintWriter out;
  	private String clazzname;
  	private ConstantPoolGen cp;
  
  	public DecompilingVisitor(JavaClass clazz, OutputStream out) {
  		this.clazz = clazz;
  		this.out = new PrintWriter(out);
  		clazzname = clazz.getClassName();
  		cp = new ConstantPoolGen(clazz.getConstantPool());
  	}
  
  	public void start() {
  		new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit();
  		out.close();
  	}
  
  	public void visitJavaClass(JavaClass clazz) {
  
  		out.println("// source " + clazz.getSourceFileName());
  		out.println(Utility.accessToString(clazz.getAccessFlags(), true) + " "
  				+ Utility.classOrInterface(clazz.getAccessFlags()) + " "
  				+ clazz.getClassName().replace('.', '/'));
  		out.println("  extends " + clazz.getSuperclassName().replace('.', '/'));
  
  		String[] interfaces = clazz.getInterfaceNames();
  
  		if (interfaces.length > 0) {
  			out.print("  implements");
  			for (int i = 0; i < interfaces.length; i++)
  				out.print(" " + interfaces[i].replace('.', '/'));
  			out.println();
  		}
  		out.println();
  	}
  
  	public void visitField(Field field) {
  		out.print("  " + Utility.accessToString(field.getAccessFlags()) + " "
  				+ field.getName() + " " + field.getSignature());
  		if (field.getAttributes().length == 0)
  			out.print("\n");
  	}
  
  	public void visitConstantValue(ConstantValue cv) {
  		out.println(" = " + cv);
  	}
  
  	private Method _method;
  
  	/**
  	 * Unfortunately Jasmin expects ".end method" after each method. Thus we've
  	 * to check for every of the method's attributes if it's the last one and
  	 * print ".end method" then.
  	 */
  	private final void printEndMethod(Attribute attr) {
  		Attribute[] attributes = _method.getAttributes();
  
  	}
  
  	public void visitDeprecated(Deprecated attribute) {
  		printEndMethod(attribute);
  	}
  
  	public void visitSynthetic(Synthetic attribute) {
  		if (_method != null)
  			printEndMethod(attribute);
  	}
  
  	public void visitMethod(Method method) {
  		this._method = method; // Remember for use in subsequent visitXXX calls
  
  		out.println("\n  " + Utility.accessToString(_method.getAccessFlags())
  				+ " " + _method.getName() + _method.getSignature());
  
  	}
  
  	public void visitExceptionTable(ExceptionTable e) {
  		String[] names = e.getExceptionNames();
  		for (int i = 0; i < names.length; i++)
  			out.println("    throws " + names[i].replace('.', '/'));
  
  		printEndMethod(e);
  	}
  
  	private Hashtable map;
  
  	public void visitCode(Code code) {
  		int label_counter = 0;
  
  		MethodGen mg = new MethodGen(_method, clazzname, cp);
  		InstructionList il = mg.getInstructionList();
  		InstructionHandle[] ihs = il.getInstructionHandles();
  
  		LocalVariableGen[] lvs = mg.getLocalVariables();
  
  		CodeExceptionGen[] ehs = mg.getExceptionHandlers();
  
  		for (int i = 0; i < lvs.length; i++) {
  			LocalVariableGen l = lvs[i];
  			out.println("    // var " + l.getIndex() + " is \"" + l.getName()
  					+ "\" " + l.getType().getSignature() + " from "
  					+ l.getStart().getPosition() + " to "
  					+ l.getEnd().getPosition());
  		}
  
  		out.print("\n");
  
  		for (int i = 0; i < ihs.length; i++) {
  			InstructionHandle ih = ihs[i];
  			Instruction inst = ih.getInstruction();
  
  			out.print("    " + ih.getPosition());
  
  			if (inst instanceof BranchInstruction) {
  				if (inst instanceof Select) { // Special cases LOOKUPSWITCH and
  											  // TABLESWITCH
  					Select s = (Select) inst;
  					int[] matchs = s.getMatchs();
  					InstructionHandle[] targets = s.getTargets();
  
  					if (s instanceof TABLESWITCH) {
  						out.println("  tableswitch " + matchs[0] + " "
  								+ matchs[matchs.length - 1]);
  
  						for (int j = 0; j < targets.length; j++)
  							out.println("        " + targets[j].getPosition());
  
  					} else { // LOOKUPSWITCH
  						out.println("  lookupswitch ");
  
  						for (int j = 0; j < targets.length; j++)
  							out.println("        " + matchs[j] + " : "
  									+ targets[j].getPosition());
  					}
  
  					out.println("        default: " + s.getTarget()); // Applies
  																	  // for
  																	  // both
  				} else {
  					BranchInstruction bi = (BranchInstruction) inst;
  					ih = bi.getTarget();
  					//str = get(ih);
  					out.println("  " + Constants.OPCODE_NAMES[bi.getOpcode()]
  							+ " " + ih);
  				}
  			} else
  				out.println("  " + inst.toString(cp.getConstantPool()));
  		}
  
  		out.print("\n");
  
  		for (int i = 0; i < ehs.length; i++) {
  			CodeExceptionGen c = ehs[i];
  			ObjectType caught = c.getCatchType();
  			String class_name = (caught == null) ? // catch any exception, used
  												   // when compiling finally
  					"all" : caught.getClassName().replace('.', '/');
  
  			out.println("    catch " + class_name + " from "
  					+ c.getStartPC().getPosition() + " to "
  					+ c.getEndPC().getPosition() + " using "
  					+ c.getHandlerPC().getPosition());
  		}
  
  		printEndMethod(code);
  	}
  }
  
  
  
  
  1.2       +3 -3      cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/analyser/Subroutines.java
  
  Index: Subroutines.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/analyser/Subroutines.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Subroutines.java	3 Jun 2004 12:43:27 -0000	1.1
  +++ Subroutines.java	24 Jun 2004 16:48:53 -0000	1.2
  @@ -449,7 +449,7 @@
   		
   		// Now make sure no instruction of a Subroutine is protected by exception handling code
   		// as is mandated by JustIces notion of subroutines.
  -		for (int i=0; i<handlers.length; i++){
  +		/*for (int i=0; i<handlers.length; i++){
   			InstructionHandle _protected = handlers[i].getStartPC();
   			while (_protected != handlers[i].getEndPC().getNext()){// Note the inclusive/inclusive notation of "generic API" exception handlers!
   				Enumeration subs = subroutines.elements();
  @@ -463,7 +463,7 @@
   				}
   				_protected = _protected.getNext();
   			}
  -		}
  +		}*/
   		
   		// Now make sure no subroutine is calling a subroutine
   		// that uses the same local variable for the RET as themselves
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript/JavaScriptHelper.java
  
  Index: JavaScriptHelper.java
  ===================================================================
  /*
   * Copyright 1999-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.apache.cocoon.components.flow.javascript;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  
  import org.apache.avalon.framework.CascadingRuntimeException;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.ResourceNotFoundException;
  import org.apache.cocoon.components.flow.Interpreter;
  import org.apache.cocoon.components.flow.java.AbstractContinuable;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.Session;
  import org.apache.commons.jxpath.JXPathIntrospector;
  import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
  import org.apache.excalibur.source.Source;
  import org.apache.excalibur.source.SourceResolver;
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.EcmaError;
  import org.mozilla.javascript.EvaluatorException;
  import org.mozilla.javascript.Function;
  import org.mozilla.javascript.JavaScriptException;
  import org.mozilla.javascript.NativeJavaClass;
  import org.mozilla.javascript.NativeJavaPackage;
  import org.mozilla.javascript.PropertyException;
  import org.mozilla.javascript.ScriptRuntime;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  import org.mozilla.javascript.Wrapper;
  import org.mozilla.javascript.tools.ToolErrorReporter;
  import org.mozilla.javascript.tools.shell.Global;
  
  /**
   * Interface with the JavaScript interpreter.
   *
   * @author <a href="mailto:ovidiu@apache.org">Ovidiu Predescu</a>
   * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
   * @since March 25, 2002
   * @version CVS $Id: JavaScriptHelper.java,v 1.1 2004/06/24 16:48:53 stephan Exp $
   */
  public class JavaScriptHelper extends AbstractContinuable
      implements ScriptHelper {
  	
  	/**
       * List of source locations that need to be resolved.
       */
      protected ArrayList needResolve = new ArrayList();
      
      /**
       * Whether reloading of scripts should be done. Specified through
       * the "reload-scripts" attribute in <code>flow.xmap</code>.
       */
      protected boolean reloadScripts;
  
      /**
       * Interval between two checks for modified script files. Specified
       * through the "check-time" XML attribute in <code>flow.xmap</code>.
       */
      protected long checkTime;
  	
  	/** A source resolver */
      protected SourceResolver sourceresolver;
      /**
       * Mapping of String objects (source uri's) to ScriptSourceEntry's
       */
      //protected Map compiledScripts = new HashMap();
  
      /**
       * LAST_EXEC_TIME
       * A long value is stored under this key in each top level JavaScript
       * thread scope object. When you enter a context any scripts whose
       * modification time is later than this value will be recompiled and reexecuted,
       * and this value will be updated to the current time.
       */
      private final static String LAST_EXEC_TIME = "__PRIVATE_LAST_EXEC_TIME__";
  
      /**
       * Key for storing a JavaScript global scope object in the Cocoon session
       */
      public static final String USER_GLOBAL_SCOPE = "FOM JavaScript GLOBAL SCOPE";
  
      // This is the only optimization level that supports continuations
      // in the Christoper Oliver's Rhino JavaScript implementation
      public static int OPTIMIZATION_LEVEL = -2;
  
      /**
       * When was the last time we checked for script modifications. Used
       * only if {@link #reloadScripts} is true.
       */
      protected long lastTimeCheck = 0;
  
      /**
       * Shared global scope for scripts and other immutable objects
       */
      private Global scope;
  
      /**
       * List of <code>String</code> objects that represent files to be
       * read in by the JavaScript interpreter.
       */
      private List topLevelScripts = new ArrayList();
  
      private JSErrorReporter errorReporter;
      private boolean enableDebugger = false;
      
      /**
       * Registers a source file with the interpreter. Using this method
       * an implementation keeps track of all the script files which are
       * compiled. This allows them to reload the script files which get
       * modified on the file system.
       *
       * <p>The parsing/compilation of a script file by an interpreter
       * happens in two phases. In the first phase the file's location is
       * registered in the <code>needResolve</code> array.
       *
       * <p>The second is possible only when a Cocoon
       * <code>Environment</code> is passed to the Interpreter. This
       * allows the file location to be resolved using Cocoon's
       * <code>SourceFactory</code> class.
       *
       * <p>Once a file's location can be resolved, it is removed from the
       * <code>needResolve</code> array and placed in the
       * <code>scripts</code> hash table. The key in this hash table is
       * the file location string, and the value is a
       * DelayedRefreshSourceWrapper instance which keeps track of when
       * the file needs to re-read.
       *
       * @param source the location of the script
       *
       * @see org.apache.cocoon.environment.Environment
       * @see org.apache.cocoon.components.source.impl.DelayedRefreshSourceWrapper
       */
      public void register(String source)
      {
          synchronized(this) {
              needResolve.add(source);
          }
      }
  
      public void configure(Configuration config) throws ConfigurationException {
          
          reloadScripts = config.getChild("reload-scripts").getValueAsBoolean(false);
          checkTime = config.getChild("check-time").getValueAsLong(1000L);
  
          String loadOnStartup
              = config.getChild("load-on-startup", true).getValue(null);
          if (loadOnStartup != null) {
              register(loadOnStartup);
          }
  
          String debugger = config.getChild("debugger").getValue(null);
          if ("enabled".equalsIgnoreCase(debugger)) {
              enableDebugger = true;
          }
      }
  
      public void initialize() throws Exception {
          if (enableDebugger) {
              if (getLogger().isDebugEnabled()) {
                  getLogger().debug("Flow debugger enabled, creating");
              }
          }
          Context context = Context.enter();
          context.setOptimizationLevel(OPTIMIZATION_LEVEL);
          context.setCompileFunctionsWithDynamicScope(true);
          context.setGeneratingDebug(true);
          // add support for Rhino objects to JXPath
          JXPathIntrospector.registerDynamicClass(Scriptable.class,
                                                  ScriptablePropertyHandler.class);
          JXPathContextReferenceImpl.addNodePointerFactory(new ScriptablePointerFactory());
  
          try {
              scope = new Global(context);
              // Access to Cocoon internal objects
              errorReporter = new JSErrorReporter(getLogger());
          } catch (Exception e) {
              Context.exit();
              e.printStackTrace();
              throw e;
          }
      }
  
      /**
       * Returns the JavaScript scope, a Scriptable object, from the user
       * session instance. Each sitemap can have a scope associated with it.
       *
       * @return a <code>Scriptable</code> value
       */
      private ThreadScope getSessionScope() throws Exception {
          Request request = getRequest();
          ThreadScope scope = null;
          Session session = request.getSession(false);
          if (session != null) {
              HashMap userScopes =
                      (HashMap)session.getAttribute(USER_GLOBAL_SCOPE);
              if (userScopes != null) {
                  // Get the scope attached to the current context
                  scope = (ThreadScope)userScopes.get(getSitemapPath());
              }
          }
          if (scope == null) {
              scope = createThreadScope();
          }
          return scope;
      }
  
      void updateSession(Scriptable scope) throws Exception {
          ThreadScope thrScope = (ThreadScope)scope;
          if (thrScope.useSession) {
              setSessionScope(scope);
          }
      }
  
      /**
       * Associates a JavaScript scope, a Scriptable object, with the
       * directory path of the current sitemap, as resolved by the
       * source resolver.
       *
       * @param scope a <code>Scriptable</code> value
       */
      private Scriptable setSessionScope(Scriptable scope) throws Exception {
          Request request = getRequest();
  
          // FIXME: Where "session scope" should go when session is invalidated?
          try {
              Session session = request.getSession(true);
  
              HashMap userScopes = (HashMap)session.getAttribute(USER_GLOBAL_SCOPE);
              if (userScopes == null) {
                  userScopes = new HashMap();
                  session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
              }
  
              // Attach the scope to the current context
              userScopes.put(getSitemapPath(), scope);
          } catch (IllegalStateException e) {
              // Session might be invalidated already.
              if (getLogger().isDebugEnabled()) {
                  getLogger().debug("Got '" + e + "' while trying to set session scope.", e);
              }
          }
          return scope;
      }
  
      private String getSitemapPath() throws Exception {
          Source src = this.sourceresolver.resolveURI(".");
          try {
              return src.getURI();
          } finally {
              this.sourceresolver.release(src);
          }
      }
  
      public static class ThreadScope extends ScriptableObject {
          static final String[] builtinPackages = {"javax", "org", "com"};
  
          ClassLoader classLoader;
  
          /* true if this scope has assigned any global vars */
          boolean useSession = false;
  
          public ThreadScope() {
              final String[] names = { "importClass"};
  
              try {
                  this.defineFunctionProperties(names, ThreadScope.class,
                                                ScriptableObject.DONTENUM);
              } catch (PropertyException e) {
                  throw new Error();  // should never happen
              }
          }
  
          public String getClassName() {
              return "ThreadScope";
          }
  
          public void put(String name, Scriptable start, Object value) {
              useSession = true;
              super.put(name, start, value);
          }
  
          public void put(int index, Scriptable start, Object value) {
              useSession = true;
              super.put(index, start, value);
          }
  
          void reset() {
              useSession = false;
          }
  
          // Override importClass to allow reloading of classes
          public static void importClass(Context cx, Scriptable thisObj,
                                         Object[] args, Function funObj) {
              for (int i = 0; i < args.length; i++) {
                  Object cl = args[i];
                  if (!(cl instanceof NativeJavaClass)) {
                      throw Context.reportRuntimeError("Not a Java class: " +
                                                         Context.toString(cl));
                  }
                  String s = ((NativeJavaClass) cl).getClassObject().getName();
                  String n = s.substring(s.lastIndexOf('.')+1);
                  thisObj.put(n, thisObj, cl);
              }
          }
  
          public void setupPackages(ClassLoader cl) throws Exception {
              final String JAVA_PACKAGE = "JavaPackage";
              if (classLoader != cl) {
                  classLoader = cl;
                  Scriptable newPackages = new NativeJavaPackage("", cl);
                  newPackages.setParentScope(this);
                  newPackages.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
                  super.put("Packages", this, newPackages);
                  for (int i = 0; i < builtinPackages.length; i++) {
                      String pkgName = builtinPackages[i];
                      Scriptable pkg = new NativeJavaPackage(pkgName, cl);
                      pkg.setParentScope(this);
                      pkg.setPrototype(ScriptableObject.getClassPrototype(this, JAVA_PACKAGE));
                      super.put(pkgName, this, pkg);
                  }
              }
          }
  
          public ClassLoader getClassLoader() {
              return classLoader;
          }
      }
  
      private ThreadScope createThreadScope() throws Exception {
          Context context = Context.getCurrentContext();
  
          ThreadScope thrScope = new ThreadScope();
  
          thrScope.setPrototype(scope);
          // We want 'thrScope' to be a new top-level scope, so set its
          // parent scope to null. This means that any variables created
          // by assignments will be properties of "thrScope".
          thrScope.setParentScope(null);
          // Put in the thread scope the Cocoon object, which gives access
          // to the interpreter object, and some Cocoon objects. See
          // FOM_Cocoon for more details.
          Object[] args = {};
          thrScope.defineProperty(LAST_EXEC_TIME,
                                  new Long(0),
                                  ScriptableObject.DONTENUM | ScriptableObject.PERMANENT);
  
          thrScope.reset();
          return thrScope;
      }
  
      /**
       * Returns a new Scriptable object to be used as the global scope
       * when running the JavaScript scripts in the context of a request.
       *
       * <p>If you want to maintain the state of global variables across
       * multiple invocations of <code>&lt;map:call
       * function="..."&gt;</code>, you need to instanciate the session
       * object which is a property of the cocoon object
       * <code>var session = cocoon.session</code>. This will place the
       * newly create Scriptable object in the user's session, where it
       * will be retrieved from at the next invocation of {@link #callFunction}.</p>
       *
       * @exception Exception if an error occurs
       */
      private void setupContext(Redirector redirector, Context context,
                                ThreadScope thrScope)
          throws Exception {
          // Try to retrieve the scope object from the session instance. If
          // no scope is found, we create a new one, but don't place it in
          // the session.
          //
          // When a user script "creates" a session using
          // cocoon.createSession() in JavaScript, the thrScope is placed in
          // the session object, where it's later retrieved from here. This
          // behaviour allows multiple JavaScript functions to share the
          // same global scope.
  
          long lastExecTime = ((Long)thrScope.get(LAST_EXEC_TIME,
                                                  thrScope)).longValue();
          boolean needsRefresh = false;
          if (reloadScripts) {
              long now = System.currentTimeMillis();
              if (now >= lastTimeCheck + checkTime) {
                  needsRefresh = true;
              }
              lastTimeCheck = now;
          }
          // We need to setup the FOM_Cocoon object according to the current
          // request. Everything else remains the same.
          //Thread.currentThread().setContextClassLoader(classLoader);
          //thrScope.setupPackages(classLoader);
  
          // Check if we need to compile and/or execute scripts
          /*synchronized (compiledScripts) {
              List execList = new ArrayList();
              // If we've never executed scripts in this scope or
              // if reload-scripts is true and the check interval has expired
              // or if new scripts have been specified in the sitemap,
              // then create a list of scripts to compile/execute
              if (lastExecTime == 0 || needsRefresh || needResolve.size() > 0) {
                  topLevelScripts.addAll(needResolve);
                  if (lastExecTime != 0 && !needsRefresh) {
                      execList.addAll(needResolve);
                  } else {
                      execList.addAll(topLevelScripts);
                  }
                  needResolve.clear();
              }
              // Compile all the scripts first. That way you can set breakpoints
              // in the debugger before they execute.
              for (int i = 0, size = execList.size(); i < size; i++) {
                  String sourceURI = (String)execList.get(i);
                  ScriptSourceEntry entry =
                      (ScriptSourceEntry)compiledScripts.get(sourceURI);
                  if (entry == null) {
                      Source src = this.sourceresolver.resolveURI(sourceURI);
                      entry = new ScriptSourceEntry(src);
                      compiledScripts.put(sourceURI, entry);
                  }
                  // Compile the script if necessary
                  entry.getScript(context, this.scope, needsRefresh, this);
              }
              // Execute the scripts if necessary
              for (int i = 0, size = execList.size(); i < size; i++) {
                  String sourceURI = (String)execList.get(i);
                  ScriptSourceEntry entry =
                      (ScriptSourceEntry)compiledScripts.get(sourceURI);
                  long lastMod = entry.getSource().getLastModified();
                  Script script = entry.getScript(context, this.scope, false, this);
                  if (lastExecTime == 0 || lastMod > lastExecTime) {
                      script.exec(context, thrScope);
                      thrScope.put(LAST_EXEC_TIME, thrScope,
                                   new Long(System.currentTimeMillis()));
                      thrScope.reset();
                  }
              }
          }*/
      }
  
      /**
       * Compile filename as JavaScript code
       *
       * @param cx Rhino context
       * @param fileName resource uri
       * @return compiled script
       */
      /*Script compileScript(Context cx, String fileName) throws Exception {
          Source src = this.sourceresolver.resolveURI(fileName);
          if (src != null) {
              synchronized (compiledScripts) {
                  ScriptSourceEntry entry =
                      (ScriptSourceEntry)compiledScripts.get(src.getURI());
                  Script compiledScript = null;
                  if (entry == null) {
                      compiledScripts.put(src.getURI(),
                              entry = new ScriptSourceEntry(src));
                  } else {
                      this.sourceresolver.release(src);
                  }
                  compiledScript = entry.getScript(cx, this.scope, false, this);
                  return compiledScript;
              }
          } else {
              throw new ResourceNotFoundException(fileName + ": not found");
          }
      }*/
  
      /*protected Script compileScript(Context cx, Scriptable scope, Source src)
              throws Exception {
          InputStream is = src.getInputStream();
          if (is != null) {
              try {
                  Reader reader = new BufferedReader(new InputStreamReader(is));
                  Script compiledScript = cx.compileReader(scope, reader,
                          src.getURI(), 1, null);
                  return compiledScript;
              } finally {
                  is.close();
              }
          } else {
              throw new ResourceNotFoundException(src.getURI() + ": not found");
          }
      }*/
  
      /**
       * Calls a JavaScript function, passing <code>params</code> as its
       * arguments. In addition to this, it makes available the parameters
       * through the <code>cocoon.parameters</code> JavaScript array
       * (indexed by the parameter names).
       *
       * @param funName a <code>String</code> value
       * @param params a <code>List</code> value
       * @param redirector
       * @exception Exception if an error occurs
       */
      public void callFunction(String funName, List params) throws Exception {
          Context context = Context.enter();
          context.setOptimizationLevel(OPTIMIZATION_LEVEL);
          context.setGeneratingDebug(true);
          context.setCompileFunctionsWithDynamicScope(true);
          context.setErrorReporter(errorReporter);
          ThreadScope thrScope = getSessionScope();
          synchronized (thrScope) {
              ClassLoader savedClassLoader =
                  Thread.currentThread().getContextClassLoader();
              try {
                  try {
                      setupContext(getRedirector(), context, thrScope);
  
                      int size = (params != null ? params.size() : 0);
                      Object[] funArgs = new Object[size];
                      Scriptable parameters = context.newObject(thrScope);
                      for (int i = 0; i < size; i++) {
                          Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
                          funArgs[i] = arg.value;
                          if (arg.name == null) {
                              arg.name = "";
                          }
                          parameters.put(arg.name, parameters, arg.value);
                      }
                      Object fun = ScriptableObject.getProperty(thrScope, funName);
                      if (fun == Scriptable.NOT_FOUND) {
                          throw new ResourceNotFoundException("Function \"javascript:" + funName + "()\" not found");
                      }
                      ScriptRuntime.call(context, fun, thrScope, funArgs, thrScope);
                  } catch (JavaScriptException ex) {
                      EvaluatorException ee = Context.reportRuntimeError(
                                                                         ToolErrorReporter.getMessage("msg.uncaughtJSException",
                                                                                                      ex.getMessage()));
                      Throwable unwrapped = unwrap(ex);
                      if (unwrapped instanceof ProcessingException) {
                          throw (ProcessingException)unwrapped;
                      }
                      throw new CascadingRuntimeException(ee.getMessage(),
                                                          unwrapped);
                  } catch (EcmaError ee) {
                      String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ee.toString());
                      if (ee.getSourceName() != null) {
                          Context.reportRuntimeError(msg, ee.getSourceName(),
                                                     ee.getLineNumber(), ee.getLineSource(),
                                                     ee.getColumnNumber());
                      } else {
                          Context.reportRuntimeError(msg);
                      }
                      throw new CascadingRuntimeException(ee.getMessage(), ee);
                  }
              } finally {
                  updateSession(thrScope);
                  Context.exit();
                  Thread.currentThread().setContextClassLoader(savedClassLoader);
              }
          }
      }
  
      /*public void handleContinuation(String id, List params,
                                     Redirector redirector) throws Exception
      {
          WebContinuation wk = continuationsMgr.lookupWebContinuation(id);
  
          if (wk == null) {
              throw new InvalidContinuationException("The continuation ID " + id + " is invalid.");
          }
  
          Context context = Context.enter();
          context.setOptimizationLevel(OPTIMIZATION_LEVEL);
          context.setGeneratingDebug(true);
          context.setCompileFunctionsWithDynamicScope(true);
  
          // Obtain the continuation object from it, and setup the
          // FOM_Cocoon object associated in the dynamic scope of the saved
          // continuation with the environment and context objects.
          Continuation k = (Continuation)wk.getContinuation();
          ThreadScope kScope = (ThreadScope)k.getParentScope();
          synchronized (kScope) {
              ClassLoader savedClassLoader =
                  Thread.currentThread().getContextClassLoader();
              FOM_Cocoon cocoon = null;
              try {
                  Thread.currentThread().setContextClassLoader(kScope.getClassLoader());
                  cocoon = (FOM_Cocoon)kScope.get("cocoon", kScope);
                  cocoon.pushCallContext(this, redirector, getServiceManager(),
                                         getAvalonContext(),
                                         getLogger(), wk);
  
                  // Register the current scope for scripts indirectly called from this function
                  FOM_JavaScriptFlowHelper.setFOM_FlowScope(cocoon.getObjectModel(), kScope);
  
                  Scriptable parameters = context.newObject(kScope);
                  int size = params != null ? params.size() : 0;
                  for (int i = 0; i < size; i++) {
                      Interpreter.Argument arg = (Interpreter.Argument)params.get(i);
                      parameters.put(arg.name, parameters, arg.value);
                  }
                  cocoon.setParameters(parameters);
                  FOM_WebContinuation fom_wk = new FOM_WebContinuation(wk);
                  fom_wk.setParentScope(kScope);
                  fom_wk.setPrototype(ScriptableObject.getClassPrototype(kScope,
                                                                         fom_wk.getClassName()));
                  Object[] args = new Object[] {k, fom_wk};
                  try {
                      ScriptableObject.callMethod(cocoon,
                                                  "handleContinuation", args);
                  } catch (JavaScriptException ex) {
                      EvaluatorException ee = Context.reportRuntimeError(
                                                                         ToolErrorReporter.getMessage("msg.uncaughtJSException",
                                                                                                      ex.getMessage()));
                      Throwable unwrapped = unwrap(ex);
                      if (unwrapped instanceof ProcessingException) {
                          throw (ProcessingException)unwrapped;
                      }
                      throw new CascadingRuntimeException(ee.getMessage(),
                                                          unwrapped);
                  } catch (EcmaError ee) {
                      String msg = ToolErrorReporter.getMessage("msg.uncaughtJSException", ee.toString());
                      if (ee.getSourceName() != null) {
                          Context.reportRuntimeError(msg, ee.getSourceName(),
                                                     ee.getLineNumber(), ee.getLineSource(),
                                                     ee.getColumnNumber());
                      } else {
                          Context.reportRuntimeError(msg);
                      }
                      throw new CascadingRuntimeException(ee.getMessage(), ee);
                  }
              } finally {
                  updateSession(kScope);
                  if (cocoon != null) {
                      cocoon.popCallContext();
                  }
                  Context.exit();
                  Thread.currentThread().setContextClassLoader(savedClassLoader);
              }
          }
      }*/
  
      private Throwable unwrap(JavaScriptException e) {
          Object value = e.getValue();
          while (value instanceof Wrapper) {
              value = ((Wrapper)value).unwrap();
          }
          if (value instanceof Throwable) {
              return (Throwable)value;
          }
          return e;
      }
  
      /*private void setupView(Scriptable scope, FOM_Cocoon cocoon, FOM_WebContinuation kont) {
          Map objectModel = ContextHelper.getObjectModel(getAvalonContext());
  
          // Make the JS live-connect objects available to the view layer
          Request request = ObjectModelHelper.getRequest(objectModel);
          Scriptable session = null;
          if (request.getSession(false) != null) {
              session = cocoon.jsGet_session();
          }
          FOM_JavaScriptFlowHelper.setFOM_Session(objectModel, session);
  
          FOM_JavaScriptFlowHelper.setFOM_Context(objectModel,
                                                  cocoon.jsGet_context());
          if (kont != null) {
              FOM_JavaScriptFlowHelper.setFOM_WebContinuation(objectModel, kont);
          }
      }*/
      
      /*protected class ScriptSourceEntry {
          final private Source source;
          private Script script;
          private long compileTime;
  
          public ScriptSourceEntry(Source source) {
              this.source = source;
          }
  
          public ScriptSourceEntry(Source source, Script script, long t) {
              this.source = source;
              this.script = script;
              this.compileTime = t;
          }
  
          public Source getSource() {
              return source;
          }
  
          public Script getScript(Context context, Scriptable scope,
                                  boolean refresh, JavaScriptHelper interpreter)
              throws Exception {
              if (refresh) {
                  source.refresh();
              }
              if (script == null || compileTime < source.getLastModified()) {
                  script = interpreter.compileScript(context, scope, source);
                  compileTime = source.getLastModified();
              }
              return script;
          }
      }*/
  }
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript/JavaScriptInterpreter.java
  
  Index: JavaScriptInterpreter.java
  ===================================================================
  /*
   * Copyright 1999-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.apache.cocoon.components.flow.javascript;
  
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.cocoon.components.ContextHelper;
  import org.apache.cocoon.components.flow.AbstractInterpreter;
  import org.apache.cocoon.components.flow.FlowHelper;
  import org.apache.cocoon.components.flow.InvalidContinuationException;
  import org.apache.cocoon.components.flow.WebContinuation;
  import org.apache.cocoon.components.flow.java.Continuation;
  import org.apache.cocoon.components.flow.java.ContinuationClassLoader;
  import org.apache.cocoon.components.flow.java.ContinuationContext;
  import org.apache.cocoon.components.flow.java.VarMap;
  import org.apache.cocoon.components.flow.java.VarMapHandler;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.Session;
  import org.apache.commons.jxpath.JXPathIntrospector;
  
  /**
   * Implementation of the java flow interpreter.
   *
   * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
   * @version CVS $Id: JavaScriptInterpreter.java,v 1.1 2004/06/24 16:48:53 stephan Exp $
   */
  public class JavaScriptInterpreter extends AbstractInterpreter implements Configurable {
  
      private boolean initialized = false;
  
      private int timeToLive = 600000;
  
      /**
       * Key for storing a global scope object in the Cocoon session
       */
      public static final String USER_GLOBAL_SCOPE = "JAVASCRIPT GLOBAL SCOPE";
  
      private ContinuationClassLoader continuationclassloader;
  
      private Configuration configuration;
  
      static {
          JXPathIntrospector.registerDynamicClass(VarMap.class, VarMapHandler.class);
      }
  
      public void configure(Configuration config) throws ConfigurationException {
          super.configure(config);
          
          continuationclassloader = new ContinuationClassLoader(Thread.currentThread().getContextClassLoader());
          continuationclassloader.setDebug(config.getAttributeAsBoolean("debug", false));
  
          Configuration[] includes = config.getChildren("include");
          for (int i = 0; i < includes.length; i++)
              continuationclassloader.addIncludeClass(includes[i].getAttribute("class"));
          
          this.configuration = config;
      }
  
      
      public ScriptHelper getScriptHelper() throws Exception {
          
          ScriptHelper flow = (ScriptHelper)continuationclassloader
  			.loadClass("org.apache.cocoon.components.flow.javascript.JavaScriptHelper").newInstance();
  
          for (Iterator scripts = needResolve.iterator(); scripts.hasNext();) {
  
              String name = (String) scripts.next();
              flow.register(name);
          }
          
          flow.configure(configuration);
          flow.initialize();
          
          return flow;
      }
  
      /**
       * Calls a Java function, passing <code>params</code> as its
       * arguments. In addition to this, it makes available the parameters
       * through the <code>cocoon.parameters</code> Java array
       * (indexed by the parameter names).
       *
       * @param function a <code>String</code> value
       * @param params a <code>List</code> value
       * @param redirector
       * @exception Exception if an error occurs
       */
      public void callFunction(String function, List params, Redirector redirector) throws Exception {
  
          if (getLogger().isDebugEnabled())
              getLogger().debug("calling function \"" + function + "\"");
  
          Request request = ContextHelper.getRequest(this.avalonContext);
          Session session = request.getSession(true);
          ScriptHelper flow = (ScriptHelper) session.getAttribute(USER_GLOBAL_SCOPE);
  
          ContinuationContext context = new ContinuationContext();
          context.setObject(flow);
          context.setAvalonContext(avalonContext);
          context.setLogger(getLogger());
          context.setServiceManager(manager);
          context.setRedirector(redirector);
          Parameters parameters = new Parameters();
          for(Iterator i=params.iterator(); i.hasNext();) {
          	Argument argument = (Argument)i.next();
          	parameters.setParameter(argument.name, argument.value);
          }
          context.setParameters(parameters);
  
          Continuation continuation = new Continuation(context);
  
          WebContinuation wk = continuationsMgr.createWebContinuation(continuation, null, timeToLive, null);
          FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
  
          continuation.registerThread();
          try {
              if (flow == null) {
              	
                  if (getLogger().isDebugEnabled())
                      getLogger().debug("create new instance of the script helper");
                  
                  flow = getScriptHelper();
                  context.setObject(flow);
              }
  
              flow.callFunction(function, params);
  
          } finally {
              // remove last object reference, which is not needed to
              // reconstruct the invocation path
              if (continuation.isCapturing())
                  continuation.getStack().popReference();
              continuation.deregisterThread();
          }
          session.setAttribute(USER_GLOBAL_SCOPE, flow);
      }
  
      public void handleContinuation(String id, List params, Redirector redirector) throws Exception {
  
          WebContinuation parentwk = continuationsMgr.lookupWebContinuation(id);
  
          if (parentwk == null) {
          /*
           * Throw an InvalidContinuationException to be handled inside the
           * <map:handle-errors> sitemap element.
           */
          throw new InvalidContinuationException("The continuation ID " + id + " is invalid."); }
  
          Continuation parentContinuation = (Continuation) parentwk.getContinuation();
          ContinuationContext parentContext = (ContinuationContext) parentContinuation.getContext();
          ContinuationContext context = new ContinuationContext();
          context.setObject(parentContext.getObject());
          context.setAvalonContext(avalonContext);
          context.setLogger(getLogger());
          context.setServiceManager(manager);
          context.setRedirector(redirector);
          Parameters parameters = new Parameters();
          for(Iterator i=params.iterator(); i.hasNext();) {
          	Argument argument = (Argument)i.next();
          	parameters.setParameter(argument.name, argument.value);
          }
          context.setParameters(parameters);
          
          Continuation continuation = new Continuation(parentContinuation, context);
  
          Request request = ContextHelper.getRequest(this.avalonContext);
          Session session = request.getSession(true);
          HashMap userScopes = (HashMap) session.getAttribute(USER_GLOBAL_SCOPE);
  
          ScriptHelper flow = (ScriptHelper) context.getObject();
  
          WebContinuation wk = continuationsMgr.createWebContinuation(continuation, parentwk, timeToLive, null);
          FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
  
          continuation.registerThread();
          try {
  
              flow.callFunction(null, null);
  
          } finally {
              // remove last object reference, which is not needed to reconstruct
              // the invocation path
              if (continuation.isCapturing())
                  continuation.getStack().popReference();
              continuation.deregisterThread();
          }
  
          session.setAttribute(USER_GLOBAL_SCOPE, flow);
      }
  }
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript/ScriptHelper.java
  
  Index: ScriptHelper.java
  ===================================================================
  /*
   * Created on 24.06.2004
   *
   * TODO To change the template for this generated file go to
   * Window - Preferences - Java - Code Generation - Code and Comments
   */
  package org.apache.cocoon.components.flow.javascript;
  
  import java.util.List;
  
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.configuration.Configurable;
  
  /**
   * @author stephan
   *
   * TODO To change the template for this generated type comment go to
   * Window - Preferences - Java - Code Generation - Code and Comments
   */
  public interface ScriptHelper extends Configurable, Initializable {
  	
  	public void register(String source);
  
  	public void callFunction(String funName, List params) throws Exception;
  }
  
  
  
  1.3       +6 -1      cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/JavaFlowTestCase.java
  
  Index: JavaFlowTestCase.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/JavaFlowTestCase.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- JavaFlowTestCase.java	23 Jun 2004 09:16:32 -0000	1.2
  +++ JavaFlowTestCase.java	24 Jun 2004 16:48:53 -0000	1.3
  @@ -160,4 +160,9 @@
           
           String id = callFunction("java", source, "parameterTest", parameters);
       }
  +    
  +    public void testClass() throws Exception {
  +        String source = "org.apache.cocoon.components.flow.java.test.SimpleFlow";
  +        String id = callFunction("java", source, "forClassTest", new HashMap());
  +    }
   }
  
  
  
  1.2       +1 -1      cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/JavaFlowTestCase.xtest
  
  Index: JavaFlowTestCase.xtest
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/JavaFlowTestCase.xtest,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JavaFlowTestCase.xtest	14 Jun 2004 14:53:56 -0000	1.1
  +++ JavaFlowTestCase.xtest	24 Jun 2004 16:48:53 -0000	1.2
  @@ -78,7 +78,7 @@
     <source-resolver class="org.apache.excalibur.source.impl.SourceResolverImpl"/>
   
     <flow-interpreters default="java">
  -    <component-instance class="org.apache.cocoon.components.flow.java.JavaInterpreter" name="java"/>
  +    <component-instance class="org.apache.cocoon.components.flow.java.JavaInterpreter" name="java" debug="false"/>
     </flow-interpreters>
   
     <continuations-manager time-to-live="3600000">
  
  
  
  1.7       +8 -0      cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/SimpleFlow.java
  
  Index: SimpleFlow.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/java/test/SimpleFlow.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- SimpleFlow.java	23 Jun 2004 09:16:32 -0000	1.6
  +++ SimpleFlow.java	24 Jun 2004 16:48:53 -0000	1.7
  @@ -20,7 +20,10 @@
   import junit.framework.Assert;
   
   import org.apache.cocoon.components.flow.java.*;
  +import org.apache.cocoon.components.flow.javascript.ScriptablePropertyHandler;
   import org.apache.cocoon.forms.FormContext;
  +import org.apache.commons.jxpath.JXPathIntrospector;
  +import org.mozilla.javascript.Scriptable;
   /*import org.apache.lucene.analysis.standard.StandardAnalyzer;
   import org.apache.lucene.queryParser.QueryParser;
   import org.apache.lucene.search.*;*/
  @@ -130,6 +133,11 @@
       	Assert.assertEquals("abc", getParameters().getParameter("p1")); 
       	Assert.assertEquals("def", getParameters().getParameter("p2"));
       	Assert.assertEquals(2.3f, getParameters().getParameterAsFloat("p3"), 0.1f);
  +    }
  +    
  +    public void doClassTest() {
  +    	JXPathIntrospector.registerDynamicClass(Scriptable.class,
  +    	    ScriptablePropertyHandler.class);
       }
   }
   
  
  
  
  1.3       +5 -3      cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.java
  
  Index: JavaScriptFlowTestCase.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- JavaScriptFlowTestCase.java	14 Jun 2004 14:53:57 -0000	1.2
  +++ JavaScriptFlowTestCase.java	24 Jun 2004 16:48:53 -0000	1.3
  @@ -16,6 +16,8 @@
   
   package org.apache.cocoon.components.flow.javascript;
   
  +import java.util.HashMap;
  +
   import org.apache.cocoon.SitemapComponentTestCase;
   
   /**
  @@ -31,7 +33,7 @@
       }
       
       public void testCalculator() throws Exception {
  -        /*String source = "resource://org/apache/cocoon/components/flow/javascript/calc.js";
  -        callFunction("java", source, "calculator", new ArrayList());*/
  +        String source = "resource://org/apache/cocoon/components/flow/javascript/calc.js";
  +        callFunction("javascript", source, "calculator", new HashMap());
       }
   }
  
  
  
  1.2       +3 -2      cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.xtest
  
  Index: JavaScriptFlowTestCase.xtest
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.xtest,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- JavaScriptFlowTestCase.xtest	4 Jun 2004 14:02:38 -0000	1.1
  +++ JavaScriptFlowTestCase.xtest	24 Jun 2004 16:48:53 -0000	1.2
  @@ -77,10 +77,11 @@
   
     <source-resolver class="org.apache.excalibur.source.impl.SourceResolverImpl"/>
   
  -  <flow-interpreters default="java">
  +  <flow-interpreters default="javascript">
       <component-instance class="org.apache.cocoon.components.flow.java.JavaInterpreter" name="java"/>
  +    <component-instance class="org.apache.cocoon.components.flow.javascript.JavaScriptInterpreter" name="javascript" debug="false"/>
     </flow-interpreters>
  -
  +  
     <continuations-manager time-to-live="3600000">
       <expirations-check type="periodic">
         <offset>180000</offset>