001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.flow.java;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: import org.apache.avalon.framework.configuration.Configurable;
027: import org.apache.avalon.framework.configuration.Configuration;
028: import org.apache.avalon.framework.configuration.ConfigurationException;
029: import org.apache.avalon.framework.parameters.Parameters;
030: import org.apache.cocoon.ProcessingException;
031: import org.apache.cocoon.components.ContextHelper;
032: import org.apache.cocoon.components.flow.AbstractInterpreter;
033: import org.apache.cocoon.components.flow.FlowHelper;
034: import org.apache.cocoon.components.flow.InvalidContinuationException;
035: import org.apache.cocoon.components.flow.WebContinuation;
036: import org.apache.cocoon.environment.Redirector;
037: import org.apache.cocoon.environment.Request;
038: import org.apache.cocoon.environment.Session;
039: import org.apache.cocoon.util.ReflectionUtils;
040: import org.apache.commons.jxpath.JXPathIntrospector;
041:
042: /**
043: * Implementation of the java flow interpreter.
044: *
045: * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
046: * @version CVS $Id: JavaInterpreter.java 433543 2006-08-22 06:22:54Z crossley $
047: */
048: public class JavaInterpreter extends AbstractInterpreter implements
049: Configurable {
050:
051: private boolean initialized = false;
052:
053: private int timeToLive = 600000;
054:
055: /**
056: * Key for storing a global scope object in the Cocoon session
057: */
058: public static final String USER_GLOBAL_SCOPE = "JAVA GLOBAL SCOPE";
059:
060: private ClassLoader classloader;
061:
062: private Map methods = new HashMap();
063:
064: static {
065: JXPathIntrospector.registerDynamicClass(VarMap.class,
066: VarMapHandler.class);
067: }
068:
069: public void configure(Configuration config)
070: throws ConfigurationException {
071: super .configure(config);
072: }
073:
074: public synchronized void initialize() throws Exception {
075:
076: if (initialized) {
077: return;
078: }
079:
080: try {
081: if (getLogger().isDebugEnabled())
082: getLogger().debug("initialize java flow interpreter");
083:
084: classloader = new ContinuationClassLoader(Thread
085: .currentThread().getContextClassLoader());
086:
087: for (Iterator scripts = needResolve.iterator(); scripts
088: .hasNext();) {
089:
090: String classname = (String) scripts.next();
091: if (getLogger().isDebugEnabled())
092: getLogger().debug(
093: "registered java class \"" + classname
094: + "\" for flow");
095:
096: if (!Continuable.class.isAssignableFrom(Class
097: .forName(classname))) {
098: getLogger()
099: .error(
100: "java class \""
101: + classname
102: + "\" doesn't implement Continuable");
103: continue;
104: }
105:
106: Class clazz = classloader.loadClass(classname);
107:
108: final Map m = ReflectionUtils.discoverMethods(clazz);
109: methods.putAll(m);
110:
111: //Only initialize if everything so far hasn't thrown any exceptions.
112: initialized = true;
113:
114: }
115: } catch (final Exception e) {
116: throw new ConfigurationException(
117: "Cannot initialize JavaInterpreter", e);
118: }
119:
120: }
121:
122: /**
123: * Calls a Java function, passing <code>params</code> as its
124: * arguments. In addition to this, it makes available the parameters
125: * through the <code>cocoon.parameters</code> Java array
126: * (indexed by the parameter names).
127: *
128: * @param function a <code>String</code> value
129: * @param params a <code>List</code> value
130: * @param redirector
131: * @exception Exception if an error occurs
132: */
133: public void callFunction(String function, List params,
134: Redirector redirector) throws Exception {
135:
136: if (!initialized)
137: initialize();
138:
139: Method method = (Method) methods.get(function);
140:
141: if (method == null) {
142: throw new ProcessingException("No method '" + function
143: + "' found. " + methods);
144: }
145:
146: if (getLogger().isDebugEnabled())
147: getLogger().debug("calling method \"" + method + "\"");
148:
149: Request request = ContextHelper.getRequest(this .avalonContext);
150: Session session = request.getSession(true);
151: HashMap userScopes = (HashMap) session
152: .getAttribute(USER_GLOBAL_SCOPE);
153: if (userScopes == null)
154: userScopes = new HashMap();
155:
156: Continuable flow = (Continuable) userScopes.get(method
157: .getDeclaringClass());
158:
159: ContinuationContext context = new ContinuationContext();
160: context.setObject(flow);
161: context.setMethod(method);
162: context.setAvalonContext(avalonContext);
163: context.setLogger(getLogger());
164: context.setServiceManager(manager);
165: context.setRedirector(redirector);
166: Parameters parameters = new Parameters();
167: for (Iterator i = params.iterator(); i.hasNext();) {
168: Argument argument = (Argument) i.next();
169: parameters.setParameter(argument.name, argument.value);
170: }
171: context.setParameters(parameters);
172:
173: Continuation continuation = new Continuation(context);
174:
175: WebContinuation wk = continuationsMgr.createWebContinuation(
176: continuation, null, timeToLive, getInterpreterID(),
177: null);
178: FlowHelper.setWebContinuation(ContextHelper
179: .getObjectModel(this .avalonContext), wk);
180:
181: continuation.registerThread();
182: try {
183: if (flow == null) {
184: if (getLogger().isDebugEnabled())
185: getLogger()
186: .debug(
187: "create new instance of \""
188: + method
189: .getDeclaringClass()
190: + "\"");
191:
192: flow = (Continuable) method.getDeclaringClass()
193: .newInstance();
194: context.setObject(flow);
195: }
196:
197: method.invoke(flow, new Object[0]);
198:
199: } catch (InvocationTargetException ite) {
200: if (ite.getTargetException() != null) {
201: if (ite.getTargetException() instanceof Exception)
202: throw (Exception) ite.getTargetException();
203: else if (ite.getTargetException() instanceof Error)
204: throw new ProcessingException(
205: "An internal error occured", ite
206: .getTargetException());
207: else if (ite.getTargetException() instanceof RuntimeException)
208: throw (RuntimeException) ite.getTargetException();
209: else
210: throw ite;
211: } else {
212: throw ite;
213: }
214: } finally {
215: // remove last object reference, which is not needed to
216: // reconstruct the invocation path
217: if (continuation.isCapturing())
218: continuation.getStack().popReference();
219: continuation.deregisterThread();
220: }
221: userScopes.put(method.getDeclaringClass(), flow);
222: session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
223: }
224:
225: public void handleContinuation(String id, List params,
226: Redirector redirector) throws Exception {
227: if (!initialized)
228: initialize();
229:
230: WebContinuation parentwk = continuationsMgr
231: .lookupWebContinuation(id, getInterpreterID());
232:
233: if (parentwk == null) {
234: /*
235: * Throw an InvalidContinuationException to be handled inside the
236: * <map:handle-errors> sitemap element.
237: */
238: throw new InvalidContinuationException(
239: "The continuation ID " + id + " is invalid.");
240: }
241:
242: Continuation parentContinuation = (Continuation) parentwk
243: .getContinuation();
244: ContinuationContext parentContext = (ContinuationContext) parentContinuation
245: .getContext();
246: ContinuationContext context = new ContinuationContext();
247: context.setObject(parentContext.getObject());
248: context.setMethod(parentContext.getMethod());
249: context.setAvalonContext(avalonContext);
250: context.setLogger(getLogger());
251: context.setServiceManager(manager);
252: context.setRedirector(redirector);
253: Parameters parameters = new Parameters();
254: for (Iterator i = params.iterator(); i.hasNext();) {
255: Argument argument = (Argument) i.next();
256: parameters.setParameter(argument.name, argument.value);
257: }
258: context.setParameters(parameters);
259:
260: Continuation continuation = new Continuation(
261: parentContinuation, context);
262:
263: Request request = ContextHelper.getRequest(this .avalonContext);
264: Session session = request.getSession(true);
265: HashMap userScopes = (HashMap) session
266: .getAttribute(USER_GLOBAL_SCOPE);
267:
268: Continuable flow = (Continuable) context.getObject();
269: Method method = context.getMethod();
270:
271: WebContinuation wk = continuationsMgr.createWebContinuation(
272: continuation, parentwk, timeToLive, getInterpreterID(),
273: null);
274: FlowHelper.setWebContinuation(ContextHelper
275: .getObjectModel(this .avalonContext), wk);
276:
277: continuation.registerThread();
278: try {
279:
280: method.invoke(flow, new Object[0]);
281:
282: } catch (InvocationTargetException ite) {
283: if (ite.getTargetException() != null) {
284: if (ite.getTargetException() instanceof Exception)
285: throw (Exception) ite.getTargetException();
286: else if (ite.getTargetException() instanceof Error)
287: throw new ProcessingException(
288: "An internal error occured", ite
289: .getTargetException());
290: else if (ite.getTargetException() instanceof RuntimeException)
291: throw (RuntimeException) ite.getTargetException();
292: else
293: throw ite;
294: } else {
295: throw ite;
296: }
297: } finally {
298: // remove last object reference, which is not needed to reconstruct
299: // the invocation path
300: if (continuation.isCapturing())
301: continuation.getStack().popReference();
302: continuation.deregisterThread();
303: }
304:
305: userScopes.put(method.getDeclaringClass(), flow);
306: session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
307: }
308: }
|