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