View Javadoc

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 }