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.registry;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.context.MuleContextAware;
12  import org.mule.api.lifecycle.Disposable;
13  import org.mule.api.lifecycle.Initialisable;
14  import org.mule.api.lifecycle.InitialisationException;
15  import org.mule.api.registry.ResolverException;
16  import org.mule.api.registry.TransformerResolver;
17  import org.mule.api.transformer.DataType;
18  import org.mule.api.transformer.DiscoverableTransformer;
19  import org.mule.api.transformer.Transformer;
20  import org.mule.config.i18n.CoreMessages;
21  import org.mule.transformer.TransformerChain;
22  import org.mule.transformer.TransformerWeighting;
23  import org.mule.transformer.simple.ObjectToByteArray;
24  import org.mule.transformer.simple.ObjectToString;
25  import org.mule.transformer.types.SimpleDataType;
26  
27  import java.util.List;
28  import java.util.Map;
29  
30  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  /**
36   * Will discover transformers based on type information only. It looks for transformers that support
37   * the source and result types passed into the method. This resolver only resolves on the first
38   * source type, which is the way transformer resolution working in Mule 2.x.
39   */
40  public class TypeBasedTransformerResolver implements TransformerResolver, MuleContextAware, Disposable, Initialisable
41  {
42      /**
43       * logger used by this class
44       */
45      protected transient final Log logger = LogFactory.getLog(TypeBasedTransformerResolver.class);
46  
47      private ObjectToString objectToString;
48      private ObjectToByteArray objectToByteArray;
49  
50      private MuleContext muleContext;
51  
52      protected Map<String, Transformer> exactTransformerCache = new ConcurrentHashMap/*<String, Transformer>*/(8);
53  
54  
55      public void setMuleContext(MuleContext context)
56      {
57          this.muleContext = context;
58      }
59  
60      public void initialise() throws InitialisationException
61      {
62          try
63          {
64              objectToString = new ObjectToString();
65              objectToByteArray = new ObjectToByteArray();
66              muleContext.getRegistry().applyProcessorsAndLifecycle(objectToString);
67              muleContext.getRegistry().applyProcessorsAndLifecycle(objectToByteArray);
68          }
69          catch (MuleException e)
70          {
71              throw new InitialisationException(e, this);
72          }
73      }
74  
75      public Transformer resolve(DataType source, DataType result) throws ResolverException
76      {
77          Transformer transformer = exactTransformerCache.get(source.toString() + result.toString());
78          if (transformer != null)
79          {
80              return transformer;
81          }
82  
83          List<Transformer> trans = muleContext.getRegistry().lookupTransformers(source, result);
84  
85          transformer = getNearestTransformerMatch(trans, source.getType(), result.getType());
86          //If an exact mach is not found, we have a 'second pass' transformer that can be used to converting to String or
87          //byte[]
88          Transformer secondPass;
89  
90          if (transformer == null)
91          {
92              //If no transformers were found but the outputType type is String or byte[] we can perform a more general search
93              // using Object.class and then convert to String or byte[] using the second pass transformer
94              if (result.getType().equals(String.class))
95              {
96                  secondPass = objectToString;
97              }
98              else if (result.getType().equals(byte[].class))
99              {
100                 secondPass = objectToByteArray;
101             }
102             else
103             {
104                 return null;
105             }
106             //Perform a more general search
107             trans = muleContext.getRegistry().lookupTransformers(source, new SimpleDataType(Object.class));
108 
109             transformer = getNearestTransformerMatch(trans, source.getType(), result.getType());
110             if (transformer != null)
111             {
112                 transformer = new TransformerChain(transformer, secondPass);
113                 try
114                 {
115                     muleContext.getRegistry().registerTransformer(transformer);
116                 }
117                 catch (MuleException e)
118                 {
119                     throw new ResolverException(e.getI18nMessage(), e);
120                 }
121             }
122         }
123 
124         if (transformer != null)
125         {
126             exactTransformerCache.put(source.toString() + result.toString(), transformer);
127         }
128         return transformer;
129     }
130 
131     protected Transformer getNearestTransformerMatch(List<Transformer> trans, Class input, Class output) throws ResolverException
132     {
133         if (trans.size() > 1)
134         {
135             if (logger.isDebugEnabled())
136             {
137                 logger.debug("Comparing transformers for best match: input = " + input + " output = " + output + " Possible transformers = " + trans);
138             }
139             TransformerWeighting weighting = null;
140             for (Transformer transformer : trans)
141             {
142                 TransformerWeighting current = new TransformerWeighting(input, output, transformer);
143                 if (weighting == null)
144                 {
145                     weighting = current;
146                 }
147                 else
148                 {
149                     int compare = current.compareTo(weighting);
150                     if (compare == 1)
151                     {
152                         weighting = current;
153                     }
154                     else if (compare == 0)
155                     {
156                         //We may have two transformers that are exactly the same, in which case we can use either i.e. use the current
157                         if (!weighting.getTransformer().getClass().equals(current.getTransformer().getClass()))
158                         {
159                             throw new ResolverException(CoreMessages.transformHasMultipleMatches(input, output,
160                                     current.getTransformer(), weighting.getTransformer()));
161                         }
162                     }
163                 }
164             }
165             return weighting.getTransformer();
166         }
167         else if (trans.size() == 0)
168         {
169             return null;
170         }
171         else
172         {
173             return trans.get(0);
174         }
175     }
176 
177     public void dispose()
178     {
179         exactTransformerCache.clear();
180     }
181 
182     public void transformerChange(Transformer transformer, RegistryAction registryAction)
183     {
184         if (transformer instanceof DiscoverableTransformer)
185         {
186             exactTransformerCache.clear();
187         }
188     }
189 }