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.config.transformer;
8   
9   import org.mule.api.MuleMessage;
10  import org.mule.api.annotations.param.Payload;
11  import org.mule.api.lifecycle.InitialisationException;
12  import org.mule.api.transformer.DataType;
13  import org.mule.api.transformer.DiscoverableTransformer;
14  import org.mule.api.transformer.TransformerException;
15  import org.mule.config.expression.ExpressionAnnotationsHelper;
16  import org.mule.config.i18n.AnnotationsMessages;
17  import org.mule.expression.transformers.ExpressionTransformer;
18  import org.mule.transformer.AbstractMessageTransformer;
19  import org.mule.transformer.types.DataTypeFactory;
20  import org.mule.transformer.types.SimpleDataType;
21  import org.mule.util.annotation.AnnotationUtils;
22  
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Modifier;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Map;
28  import java.util.WeakHashMap;
29  
30  /**
31   * Creates a Mule {@link org.mule.api.transformer.Transformer} proxy around a transform method. The
32   * transformer will be given a generated name which is the short name of the class and the method name
33   * separated with a '.' i.e. 'MyTransformers.fooToBar'
34   */
35  public class AnnotatedTransformerProxy extends AbstractMessageTransformer implements DiscoverableTransformer
36  {
37      private int weighting;
38  
39      private Object proxy;
40  
41      private Method transformMethod;
42      private boolean messageAware = false;
43      private ExpressionTransformer paramTransformer = null;
44      private Collection<TransformerArgumentResolver> resolvers;
45      private Map<Class<?>, Object> cachedObjects = new WeakHashMap<Class<?>, Object>();
46      private boolean sourceAnnotated = false;
47  
48      public AnnotatedTransformerProxy(int weighting, Object proxy, Method transformMethod, Class[] additionalSourceTypes, String sourceMimeType, String resultMimeType) throws TransformerException, InitialisationException
49      {
50          this.weighting = weighting;
51          this.proxy = proxy;
52  
53          //By default we allow a transformer to return null
54          setAllowNullReturn(true);
55  
56          validateMethod(transformMethod, additionalSourceTypes);
57  
58          this.transformMethod = transformMethod;
59          setReturnDataType(DataTypeFactory.createFromReturnType(transformMethod));
60  
61          messageAware = MuleMessage.class.isAssignableFrom(transformMethod.getParameterTypes()[0]);
62          this.transformMethod = transformMethod;
63          if (additionalSourceTypes.length > 0)
64          {
65              if (messageAware)
66              {
67                  logger.error("Transformer: " + getName() + " is MuleMessage aware, this means additional source types configured on the transformer will be ignorred. Source types are: " + Arrays.toString(additionalSourceTypes));
68              }
69              else
70              {
71                  for (int i = 0; i < additionalSourceTypes.length; i++)
72                  {
73                      registerSourceType(new SimpleDataType(additionalSourceTypes[i], sourceMimeType));
74  
75                  }
76              }
77          }
78          //The first Param is the always the object to transform
79          Class<?> source = transformMethod.getParameterTypes()[0];
80          registerSourceType(DataTypeFactory.create(source));
81          sourceAnnotated = (transformMethod.getParameterAnnotations()[0].length > 0 &&
82                  transformMethod.getParameterAnnotations()[0][0] instanceof Payload);
83  
84          setName(proxy.getClass().getSimpleName() + "." + transformMethod.getName());
85      }
86  
87      protected void validateMethod(Method method, Class[] sourceTypes) throws IllegalArgumentException
88      {
89          int mods = method.getModifiers();
90          if(Modifier.isAbstract(mods) || Modifier.isInterface(mods) || !Modifier.isPublic(mods)
91                  || method.getReturnType().equals(Void.TYPE) || method.getParameterTypes().length==0 ||
92                  method.getReturnType().equals(Object.class) || Arrays.asList(method.getParameterTypes()).contains(Object.class) ||
93                  Arrays.asList(sourceTypes).contains(Object.class))
94          {
95              //May lift the ban on OBject return and source types
96              //The details as to why the method is invalid are in the message
97              throw new IllegalArgumentException(AnnotationsMessages.transformerMethodNotValid(method).getMessage());
98          }
99      }
100 
101     @Override
102     public void initialise() throws InitialisationException
103     {
104         super.initialise();
105         if (AnnotationUtils.methodHasParamAnnotations(transformMethod))
106         {
107             try
108             {
109                 paramTransformer = ExpressionAnnotationsHelper.getTransformerForMethodWithAnnotations(transformMethod, muleContext);
110             }
111             catch (TransformerException e)
112             {
113                 throw new InitialisationException(e, this);
114             }
115         }
116         resolvers = muleContext.getRegistry().lookupObjects(TransformerArgumentResolver.class);
117     }
118 
119     @Override
120     public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException
121     {
122         Object firstArg = null;
123         Object[] params = new Object[transformMethod.getParameterTypes().length];
124         int paramCounter = 0;
125 
126         //the param transformer will convert the message to one or more objects depending on the annotations on the method
127         //parameter
128         if (paramTransformer != null)
129         {
130             Object paramArgs = paramTransformer.transformMessage(message, outputEncoding);
131 
132             if (paramArgs != null && paramArgs.getClass().isArray())
133             {
134                 Object[] temp = (Object[]) paramArgs;
135                 //if the source is annotated, the paramTransformer will figure out the first parameter
136                 if (!sourceAnnotated)
137                 {
138                     paramCounter++;
139                 }
140                 for (int i = 0; i < temp.length; i++)
141                 {
142                     params[paramCounter++] = temp[i];
143                 }
144             }
145             else
146             {
147                 //if the source is annotated, the paramTransformer will figure out the first parameter
148                 if (!sourceAnnotated)
149                 {
150                     paramCounter++;
151                 }
152                 params[paramCounter++] = paramArgs;
153             }
154         }
155 
156         if (messageAware)
157         {
158             firstArg = message;
159         }
160         else if (!sourceAnnotated)
161         {
162             //This will perform any additional transformation from the source type to the method parameter type
163             try
164             {
165                 firstArg = message.getPayload(DataTypeFactory.create(transformMethod.getParameterTypes()[0]));
166             }
167             catch (TransformerException e)
168             {
169                 throw new TransformerException(e.getI18nMessage(), this, e);
170             }
171         }
172 
173         //if the source is annotated, the paramTransformer will figure out the first parameter
174         if (!sourceAnnotated)
175         {
176             params[0] = firstArg;
177             if (paramCounter == 0)
178             {
179                 paramCounter++;
180             }
181         }
182         //Lets see if there are any context objects to inject (i.e. JAXB)
183         if (paramCounter < transformMethod.getParameterTypes().length)
184         {
185             for (int i = paramCounter; i < transformMethod.getParameterTypes().length; i++)
186             {
187                 Object o;
188                 Class<?> type = transformMethod.getParameterTypes()[i];
189                 o = cachedObjects.get(type);
190                 if (o != null)
191                 {
192                     params[paramCounter++] = o;
193                     continue;
194                 }
195                 DataType<?> source = DataTypeFactory.createFromParameterType(transformMethod, 0);
196 
197                 for (TransformerArgumentResolver resolver : resolvers)
198                 {
199                     try
200                     {
201                         o = resolver.resolve(type, source, this.returnType, muleContext);
202                         if (o != null)
203                         {
204                             params[paramCounter++] = o;
205                             cachedObjects.put(type, o);
206                             break;
207                         }
208                     }
209                     catch (Exception e)
210                     {
211                         throw new TransformerException(this, e);
212                     }
213 
214                 }
215                 if (o == null)
216                 {
217                     throw new IllegalArgumentException("Failed to find resolver for object type: " + type + " for transform method: " + transformMethod);
218                 }
219             }
220         }
221         try
222         {
223             return transformMethod.invoke(proxy, params);
224         }
225         catch (Exception e)
226         {
227             e.printStackTrace();
228             throw new TransformerException(this, e);
229         }
230     }
231 
232     public int getPriorityWeighting()
233     {
234         return weighting;
235     }
236 
237     public void setPriorityWeighting(int weighting)
238     {
239         throw new UnsupportedOperationException("setPriorityWeighting");
240     }
241 
242     @Override
243     public boolean equals(Object o)
244     {
245         if (this == o)
246         {
247             return true;
248         }
249         if (o == null || getClass() != o.getClass())
250         {
251             return false;
252         }
253 
254         AnnotatedTransformerProxy that = (AnnotatedTransformerProxy) o;
255 
256         if (messageAware != that.messageAware)
257         {
258             return false;
259         }
260         if (weighting != that.weighting)
261         {
262             return false;
263         }
264         if (proxy != null ? !proxy.equals(that.proxy) : that.proxy != null)
265         {
266             return false;
267         }
268         if (transformMethod != null ? !transformMethod.equals(that.transformMethod) : that.transformMethod != null)
269         {
270             return false;
271         }
272 
273         return true;
274     }
275 
276     @Override
277     public int hashCode()
278     {
279         int result = weighting;
280         result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
281         result = 31 * result + (transformMethod != null ? transformMethod.hashCode() : 0);
282         result = 31 * result + (messageAware ? 1 : 0);
283         return result;
284     }
285 
286 
287 }