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 }