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.IOException;
020: import java.io.InputStream;
021: import java.io.InputStreamReader;
022: import java.io.StringReader;
023: import java.util.Collections;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Stack;
027: import java.util.WeakHashMap;
028:
029: import org.apache.servicemix.jbi.messaging.PojoMarshaler;
030: import org.mozilla.javascript.Context;
031: import org.mozilla.javascript.Function;
032: import org.mozilla.javascript.JavaScriptException;
033: import org.mozilla.javascript.Script;
034: import org.mozilla.javascript.Scriptable;
035: import org.mozilla.javascript.ScriptableObject;
036: import org.mozilla.javascript.WrappedException;
037: import org.mozilla.javascript.continuations.Continuation;
038: import org.springframework.core.io.DefaultResourceLoader;
039:
040: public class InMemoryProcessExecutor implements IProcessExecutor {
041:
042: private IProcessManager processManager;
043: private PojoMarshaler marshaler = new XStreamMarshaler();
044: private List<String> libraries;
045: private DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
046: private StackTraceParser stackTraceParser = new StackTraceParser();
047: private Map<String, Object> context;
048: private Map<String, Scriptable> scopeCache = Collections
049: .synchronizedMap(new WeakHashMap<String, Scriptable>());
050: private boolean cacheScope = true;
051: private String processMethodName = "process";
052: private ContinuationErrorThrower continuationErrorThrower = new ContinuationErrorThrower();
053:
054: public InMemoryProcessExecutor(IProcessManager processManager) {
055: this .processManager = processManager;
056: }
057:
058: public InMemoryProcessExecutor() {
059:
060: }
061:
062: /* (non-Javadoc)
063: * @see org.bpmscript.jbi.IProcessManager#send(java.util.Stack, org.bpmscript.IProcessInstance, java.lang.Object)
064: */
065: public synchronized ExecutorResult send(
066: Stack processInstanceIdStack,
067: IProcessInstance processInstance, Object message,
068: Map<String, Object> invocationContext) throws IOException,
069: ClassNotFoundException, BpmScriptException {
070: Object result = null;
071: Context cx = Context.enter();
072: try {
073: Scriptable scope = getScope(processInstance, cx,
074: invocationContext);
075:
076: InMemoryProcessInstance instance = (InMemoryProcessInstance) processInstance;
077:
078: if (instance.getContinuationObject() == null) {
079:
080: // get the main function and execute it
081: String operation = instance.getOperation();
082: Object object = scope.get(operation, scope);
083: Object processObject = scope.get(processMethodName,
084: scope);
085:
086: if (object == null || !(object instanceof Function)) {
087: throw new BpmScriptException("operation "
088: + operation + "not found");
089: }
090:
091: if (processObject == null
092: || !(processObject instanceof Function)) {
093: throw new BpmScriptException("process operation "
094: + processMethodName + "not found");
095: }
096:
097: Function function = (Function) object;
098: Function processFunction = (Function) processObject;
099: try {
100: result = processFunction.call(cx, scope, scope,
101: new Object[] {
102: function,
103: instance.getId(),
104: Context.javaToJS(message, scope),
105: Context.javaToJS(
106: processInstanceIdStack,
107: scope) });
108:
109: } catch (ContinuationError error) {
110: Continuation cont = (Continuation) error
111: .getContinuation();
112: return new InMemoryPausedResult(cont,
113: stackTraceParser.getScriptStackTrace(error
114: .getEvaluatorException()));
115: } catch (JavaScriptException ex) {
116: Exception exception = new Exception(ex.getMessage());
117: exception.setStackTrace(ex.getStackTrace());
118: return new FailedResult(exception, stackTraceParser
119: .getScriptStackTrace(ex));
120: } catch (WrappedException e) {
121: return new FailedResult(e.getCause(),
122: stackTraceParser.getScriptStackTrace(e));
123: } catch (Throwable e) {
124: return new FailedResult(e, null);
125: }
126:
127: } else {
128: // load in the continuation
129: Continuation unfrozen = (Continuation) instance
130: .getContinuationObject();
131: // run the continuation
132: try {
133: result = unfrozen.call(cx, scope, scope,
134: new Object[] { Context.javaToJS(message,
135: scope) });
136: } catch (ContinuationError error) {
137: Continuation cont = (Continuation) error
138: .getContinuation();
139: return new InMemoryPausedResult(cont,
140: stackTraceParser.getScriptStackTrace(error
141: .getEvaluatorException()));
142: } catch (JavaScriptException ex) {
143: Exception exception = new Exception(ex.getMessage());
144: exception.setStackTrace(ex.getStackTrace());
145: return new FailedResult(exception, stackTraceParser
146: .getScriptStackTrace(ex));
147: } catch (WrappedException e) {
148: return new FailedResult(e.getCause(),
149: stackTraceParser.getScriptStackTrace(e));
150: } catch (Throwable e) {
151: return new FailedResult(e, null);
152: }
153: }
154: } catch (Throwable e) {
155: return new FailedResult(e, null);
156: } finally {
157: Context.exit();
158: }
159: return new CompletedResult(result);
160: }
161:
162: private Scriptable getNewScope(IProcessInstance instance,
163: Context cx, Map<String, Object> invocationContext)
164: throws IOException, BpmScriptException {
165: // create scripting environment (i.e. load everything)
166: ScriptableObject scope = cx.initStandardObjects();
167: cx.setOptimizationLevel(-1);
168: ScriptableObject.putProperty(scope, "out", Context.javaToJS(
169: System.out, scope));
170: ScriptableObject.putProperty(scope, "marshaler", Context
171: .javaToJS(marshaler, scope));
172: ScriptableObject.putProperty(scope, "continuationErrorThrower",
173: Context.javaToJS(continuationErrorThrower, scope));
174:
175: if (context != null) {
176: for (Map.Entry<String, Object> entry : context.entrySet()) {
177: ScriptableObject.putProperty(scope, entry.getKey(),
178: Context.javaToJS(entry.getValue(), scope));
179: }
180: }
181: if (invocationContext != null) {
182: for (Map.Entry<String, Object> entry : invocationContext
183: .entrySet()) {
184: ScriptableObject.putProperty(scope, entry.getKey(),
185: Context.javaToJS(entry.getValue(), scope));
186: }
187: }
188:
189: for (String library : libraries) {
190: library = library.trim();
191: resourceLoader.setClassLoader(this .getClass()
192: .getClassLoader());
193: InputStream inputStream = resourceLoader.getResource(
194: library).getInputStream();
195: Script script = cx.compileReader(new InputStreamReader(
196: inputStream), library, 0, null);
197: script.exec(cx, scope);
198: }
199: IProcessSource mainSource = processManager
200: .getMainSource(instance.getProcess().getId());
201: Script script = cx.compileReader(new StringReader(mainSource
202: .getSource()), mainSource.getName(), 0, null);
203: script.exec(cx, scope);
204: return scope;
205: }
206:
207: private Scriptable getScopeCached(IProcessInstance instance,
208: Context cx, Map<String, Object> invocationContext)
209: throws IOException, BpmScriptException {
210: Scriptable scope = scopeCache
211: .get(instance.getProcess().getId());
212: if (scope == null) {
213: scope = getNewScope(instance, cx, invocationContext);
214: scopeCache.put(instance.getProcess().getId(), scope);
215: }
216: return scope;
217: }
218:
219: private Scriptable getScope(IProcessInstance instance, Context cx,
220: Map<String, Object> invocationContext) throws IOException,
221: BpmScriptException {
222: return cacheScope ? getScopeCached(instance, cx,
223: invocationContext) : getNewScope(instance, cx,
224: invocationContext);
225: }
226:
227: public void setProcessManager(IProcessManager processManager) {
228: this .processManager = processManager;
229: }
230:
231: public PojoMarshaler getMarshaler() {
232: return marshaler;
233: }
234:
235: public void setMarshaler(PojoMarshaler marshaler) {
236: this .marshaler = marshaler;
237: }
238:
239: public Map<String, Object> getContext() {
240: return context;
241: }
242:
243: public void setContext(Map<String, Object> context) {
244: this .context = context;
245: }
246:
247: public void setLibraries(List<String> libraries) {
248: this .libraries = libraries;
249: }
250:
251: public void setStackTraceParser(StackTraceParser stackTraceParser) {
252: this.stackTraceParser = stackTraceParser;
253: }
254:
255: }
|