1 //======================================================================== 2 //$Id: AnnotationParser.java 3680 2008-09-21 10:37:13Z janb $ 3 //Copyright 2006 Mort Bay Consulting Pty. Ltd. 4 //------------------------------------------------------------------------ 5 //Licensed under the Apache License, Version 2.0 (the "License"); 6 //you may not use this file except in compliance with the License. 7 //You may obtain a copy of the License at 8 //http://www.apache.org/licenses/LICENSE-2.0 9 //Unless required by applicable law or agreed to in writing, software 10 //distributed under the License is distributed on an "AS IS" BASIS, 11 //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 //See the License for the specific language governing permissions and 13 //limitations under the License. 14 //======================================================================== 15 16 package org.mortbay.jetty.annotations; 17 18 import java.lang.annotation.Annotation; 19 import java.lang.reflect.Field; 20 import java.lang.reflect.Method; 21 import java.lang.reflect.Modifier; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.ListIterator; 27 28 import org.mortbay.jetty.plus.annotation.InjectionCollection; 29 import org.mortbay.jetty.plus.annotation.LifeCycleCallbackCollection; 30 import org.mortbay.jetty.plus.annotation.RunAsCollection; 31 import org.mortbay.jetty.servlet.Holder; 32 import org.mortbay.jetty.servlet.ServletHolder; 33 import org.mortbay.jetty.webapp.WebAppContext; 34 import org.mortbay.log.Log; 35 import org.mortbay.util.IntrospectionUtil; 36 37 /** 38 * AnnotationParser 39 * 40 * None of the common annotations are inheritable, thus 41 * calling getAnnotations() is exactly equivalent to 42 * getDeclaredAnnotations(). Therefore, in order to find 43 * all relevant annotations, the full inheritance tree of 44 * a class must be considered. 45 * 46 * From the spec: 47 * Class-level annotations only affect the class they 48 * annotate and their members, that is, its methods and fields. 49 * They never affect a member declared by a superclass, even 50 * if it is not hidden or overridden by the class in question. 51 * 52 * In addition to affecting the annotated class, class-level 53 * annotations may act as a shorthand for member-level annotations. 54 * If a member carries a specific member-level annotation, any 55 * annotations of the same type implied by a class-level annotation 56 * are ignored. In other words, explicit member-level annotations 57 * have priority over member-level annotations implied by a class-level 58 * annotation. For example, a @WebService annotation on a class implies 59 * that all the public method in the class that it is applied on are 60 * annotated with @WebMethod if there is no @WebMethod annotation on 61 * any of the methods. However if there is a @WebMethod annotation on 62 * any method then the @WebService does not imply the presence of 63 * @WebMethod on the other public methods in the class. 64 * 65 * The interfaces implemented by a class never contribute annotations 66 * to the class itself or any of its members. 67 * 68 * Members inherited from a superclass and which are not hidden or 69 * overridden maintain the annotations they had in the class that 70 * declared them, including member-level annotations implied by 71 * class-level ones. 72 * 73 * Member-level annotations on a hidden or overridden member are 74 * always ignored 75 */ 76 public class AnnotationParser 77 { 78 /** 79 * Examine the class hierarchy for a class, finding all annotations. Then, merge any 80 * servlet2.5 spec annotations found with those already existing (from parsing web.xml) 81 * respecting the overriding rules found in the spec. 82 * 83 * @param webApp the webapp 84 * @param clazz the class to inspect 85 * @param runAs any run-as elements from web.xml 86 * @param injections any injections specified in web.xml 87 * @param callbacks any postconstruct/predestroy callbacks in web.xml 88 */ 89 public static void parseAnnotations (WebAppContext webApp, Class clazz, RunAsCollection runAs, InjectionCollection injections, LifeCycleCallbackCollection callbacks) 90 { 91 if (clazz==null) 92 return; 93 AnnotationCollection annotations = processClass(clazz); 94 annotations.setWebAppContext(webApp); 95 annotations.processRunAsAnnotations(runAs); 96 annotations.processResourcesAnnotations(); 97 annotations.processResourceAnnotations(injections); 98 annotations.processLifeCycleCallbackAnnotations(callbacks); 99 } 100 101 102 103 /** 104 * Examine the class hierarchy for this class looking for annotations. 105 * 106 * @param clazz 107 * @return AnnotationCollection 108 */ 109 static AnnotationCollection processClass (Class clazz) 110 { 111 AnnotationCollection collection = new AnnotationCollection(); 112 if (clazz==null) 113 return collection; 114 115 collection.setTargetClass(clazz); 116 117 //add any class level annotations 118 collection.addClass(clazz); 119 120 //Add all the fields with annotations. 121 Field[] fields = clazz.getDeclaredFields(); 122 //For each field, get all of it's annotations 123 for (int i=0; i<fields.length; i++) 124 { 125 collection.addField(fields[i]); 126 } 127 128 //Get all the methods with annotations 129 Method[] methods = clazz.getDeclaredMethods(); 130 for (int i=0; i<methods.length;i++) 131 { 132 collection.addMethod(methods[i]); 133 } 134 135 //process the inheritance hierarchy for the class 136 Class ancestor = clazz.getSuperclass(); 137 while (ancestor!=null && (!ancestor.equals(Object.class))) 138 { 139 processHierarchy (clazz, ancestor, collection); 140 ancestor = ancestor.getSuperclass(); 141 } 142 143 return collection; 144 } 145 146 147 148 /** 149 * Methods which are inherited retain their annotations. 150 * Methods which are not inherited and not overridden or hidden must also have their annotations processed. 151 * An overridden method can remove or change it's annotations. 152 * @param targetClazz 153 * @param ancestor 154 * @param targetClazzMethods 155 */ 156 private static void processHierarchy (Class targetClazz, Class ancestor, AnnotationCollection collection) 157 { 158 if (targetClazz==null) 159 return; 160 if (ancestor==null) 161 return; 162 163 //If the ancestor has class level annotations, remember it 164 collection.addClass(ancestor); 165 166 //Get annotations on the declared methods of the ancestor class. 167 //For each declared method that has an annotation, we need to 168 //determine if that method is inheritable&&!overridden or hidden 169 //in derived classes of the ancestor, in which case it contributes 170 //an annotation to the collection 171 //OR 172 //if the method is not inheritable, but has an annotation, it still 173 //contributes an annotation (even private non-inherited methods must 174 //have their annotations honoured) 175 Method[] methods = ancestor.getDeclaredMethods(); 176 for (int i=0; i<methods.length;i++) 177 { 178 if (methods[i].getAnnotations().length > 0) 179 { 180 if (!isOverriddenOrHidden(targetClazz, methods[i])) 181 collection.addMethod(methods[i]); 182 } 183 } 184 185 //Get annotations on declared fields. For each field work out if it is 186 //overridden or hidden in targetClazz 187 Field[] fields = ancestor.getDeclaredFields(); 188 for (int i=0;i<fields.length;i++) 189 { 190 if (fields[i].getAnnotations().length > 0) 191 { 192 //the field has annotations, so check to see if it should be inherited 193 //field is inheritable if it is: 194 // NOT private 195 // of package scope and of the same package 196 if (!isHidden(targetClazz, fields[i])) 197 collection.addField(fields[i]); 198 199 } 200 } 201 } 202 203 204 205 206 /** 207 * isOverriddenOrHidden 208 * 209 * Find out if method is overridden or hidden in the hierarchy down towards the 210 * most derived targetClass. 211 * 212 * case private: 213 * never inherited so therefore cannot be overridden or hidden return false; 214 * 215 * case public: 216 * case protected: 217 * inherited if no class from derived up to class declaring the method declares a method of the same signature 218 * 219 * case package: 220 * inherited if all classes in same package from derived to declaring class and no method of the same signature 221 * 222 * @param derivedClass the most derived class we are processing 223 * @param superclassMethod a method to check for being overridden or hidden 224 */ 225 private static boolean isOverriddenOrHidden (Class derivedClass, Method superclassMethod) 226 { 227 if (Modifier.isPrivate(superclassMethod.getModifiers())) 228 return false; //private methods cannot be inherited therefore cannot be overridden 229 230 if (Modifier.isPublic(superclassMethod.getModifiers()) || Modifier.isProtected(superclassMethod.getModifiers())) 231 { 232 //check to see if any class from most derived up to the declaring class for the method contains a method of the same sig 233 boolean sameSig = false; 234 Class c = derivedClass; 235 while (c != superclassMethod.getDeclaringClass()&&!sameSig) 236 { 237 sameSig = IntrospectionUtil.containsSameMethodSignature(superclassMethod, c, false); 238 c = c.getSuperclass(); 239 } 240 return sameSig; 241 } 242 243 //package protected 244 //check to see if any class from most derived up to declaring class contains method of same sig and that all 245 //intervening classes are of the same package (otherwise inheritance is blocked) 246 boolean sameSig = false; 247 Class c = derivedClass; 248 while (c != superclassMethod.getDeclaringClass() && !sameSig) 249 { 250 sameSig = IntrospectionUtil.containsSameMethodSignature(superclassMethod, c, true); 251 c = c.getSuperclass(); 252 } 253 return sameSig; 254 } 255 256 257 258 /** 259 * isHidden determines if a field from a superclass is hidden by field 260 * of the same name in any of the derived classes. 261 * 262 * We check upwards from the most derived class to the class containing 263 * the field. 264 * @param derivedClass the most derived class 265 * @param superclassField 266 * @return 267 */ 268 private static boolean isHidden (Class derivedClass, Field superclassField) 269 { 270 if (Modifier.isPrivate(superclassField.getModifiers())) 271 return false; //private methods are never inherited therefore never hidden 272 273 if (Modifier.isPublic(superclassField.getModifiers()) || Modifier.isProtected(superclassField.getModifiers())) 274 { 275 boolean hidden = false; 276 Class c = derivedClass; 277 while (!c.equals(superclassField.getDeclaringClass()) && !hidden) 278 { 279 hidden = IntrospectionUtil.containsSameFieldName(superclassField, c, false); 280 c=c.getSuperclass(); 281 } 282 return hidden; 283 } 284 285 //Package scope 286 //Derived classes hide the field if they are in the same package and have same field name 287 boolean hidden = false; 288 Class c = derivedClass; 289 while (!c.equals(superclassField.getDeclaringClass()) && !hidden) 290 { 291 hidden = IntrospectionUtil.containsSameFieldName(superclassField, c, true); 292 } 293 return hidden; 294 } 295 }