View Javadoc

    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 }