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.bpmscript;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.InputStreamReader;
024: import java.io.StringReader;
025: import java.util.Collections;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Stack;
029: import java.util.WeakHashMap;
030: import java.util.zip.GZIPInputStream;
031: import java.util.zip.GZIPOutputStream;
032:
033: import org.apache.servicemix.jbi.messaging.PojoMarshaler;
034: import org.mozilla.javascript.Context;
035: import org.mozilla.javascript.EcmaError;
036: import org.mozilla.javascript.Function;
037: import org.mozilla.javascript.JavaScriptException;
038: import org.mozilla.javascript.Script;
039: import org.mozilla.javascript.Scriptable;
040: import org.mozilla.javascript.ScriptableObject;
041: import org.mozilla.javascript.WrappedException;
042: import org.mozilla.javascript.continuations.Continuation;
043: import org.mozilla.javascript.serialize.ScriptableInputStream;
044: import org.mozilla.javascript.serialize.ScriptableOutputStream;
045: import org.springframework.core.io.DefaultResourceLoader;
046:
047: public class ProcessExecutor implements IProcessExecutor {
048:
049: protected final transient org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
050: .getLog(getClass());
051:
052: private IProcessManager processManager;
053: private PojoMarshaler marshaler = new XStreamMarshaler();
054: private List<String> libraries;
055: private DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
056: private StackTraceParser stackTraceParser = new StackTraceParser();
057: private Map<String, Object> context;
058: private Map<String, Scriptable> scopeCache = Collections
059: .synchronizedMap(new WeakHashMap<String, Scriptable>());
060: private boolean cacheScope = true;
061: private String processMethodName = "process";
062: private boolean compress = false;
063: private ContinuationErrorThrower continuationErrorThrower = new ContinuationErrorThrower();
064:
065: public ProcessExecutor(IProcessManager processManager) {
066: this .processManager = processManager;
067: }
068:
069: public ProcessExecutor() {
070:
071: }
072:
073: /* (non-Javadoc)
074: * @see org.bpmscript.jbi.IProcessManager#send(java.util.Stack, org.bpmscript.IProcessInstance, java.lang.Object)
075: */
076: public ExecutorResult send(Stack processInstanceIdStack,
077: IProcessInstance instance, Object message,
078: Map<String, Object> invocationContext) throws IOException,
079: ClassNotFoundException, BpmScriptException {
080: Object result = null;
081: Context cx = Context.enter();
082: try {
083: Scriptable scope = getScope(instance, cx, invocationContext);
084:
085: if (instance.getContinuation() == null) {
086:
087: // get the main function and execute it
088: String operation = instance.getOperation();
089: Object object = scope.get(operation, scope);
090: Object processObject = scope.get(processMethodName,
091: scope);
092:
093: if (object == null || !(object instanceof Function)) {
094: throw new BpmScriptException("operation "
095: + operation + "not found");
096: }
097:
098: if (processObject == null
099: || !(processObject instanceof Function)) {
100: throw new BpmScriptException("process operation "
101: + processMethodName + "not found");
102: }
103:
104: Function function = (Function) object;
105: Function processFunction = (Function) processObject;
106: try {
107: result = processFunction.call(cx, scope, scope,
108: new Object[] {
109: function,
110: instance.getId(),
111: Context.javaToJS(message, scope),
112: Context.javaToJS(
113: processInstanceIdStack,
114: scope) });
115: } catch (ContinuationError error) {
116: Continuation cont = error.getContinuation();
117:
118: ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
119:
120: ScriptableOutputStream outputStream = new ScriptableOutputStream(
121: compress ? new GZIPOutputStream(byteOut)
122: : byteOut, scope);
123:
124: outputStream.addExcludedName("deliveryChannel");
125: outputStream
126: .addExcludedName("continuationErrorThrower");
127: outputStream.addExcludedName("marshaler");
128: outputStream.addExcludedName("out");
129: outputStream.addExcludedName("log");
130:
131: if (context != null) {
132: for (String name : context.keySet()) {
133: outputStream.addExcludedName(name);
134: }
135: }
136: if (invocationContext != null) {
137: for (String name : invocationContext.keySet()) {
138: outputStream.addExcludedName(name);
139: }
140: }
141:
142: outputStream.writeObject(cont);
143: outputStream.flush();
144: outputStream.close();
145:
146: byte[] bytes = byteOut.toByteArray();
147:
148: return new PausedResult(bytes, cont,
149: stackTraceParser.getScriptStackTrace(error
150: .getEvaluatorException()));
151: } catch (JavaScriptException ex) {
152: // otherwise, if it's a regular exception throw it
153: Exception exception = new Exception(ex.getMessage());
154: exception.setStackTrace(ex.getStackTrace());
155: return new FailedResult(exception, stackTraceParser
156: .getScriptStackTrace(ex));
157: } catch (WrappedException e) {
158: return new FailedResult(e.getCause(),
159: stackTraceParser.getScriptStackTrace(e));
160: } catch (EcmaError e) {
161: log.warn(e, e);
162: return new FailedResult(new SerializeableEcmaError(
163: e), null);
164: } catch (Throwable e) {
165: log.warn(e, e);
166: return new FailedResult(e, null);
167: }
168:
169: } else {
170:
171: Continuation unfrozen = null;
172:
173: // load in the continuation
174: ScriptableInputStream inputStream = null;
175: if (compress) {
176: inputStream = new ScriptableInputStream(
177: new GZIPInputStream(
178: new ByteArrayInputStream(instance
179: .getContinuation())), scope);
180: } else {
181: inputStream = new ScriptableInputStream(
182: new ByteArrayInputStream(instance
183: .getContinuation()), scope);
184: }
185: result = unfrozen = (Continuation) inputStream
186: .readObject();
187:
188: // run the continuation
189: try {
190: unfrozen.call(cx, scope, scope,
191: new Object[] { Context.javaToJS(message,
192: scope) });
193:
194: } catch (ContinuationError error) {
195: Continuation cont = error.getContinuation();
196: ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
197: ScriptableOutputStream outputStream = new ScriptableOutputStream(
198: compress ? new GZIPOutputStream(byteOut)
199: : byteOut, scope);
200: outputStream.addExcludedName("deliveryChannel");
201: outputStream.addExcludedName("marshaler");
202: outputStream.addExcludedName("out");
203: outputStream.addExcludedName("log");
204:
205: if (context != null) {
206: for (String name : context.keySet()) {
207: outputStream.addExcludedName(name);
208: }
209: }
210: if (invocationContext != null) {
211: for (String name : invocationContext.keySet()) {
212: outputStream.addExcludedName(name);
213: }
214: }
215: outputStream.writeObject(cont);
216: outputStream.flush();
217: outputStream.close();
218: byte[] bytes = byteOut.toByteArray();
219: return new PausedResult(bytes, cont,
220: stackTraceParser.getScriptStackTrace(error
221: .getEvaluatorException()));
222: } catch (JavaScriptException ex) {
223: // otherwise, if it's a regular exception throw it
224: Exception exception = new Exception(ex.getMessage());
225: exception.setStackTrace(ex.getStackTrace());
226: return new FailedResult(exception, stackTraceParser
227: .getScriptStackTrace(ex));
228: } catch (WrappedException e) {
229: return new FailedResult(e.getCause(),
230: stackTraceParser.getScriptStackTrace(e));
231: } catch (EcmaError e) {
232: log.warn(e, e);
233: return new FailedResult(new SerializeableEcmaError(
234: e), null);
235: } catch (Throwable e) {
236: log.warn(e, e);
237: return new FailedResult(e, null);
238: }
239: }
240: } catch (Throwable e) {
241: log.warn(e, e);
242: return new FailedResult(e, null);
243: } finally {
244: Context.exit();
245: }
246: return new CompletedResult(result);
247: }
248:
249: private Scriptable getNewScope(IProcessInstance instance,
250: Context cx, Map<String, Object> invocationContext)
251: throws IOException, BpmScriptException {
252: // create scripting environment (i.e. load everything)
253: ScriptableObject scope = cx.initStandardObjects();
254: cx.setOptimizationLevel(-1);
255: ScriptableObject.putProperty(scope, "out", Context.javaToJS(
256: System.out, scope));
257: ScriptableObject.putProperty(scope, "log", Context.javaToJS(
258: log, scope));
259: ScriptableObject.putProperty(scope, "marshaler", Context
260: .javaToJS(marshaler, scope));
261: ScriptableObject.putProperty(scope, "continuationErrorThrower",
262: Context.javaToJS(continuationErrorThrower, scope));
263:
264: if (context != null) {
265: for (Map.Entry<String, Object> entry : context.entrySet()) {
266: ScriptableObject.putProperty(scope, entry.getKey(),
267: Context.javaToJS(entry.getValue(), scope));
268: }
269: }
270: if (invocationContext != null) {
271: for (Map.Entry<String, Object> entry : invocationContext
272: .entrySet()) {
273: ScriptableObject.putProperty(scope, entry.getKey(),
274: Context.javaToJS(entry.getValue(), scope));
275: }
276: }
277:
278: for (String library : libraries) {
279: library = library.trim();
280: resourceLoader.setClassLoader(this .getClass()
281: .getClassLoader());
282: InputStream inputStream = resourceLoader.getResource(
283: library).getInputStream();
284: Script script = cx.compileReader(new InputStreamReader(
285: inputStream), library, 0, null);
286: script.exec(cx, scope);
287: }
288: IProcessSource mainSource = processManager
289: .getMainSource(instance.getProcess().getId());
290: Script script = cx.compileReader(new StringReader(mainSource
291: .getSource()), mainSource.getName(), 0, null);
292: script.exec(cx, scope);
293: return scope;
294: }
295:
296: private Scriptable getScopeCached(IProcessInstance instance,
297: Context cx, Map<String, Object> invocationContext)
298: throws IOException, BpmScriptException {
299: Scriptable scope = scopeCache
300: .get(instance.getProcess().getId());
301: if (scope == null) {
302: scope = getNewScope(instance, cx, invocationContext);
303: scopeCache.put(instance.getProcess().getId(), scope);
304: }
305: return scope;
306: }
307:
308: private Scriptable getScope(IProcessInstance instance, Context cx,
309: Map<String, Object> invocationContext) throws IOException,
310: BpmScriptException {
311: return cacheScope ? getScopeCached(instance, cx,
312: invocationContext) : getNewScope(instance, cx,
313: invocationContext);
314: }
315:
316: public void setProcessManager(IProcessManager processManager) {
317: this .processManager = processManager;
318: }
319:
320: public PojoMarshaler getMarshaler() {
321: return marshaler;
322: }
323:
324: public void setMarshaler(PojoMarshaler marshaler) {
325: this .marshaler = marshaler;
326: }
327:
328: public Map<String, Object> getContext() {
329: return context;
330: }
331:
332: public void setContext(Map<String, Object> context) {
333: this .context = context;
334: }
335:
336: public void setLibraries(List<String> libraries) {
337: this .libraries = libraries;
338: }
339:
340: public void setStackTraceParser(StackTraceParser stackTraceParser) {
341: this .stackTraceParser = stackTraceParser;
342: }
343:
344: public void setCompress(boolean compress) {
345: this.compress = compress;
346: }
347:
348: }
|