1
2
3
4
5
6
7
8
9
10
11 package org.mule.impl.model.resolvers;
12
13 import org.mule.config.MuleProperties;
14 import org.mule.config.i18n.CoreMessages;
15 import org.mule.impl.MuleMessage;
16 import org.mule.impl.NoSatisfiableMethodsException;
17 import org.mule.impl.OptimizedRequestContext;
18 import org.mule.impl.TooManySatisfiableMethodsException;
19 import org.mule.impl.VoidResult;
20 import org.mule.providers.NullPayload;
21 import org.mule.umo.UMOEventContext;
22 import org.mule.umo.lifecycle.Callable;
23 import org.mule.umo.model.UMOEntryPoint;
24 import org.mule.util.ClassUtils;
25
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.Arrays;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Set;
33
34 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
35 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
36 import org.apache.commons.lang.BooleanUtils;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40
41
42
43
44
45
46
47
48 public class DynamicEntryPoint implements UMOEntryPoint
49 {
50
51
52
53 protected static final Log logger = LogFactory.getLog(DynamicEntryPoint.class);
54
55
56 protected static final Set IgnoredMethodNames = new HashSet(Arrays.asList(new String[]{"equals",
57 "getInvocationHandler"}));
58
59
60 private final ConcurrentMap entryPoints = new ConcurrentHashMap();
61
62 public DynamicEntryPoint()
63 {
64 super();
65 }
66
67 protected Method addMethodByArgumentType(Method method, String payloadClass)
68 {
69 Method previousMethod = (Method) entryPoints.putIfAbsent(payloadClass, method);
70 return (previousMethod != null ? previousMethod : method);
71 }
72
73 protected Method addMethodByName(Method method, String payloadClass)
74 {
75 String methodName = method.getName();
76
77 ConcurrentMap argumentTypes = (ConcurrentMap) entryPoints.get(methodName);
78 if (argumentTypes == null)
79 {
80 argumentTypes = new ConcurrentHashMap();
81 ConcurrentMap previousTypes = (ConcurrentMap) entryPoints.putIfAbsent(methodName, argumentTypes);
82 if (previousTypes != null)
83 {
84 argumentTypes = previousTypes;
85 }
86 }
87
88 Method previousMethod = (Method) argumentTypes.putIfAbsent(payloadClass, method);
89 return (previousMethod != null ? previousMethod : method);
90 }
91
92 protected Method getMethodByArgumentType(String argumentType)
93 {
94 return (Method) entryPoints.get(argumentType);
95 }
96
97 protected Method getMethodByName(String methodName, String argumentType)
98 {
99 ConcurrentMap argumentTypes = (ConcurrentMap) entryPoints.get(methodName);
100 return (argumentTypes != null ? (Method) argumentTypes.get(argumentType) : null);
101 }
102
103 public Object invoke(Object component, UMOEventContext context) throws Exception
104 {
105 Method method = null;
106 Object payload = null;
107
108
109 boolean ignoreMethod = BooleanUtils.toBoolean((Boolean) context.getMessage().removeProperty(
110 MuleProperties.MULE_IGNORE_METHOD_PROPERTY));
111
112 if (!ignoreMethod)
113 {
114
115 Object methodOverride = context.getMessage().removeProperty(MuleProperties.MULE_METHOD_PROPERTY);
116
117 if (methodOverride instanceof Method)
118 {
119
120 method = (Method) methodOverride;
121 }
122 else if (methodOverride != null)
123 {
124 payload = context.getTransformedMessage();
125 String payloadClassName = payload.getClass().getName();
126
127
128 String methodOverrideName = methodOverride.toString();
129 method = this.getMethodByName(methodOverrideName, payloadClassName);
130
131
132 if (method == null)
133 {
134
135 List matchingMethods = ClassUtils.getSatisfiableMethods(component.getClass(), ClassUtils
136 .getClassTypes(payload), true, true, IgnoredMethodNames);
137
138
139 for (Iterator i = matchingMethods.iterator(); i.hasNext();)
140 {
141 Method candidate = (Method) i.next();
142 if (candidate.getName().equals(methodOverride))
143 {
144 method = candidate;
145 break;
146 }
147 }
148
149
150 this.validateMethod(component, method, methodOverrideName);
151
152
153
154 if (method != null)
155 {
156 method = this.addMethodByName(method, payloadClassName);
157 }
158 }
159 }
160 }
161
162
163 if (method == null)
164 {
165
166 if (component instanceof Callable)
167 {
168 method = Callable.class.getMethods()[0];
169 payload = context;
170 }
171 else
172 {
173
174
175 method = this.getMethodByArgumentType(context.getClass().getName());
176 if (method == null)
177 {
178
179 payload = context.getTransformedMessage();
180 method = this.getMethodByArgumentType(payload.getClass().getName());
181 if (method != null)
182 {
183 OptimizedRequestContext.unsafeRewriteEvent(new MuleMessage(payload, context.getMessage()));
184 }
185 }
186 else
187 {
188 payload = context;
189 }
190 }
191 }
192
193
194 if (method == null)
195 {
196
197 List methods = ClassUtils.getSatisfiableMethods(component.getClass(), ClassUtils
198 .getClassTypes(context), true, false, IgnoredMethodNames);
199
200 int numMethods = methods.size();
201 if (numMethods > 1)
202 {
203
204 TooManySatisfiableMethodsException tmsmex = new TooManySatisfiableMethodsException(component
205 .getClass(), methods);
206 throw new InvocationTargetException(tmsmex, "There must be only one method accepting "
207 + context.getClass().getName() + " in component "
208 + component.getClass().getName());
209 }
210 else if (numMethods == 1)
211 {
212
213 payload = context;
214 method = this.addMethodByArgumentType((Method) methods.get(0), payload.getClass().getName());
215 }
216 else
217 {
218
219 payload = context.getTransformedMessage();
220 OptimizedRequestContext.unsafeRewriteEvent(new MuleMessage(payload, context.getMessage()));
221
222 methods = ClassUtils.getSatisfiableMethods(component.getClass(), ClassUtils
223 .getClassTypes(payload), true, true, IgnoredMethodNames);
224
225 numMethods = methods.size();
226
227 if (numMethods > 1)
228 {
229
230 throw new TooManySatisfiableMethodsException(component.getClass(), methods);
231 }
232 else if (numMethods == 1)
233 {
234
235 method = this.addMethodByArgumentType((Method) methods.get(0), payload.getClass()
236 .getName());
237 }
238 else
239 {
240
241 throw new NoSatisfiableMethodsException(component.getClass(), ClassUtils
242 .getClassTypes(payload));
243 }
244 }
245 }
246
247 if (payload == null)
248 {
249 payload = context.getTransformedMessage();
250 OptimizedRequestContext.unsafeRewriteEvent(new MuleMessage(payload, context.getMessage()));
251 }
252
253 if (logger.isDebugEnabled())
254 {
255 logger.debug("Dynamic Entrypoint using method: " + component.getClass().getName() + "."
256 + method.getName() + "(" + payload.getClass().getName() + ")");
257 }
258
259 return this.invokeMethod(component, method, payload);
260 }
261
262
263
264
265 protected Object invokeMethod(Object component, Method method, Object argument)
266 throws InvocationTargetException, IllegalAccessException
267 {
268 String methodCall = null;
269
270 if (logger.isDebugEnabled())
271 {
272 methodCall = component.getClass().getName() + "." + method.getName() + "("
273 + argument.getClass().getName() + ")";
274 logger.debug("Invoking " + methodCall);
275 }
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 Object[] invocationArgs;
291
292 if (argument.getClass().isArray())
293 {
294 if (Object[].class.isAssignableFrom(argument.getClass()))
295 {
296 invocationArgs = (Object[]) argument;
297 }
298 else
299 {
300 invocationArgs = new Object[]{argument};
301 }
302 }
303 else if (argument instanceof NullPayload)
304 {
305 invocationArgs = null;
306 }
307 else
308 {
309 invocationArgs = new Object[]{argument};
310 }
311
312 Object result = method.invoke(component, invocationArgs);
313 if (method.getReturnType().equals(Void.TYPE))
314 {
315 result = VoidResult.getInstance();
316 }
317
318 if (logger.isDebugEnabled())
319 {
320 logger.debug("Result of call " + methodCall + " is " + (result == null ? "null" : "not null"));
321 }
322
323 return result;
324 }
325
326
327
328
329
330 protected void validateMethod(Object component, Method method, String methodName)
331 throws NoSuchMethodException
332 {
333 boolean fallback = component instanceof Callable;
334
335 if (method != null)
336 {
337
338 try
339 {
340 component.getClass().getMethod(method.getName(), method.getParameterTypes());
341 }
342 catch (NoSuchMethodException e)
343 {
344 if (!fallback)
345 {
346 throw e;
347 }
348 }
349 }
350 else
351 {
352 if (!fallback)
353 {
354 throw new NoSuchMethodException(
355 CoreMessages.methodWithParamsNotFoundOnObject(methodName, "unknown",
356 component.getClass()).toString());
357 }
358 }
359 }
360
361 }