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 }