001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.runtime.evaluator;
046:
047: import com.ibm.bsf.BSFException;
048: import com.ibm.bsf.BSFManager;
049: import org.apache.commons.logging.Log;
050: import org.apache.commons.logging.LogFactory;
051: import org.obe.client.api.repository.RepositoryException;
052: import org.obe.spi.WorkflowContext;
053: import org.obe.spi.evaluator.EvaluatorException;
054: import org.obe.spi.model.AttributeInstance;
055: import org.obe.spi.model.ProcessInstance;
056:
057: import java.util.Iterator;
058: import java.util.Map;
059:
060: /**
061: * Invokes the BSF to evaluate an expression in an arbitrary scripting language.
062: *
063: * @author Adrian Price
064: */
065: public final class BSFEvaluator extends AbstractEvaluator {
066: private static final Log _logger = LogFactory
067: .getLog(BSFEvaluator.class);
068: private String _lang;
069:
070: public BSFEvaluator(String lang) {
071: if (!BSFManager.isLanguageRegistered(lang))
072: throw new IllegalArgumentException(lang);
073: _lang = lang;
074: }
075:
076: public Object evaluateExpression(String expr, WorkflowContext ctx)
077: throws EvaluatorException {
078:
079: try {
080: BSFManager bsfMgr = new BSFManager();
081: // BSFEngine engine = bsfMgr.loadScriptingEngine(_lang);
082:
083: /*
084: // TODO: figure out how to provide a usable evaluator context.
085:
086: // N.B. The ObjectRegistry approach doesn't work out too well
087: // because the registered objects are only accessible to scripts
088: // via an API call. They cannot be simply referenced by name.
089:
090: // This scheme is a little skewed compared to the JXPath version,
091: // because XPath makes a convenient distinction between the context
092: // node and variables. Other scripting languages do not make this
093: // distinction.
094: ObjectRegistry reg;
095: if (ctx instanceof EngineContext) {
096: final EngineContext wfCtx = (EngineContext)ctx;
097: reg = new ObjectRegistry() {
098: final Map _data;
099:
100: {
101: // Provide access to the workflow instance data.
102: try {
103: ProcessInstance pi = wfCtx.getProcessInstance();
104: _data = pi == null
105: ? Collections.EMPTY_MAP
106: : pi.getAttributeInstances();
107: } catch (RepositoryException e) {
108: throw new OBERuntimeException(e);
109: }
110: }
111:
112: public Object lookup(String tag)
113: throws IllegalArgumentException {
114:
115: if (_data.containsKey(tag))
116: return ((AttributeInstance)
117: _data.get(tag)).getValue();
118: else if (tag.equals("application"))
119: return wfCtx.getApplication();
120: else if (tag.equals("event"))
121: return wfCtx.getEvent();
122: else
123: return super.lookup(tag);
124: }
125: };
126: } else {
127: // This approach provides access to the properties of the
128: // context bean by name. This makes the evaluator behave
129: // identically to the JXPath evaluator for arbitrary (i.e.,
130: // non-workflow) contexts.
131: reg = new ObjectRegistry() {
132: final Map _getters;
133:
134: {
135: if (ctx != null) {
136: _getters = new HashMap();
137: BeanInfo bi;
138: try {
139: bi = Introspector.getBeanInfo(ctx.getClass());
140: } catch (IntrospectionException e) {
141: throw new OBERuntimeException(e);
142: }
143: PropertyDescriptor[] propDescs =
144: bi.getPropertyDescriptors();
145: for (int i = 0; i < propDescs.length; i++) {
146: PropertyDescriptor propDesc = propDescs[i];
147: _getters.put(propDesc.getName(),
148: propDesc.getReadMethod());
149: }
150: } else {
151: _getters = Collections.EMPTY_MAP;
152: }
153: }
154:
155: public Object lookup(String tag)
156: throws IllegalArgumentException {
157:
158: Method getter = (Method)_getters.get(tag);
159: if (getter != null) {
160: try {
161: return getter.invoke(ctx, new Object[] {tag});
162: } catch (IllegalAccessException e) {
163: throw new OBERuntimeException(e);
164: } catch (InvocationTargetException e) {
165: throw new OBERuntimeException(e);
166: }
167: } else {
168: return super.lookup(tag);
169: }
170: }
171: };
172: }
173: bsfMgr.setObjectRegistry(reg);
174: */
175:
176: // Declare the magic 'ctx' bean.
177: bsfMgr.declareBean(EvaluationContext.CONTEXT, ctx,
178: WorkflowContext.class);
179:
180: // Declare the process instance attributes as beans.
181: String sourceFile = ctx.getWorkflow() == null ? "(unknown)"
182: : ctx.getWorkflow().getPackageId() + ".xpdl";
183: ProcessInstance procInst = ctx.getProcessInstance();
184: if (procInst != null) {
185: Map map = procInst.getAttributeInstances();
186: boolean debug = _logger.isDebugEnabled();
187: for (Iterator iter = map.entrySet().iterator(); iter
188: .hasNext();) {
189: Map.Entry entry = (Map.Entry) iter.next();
190: Object value = ((AttributeInstance) entry
191: .getValue()).getValue();
192: if (value != null) {
193: bsfMgr.declareBean((String) entry.getKey(),
194: value, value.getClass());
195: if (debug)
196: _logger.debug("Declared bean: " + entry);
197: }
198: // bsfMgr.registerBean((String)entry.getKey(), value);
199: // if (debug)
200: // _logger.debug("Registered bean: " + entry);
201: }
202: }
203:
204: // TODO: optimize performance by precompilation, pooling, caching...
205: // result = engine.eval(sourceFile, 0, 0, expr);
206:
207: return bsfMgr.eval(_lang, sourceFile, 0, 0, expr);
208: } catch (BSFException e) {
209: throw new EvaluatorException(e);
210: } catch (RepositoryException e) {
211: throw new EvaluatorException(e);
212: }
213: }
214: }
|