View Javadoc

1   /*
2    * $Id: AbstractAnnotatedTransformerArgumentResolver.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.config.transformer;
11  
12  import org.mule.api.MuleContext;
13  import org.mule.api.lifecycle.Disposable;
14  import org.mule.api.transformer.DataType;
15  import org.mule.transformer.types.CollectionDataType;
16  import org.mule.util.annotation.AnnotationUtils;
17  
18  import java.io.IOException;
19  import java.util.Set;
20  import java.util.concurrent.CopyOnWriteArraySet;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  /**
26   * An abstract resolver that can be extend to resolve/create an object based on Annotated classes. These classes can be
27   * scanned and used to create the context.  Typically this class will be used to create resolvers for binding frameworks
28   * where the data type classes are annotated with binding information.
29   */
30  public abstract class AbstractAnnotatedTransformerArgumentResolver implements TransformerArgumentResolver, Disposable
31  {
32      public static final String[] ignoredPackages = {"java.", "javax.", "org.w3c.", "org.mule.transport.", "org.mule.module."};
33  
34      /**
35       * logger used by this class
36       */
37      protected transient final Log logger = LogFactory.getLog(getClass());
38  
39      //We cache the Json classes so we don't scan them each time a context is needed
40      private Set<Class> matchingClasses = new CopyOnWriteArraySet<Class>();
41  
42      //We also cache the classes that did not match so that we dont scan them again either
43      private Set<Class> nonMatchingClasses = new CopyOnWriteArraySet<Class>();
44  
45      public <T> T resolve(Class<T> type, DataType source, DataType result, MuleContext context) throws Exception
46      {
47          //Lets make sure this is the right resolver for the object type and that we haven't scanned these before
48          //and determined they are not Json classes
49          if(!getArgumentClass().isAssignableFrom(type) || isNonMatching(source, result))
50          {
51              return null;
52          }
53  
54          Class annotatedType = (result instanceof CollectionDataType ? ((CollectionDataType)result).getItemType() : result.getType());
55  
56          //Check the cache first
57          boolean isAnnotated = matchingClasses.contains(annotatedType);
58          if(!isAnnotated)
59          {
60              //then scan the class for annotations
61              isAnnotated = findAnnotation(annotatedType);
62          }
63  
64          if (!isAnnotated)
65          {
66              annotatedType = source.getType();
67              //Check the cache first
68              isAnnotated = matchingClasses.contains(annotatedType);
69              if(!isAnnotated)
70              {
71                  //then scan the class for annotations
72                  isAnnotated = AnnotationUtils.hasAnnotationWithPackage(getAnnotationsPackageName(), annotatedType);
73              }
74          }
75  
76          Object argument = context.getRegistry().lookupObject(getArgumentClass());
77  
78          if (!isAnnotated)
79          {
80              //We didn't find Json annotations anywhere, lets cache the classes so we don't need to scan again
81              nonMatchingClasses.add(source.getType());
82              nonMatchingClasses.add(result.getType());
83  
84              //Finally if there is an Object Mapper configured we should return it
85              return (T)argument;
86          }
87          else
88          {
89              matchingClasses.add(annotatedType);
90          }
91  
92  
93          if (argument == null)
94          {
95              logger.info("No common Object of type '" + getArgumentClass() + "' configured, creating a local one for: " + source + ", " + result);
96              argument = createArgument(annotatedType, context);
97          }
98          return (T)argument;
99  
100     }
101 
102     protected boolean findAnnotation(Class annotatedType) throws IOException
103     {
104         
105         if(annotatedType.getPackage()==null)
106         {
107             return false;
108         }
109 
110         for (String ignoredPackage : ignoredPackages)
111         {
112             if(annotatedType.getPackage().getName().startsWith(ignoredPackage))
113             {
114                 return false;
115             }
116         }
117         return AnnotationUtils.hasAnnotationWithPackage(getAnnotationsPackageName(), annotatedType);
118     }
119 
120     protected boolean isNonMatching(DataType source, DataType result)
121     {
122         return nonMatchingClasses.contains(result.getType()) && nonMatchingClasses.contains(source.getType());
123     }
124 
125     public void dispose()
126     {
127         nonMatchingClasses.clear();
128         matchingClasses.clear();
129     }
130 
131     public Set<Class> getMatchingClasses()
132     {
133         return matchingClasses;
134     }
135 
136     public Set<Class> getNonMatchingClasses()
137     {
138         return nonMatchingClasses;
139     }
140 
141     /**
142      * The object type that this resolver will discover or create
143      * @return  The object type that this resolver will discover or create
144      */
145     protected abstract Class<?> getArgumentClass();
146 
147     /**
148      * If the resolver cannot locate the required object of type {@link #getArgumentClass()} this method will be invoked
149      * an instance of the object.
150      *
151      * @param annotatedType the annotated object that was matched
152      * @param muleContext the current Mule context.
153      * @return a new instance of the object being resolved.  This method may also retain a shared instance and possible add
154      * configuration to the instance based on this invocation
155      * @throws Exception if the object cannot be created
156      */
157     protected abstract Object createArgument(Class<?> annotatedType, MuleContext muleContext) throws Exception;
158 
159     /**
160      * This resolver scans a class for annotations in this package.  Note this behaviour can be changed by overloading
161      * the {@link #findAnnotation(Class)} method and search based on your own criteria
162      * @return the package of the annotation(s) to scan for
163      */
164     protected abstract String getAnnotationsPackageName();
165 }