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.module.json.transformers;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.context.MuleContextAware;
11  import org.mule.api.lifecycle.Disposable;
12  import org.mule.api.registry.RegistrationException;
13  import org.mule.api.registry.ResolverException;
14  import org.mule.api.registry.TransformerResolver;
15  import org.mule.api.transformer.DataType;
16  import org.mule.api.transformer.Transformer;
17  import org.mule.config.i18n.CoreMessages;
18  import org.mule.transformer.simple.ObjectToString;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.codehaus.jackson.map.ObjectMapper;
23  
24  import java.util.List;
25  import java.util.Map;
26  import java.util.WeakHashMap;
27  
28  /**
29   * A {@link org.mule.api.registry.TransformerResolver} implementation used to discover whether the current transform
30   * requests requires Json mashaling. The resolver will scan the source and return type for Jackson (http://jackson.codehaus.org) Json annotations and will configure
31   * a JSON transformer accordingly.  The transformer is cached and will be used for any subsequent requests.
32   *
33   * The {@link org.codehaus.jackson.map.ObjectMapper} instance needed for the transform can be discovered from the registry, this means one can be
34   * pre-configured in Spring or Guice.  If there is no pre-configured {@link org.codehaus.jackson.map.ObjectMapper} one will be created with the
35   * annotated JSON class.  This context will cached with the transformer.
36   *
37   * @since 3.0
38   */
39  public class JsonTransformerResolver implements TransformerResolver, MuleContextAware, Disposable
40  {
41      public static final String JSON_MIME_TYPE = "application/json";
42      /**
43       * logger used by this class
44       */
45      protected transient final Log logger = LogFactory.getLog(JsonTransformerResolver.class);
46      
47      private MuleContext muleContext;
48  
49      //We cache the the transformers, this will get cleared when the server shuts down
50      private Map<String, Transformer> transformerCache = new WeakHashMap<String, Transformer>();
51  
52      private JsonMapperResolver resolver;
53  
54      public void setMuleContext(MuleContext context)
55      {
56          muleContext = context;
57      }
58  
59      public Transformer resolve(DataType<?> source, DataType<?> result) throws ResolverException
60      {
61          //Check the cache
62          Transformer t = transformerCache.get(source.toString() + result.toString());
63  
64          if (t != null)
65          {
66              return t;
67          }
68  
69          try
70          {
71              ObjectMapper mapper = getMapperResolver().resolve(ObjectMapper.class, source, result, muleContext);
72  
73              if(mapper==null)
74              {
75                  return null;
76              }
77              boolean marshal;
78              Class<?> annotatedType;
79  
80              //Check the class caches before we start scanning classes
81              if (getMapperResolver().getMatchingClasses().contains(result.getType()))
82              {
83                  annotatedType = result.getType();
84                  //Set the correct mime type on the raw type
85                  source = source.cloneDataType();
86                  source.setMimeType(JSON_MIME_TYPE);
87                  marshal = false;
88              }
89              else if (getMapperResolver().getMatchingClasses().contains(source.getType()))
90              {
91                  annotatedType = source.getType();
92                  //Set the correct mime type on the raw type
93                  result = result.cloneDataType();
94                  result.setMimeType(JSON_MIME_TYPE);
95                  marshal = true;
96              }
97              else
98              {
99                  return null;
100             }
101 
102 
103             //At this point we know we are dealing with Json, now lets check the registry to see if there is an exact
104             //transformer that matches our criteria
105             List<Transformer> ts = muleContext.getRegistry().lookupTransformers(source, result);
106             //ObjectToString continues to cause pain to auto transforms, here
107             //we check explicitly since we want to generate a Json transformer if
108             //one does not already exist in the context
109             if (ts.size() == 1 && !(ts.get(0) instanceof ObjectToString))
110             {
111                 t = ts.get(0);
112             }
113             else if (marshal)
114             {
115                 ObjectToJson otj = new ObjectToJson();
116                 otj.setSourceClass(annotatedType);
117                 otj.setReturnDataType(result);
118                 otj.setMapper(mapper);
119                 muleContext.getRegistry().applyProcessorsAndLifecycle(otj);
120                 t = otj;
121             }
122             else
123             {
124                 JsonToObject jto = new JsonToObject();
125                 jto.setReturnDataType(result);
126                 jto.setMapper(mapper);
127                 muleContext.getRegistry().applyProcessorsAndLifecycle(jto);
128                 t = jto;
129             }
130 
131             transformerCache.put(source.toString() + result.toString(), t);
132             return t;
133 
134         }
135         catch (Exception e)
136         {
137             throw new ResolverException(CoreMessages.createStaticMessage("Failed to unmarshal"), e);
138         }
139     }
140 
141     public void transformerChange(Transformer transformer, RegistryAction registryAction)
142     {
143         //nothing to do
144     }
145 
146     public void dispose()
147     {
148         transformerCache.clear();
149     }
150 
151     protected JsonMapperResolver getMapperResolver() throws ResolverException
152     {
153         if(resolver==null)
154         {
155             try
156             {
157                 resolver = muleContext.getRegistry().lookupObject(JsonMapperResolver.class);
158             }
159             catch (RegistrationException e)
160             {
161                 throw new ResolverException(e.getI18nMessage(), e);
162             }
163         }
164         return resolver;
165     }
166 }