001 /* 002 * Copyright 1999-2001,2004 The Apache Software Foundation. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.apache.commons.workflow.core; 018 019 020 import java.lang.reflect.InvocationTargetException; 021 import java.lang.reflect.Method; 022 import java.util.ArrayList; 023 import org.apache.commons.jxpath.JXPathContext; 024 import org.apache.commons.workflow.Context; 025 import org.apache.commons.workflow.Descriptor; 026 import org.apache.commons.workflow.StepException; 027 import org.apache.commons.workflow.base.DescriptorStep; 028 029 030 /** 031 * <p>Call the specified method of the specified bean in the specified 032 * scope, passing arguments as specified by associated <code>Descriptor</code> 033 * objects. The <strong>first</strong> associated <code>Descriptor</code> 034 * identifies the Java object on whom method invocation shall take place.</p> 035 * 036 * <p><strong>FIXME</strong> - Better way to deal with exceptions???</p> 037 * 038 * <p>Supported Attributes:</p> 039 * <ul> 040 * <li><strong>method</strong> - Name of the public method to be called 041 * on the bean specified by either <code>name</code> and 042 * <code>scope</code>, or by <code>xpath</code>.</li> 043 * </ul> 044 * 045 * @version $Revision: 155475 $ $Date: 2005-02-26 13:31:11 +0000 (Sat, 26 Feb 2005) $ 046 * @author Craig R. McClanahan 047 */ 048 049 public class InvokeStep extends DescriptorStep { 050 051 052 // ----------------------------------------------------------= Constructors 053 054 055 /** 056 * Construct a default instance of this Step. 057 */ 058 public InvokeStep() { 059 060 super(); 061 062 } 063 064 065 /** 066 * Construct an instance of this Step with the specified identifier. 067 * 068 * @param id Step identifier 069 */ 070 public InvokeStep(String id) { 071 072 super(); 073 setId(id); 074 075 } 076 077 078 /** 079 * Construct a fully configured instance of this Step. 080 * 081 * @param id Step identifier 082 * @param method Method name 083 */ 084 public InvokeStep(String id, String method) { 085 086 super(); 087 setId(id); 088 setMethod(method); 089 090 } 091 092 093 /** 094 * Construct a fully configured instance of this Step. 095 * 096 * @param id Step identifier 097 * @param method Method name 098 * @param descriptor Descriptor for the bean on which to invoke 099 */ 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 }