View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.config.annotation;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.WeakHashMap;
28  import javax.annotation.PostConstruct;
29  import javax.annotation.PreDestroy;
30  import javax.naming.NamingException;
31  
32  import org.apache.myfaces.shared.util.ClassUtils;
33  
34  /**
35   * See SRV.14.5 Servlet Specification Version 2.5 JSR 154
36   * and Common Annotations for the Java Platform JSR 250
37  
38   */
39  
40  public class NoInjectionAnnotationLifecycleProvider implements LifecycleProvider2
41  {
42       /**
43       * Cache the Method instances per ClassLoader using the Class-Name.
44       * NOTE that we do it this way, because the only other valid way in order to support a shared
45       * classloader scenario would be to use a WeakHashMap<Class<?>, Method[]>, but this
46       * creates a cyclic reference between the key and the value of the WeakHashMap which will
47       * most certainly cause a memory leak! Furthermore we can manually cleanup the Map when
48       * the webapp is undeployed just by removing the Map for the current ClassLoader. 
49       */
50      private volatile static WeakHashMap<ClassLoader, Map<Class,Method[]> > declaredMethodBeans = 
51              new WeakHashMap<ClassLoader, Map<Class, Method[]>>();
52  
53      private static Map<Class,Method[]> getDeclaredMethodBeansMap()
54      {
55          ClassLoader cl = ClassUtils.getContextClassLoader();
56          
57          Map<Class,Method[]> metadata = (Map<Class,Method[]>)
58                  declaredMethodBeans.get(cl);
59  
60          if (metadata == null)
61          {
62              // Ensure thread-safe put over _metadata, and only create one map
63              // per classloader to hold metadata.
64              synchronized (declaredMethodBeans)
65              {
66                  metadata = createDeclaredMethodBeansMap(cl, metadata);
67              }
68          }
69  
70          return metadata;
71      }
72      
73      private static Map<Class,Method[]> createDeclaredMethodBeansMap(
74              ClassLoader cl, Map<Class,Method[]> metadata)
75      {
76          metadata = (Map<Class,Method[]>) declaredMethodBeans.get(cl);
77          if (metadata == null)
78          {
79              metadata = new HashMap<Class,Method[]>();
80              declaredMethodBeans.put(cl, metadata);
81          }
82          return metadata;
83      }
84  
85      public Object newInstance(String className)
86             throws InstantiationException, IllegalAccessException, NamingException,
87              InvocationTargetException, ClassNotFoundException
88      {
89          Class clazz = ClassUtils.classForName(className);
90          Object object = clazz.newInstance();
91          processAnnotations(object);
92          //postConstruct(object);
93          return object;
94      }
95      
96      
97      Method[] getDeclaredMethods(Class clazz)
98      {
99          Map<Class,Method[]> declaredMethodBeansMap = getDeclaredMethodBeansMap();
100         Method[] methods = declaredMethodBeansMap.get(clazz);
101         if (methods == null)
102         {
103             methods = clazz.getDeclaredMethods();
104             synchronized(declaredMethodBeansMap)
105             {
106                 declaredMethodBeansMap.put(clazz, methods);
107             }
108         }
109         return methods;
110     }
111 
112     /**
113      * Call postConstruct method on the specified instance.
114      */
115     public void postConstruct(Object instance)
116             throws IllegalAccessException, InvocationTargetException
117     {
118         // TODO the servlet spec is not clear about searching in superclass??
119         Class clazz = instance.getClass();
120         Method[] methods = getDeclaredMethods(clazz);
121         if (methods == null)
122         {
123             methods = clazz.getDeclaredMethods();
124             Map<Class,Method[]> declaredMethodBeansMap = getDeclaredMethodBeansMap();
125             synchronized(declaredMethodBeansMap)
126             {
127                 declaredMethodBeansMap.put(clazz, methods);
128             }
129         }
130         Method postConstruct = null;
131         for (int i = 0; i < methods.length; i++)
132         {
133             Method method = methods[i];
134             if (method.isAnnotationPresent(PostConstruct.class))
135             {
136                 // a method that does not take any arguments
137                 // the method must not be static
138                 // must not throw any checked expections
139                 // the return value must be void
140                 // the method may be public, protected, package private or private
141 
142                 if ((postConstruct != null)
143                         || (method.getParameterTypes().length != 0)
144                         || (Modifier.isStatic(method.getModifiers()))
145                         || (method.getExceptionTypes().length > 0)
146                         || (!method.getReturnType().getName().equals("void")))
147                 {
148                     throw new IllegalArgumentException("Invalid PostConstruct annotation");
149                 }
150                 postConstruct = method;
151             }
152         }
153 
154         invokeAnnotatedMethod(postConstruct, instance);
155 
156     }
157 
158     public void destroyInstance(Object instance)
159             throws IllegalAccessException, InvocationTargetException
160     {
161 
162         // TODO the servlet spec is not clear about searching in superclass??
163         // May be only check non private fields and methods
164         Class clazz = instance.getClass();
165         Method[] methods = getDeclaredMethods(clazz);
166         Method preDestroy = null;
167         for (int i = 0; i < methods.length; i++)
168         {
169             Method method = methods[i];
170             if (method.isAnnotationPresent(PreDestroy.class))
171             {
172                 // must not throw any checked expections
173                 // the method must not be static
174                 // must not throw any checked expections
175                 // the return value must be void
176                 // the method may be public, protected, package private or private
177 
178                 if ((preDestroy != null)
179                         || (method.getParameterTypes().length != 0)
180                         || (Modifier.isStatic(method.getModifiers()))
181                         || (method.getExceptionTypes().length > 0)
182                         || (!method.getReturnType().getName().equals("void")))
183                 {
184                     throw new IllegalArgumentException("Invalid PreDestroy annotation");
185                 }
186                 preDestroy = method;
187             }
188         }
189 
190         invokeAnnotatedMethod(preDestroy, instance);
191 
192     }
193 
194     private void invokeAnnotatedMethod(Method method, Object instance)
195                 throws IllegalAccessException, InvocationTargetException
196     {
197         // At the end the annotated
198         // method is invoked
199         if (method != null)
200         {
201             boolean accessibility = method.isAccessible();
202             method.setAccessible(true);
203             method.invoke(instance);
204             method.setAccessible(accessibility);
205         }
206     }
207 
208      /**
209      * Inject resources in specified instance.
210      */
211     protected void processAnnotations(Object instance)
212             throws IllegalAccessException, InvocationTargetException, NamingException
213     {
214 
215     }
216 
217 }