View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.processor;
8   
9   import org.mule.DefaultMuleEvent;
10  import org.mule.DefaultMuleMessage;
11  import org.mule.api.MessagingException;
12  import org.mule.api.MuleContext;
13  import org.mule.api.MuleEvent;
14  import org.mule.api.MuleException;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.context.MuleContextAware;
17  import org.mule.api.expression.ExpressionManager;
18  import org.mule.api.lifecycle.Initialisable;
19  import org.mule.api.lifecycle.InitialisationException;
20  import org.mule.api.processor.MessageProcessor;
21  import org.mule.api.registry.RegistrationException;
22  import org.mule.api.transformer.DataType;
23  import org.mule.api.transformer.Transformer;
24  import org.mule.api.transformer.TransformerException;
25  import org.mule.config.i18n.CoreMessages;
26  import org.mule.transformer.TransformerTemplate;
27  import org.mule.transformer.types.DataTypeFactory;
28  import org.mule.transport.NullPayload;
29  import org.mule.util.ClassUtils;
30  import org.mule.util.TemplateParser;
31  import org.mule.util.TemplateParser.PatternInfo;
32  
33  import java.lang.reflect.Method;
34  import java.util.ArrayList;
35  import java.util.Arrays;
36  import java.util.Collection;
37  import java.util.Collections;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Map.Entry;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  
46  /**
47   * <code>InvokerMessageProcessor</code> invokes a specified method of an object. An
48   * array of argument expressions can be provided to map the message to the method
49   * arguments. The method used is determined by the method name along with the number
50   * of argument expressions provided. The results of the expression evaluations will
51   * automatically be transformed where possible to the method argument type. Multiple
52   * methods with the same name and same number of arguments are not supported
53   * currently.
54   */
55  public class InvokerMessageProcessor implements MessageProcessor, Initialisable, MuleContextAware
56  {
57      protected final transient Log logger = LogFactory.getLog(getClass());
58  
59      protected Object object;
60      protected Class<?> objectType;
61      protected String methodName;
62      protected List<?> arguments = new ArrayList<Object>();;
63      protected Class<?>[] argumentTypes;
64      protected String name;
65      protected PatternInfo patternInfo = TemplateParser.createMuleStyleParser().getStyle();
66  
67      protected Method method;
68      protected ExpressionManager expressionManager;
69      protected MuleContext muleContext;
70  
71      public void initialise() throws InitialisationException
72      {
73          if (object == null)
74          {
75              lookupObjectInstance();
76          }
77  
78          resolveMethodToInvoke();
79  
80          expressionManager = muleContext.getExpressionManager();
81      }
82  
83      protected void resolveMethodToInvoke() throws InitialisationException
84      {
85          if (argumentTypes != null)
86          {
87              method = ClassUtils.getMethod(object.getClass(), methodName, argumentTypes);
88              if (method == null)
89              {
90                  throw new InitialisationException(CoreMessages.methodWithParamsNotFoundOnObject(methodName,
91                      argumentTypes, object.getClass()), this);
92              }
93          }
94          else
95          {
96              List<Method> matchingMethods = new ArrayList<Method>();
97              int argSize = arguments != null ? arguments.size() : 0;
98              for (Method methodCandidate : object.getClass().getMethods())
99              {
100                 if (methodCandidate.getName().equals(methodName)
101                     && methodCandidate.getParameterTypes().length == argSize)
102                     matchingMethods.add(methodCandidate);
103             }
104             if (matchingMethods.size() == 1)
105             {
106                 method = matchingMethods.get(0);
107                 argumentTypes = method.getParameterTypes();
108             }
109             else
110             {
111                 throw new InitialisationException(CoreMessages.methodWithNumParamsNotFoundOnObject(
112                     methodName, arguments.size(), object), this);
113             }
114         }
115 
116         if (logger.isDebugEnabled())
117         {
118             logger.debug(String.format("Initialised %s to use method: '%s'", this, method));
119         }
120     }
121 
122     protected void lookupObjectInstance() throws InitialisationException
123     {
124         if (logger.isDebugEnabled())
125         {
126             logger.debug(String.format(
127                 "No object instance speciedied.  Looking up single instance of type %s in mule registry",
128                 objectType));
129         }
130 
131         try
132         {
133             object = muleContext.getRegistry().lookupObject(objectType);
134         }
135         catch (RegistrationException e)
136         {
137             throw new InitialisationException(
138                 CoreMessages.initialisationFailure(String.format(
139                     "Muliple instances of '%s' were found in the registry so you need to configure a specific instance",
140                     objectType)), this);
141         }
142         if (object == null)
143         {
144             throw new InitialisationException(CoreMessages.initialisationFailure(String.format(
145                 "No instance of '%s' was found in the registry", objectType)), this);
146 
147         }
148     }
149 
150     public MuleEvent process(MuleEvent event) throws MuleException
151     {
152         MuleEvent resultEvent = event;
153         Object[] args = evaluateArguments(event, arguments);
154 
155         if (logger.isDebugEnabled())
156         {
157             logger.debug(String.format("Invoking  '%s' of '%s' with arguments: '%s'", method.getName(),
158                 object, args));
159         }
160 
161         try
162         {
163             Object result = method.invoke(object, args);
164             if (!method.getReturnType().equals(void.class))
165             {
166                 resultEvent = createResultEvent(event, result);
167             }
168         }
169         catch (Exception e)
170         {
171             throw new MessagingException(CoreMessages.failedToInvoke(object.toString()), event, e);
172         }
173         return resultEvent;
174     }
175 
176     protected Object[] evaluateArguments(MuleEvent event, List<?> argumentTemplates)
177         throws MessagingException
178     {
179         int argSize = argumentTemplates != null ? argumentTemplates.size() : 0;
180         Object[] args = new Object[argSize];
181         MuleMessage message = event.getMessage();
182         try
183         {
184             for (int i = 0; i < args.length; i++)
185             {
186                 Object argumentTemplate = argumentTemplates.get(i);
187                 if (argumentTemplate != null)
188                 {
189                     args[i] = transformArgument(evaluateExpressionCandidate(argumentTemplate, message),
190                         argumentTypes[i]);
191                 }
192             }
193             return args;
194         }
195         catch (TransformerException e)
196         {
197             throw new MessagingException(event, e);
198         }
199     }
200 
201     @SuppressWarnings("unchecked")
202     protected Object evaluateExpressionCandidate(Object expressionCandidate, MuleMessage message)
203         throws TransformerException
204     {
205         if (expressionCandidate instanceof Collection<?>)
206         {
207             Collection<Object> collectionTemplate = (Collection<Object>) expressionCandidate;
208             Collection<Object> newCollection = new ArrayList<Object>();
209             for (Object object : collectionTemplate)
210             {
211                 newCollection.add(evaluateExpressionCandidate(object, message));
212             }
213             return newCollection;
214         }
215         else if (expressionCandidate instanceof Map<?, ?>)
216         {
217             Map<Object, Object> mapTemplate = (Map<Object, Object>) expressionCandidate;
218             Map<Object, Object> newMap = new HashMap<Object, Object>();
219             for (Entry<Object, Object> entry : mapTemplate.entrySet())
220             {
221                 newMap.put(evaluateExpressionCandidate(entry.getKey(), message), evaluateExpressionCandidate(
222                     entry.getValue(), message));
223             }
224             return newMap;
225         }
226         else if (expressionCandidate instanceof String[])
227         {
228             String[] stringArrayTemplate = (String[]) expressionCandidate;
229             Object[] newArray = new String[stringArrayTemplate.length];
230             for (int j = 0; j < stringArrayTemplate.length; j++)
231             {
232                 newArray[j] = evaluateExpressionCandidate(stringArrayTemplate[j], message);
233             }
234             return newArray;
235         }
236         if (expressionCandidate instanceof String)
237         {
238             Object arg;
239             String expression = (String) expressionCandidate;
240             // If string contains is a single expression then evaluate otherwise
241             // parse. We can't use parse() always because that will convert
242             // everything to a string
243             if (expression.startsWith(patternInfo.getPrefix())
244                 && expression.endsWith(patternInfo.getSuffix()))
245             {
246                 arg = expressionManager.evaluate(expression, message);
247             }
248             else
249             {
250                 arg = expressionManager.parse(expression, message);
251             }
252 
253             // If expression evaluates to a MuleMessage then use it's payload
254             if (arg instanceof MuleMessage)
255             {
256                 arg = ((MuleMessage) arg).getPayload();
257             }
258             return arg;
259         }
260         else
261         {
262             // Not an expression so use object itself
263             return expressionCandidate;
264         }
265     }
266 
267     private Object transformArgument(Object arg, Class<?> type) throws TransformerException
268     {
269         if (!(type.isAssignableFrom(arg.getClass())))
270         {
271             DataType<?> source = DataTypeFactory.create(arg.getClass());
272             DataType<?> target = DataTypeFactory.create(type);
273             // Throws TransformerException if no suitable transformer is found
274             Transformer t = muleContext.getRegistry().lookupTransformer(source, target);
275             arg = t.transform(arg);
276         }
277         return arg;
278     }
279 
280     public void setObject(Object object)
281     {
282         this.object = object;
283     }
284 
285     public void setMethodName(String methodName)
286     {
287         this.methodName = methodName;
288     }
289 
290     public void setArgumentExpressionsString(String arguments)
291     {
292         this.arguments = Arrays.asList(arguments.split("\\s*,\\s*"));
293     }
294 
295     public void setArguments(List<?> arguments)
296     {
297         this.arguments = arguments;
298     }
299 
300     protected MuleEvent createResultEvent(MuleEvent event, Object result) throws MuleException
301     {
302         if (result instanceof MuleMessage)
303         {
304             return new DefaultMuleEvent((MuleMessage) result, event);
305         }
306         else if (result != null)
307         {
308             event.getMessage().applyTransformers(
309                 event,
310                 Collections.<Transformer> singletonList(new TransformerTemplate(
311                     new TransformerTemplate.OverwitePayloadCallback(result))));
312             return event;
313         }
314         else
315         {
316             return new DefaultMuleEvent(new DefaultMuleMessage(NullPayload.getInstance(),
317                 event.getMuleContext()), event);
318         }
319     }
320 
321     public String getName()
322     {
323         return name;
324     }
325 
326     public void setName(String name)
327     {
328         this.name = name;
329     }
330 
331     public void setArgumentTypes(Class<?>[] argumentTypes)
332     {
333         this.argumentTypes = argumentTypes;
334     }
335 
336     @Override
337     public String toString()
338     {
339         return String.format(
340             "InvokerMessageProcessor [name=%s, object=%s, methodName=%s, argExpressions=%s, argTypes=%s]",
341             name, object, methodName, arguments, argumentTypes);
342     }
343 
344     public void setMuleContext(MuleContext context)
345     {
346         this.muleContext = context;
347     }
348 
349     public void setObjectType(Class<?> objectType)
350     {
351         this.objectType = objectType;
352     }
353 
354 }