View Javadoc

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