View Javadoc

1   /*
2    * Copyright (c) 2010 Carman Consulting, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.metastopheles.annotation;
18  
19  import org.metastopheles.BeanMetaData;
20  import org.metastopheles.BeanMetaDataFactory;
21  import org.metastopheles.MetaDataDecorator;
22  import org.metastopheles.MetaDataObject;
23  import org.metastopheles.MethodMetaData;
24  import org.metastopheles.PropertyMetaData;
25  import org.scannotation.AnnotationDB;
26  import org.scannotation.ClasspathUrlFinder;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import java.io.IOException;
31  import java.lang.annotation.Annotation;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.lang.reflect.Modifier;
35  import java.net.URL;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.TreeMap;
40  
41  public class AnnotationBeanMetaDataFactory extends BeanMetaDataFactory
42  {
43  //**********************************************************************************************************************
44  // Fields
45  //**********************************************************************************************************************
46  
47      private final Logger logger = LoggerFactory.getLogger(getClass());
48  
49  //**********************************************************************************************************************
50  // Constructors
51  //**********************************************************************************************************************
52  
53      public AnnotationBeanMetaDataFactory()
54      {
55          this(ClasspathUrlFinder.findClassPaths());
56      }
57  
58      public AnnotationBeanMetaDataFactory(Class baseClass)
59      {
60          this(ClasspathUrlFinder.findResourceBases(baseClass.getName().replace('.', '/') + ".class", baseClass.getClassLoader() == null ? ClassLoader.getSystemClassLoader() : baseClass.getClassLoader()));
61      }
62  
63      public AnnotationBeanMetaDataFactory(URL... urls)
64      {
65          AnnotationDB db = new AnnotationDB();
66          try
67          {
68              db.setScanClassAnnotations(false);
69              db.setScanMethodAnnotations(true);
70              db.setScanFieldAnnotations(false);
71              db.setScanParameterAnnotations(false);
72              db.crossReferenceMetaAnnotations();
73              db.scanArchives(urls);
74              // db.crossReferenceImplementedInterfaces();
75              final Map<String, Set<String>> index = db.getAnnotationIndex();
76              Map<String, Object> targets = new TreeMap<String, Object>();
77  
78              final Set<String> manuallyScannedClasses = index.get(ScanMe.class.getName());
79              if (manuallyScannedClasses != null && !manuallyScannedClasses.isEmpty())
80              {
81                  for (String className : manuallyScannedClasses)
82                  {
83                      try
84                      {
85                          Class c = Class.forName(className);
86                          while (c != null)
87                          {
88                              for (String annotationName : index.keySet())
89                              {
90                                  Set<String> annotatedClasses = index.get(annotationName);
91                                  if (annotatedClasses.contains(c.getName()))
92                                  {
93                                      annotatedClasses.add(className);
94                                  }
95                              }
96                              c = c.getSuperclass();
97                          }
98                      }
99                      catch (ClassNotFoundException e)
100                     {
101                         logger.error("Unable to load manually scanned class " + className + ".", e);
102                     }
103 
104                 }
105             }
106             scanForMetaDataMethods(index, targets, BeanMetaData.class, BeanDecorator.class, getBeanMetaDataDecorators());
107             scanForMetaDataMethods(index, targets, PropertyMetaData.class, PropertyDecorator.class, getPropertyMetaDataDecorators());
108             scanForMetaDataMethods(index, targets, MethodMetaData.class, MethodDecorator.class, getMethodMetaDataDecorators());
109         }
110         catch (IOException e)
111         {
112             throw new RuntimeException("Unable to scan classpath for annotations.", e);
113         }
114         catch (AnnotationDB.CrossReferenceException e)
115         {
116             throw new RuntimeException("Unable to scan classpath for annotations.", e);
117         }
118     }
119 
120 //**********************************************************************************************************************
121 // Other Methods
122 //**********************************************************************************************************************
123 
124     @SuppressWarnings("unchecked")
125     private <T extends MetaDataObject> void scanForMetaDataMethods(Map<String, Set<String>> index, Map<String, Object> targets, Class<T> metaDataType, Class<? extends Annotation> markerAnnotationType, List<MetaDataDecorator<T>> decorators)
126     {
127         final Set<String> classes = index.get(markerAnnotationType.getName());
128         if (classes == null || classes.isEmpty())
129         {
130             return;
131         }
132 
133         for (String className : classes)
134         {
135             try
136             {
137                 Class<?> c = Class.forName(className);
138                 for (Method method : c.getMethods())
139                 {
140                     if (method.isAnnotationPresent(markerAnnotationType))
141                     {
142                         Class[] parameterTypes = method.getParameterTypes();
143                         if (parameterTypes.length == 2 && metaDataType.equals(parameterTypes[0]) && parameterTypes[1].isAnnotation())
144                         {
145                             Class<? extends Annotation> annotationType = (Class<? extends Annotation>) parameterTypes[1];
146                             if (Modifier.isStatic(method.getModifiers()))
147                             {
148                                 logger.debug("Adding static decorator method " + method);
149                                 decorators.add(new MethodBasedDecorator(annotationType, null, method));
150                             }
151                             else if (!Modifier.isAbstract(c.getModifiers()))
152                             {
153                                 logger.debug("Adding decorator method " + method);
154                                 Object target = targets.get(c.getName());
155                                 if (target == null)
156                                 {
157                                     logger.debug("Instantiating " + c.getName() + " instance to handle decorator methods found...");
158                                     target = c.newInstance();
159                                     targets.put(c.getName(), target);
160                                 }
161 
162                                 decorators.add(new MethodBasedDecorator(annotationType, target, method));
163                             }
164                         }
165                     }
166                 }
167             }
168             catch (ClassNotFoundException e)
169             {
170                 logger.error("Unable to load class " + className + ", skipping annotations.", e);
171             }
172             catch (InstantiationException e)
173             {
174                 logger.error("Unable to instantiate object of type " + className + ".", e);
175             }
176             catch (IllegalAccessException e)
177             {
178                 logger.error("Unable to instantiate object of type " + className + ".", e);
179             }
180         }
181     }
182 
183 //**********************************************************************************************************************
184 // Inner Classes
185 //**********************************************************************************************************************
186 
187     private static class MethodBasedDecorator<T extends MetaDataObject, A extends Annotation> extends AnnotationBasedMetaDataDecorator<T, A>
188     {
189         private final Object target;
190         private final Method method;
191 
192         private MethodBasedDecorator(Class<A> annotationType, Object target, Method method)
193         {
194             super(annotationType);
195             this.target = target;
196             this.method = method;
197         }
198 
199         @Override
200         protected void decorate(T metaData, Annotation annotation)
201         {
202             try
203             {
204                 method.invoke(target, metaData, annotation);
205             }
206             catch (IllegalAccessException e)
207             {
208                 throw new RuntimeException("Unable to access decorator method " + method + ".", e);
209             }
210             catch (InvocationTargetException e)
211             {
212                 throw new RuntimeException("Decorator method " + method + " threw an exception.", e.getTargetException());
213             }
214         }
215     }
216 }