View Javadoc

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