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.spi.impl;
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  import org.apache.myfaces.spi.InjectionProvider;
34  import org.apache.myfaces.spi.InjectionProviderException;
35  
36  /**
37   * See SRV.14.5 Servlet Specification Version 2.5 JSR 154
38   * and Common Annotations for the Java Platform JSR 250
39  
40   */
41  
42  public class NoInjectionAnnotationInjectionProvider extends InjectionProvider
43  {
44       /**
45       * Cache the Method instances per ClassLoader using the Class-Name.
46       * NOTE that we do it this way, because the only other valid way in order to support a shared
47       * classloader scenario would be to use a WeakHashMap<Class<?>, Method[]>, but this
48       * creates a cyclic reference between the key and the value of the WeakHashMap which will
49       * most certainly cause a memory leak! Furthermore we can manually cleanup the Map when
50       * the webapp is undeployed just by removing the Map for the current ClassLoader. 
51       */
52      private volatile static WeakHashMap<ClassLoader, Map<Class,Method[]> > declaredMethodBeans = 
53              new WeakHashMap<ClassLoader, Map<Class, Method[]>>();
54  
55      private static Map<Class,Method[]> getDeclaredMethodBeansMap()
56      {
57          ClassLoader cl = ClassUtils.getContextClassLoader();
58          
59          Map<Class,Method[]> metadata = (Map<Class,Method[]>)
60                  declaredMethodBeans.get(cl);
61  
62          if (metadata == null)
63          {
64              // Ensure thread-safe put over _metadata, and only create one map
65              // per classloader to hold metadata.
66              synchronized (declaredMethodBeans)
67              {
68                  metadata = createDeclaredMethodBeansMap(cl, metadata);
69              }
70          }
71  
72          return metadata;
73      }
74      
75      private static Map<Class,Method[]> createDeclaredMethodBeansMap(
76              ClassLoader cl, Map<Class,Method[]> metadata)
77      {
78          metadata = (Map<Class,Method[]>) declaredMethodBeans.get(cl);
79          if (metadata == null)
80          {
81              metadata = new HashMap<Class,Method[]>();
82              declaredMethodBeans.put(cl, metadata);
83          }
84          return metadata;
85      }
86  
87      @Override
88      public Object inject(Object instance) throws InjectionProviderException
89      {
90          try
91          {
92              processAnnotations(instance);
93          }
94          catch (IllegalAccessException ex)
95          {
96              throw new InjectionProviderException(ex);
97          }
98          catch (InvocationTargetException ex)
99          {
100             throw new InjectionProviderException(ex);
101         }
102         catch (NamingException ex)
103         {
104             throw new InjectionProviderException(ex);
105         }
106         return null;
107     }
108     
109     
110     Method[] getDeclaredMethods(Class clazz)
111     {
112         Map<Class,Method[]> declaredMethodBeansMap = getDeclaredMethodBeansMap();
113         Method[] methods = declaredMethodBeansMap.get(clazz);
114         if (methods == null)
115         {
116             methods = clazz.getDeclaredMethods();
117             synchronized(declaredMethodBeansMap)
118             {
119                 declaredMethodBeansMap.put(clazz, methods);
120             }
121         }
122         return methods;
123     }
124 
125     /**
126      * Call postConstruct method on the specified instance.
127      */
128     @Override
129     public void postConstruct(Object instance, Object creationMetaData) throws InjectionProviderException
130     {
131         // TODO the servlet spec is not clear about searching in superclass??
132         Class clazz = instance.getClass();
133         Method[] methods = getDeclaredMethods(clazz);
134         if (methods == null)
135         {
136             methods = clazz.getDeclaredMethods();
137             Map<Class,Method[]> declaredMethodBeansMap = getDeclaredMethodBeansMap();
138             synchronized(declaredMethodBeansMap)
139             {
140                 declaredMethodBeansMap.put(clazz, methods);
141             }
142         }
143         Method postConstruct = null;
144         for (int i = 0; i < methods.length; i++)
145         {
146             Method method = methods[i];
147             if (method.isAnnotationPresent(PostConstruct.class))
148             {
149                 // a method that does not take any arguments
150                 // the method must not be static
151                 // must not throw any checked expections
152                 // the return value must be void
153                 // the method may be public, protected, package private or private
154 
155                 if ((postConstruct != null)
156                         || (method.getParameterTypes().length != 0)
157                         || (Modifier.isStatic(method.getModifiers()))
158                         || (method.getExceptionTypes().length > 0)
159                         || (!method.getReturnType().getName().equals("void")))
160                 {
161                     throw new IllegalArgumentException("Invalid PostConstruct annotation");
162                 }
163                 postConstruct = method;
164             }
165         }
166         try
167         {
168             invokeAnnotatedMethod(postConstruct, instance);
169         }
170         catch (IllegalAccessException ex)
171         {
172             throw new InjectionProviderException(ex);
173         }
174         catch (InvocationTargetException ex)
175         {
176             throw new InjectionProviderException(ex);
177         }
178     }
179 
180     @Override
181     public void preDestroy(Object instance, Object creationMetaData) throws InjectionProviderException
182     {
183 
184         // TODO the servlet spec is not clear about searching in superclass??
185         // May be only check non private fields and methods
186         Class clazz = instance.getClass();
187         Method[] methods = getDeclaredMethods(clazz);
188         Method preDestroy = null;
189         for (int i = 0; i < methods.length; i++)
190         {
191             Method method = methods[i];
192             if (method.isAnnotationPresent(PreDestroy.class))
193             {
194                 // must not throw any checked expections
195                 // the method must not be static
196                 // must not throw any checked expections
197                 // the return value must be void
198                 // the method may be public, protected, package private or private
199 
200                 if ((preDestroy != null)
201                         || (method.getParameterTypes().length != 0)
202                         || (Modifier.isStatic(method.getModifiers()))
203                         || (method.getExceptionTypes().length > 0)
204                         || (!method.getReturnType().getName().equals("void")))
205                 {
206                     throw new IllegalArgumentException("Invalid PreDestroy annotation");
207                 }
208                 preDestroy = method;
209             }
210         }
211 
212         try
213         {
214             invokeAnnotatedMethod(preDestroy, instance);
215         }
216         catch (IllegalAccessException ex)
217         {
218             throw new InjectionProviderException(ex);
219         }
220         catch (InvocationTargetException ex)
221         {
222             throw new InjectionProviderException(ex);
223         }
224     }
225 
226     private void invokeAnnotatedMethod(Method method, Object instance)
227                 throws IllegalAccessException, InvocationTargetException
228     {
229         // At the end the annotated
230         // method is invoked
231         if (method != null)
232         {
233             boolean accessibility = method.isAccessible();
234             method.setAccessible(true);
235             method.invoke(instance);
236             method.setAccessible(accessibility);
237         }
238     }
239 
240      /**
241      * Inject resources in specified instance.
242      */
243     protected void processAnnotations(Object instance)
244             throws IllegalAccessException, InvocationTargetException, NamingException
245     {
246 
247     }
248 }