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.tool;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.obe.XMLException;
050: import org.obe.client.api.repository.JavaMethodMetaData;
051: import org.obe.client.api.repository.RepositoryException;
052: import org.obe.client.api.tool.ToolInvocation;
053: import org.obe.spi.WorkflowContext;
054: import org.obe.util.ClassUtils;
055: import org.obe.xpdl.OBENames;
056: import org.obe.xpdl.model.data.ParameterMode;
057: import org.obe.xpdl.model.misc.ExtendedAttributes;
058:
059: import java.lang.reflect.InvocationTargetException;
060: import java.lang.reflect.Method;
061: import java.lang.reflect.Modifier;
062: import java.util.ArrayList;
063: import java.util.List;
064: import java.util.Map;
065:
066: /**
067: * A tool agent that executes a method on a target Java class or object. If the
068: * Java method needs to support INOUT parameter types then the return value must
069: * be a Map; otherwise, the method result will assigned to all INOUT and OUT
070: * parameters.
071: *
072: * @author Adrian Price
073: */
074: public class JavaMethod extends AbstractToolAgent {
075: private static final Log _logger = LogFactory
076: .getLog(JavaMethod.class);
077: private Class _class;
078: private Method _method;
079: private boolean _instanceMethod;
080: protected Object _target;
081:
082: protected static Object getTargetFromWorkflow()
083: throws XMLException, RepositoryException {
084:
085: Object target = null;
086:
087: // Check whether the workflow specifies the target object.
088: WorkflowContext ctx = getWorkflowContext();
089: if (ctx != null) {
090: ExtendedAttributes extAttrs = ctx.getTool()
091: .getExtendedAttributes();
092: if (extAttrs != null) {
093: String targetVar = extAttrs.get(OBENames.OBE_TARGET);
094: if (targetVar != null) {
095: target = ctx.getProcessInstance()
096: .getAttributeInstance(targetVar).getValue();
097: }
098: if (target == null) {
099: _logger.warn("Target variable is null: "
100: + targetVar);
101: } else if (_logger.isDebugEnabled()) {
102: _logger.debug("Using target: " + target);
103: }
104: }
105: }
106:
107: return target;
108: }
109:
110: /**
111: * Construct a new Java method tool agent. The method must be thread-safe.
112: *
113: * @param metadata Metadata about the class and method to invoke.
114: */
115: public JavaMethod(JavaMethodMetaData metadata)
116: throws ClassNotFoundException, NoSuchMethodException {
117:
118: setMethod(Class.forName(metadata.getClassName()), metadata
119: .getMethod(), metadata.getMethodSig());
120: }
121:
122: protected final void setMethod(Class clazz, String method,
123: String[] argTypes) throws ClassNotFoundException,
124: NoSuchMethodException {
125:
126: if (clazz == null)
127: throw new IllegalArgumentException("class cannot be null");
128: _class = clazz;
129: _method = clazz.getMethod(method, ClassUtils
130: .classesForNames(argTypes));
131:
132: // If it's a non-static method, we'll need a target.
133: _instanceMethod = !Modifier.isStatic(_method.getModifiers());
134: }
135:
136: protected int _invokeApplication(ToolInvocation ti)
137: throws InterruptedException, InvocationTargetException {
138:
139: // Setup the parameters.
140: List values = new ArrayList();
141: for (int i = 0; i < ti.parameters.length; i++) {
142: if (ti.parameters[i].getMode() != ParameterMode.OUT) {
143: // TODO: coerce parameter values to required types.
144: values.add(ti.parameters[i].getValue());
145: }
146: }
147:
148: if (_logger.isDebugEnabled())
149: _logger.debug("Invoking Java method: " + _method);
150:
151: Object result;
152: try {
153: // Invoke the method.
154: _status = ACTIVE;
155: result = _method.invoke(getTarget(), values.toArray());
156: } catch (InstantiationException e) {
157: throw new InvocationTargetException(e);
158: } catch (IllegalAccessException e) {
159: throw new InvocationTargetException(e);
160: } catch (RepositoryException e) {
161: throw new InvocationTargetException(e);
162: } catch (XMLException e) {
163: throw new InvocationTargetException(e);
164: }
165: boolean isMap = result instanceof Map;
166:
167: // Create the return values array.
168: // TODO: think more deeply about OUT vs. INOUT semantics in Java.
169: // Using a Map or the return value is a hokey way of doing things.
170: // Other implementations (JAX-RPC, for example), using ~Holder objects.
171: for (int i = 0; i < ti.parameters.length; i++) {
172: if (ti.parameters[i].getMode() != ParameterMode.IN) {
173: if (isMap) {
174: Object value = ((Map) result).get(ti.parameters[i]
175: .getFormalParm().getId());
176: ti.parameters[i].setValue(value);
177: } else {
178: ti.parameters[i].setValue(result);
179: break;
180: }
181: }
182: }
183: return 0;
184: }
185:
186: protected Object getTarget() throws XMLException,
187: RepositoryException, InstantiationException,
188: IllegalAccessException, InvocationTargetException {
189:
190: // If this is a non-static method we'll need a target instance.
191: Object target = null;
192: if (_instanceMethod) {
193: target = getTargetFromWorkflow();
194:
195: // N.B. Assumes threadsafe method and default ctor.
196: if (target == null) {
197: target = _target;
198: if (target == null) {
199: _target = target = _class.newInstance();
200: if (_logger.isDebugEnabled())
201: _logger.debug("Instantiated target: " + target);
202: }
203: }
204: }
205: return target;
206: }
207: }
|