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:[EMAIL PROTECTED]">Ovidiu Predescu</a> * @author <a href="mailto:[EMAIL PROTECTED]">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 [EMAIL PROTECTED] #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><map:call * function="..."></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 [EMAIL PROTECTED] #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:[EMAIL PROTECTED]">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>