blob: 76c6f1c20924749c8ec4548c7a883df3353c9dbb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.ProcessingException;
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.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.util.ReflectionUtils;
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$
*/
public class JavaInterpreter 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 = "JAVA GLOBAL SCOPE";
private ClassLoader classloader;
private Map methods = new HashMap();
static {
JXPathIntrospector.registerDynamicClass(VarMap.class, VarMapHandler.class);
}
public void configure(Configuration config) throws ConfigurationException {
super.configure(config);
}
public synchronized void initialize() throws Exception {
if (initialized) {
return;
}
try {
if (getLogger().isDebugEnabled())
getLogger().debug("initialize java flow interpreter");
classloader = new ContinuationClassLoader(Thread.currentThread().getContextClassLoader());
for (Iterator scripts = needResolve.iterator(); scripts.hasNext();) {
String classname = (String) scripts.next();
if (getLogger().isDebugEnabled())
getLogger().debug("registered java class \"" + classname + "\" for flow");
if (!Continuable.class.isAssignableFrom(Class.forName(classname))) {
getLogger().error("java class \"" + classname + "\" doesn't implement Continuable");
continue;
}
Class clazz = classloader.loadClass(classname);
final Map m = ReflectionUtils.discoverMethods(clazz);
methods.putAll(m);
//Only initialize if everything so far hasn't thrown any exceptions.
initialized = true;
}
} catch (final Exception e) {
throw new ConfigurationException("Cannot initialize JavaInterpreter", e);
}
}
/**
* 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 (!initialized)
initialize();
Method method = (Method) methods.get(function);
if (method == null) {
throw new ProcessingException("No method '" + function + "' found. " + methods);
}
if (getLogger().isDebugEnabled())
getLogger().debug("calling method \"" + method + "\"");
Request request = ContextHelper.getRequest(this.avalonContext);
Session session = request.getSession(true);
HashMap userScopes = (HashMap) session.getAttribute(USER_GLOBAL_SCOPE);
if (userScopes == null)
userScopes = new HashMap();
Continuable flow = (Continuable) userScopes.get(method.getDeclaringClass());
ContinuationContext context = new ContinuationContext();
context.setObject(flow);
context.setMethod(method);
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(function, context);
WebContinuation wk = continuationsMgr.createWebContinuation(
continuation, null, timeToLive, getInterpreterID(), null);
FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
continuation.registerThread();
try {
if (flow == null) {
if (getLogger().isDebugEnabled())
getLogger().debug("create new instance of \""+method.getDeclaringClass()+"\"");
flow = (Continuable) method.getDeclaringClass().newInstance();
context.setObject(flow);
}
method.invoke(flow, new Object[0]);
} catch (InvocationTargetException ite) {
if (ite.getTargetException() != null) {
if (ite.getTargetException() instanceof Exception)
throw (Exception) ite.getTargetException();
else if (ite.getTargetException() instanceof Error)
throw new ProcessingException("An internal error occured", ite.getTargetException());
else
throw ite;
} else {
throw ite;
}
} finally {
// remove last object reference, which is not needed to reconstruct
// the invocation path
if (continuation.isCapturing())
continuation.getStack().popReference();
continuation.deregisterThread();
}
userScopes.put(method.getDeclaringClass(), flow);
session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
}
public void handleContinuation(String id, List params, Redirector redirector)
throws Exception {
if (!initialized)
initialize();
WebContinuation parentwk = continuationsMgr.lookupWebContinuation(id, getInterpreterID());
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();
Method method = (Method) methods.get(parentContinuation.getFunctionName());
Request request = ContextHelper.getRequest(this.avalonContext);
Session session = request.getSession(true);
HashMap userScopes = (HashMap) session.getAttribute(USER_GLOBAL_SCOPE);
Continuable flow = (Continuable) userScopes.get(method.getDeclaringClass());
ContinuationContext context = new ContinuationContext();
context.setObject(flow);
context.setMethod(method);
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);
WebContinuation wk = continuationsMgr.createWebContinuation(
continuation, parentwk, timeToLive, getInterpreterID(), null);
FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
continuation.registerThread();
try {
method.invoke(flow, new Object[0]);
} catch (InvocationTargetException ite) {
if (ite.getTargetException() != null) {
if (ite.getTargetException() instanceof Exception)
throw (Exception) ite.getTargetException();
else if (ite.getTargetException() instanceof Error)
throw new ProcessingException("An internal error occured", ite.getTargetException());
else
throw ite;
} else {
throw ite;
}
} finally {
// remove last object reference, which is not needed to reconstruct
// the invocation path
if (continuation.isCapturing())
continuation.getStack().popReference();
continuation.deregisterThread();
}
userScopes.put(method.getDeclaringClass(), flow);
session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
}
}