1 /*
2 * Copyright 1999-2001,2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.commons.workflow.core;
18
19
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.ArrayList;
23 import org.apache.commons.jxpath.JXPathContext;
24 import org.apache.commons.workflow.Context;
25 import org.apache.commons.workflow.Descriptor;
26 import org.apache.commons.workflow.StepException;
27 import org.apache.commons.workflow.base.DescriptorStep;
28
29
30 /**
31 * <p>Call the specified method of the specified bean in the specified
32 * scope, passing arguments as specified by associated <code>Descriptor</code>
33 * objects. The <strong>first</strong> associated <code>Descriptor</code>
34 * identifies the Java object on whom method invocation shall take place.</p>
35 *
36 * <p><strong>FIXME</strong> - Better way to deal with exceptions???</p>
37 *
38 * <p>Supported Attributes:</p>
39 * <ul>
40 * <li><strong>method</strong> - Name of the public method to be called
41 * on the bean specified by either <code>name</code> and
42 * <code>scope</code>, or by <code>xpath</code>.</li>
43 * </ul>
44 *
45 * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $
46 * @author Craig R. McClanahan
47 */
48
49 public class InvokeStep extends DescriptorStep {
50
51
52 // ----------------------------------------------------------= Constructors
53
54
55 /**
56 * Construct a default instance of this Step.
57 */
58 public InvokeStep() {
59
60 super();
61
62 }
63
64
65 /**
66 * Construct an instance of this Step with the specified identifier.
67 *
68 * @param id Step identifier
69 */
70 public InvokeStep(String id) {
71
72 super();
73 setId(id);
74
75 }
76
77
78 /**
79 * Construct a fully configured instance of this Step.
80 *
81 * @param id Step identifier
82 * @param method Method name
83 */
84 public InvokeStep(String id, String method) {
85
86 super();
87 setId(id);
88 setMethod(method);
89
90 }
91
92
93 /**
94 * Construct a fully configured instance of this Step.
95 *
96 * @param id Step identifier
97 * @param method Method name
98 * @param descriptor Descriptor for the bean on which to invoke
99 */
100 public InvokeStep(String id, String method, Descriptor descriptor) {
101
102 super();
103 setId(id);
104 setMethod(method);
105 addDescriptor(descriptor);
106
107 }
108
109
110 // ------------------------------------------------------------- Properties
111
112
113 /**
114 * The method name to be invoked.
115 */
116 protected String method = null;
117
118 public String getMethod() {
119 return (this.method);
120 }
121
122 public void setMethod(String method) {
123 this.method = method;
124 }
125
126
127 // --------------------------------------------------------- Public Methods
128
129
130 /**
131 * Perform the executable actions related to this Step, in the context of
132 * the specified Context.
133 *
134 * @param context The Context that is tracking our execution state
135 *
136 * @exception StepException if a processing error has occurred
137 */
138 public void execute(Context context) throws StepException {
139
140 // Identify the object on whom a method is to be invoked
141 Descriptor descriptors[] = findDescriptors();
142 if (descriptors.length < 1)
143 throw new StepException("No object descriptor on which to invoke",
144 this);
145 Object bean = descriptors[0].get(context);
146 if (bean == null)
147 throw new StepException("No object bean on which to invoke",
148 this);
149
150 // Assembe arrays of parameter types and values
151 Class types[] = new Class[descriptors.length - 1];
152 Object values[] = new Object[descriptors.length - 1];
153 for (int i = 1; i < descriptors.length; i++) {
154 values[i-1] = descriptors[i].get(context);
155 types[i-1] = descriptors[i].getType();
156 if (types[i-1] == null) {
157 if (values[i-1] == null)
158 types[i-1] = Object.class;
159 else
160 types[i-1] = values[i-1].getClass();
161 }
162 }
163
164 // Identify a compatible method signature on the bean
165 Method method = findMethod(bean, this.method, types);
166 if (method == null)
167 throw new StepException("No available method " +
168 signature(this.method, types),
169 this);
170
171 // Invoke the requested method on the requested bean
172 // FIXME - better way to deal with exceptions?
173 try {
174 Class clazz = method.getReturnType();
175 if ((clazz != null) && (clazz != Void.TYPE)) {
176 Object result = method.invoke(bean, values);
177 context.push(result);
178 } else {
179 method.invoke(bean, values);
180 }
181 } catch (InvocationTargetException t) {
182 Throwable cause = t.getTargetException();
183 throw new StepException("Invoke exception", cause, this);
184 } catch (Throwable t) {
185 throw new StepException("Invoke exception", t, this);
186 }
187
188 }
189
190
191 /**
192 * Render a string representation of this Step.
193 */
194 public String toString() {
195
196 StringBuffer sb = new StringBuffer("<core:invoke");
197 if (getId() != null) {
198 sb.append(" id=\"");
199 sb.append(getId());
200 sb.append("\"");
201 }
202 sb.append(" method=\"");
203 sb.append(getMethod());
204 sb.append("\"");
205 sb.append(">");
206 Descriptor descriptors[] = findDescriptors();
207 for (int i = 0; i < descriptors.length; i++)
208 sb.append(descriptors[i].toString());
209 sb.append("</core:invoke>");
210 return (sb.toString());
211
212 }
213
214
215 // ------------------------------------------------------ Protected Methods
216
217
218 /**
219 * Return a <code>Method</code> of the specified <code>Class</code> with
220 * the specified method name, that takes the specified parameter types,
221 * if there is one. Otherwise, return <code>null</code>.
222 *
223 * @param bean Bean on which method searching is to be done
224 * @param name Method name to search for
225 * @param types Parameter types to search for
226 */
227 protected Method findMethod(Object bean, String name, Class types[]) {
228
229 try {
230 return (bean.getClass().getMethod(name, types));
231 } catch (NoSuchMethodException e) {
232 return (null);
233 }
234
235 }
236
237
238 /**
239 * Return a method signature useful in debugging and exception messages.
240 *
241 * @param name Method name
242 * @param types Parameter types
243 */
244 protected String signature(String name, Class types[]) {
245
246 StringBuffer sb = new StringBuffer(name);
247 sb.append('(');
248 for (int i = 0; i < types.length; i++) {
249 if (i > 0)
250 sb.append(',');
251 sb.append(types[i].getName());
252 }
253 sb.append(')');
254 return (sb.toString());
255
256 }
257
258
259 }