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