View Javadoc

1   /*
2    * $Id: InvokerMessageProcessor.java 20637 2010-12-11 02:32:12Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.processor;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.DefaultMuleMessage;
15  import org.mule.api.MessagingException;
16  import org.mule.api.MuleContext;
17  import org.mule.api.MuleEvent;
18  import org.mule.api.MuleException;
19  import org.mule.api.MuleMessage;
20  import org.mule.api.context.MuleContextAware;
21  import org.mule.api.expression.ExpressionManager;
22  import org.mule.api.lifecycle.Initialisable;
23  import org.mule.api.lifecycle.InitialisationException;
24  import org.mule.api.processor.MessageProcessor;
25  import org.mule.api.registry.RegistrationException;
26  import org.mule.api.transformer.DataType;
27  import org.mule.api.transformer.Transformer;
28  import org.mule.api.transformer.TransformerException;
29  import org.mule.config.i18n.CoreMessages;
30  import org.mule.transformer.TransformerTemplate;
31  import org.mule.transformer.types.DataTypeFactory;
32  import org.mule.transport.NullPayload;
33  import org.mule.util.ClassUtils;
34  import org.mule.util.TemplateParser;
35  import org.mule.util.TemplateParser.PatternInfo;
36  
37  import java.lang.reflect.Method;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collection;
41  import java.util.Collections;
42  import java.util.HashMap;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Map.Entry;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  
50  /**
51   * <code>InvokerMessageProcessor</code> invokes a specified method of an object. An
52   * array of argument expressions can be provided to map the message to the method
53   * arguments. The method used is determined by the method name along with the number
54   * of argument expressions provided. The results of the expression evaluations will
55   * automatically be transformed where possible to the method argument type. Multiple
56   * methods with the same name and same number of arguments are not supported
57   * currently.
58   */
59  public class InvokerMessageProcessor implements MessageProcessor, Initialisable, MuleContextAware
60  {
61      protected final transient Log logger = LogFactory.getLog(getClass());
62  
63      protected Object object;
64      protected Class<?> objectType;
65      protected String methodName;
66      protected List<?> arguments;
67      protected Class<?>[] argumentTypes;
68      protected String name;
69      protected PatternInfo patternInfo = TemplateParser.createMuleStyleParser().getStyle();
70  
71      protected Method method;
72      protected ExpressionManager expressionManager;
73      protected MuleContext muleContext;
74  
75      public void initialise() throws InitialisationException
76      {
77          if (object == null)
78          {
79              lookupObjectInstance();
80          }
81  
82          resolveMethodToInvoke();
83  
84          expressionManager = muleContext.getExpressionManager();
85      }
86  
87      protected void resolveMethodToInvoke() throws InitialisationException
88      {
89          if (argumentTypes != null)
90          {
91              method = ClassUtils.getMethod(object.getClass(), methodName, argumentTypes);
92              if (method == null)
93              {
94                  throw new InitialisationException(CoreMessages.methodWithParamsNotFoundOnObject(methodName,
95                      argumentTypes, object.getClass()), this);
96              }
97          }
98          else
99          {
100             List<Method> matchingMethods = new ArrayList<Method>();
101             for (Method methodCandidate : object.getClass().getMethods())
102             {
103                 if (methodCandidate.getName().equals(methodName)
104                     && methodCandidate.getParameterTypes().length == arguments.size())
105                     matchingMethods.add(methodCandidate);
106             }
107             if (matchingMethods.size() == 1)
108             {
109                 method = matchingMethods.get(0);
110                 argumentTypes = method.getParameterTypes();
111             }
112             else
113             {
114                 throw new InitialisationException(CoreMessages.methodWithNumParamsNotFoundOnObject(
115                     methodName, arguments.size(), object), this);
116             }
117         }
118 
119         if (logger.isDebugEnabled())
120         {
121             logger.debug(String.format("Initialised %s to use method: '%s'", this, method));
122         }
123     }
124 
125     protected void lookupObjectInstance() throws InitialisationException
126     {
127         if (logger.isDebugEnabled())
128         {
129             logger.debug(String.format(
130                 "No object instance speciedied.  Looking up single instance of type %s in mule registry",
131                 objectType));
132         }
133 
134         try
135         {
136             object = muleContext.getRegistry().lookupObject(objectType);
137         }
138         catch (RegistrationException e)
139         {
140             throw new InitialisationException(
141                 CoreMessages.initialisationFailure(String.format(
142                     "Muliple instances of '%s' were found in the registry so you need to configure a specific instance",
143                     objectType)), this);
144         }
145         if (object == null)
146         {
147             throw new InitialisationException(CoreMessages.initialisationFailure(String.format(
148                 "No instance of '%s' was found in the registry", objectType)), this);
149 
150         }
151     }
152 
153     public MuleEvent process(MuleEvent event) throws MuleException
154     {
155         MuleEvent resultEvent = event;
156         Object[] args = evaluateArguments(event, arguments);
157 
158         if (logger.isDebugEnabled())
159         {
160             logger.debug(String.format("Invoking  '%s' of '%s' with arguments: '%s'", method.getName(),
161                 object, args));
162         }
163 
164         try
165         {
166             Object result = method.invoke(object, args);
167             if (!method.getReturnType().equals(void.class))
168             {
169                 resultEvent = createResultEvent(event, result);
170             }
171         }
172         catch (Exception e)
173         {
174             throw new MessagingException(CoreMessages.failedToInvoke(object.toString()), event, e);
175         }
176         return resultEvent;
177     }
178 
179     protected Object[] evaluateArguments(MuleEvent event, List<?> argumentTemplates)
180         throws MessagingException
181     {
182         Object[] args = new Object[argumentTemplates.size()];
183         MuleMessage message = event.getMessage();
184         try
185         {
186             for (int i = 0; i < args.length; i++)
187             {
188                 Object argumentTemplate = argumentTemplates.get(i);
189                 if (argumentTemplate != null)
190                 {
191                     args[i] = transformArgument(evaluateExpressionCandidate(argumentTemplate, message),
192                         argumentTypes[i]);
193                 }
194             }
195             return args;
196         }
197         catch (TransformerException e)
198         {
199             throw new MessagingException(event, e);
200         }
201     }
202 
203     @SuppressWarnings("unchecked")
204     protected Object evaluateExpressionCandidate(Object expressionCandidate, MuleMessage message)
205         throws TransformerException
206     {
207         if (expressionCandidate instanceof Collection<?>)
208         {
209             Collection<Object> collectionTemplate = (Collection<Object>) expressionCandidate;
210             Collection<Object> newCollection = new ArrayList<Object>();
211             for (Object object : collectionTemplate)
212             {
213                 newCollection.add(evaluateExpressionCandidate(object, message));
214             }
215             return newCollection;
216         }
217         else if (expressionCandidate instanceof Map<?, ?>)
218         {
219             Map<Object, Object> map = (Map<Object, Object>) expressionCandidate;
220             for (Entry<Object, Object> entry : map.entrySet())
221             {
222                 map.put(entry.getKey(), evaluateExpressionCandidate(entry.getValue(), message));
223             }
224             return map;
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 }