View Javadoc

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