View Javadoc

1   /*
2    * $Id: JAXBTransformerResolver.java 19191 2010-08-25 21:05:23Z tcarlson $
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.xml.transformer.jaxb;
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  import org.mule.util.annotation.AnnotationUtils;
23  
24  import java.io.IOException;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.concurrent.ConcurrentHashMap;
28  
29  import javax.xml.bind.JAXBContext;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  /**
35   * A {@link org.mule.api.registry.TransformerResolver} implementation used to discover whether the current transform
36   * requests requires JAXB. The resolver will scan the source and return type for JAXB 2 annotations and will configure
37   * a JAXB transformer accordingly.  The transformer is cached and will be used for any subsequent requests.
38   *
39   * The {@link javax.xml.bind.JAXBContext} instance needed for the transform can be discovered from the registry, this means one can be
40   * pre-configured in Spring or Guice.  If there is no pre-configured {@link javax.xml.bind.JAXBContext} one will be created with the
41   * annotated JAXB class.  This context will cached with the transformer.
42   *
43   * @since 3.0
44   */
45  public class JAXBTransformerResolver implements TransformerResolver, MuleContextAware, Disposable
46  {
47      public static final String[] ignoredPackages = {"java.,javax.,org.w3c.,org.mule.transport., org.mule.module."};
48  
49      /**
50       * logger used by this class
51       */
52      protected transient final Log logger = LogFactory.getLog(JAXBTransformerResolver.class);
53  
54      private MuleContext muleContext;
55  
56      //We cache the the transformers, this will get cleared when the server shuts down
57      private Map<String, Transformer> transformerCache = new ConcurrentHashMap<String, Transformer>();
58  
59      private JAXBContextResolver resolver;
60  
61      public void setMuleContext(MuleContext context)
62      {
63          muleContext = context;
64      }
65  
66      public Transformer resolve(DataType source, DataType result) throws ResolverException
67      {
68          Transformer t = transformerCache.get(source.toString() + result.toString());
69          if (t != null)
70          {
71              return t;
72          }
73  
74          try
75          {
76              JAXBContext jaxb = getContextResolver().resolve(JAXBContext.class, source, result, muleContext);
77  
78              if(jaxb==null)
79              {
80                  return null;
81              }
82              boolean marshal = false;
83              Class annotatedType = null;
84  
85  
86              if (getContextResolver().getMatchingClasses().contains(result.getType()))
87              {
88                  annotatedType = result.getType();
89                  marshal = false;
90              }
91              else
92              {
93                  if (getContextResolver().getMatchingClasses().contains(source.getType()))
94                  {
95                      annotatedType = source.getType();
96                      marshal = true;
97                  }
98              }
99  
100             if (annotatedType == null)
101             {
102                 annotatedType = result.getType();
103                 boolean isJAXB = hasJaxbAnnotations(annotatedType);
104                 if (!isJAXB)
105                 {
106                     marshal = true;
107                     annotatedType = source.getType();
108                     isJAXB = hasJaxbAnnotations(annotatedType);
109                 }
110 
111                 if (!isJAXB)
112                 {
113                     return null;
114                 }
115             }
116             //At this point we know we are dealing with JAXB, now lets check the registry to see if there is an exact
117             //transformer that matches our criteria
118             List<Transformer> ts = muleContext.getRegistry().lookupTransformers(source, result);
119             if (ts.size() == 1 && !(ts.get(0) instanceof ObjectToString))
120             {
121                 t = ts.get(0);
122             }
123             else
124             {
125                 if (marshal)
126                 {
127                     t = new JAXBMarshallerTransformer(jaxb, result);
128                 }
129                 else
130                 {
131                     t = new JAXBUnmarshallerTransformer(jaxb, result);
132                 }
133             }
134 
135             transformerCache.put(source.toString() + result.toString(), t);
136             return t;
137 
138         }
139         catch (Exception e)
140         {
141             throw new ResolverException(CoreMessages.createStaticMessage("Failed to unmarshal"), e);
142         }
143     }
144 
145     public void transformerChange(Transformer transformer, RegistryAction registryAction)
146     {
147         //nothing to do
148     }
149 
150     public void dispose()
151     {
152         transformerCache.clear();
153     }
154 
155     protected JAXBContextResolver getContextResolver() throws RegistrationException
156     {
157         if(resolver==null)
158         {
159             resolver = muleContext.getRegistry().lookupObject(JAXBContextResolver.class);
160         }
161         return resolver;
162     }
163 
164     protected boolean hasJaxbAnnotations(Class annotatedType)
165     {
166         String p = annotatedType.getPackage().getName();
167         for (int i = 0; i < ignoredPackages.length; i++)
168         {
169             if(p.startsWith(ignoredPackages[i])) return false;
170         }
171 
172         try
173         {
174              return AnnotationUtils.hasAnnotationWithPackage("javax.xml.bind.annotation", annotatedType);
175         }
176         catch (IOException e)
177         {
178             logger.warn("Failed to scan class for Jaxb annotations: " + annotatedType, e);
179             return false;
180         }
181     }
182 }