| /* |
| * 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); |
| } |
| } |