1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.core;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.lang.annotation.Annotation;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.security.AccessController;
26 import java.security.PrivilegedAction;
27 import java.security.PrivilegedActionException;
28 import java.security.PrivilegedExceptionAction;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Properties;
36 import java.util.Set;
37
38 import javax.annotation.PostConstruct;
39 import javax.annotation.PreDestroy;
40 import javax.annotation.Resource;
41 import javax.ejb.EJB;
42 import javax.naming.Context;
43 import javax.naming.NamingException;
44 import javax.persistence.PersistenceContext;
45 import javax.persistence.PersistenceUnit;
46 import javax.xml.ws.WebServiceRef;
47
48 import org.apache.catalina.ContainerServlet;
49 import org.apache.catalina.Globals;
50 import org.apache.catalina.security.SecurityUtil;
51 import org.apache.catalina.util.Introspection;
52 import org.apache.juli.logging.Log;
53 import org.apache.tomcat.InstanceManager;
54 import org.apache.tomcat.util.ExceptionUtils;
55 import org.apache.tomcat.util.collections.ManagedConcurrentWeakHashMap;
56 import org.apache.tomcat.util.res.StringManager;
57
58 public class DefaultInstanceManager implements InstanceManager {
59
60     // Used when there are no annotations in a class
61     private static final AnnotationCacheEntry[] ANNOTATIONS_EMPTY
62         = new AnnotationCacheEntry[0];
63
64     /**
65      * The string manager for this package.
66      */

67     protected static final StringManager sm =
68         StringManager.getManager(Constants.Package);
69
70     private static final boolean EJB_PRESENT;
71     private static final boolean JPA_PRESENT;
72     private static final boolean WS_PRESENT;
73
74     static {
75         Class<?> clazz = null;
76         try {
77             clazz = Class.forName("javax.ejb.EJB");
78         } catch (ClassNotFoundException cnfe) {
79             // Expected
80         }
81         EJB_PRESENT = (clazz != null);
82
83         clazz = null;
84         try {
85             clazz = Class.forName("javax.persistence.PersistenceContext");
86         } catch (ClassNotFoundException cnfe) {
87             // Expected
88         }
89         JPA_PRESENT = (clazz != null);
90
91         clazz = null;
92         try {
93             clazz = Class.forName("javax.xml.ws.WebServiceRef");
94         } catch (ClassNotFoundException cnfe) {
95             // Expected
96         }
97         WS_PRESENT = (clazz != null);
98     }
99
100
101     private final Context context;
102     private final Map<String, Map<String, String>> injectionMap;
103     protected final ClassLoader classLoader;
104     protected final ClassLoader containerClassLoader;
105     protected final boolean privileged;
106     protected final boolean ignoreAnnotations;
107     private final Set<String> restrictedClasses;
108     private final ManagedConcurrentWeakHashMap<Class<?>, AnnotationCacheEntry[]> annotationCache =
109             new ManagedConcurrentWeakHashMap<>();
110     private final Map<String, String> postConstructMethods;
111     private final Map<String, String> preDestroyMethods;
112
113     public DefaultInstanceManager(Context context,
114             Map<String, Map<String, String>> injectionMap,
115             org.apache.catalina.Context catalinaContext,
116             ClassLoader containerClassLoader) {
117         classLoader = catalinaContext.getLoader().getClassLoader();
118         privileged = catalinaContext.getPrivileged();
119         this.containerClassLoader = containerClassLoader;
120         ignoreAnnotations = catalinaContext.getIgnoreAnnotations();
121         Log log = catalinaContext.getLogger();
122         Set<String> classNames = new HashSet<>();
123         loadProperties(classNames,
124                 "org/apache/catalina/core/RestrictedServlets.properties",
125                 "defaultInstanceManager.restrictedServletsResource", log);
126         loadProperties(classNames,
127                 "org/apache/catalina/core/RestrictedListeners.properties",
128                 "defaultInstanceManager.restrictedListenersResource", log);
129         loadProperties(classNames,
130                 "org/apache/catalina/core/RestrictedFilters.properties",
131                 "defaultInstanceManager.restrictedFiltersResource", log);
132         restrictedClasses = Collections.unmodifiableSet(classNames);
133         this.context = context;
134         this.injectionMap = injectionMap;
135         this.postConstructMethods = catalinaContext.findPostConstructMethods();
136         this.preDestroyMethods = catalinaContext.findPreDestroyMethods();
137     }
138
139     @Override
140     public Object newInstance(Class<?> clazz) throws IllegalAccessException,
141             InvocationTargetException, NamingException, InstantiationException,
142             IllegalArgumentException, NoSuchMethodException, SecurityException {
143         return newInstance(clazz.getConstructor().newInstance(), clazz);
144     }
145
146     @Override
147     public Object newInstance(String className) throws IllegalAccessException,
148             InvocationTargetException, NamingException, InstantiationException,
149             ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException {
150         Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
151         return newInstance(clazz.getConstructor().newInstance(), clazz);
152     }
153
154     @Override
155     public Object newInstance(final String className, final ClassLoader classLoader)
156             throws IllegalAccessException, NamingException, InvocationTargetException,
157             InstantiationException, ClassNotFoundException, IllegalArgumentException,
158             NoSuchMethodException, SecurityException {
159         Class<?> clazz = classLoader.loadClass(className);
160         return newInstance(clazz.getConstructor().newInstance(), clazz);
161     }
162
163     @Override
164     public void newInstance(Object o)
165             throws IllegalAccessException, InvocationTargetException, NamingException {
166         newInstance(o, o.getClass());
167     }
168
169     private Object newInstance(Object instance, Class<?> clazz)
170             throws IllegalAccessException, InvocationTargetException, NamingException {
171         if (!ignoreAnnotations) {
172             Map<String, String> injections = assembleInjectionsFromClassHierarchy(clazz);
173             populateAnnotationsCache(clazz, injections);
174             processAnnotations(instance, injections);
175             postConstruct(instance, clazz);
176         }
177         return instance;
178     }
179
180     private Map<String, String> assembleInjectionsFromClassHierarchy(Class<?> clazz) {
181         Map<String, String> injections = new HashMap<>();
182         Map<String, String> currentInjections = null;
183         while (clazz != null) {
184             currentInjections = this.injectionMap.get(clazz.getName());
185             if (currentInjections != null) {
186                 injections.putAll(currentInjections);
187             }
188             clazz = clazz.getSuperclass();
189         }
190         return injections;
191     }
192
193     @Override
194     public void destroyInstance(Object instance) throws IllegalAccessException,
195             InvocationTargetException {
196         if (!ignoreAnnotations) {
197             preDestroy(instance, instance.getClass());
198         }
199     }
200
201     /**
202      * Call postConstruct method on the specified instance recursively from
203      * deepest superclass to actual class.
204      *
205      * @param instance object to call postconstruct methods on
206      * @param clazz    (superclass to examine for postConstruct annotation.
207      * @throws IllegalAccessException if postConstruct method is inaccessible.
208      * @throws java.lang.reflect.InvocationTargetException
209      *                                if call fails
210      */

211     protected void postConstruct(Object instance, final Class<?> clazz)
212             throws IllegalAccessException, InvocationTargetException {
213         if (context == null) {
214             // No resource injection
215             return;
216         }
217
218         Class<?> superClass = clazz.getSuperclass();
219         if (superClass != Object.class) {
220             postConstruct(instance, superClass);
221         }
222
223         // At the end the postconstruct annotated
224         // method is invoked
225         AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
226         for (AnnotationCacheEntry entry : annotations) {
227             if (entry.getType() == AnnotationCacheEntryType.POST_CONSTRUCT) {
228                 Method postConstruct = getMethod(clazz, entry);
229                 synchronized (postConstruct) {
230                     boolean accessibility = postConstruct.isAccessible();
231                     postConstruct.setAccessible(true);
232                     postConstruct.invoke(instance);
233                     postConstruct.setAccessible(accessibility);
234                 }
235             }
236         }
237     }
238
239
240     /**
241      * Call preDestroy method on the specified instance recursively from deepest
242      * superclass to actual class.
243      *
244      * @param instance object to call preDestroy methods on
245      * @param clazz    (superclass to examine for preDestroy annotation.
246      * @throws IllegalAccessException if preDestroy method is inaccessible.
247      * @throws java.lang.reflect.InvocationTargetException
248      *                                if call fails
249      */

250     protected void preDestroy(Object instance, final Class<?> clazz)
251             throws IllegalAccessException, InvocationTargetException {
252         Class<?> superClass = clazz.getSuperclass();
253         if (superClass != Object.class) {
254             preDestroy(instance, superClass);
255         }
256
257         // At the end the postconstruct annotated
258         // method is invoked
259         AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
260         if (annotations == null) {
261             // instance not created through the instance manager
262             return;
263         }
264         for (AnnotationCacheEntry entry : annotations) {
265             if (entry.getType() == AnnotationCacheEntryType.PRE_DESTROY) {
266                 Method preDestroy = getMethod(clazz, entry);
267                 synchronized (preDestroy) {
268                     boolean accessibility = preDestroy.isAccessible();
269                     preDestroy.setAccessible(true);
270                     preDestroy.invoke(instance);
271                     preDestroy.setAccessible(accessibility);
272                 }
273             }
274         }
275     }
276
277
278     @Override
279     public void backgroundProcess() {
280         annotationCache.maintain();
281     }
282
283
284     /**
285      * Make sure that the annotations cache has been populated for the provided
286      * class.
287      *
288      * @param clazz         clazz to populate annotations for
289      * @param injections    map of injections for this class from xml deployment
290      *                      descriptor
291      * @throws IllegalAccessException       if injection target is inaccessible
292      * @throws javax.naming.NamingException if value cannot be looked up in jndi
293      * @throws java.lang.reflect.InvocationTargetException
294      *                                      if injection fails
295      */

296     protected void populateAnnotationsCache(Class<?> clazz,
297             Map<String, String> injections) throws IllegalAccessException,
298             InvocationTargetException, NamingException {
299
300         List<AnnotationCacheEntry> annotations = null;
301         Set<String> injectionsMatchedToSetter = new HashSet<>();
302
303         while (clazz != null) {
304             AnnotationCacheEntry[] annotationsArray = annotationCache.get(clazz);
305             if (annotationsArray == null) {
306                 if (annotations == null) {
307                     annotations = new ArrayList<>();
308                 } else {
309                     annotations.clear();
310                 }
311
312                 // Initialize methods annotations
313                 Method[] methods = Introspection.getDeclaredMethods(clazz);
314                 Method postConstruct = null;
315                 String postConstructFromXml = postConstructMethods.get(clazz.getName());
316                 Method preDestroy = null;
317                 String preDestroyFromXml = preDestroyMethods.get(clazz.getName());
318                 for (Method method : methods) {
319                     if (context != null) {
320                         // Resource injection only if JNDI is enabled
321                         if (injections != null && Introspection.isValidSetter(method)) {
322                             String fieldName = Introspection.getPropertyName(method);
323                             injectionsMatchedToSetter.add(fieldName);
324                             if (injections.containsKey(fieldName)) {
325                                 annotations.add(new AnnotationCacheEntry(
326                                         method.getName(),
327                                         method.getParameterTypes(),
328                                         injections.get(fieldName),
329                                         AnnotationCacheEntryType.SETTER));
330                                 continue;
331                             }
332                         }
333                         Resource resourceAnnotation;
334                         Annotation ejbAnnotation;
335                         Annotation webServiceRefAnnotation;
336                         Annotation persistenceContextAnnotation;
337                         Annotation persistenceUnitAnnotation;
338                         if ((resourceAnnotation = method.getAnnotation(Resource.class)) != null) {
339                             annotations.add(new AnnotationCacheEntry(
340                                     method.getName(),
341                                     method.getParameterTypes(),
342                                     resourceAnnotation.name(),
343                                     AnnotationCacheEntryType.SETTER));
344                         } else if (EJB_PRESENT &&
345                                 (ejbAnnotation = method.getAnnotation(EJB.class)) != null) {
346                             annotations.add(new AnnotationCacheEntry(
347                                     method.getName(),
348                                     method.getParameterTypes(),
349                                     ((EJB) ejbAnnotation).name(),
350                                     AnnotationCacheEntryType.SETTER));
351                         } else if (WS_PRESENT && (webServiceRefAnnotation =
352                                 method.getAnnotation(WebServiceRef.class)) != null) {
353                             annotations.add(new AnnotationCacheEntry(
354                                     method.getName(),
355                                     method.getParameterTypes(),
356                                     ((WebServiceRef) webServiceRefAnnotation).name(),
357                                     AnnotationCacheEntryType.SETTER));
358                         } else if (JPA_PRESENT && (persistenceContextAnnotation =
359                                 method.getAnnotation(PersistenceContext.class)) != null) {
360                             annotations.add(new AnnotationCacheEntry(
361                                     method.getName(),
362                                     method.getParameterTypes(),
363                                     ((PersistenceContext) persistenceContextAnnotation).name(),
364                                     AnnotationCacheEntryType.SETTER));
365                         } else if (JPA_PRESENT && (persistenceUnitAnnotation =
366                                 method.getAnnotation(PersistenceUnit.class)) != null) {
367                             annotations.add(new AnnotationCacheEntry(
368                                     method.getName(),
369                                     method.getParameterTypes(),
370                                     ((PersistenceUnit) persistenceUnitAnnotation).name(),
371                                     AnnotationCacheEntryType.SETTER));
372                         }
373                     }
374
375                     postConstruct = findPostConstruct(postConstruct, postConstructFromXml, method);
376
377                     preDestroy = findPreDestroy(preDestroy, preDestroyFromXml, method);
378                 }
379
380                 if (postConstruct != null) {
381                     annotations.add(new AnnotationCacheEntry(
382                             postConstruct.getName(),
383                             postConstruct.getParameterTypes(), null,
384                             AnnotationCacheEntryType.POST_CONSTRUCT));
385                 } else if (postConstructFromXml != null) {
386                     throw new IllegalArgumentException(sm.getString("defaultInstanceManager.postConstructNotFound",
387                         postConstructFromXml, clazz.getName()));
388                 }
389                 if (preDestroy != null) {
390                     annotations.add(new AnnotationCacheEntry(
391                             preDestroy.getName(),
392                             preDestroy.getParameterTypes(), null,
393                             AnnotationCacheEntryType.PRE_DESTROY));
394                 } else if (preDestroyFromXml != null) {
395                     throw new IllegalArgumentException(sm.getString("defaultInstanceManager.preDestroyNotFound",
396                         preDestroyFromXml, clazz.getName()));
397                 }
398
399                 if (context != null) {
400                     // Initialize fields annotations for resource injection if
401                     // JNDI is enabled
402                     Field[] fields = Introspection.getDeclaredFields(clazz);
403                     for (Field field : fields) {
404                         Resource resourceAnnotation;
405                         Annotation ejbAnnotation;
406                         Annotation webServiceRefAnnotation;
407                         Annotation persistenceContextAnnotation;
408                         Annotation persistenceUnitAnnotation;
409                         String fieldName = field.getName();
410                         if (injections != null && injections.containsKey(fieldName) && !injectionsMatchedToSetter.contains(fieldName)) {
411                             annotations.add(new AnnotationCacheEntry(
412                                     fieldName, null,
413                                     injections.get(fieldName),
414                                     AnnotationCacheEntryType.FIELD));
415                         } else if ((resourceAnnotation =
416                                 field.getAnnotation(Resource.class)) != null) {
417                             annotations.add(new AnnotationCacheEntry(fieldName, null,
418                                     resourceAnnotation.name(), AnnotationCacheEntryType.FIELD));
419                         } else if (EJB_PRESENT &&
420                                 (ejbAnnotation = field.getAnnotation(EJB.class)) != null) {
421                             annotations.add(new AnnotationCacheEntry(fieldName, null,
422                                     ((EJB) ejbAnnotation).name(), AnnotationCacheEntryType.FIELD));
423                         } else if (WS_PRESENT && (webServiceRefAnnotation =
424                                 field.getAnnotation(WebServiceRef.class)) != null) {
425                             annotations.add(new AnnotationCacheEntry(fieldName, null,
426                                     ((WebServiceRef) webServiceRefAnnotation).name(),
427                                     AnnotationCacheEntryType.FIELD));
428                         } else if (JPA_PRESENT && (persistenceContextAnnotation =
429                                 field.getAnnotation(PersistenceContext.class)) != null) {
430                             annotations.add(new AnnotationCacheEntry(fieldName, null,
431                                     ((PersistenceContext) persistenceContextAnnotation).name(),
432                                     AnnotationCacheEntryType.FIELD));
433                         } else if (JPA_PRESENT && (persistenceUnitAnnotation =
434                                 field.getAnnotation(PersistenceUnit.class)) != null) {
435                             annotations.add(new AnnotationCacheEntry(fieldName, null,
436                                     ((PersistenceUnit) persistenceUnitAnnotation).name(),
437                                     AnnotationCacheEntryType.FIELD));
438                         }
439                     }
440                 }
441
442                 if (annotations.isEmpty()) {
443                     // Use common object to save memory
444                     annotationsArray = ANNOTATIONS_EMPTY;
445                 } else {
446                     annotationsArray = annotations.toArray(
447                             new AnnotationCacheEntry[annotations.size()]);
448                 }
449                 synchronized (annotationCache) {
450                     annotationCache.put(clazz, annotationsArray);
451                 }
452             }
453             clazz = clazz.getSuperclass();
454         }
455     }
456
457
458     /**
459      * Inject resources in specified instance.
460      *
461      * @param instance   instance to inject into
462      * @param injections map of injections for this class from xml deployment descriptor
463      * @throws IllegalAccessException       if injection target is inaccessible
464      * @throws javax.naming.NamingException if value cannot be looked up in jndi
465      * @throws java.lang.reflect.InvocationTargetException
466      *                                      if injection fails
467      */

468     protected void processAnnotations(Object instance, Map<String, String> injections)
469             throws IllegalAccessException, InvocationTargetException, NamingException {
470
471         if (context == null) {
472             // No resource injection
473             return;
474         }
475
476         Class<?> clazz = instance.getClass();
477
478         while (clazz != null) {
479             AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
480             for (AnnotationCacheEntry entry : annotations) {
481                 if (entry.getType() == AnnotationCacheEntryType.SETTER) {
482                     lookupMethodResource(context, instance,
483                             getMethod(clazz, entry),
484                             entry.getName(), clazz);
485                 } else if (entry.getType() == AnnotationCacheEntryType.FIELD) {
486                     lookupFieldResource(context, instance,
487                             getField(clazz, entry),
488                             entry.getName(), clazz);
489                 }
490             }
491             clazz = clazz.getSuperclass();
492         }
493     }
494
495
496     /**
497      * Makes cache size available to unit tests.
498      *
499      * @return the cache size
500      */

501     protected int getAnnotationCacheSize() {
502         return annotationCache.size();
503     }
504
505
506     protected Class<?> loadClassMaybePrivileged(final String className,
507             final ClassLoader classLoader) throws ClassNotFoundException {
508         Class<?> clazz;
509         if (SecurityUtil.isPackageProtectionEnabled()) {
510             try {
511                 clazz = AccessController.doPrivileged(
512                         new PrivilegedLoadClass(className, classLoader));
513             } catch (PrivilegedActionException e) {
514                 Throwable t = e.getCause();
515                 if (t instanceof ClassNotFoundException) {
516                     throw (ClassNotFoundException) t;
517                 }
518                 throw new RuntimeException(t);
519             }
520         } else {
521             clazz = loadClass(className, classLoader);
522         }
523         checkAccess(clazz);
524         return clazz;
525     }
526
527     protected Class<?> loadClass(String className, ClassLoader classLoader)
528             throws ClassNotFoundException {
529         if (className.startsWith("org.apache.catalina")) {
530             return containerClassLoader.loadClass(className);
531         }
532         try {
533             Class<?> clazz = containerClassLoader.loadClass(className);
534             if (ContainerServlet.class.isAssignableFrom(clazz)) {
535                 return clazz;
536             }
537         } catch (Throwable t) {
538             ExceptionUtils.handleThrowable(t);
539         }
540         return classLoader.loadClass(className);
541     }
542
543     private void checkAccess(Class<?> clazz) {
544         if (privileged) {
545             return;
546         }
547         if (ContainerServlet.class.isAssignableFrom(clazz)) {
548             throw new SecurityException(sm.getString(
549                     "defaultInstanceManager.restrictedContainerServlet", clazz));
550         }
551         while (clazz != null) {
552             if (restrictedClasses.contains(clazz.getName())) {
553                 throw new SecurityException(sm.getString(
554                         "defaultInstanceManager.restrictedClass", clazz));
555             }
556             clazz = clazz.getSuperclass();
557         }
558     }
559
560     /**
561      * Inject resources in specified field.
562      *
563      * @param context  jndi context to extract value from
564      * @param instance object to inject into
565      * @param field    field target for injection
566      * @param name     jndi name value is bound under
567      * @param clazz    class annotation is defined in
568      * @throws IllegalAccessException       if field is inaccessible
569      * @throws javax.naming.NamingException if value is not accessible in naming context
570      */

571     protected static void lookupFieldResource(Context context,
572             Object instance, Field field, String name, Class<?> clazz)
573             throws NamingException, IllegalAccessException {
574
575         Object lookedupResource;
576         boolean accessibility;
577
578         String normalizedName = normalize(name);
579
580         if ((normalizedName != null) && (normalizedName.length() > 0)) {
581             lookedupResource = context.lookup(normalizedName);
582         } else {
583             lookedupResource =
584                 context.lookup(clazz.getName() + "/" + field.getName());
585         }
586
587         synchronized (field) {
588             accessibility = field.isAccessible();
589             field.setAccessible(true);
590             field.set(instance, lookedupResource);
591             field.setAccessible(accessibility);
592         }
593     }
594
595     /**
596      * Inject resources in specified method.
597      *
598      * @param context  jndi context to extract value from
599      * @param instance object to inject into
600      * @param method   field target for injection
601      * @param name     jndi name value is bound under
602      * @param clazz    class annotation is defined in
603      * @throws IllegalAccessException       if method is inaccessible
604      * @throws javax.naming.NamingException if value is not accessible in naming context
605      * @throws java.lang.reflect.InvocationTargetException
606      *                                      if setter call fails
607      */

608     protected static void lookupMethodResource(Context context,
609             Object instance, Method method, String name, Class<?> clazz)
610             throws NamingException, IllegalAccessException, InvocationTargetException {
611
612         if (!Introspection.isValidSetter(method)) {
613             throw new IllegalArgumentException(
614                     sm.getString("defaultInstanceManager.invalidInjection"));
615         }
616
617         Object lookedupResource;
618         boolean accessibility;
619
620         String normalizedName = normalize(name);
621
622         if ((normalizedName != null) && (normalizedName.length() > 0)) {
623             lookedupResource = context.lookup(normalizedName);
624         } else {
625             lookedupResource = context.lookup(
626                     clazz.getName() + "/" + Introspection.getPropertyName(method));
627         }
628
629         synchronized (method) {
630             accessibility = method.isAccessible();
631             method.setAccessible(true);
632             method.invoke(instance, lookedupResource);
633             method.setAccessible(accessibility);
634         }
635     }
636
637     private static void loadProperties(Set<String> classNames, String resourceName,
638             String messageKey, Log log) {
639         Properties properties = new Properties();
640         ClassLoader cl = DefaultInstanceManager.class.getClassLoader();
641         try (InputStream is = cl.getResourceAsStream(resourceName)) {
642             if (is == null) {
643                 log.error(sm.getString(messageKey, resourceName));
644             } else {
645                 properties.load(is);
646             }
647         } catch (IOException ioe) {
648             log.error(sm.getString(messageKey, resourceName), ioe);
649         }
650         if (properties.isEmpty()) {
651             return;
652         }
653         for (Map.Entry<Object, Object> e : properties.entrySet()) {
654             if ("restricted".equals(e.getValue())) {
655                 classNames.add(e.getKey().toString());
656             } else {
657                 log.warn(sm.getString(
658                         "defaultInstanceManager.restrictedWrongValue",
659                         resourceName, e.getKey(), e.getValue()));
660             }
661         }
662     }
663
664     private static String normalize(String jndiName){
665         if(jndiName != null && jndiName.startsWith("java:comp/env/")){
666             return jndiName.substring(14);
667         }
668         return jndiName;
669     }
670
671     private static Method getMethod(final Class<?> clazz,
672             final AnnotationCacheEntry entry) {
673         Method result = null;
674         if (Globals.IS_SECURITY_ENABLED) {
675             result = AccessController.doPrivileged(new PrivilegedGetMethod(clazz, entry));
676         } else {
677             try {
678                 result = clazz.getDeclaredMethod(
679                         entry.getAccessibleObjectName(), entry.getParamTypes());
680             } catch (NoSuchMethodException e) {
681                 // Should never happen. On that basis don't log it.
682             }
683         }
684         return result;
685     }
686
687     private static Field getField(final Class<?> clazz,
688             final AnnotationCacheEntry entry) {
689         Field result = null;
690         if (Globals.IS_SECURITY_ENABLED) {
691             result = AccessController.doPrivileged(new PrivilegedGetField(clazz, entry));
692         } else {
693             try {
694                 result = clazz.getDeclaredField(entry.getAccessibleObjectName());
695             } catch (NoSuchFieldException e) {
696                 // Should never happen. On that basis don't log it.
697             }
698         }
699         return result;
700     }
701
702
703     private static Method findPostConstruct(Method currentPostConstruct,
704             String postConstructFromXml, Method method) {
705         return findLifecycleCallback(currentPostConstruct,
706             postConstructFromXml, method, PostConstruct.class);
707     }
708
709     private static Method findPreDestroy(Method currentPreDestroy,
710         String preDestroyFromXml, Method method) {
711         return findLifecycleCallback(currentPreDestroy,
712             preDestroyFromXml, method, PreDestroy.class);
713     }
714
715     private static Method findLifecycleCallback(Method currentMethod,
716             String methodNameFromXml, Method method,
717             Class<? extends Annotation> annotation) {
718         Method result = currentMethod;
719         if (methodNameFromXml != null) {
720             if (method.getName().equals(methodNameFromXml)) {
721                 if (!Introspection.isValidLifecycleCallback(method)) {
722                     throw new IllegalArgumentException(
723                             "Invalid " + annotation.getName() + " annotation");
724                 }
725                 result = method;
726             }
727         } else {
728             if (method.isAnnotationPresent(annotation)) {
729                 if (currentMethod != null || !Introspection.isValidLifecycleCallback(method)) {
730                     throw new IllegalArgumentException(
731                             "Invalid " + annotation.getName() + " annotation");
732                 }
733                 result = method;
734             }
735         }
736         return result;
737     }
738
739     private static final class AnnotationCacheEntry {
740         private final String accessibleObjectName;
741         private final Class<?>[] paramTypes;
742         private final String name;
743         private final AnnotationCacheEntryType type;
744
745         public AnnotationCacheEntry(String accessibleObjectName,
746                 Class<?>[] paramTypes, String name,
747                 AnnotationCacheEntryType type) {
748             this.accessibleObjectName = accessibleObjectName;
749             this.paramTypes = paramTypes;
750             this.name = name;
751             this.type = type;
752         }
753
754         public String getAccessibleObjectName() {
755             return accessibleObjectName;
756         }
757
758         public Class<?>[] getParamTypes() {
759             return paramTypes;
760         }
761
762         public String getName() {
763             return name;
764         }
765         public AnnotationCacheEntryType getType() {
766             return type;
767         }
768     }
769
770
771     private enum AnnotationCacheEntryType {
772         FIELD, SETTER, POST_CONSTRUCT, PRE_DESTROY
773     }
774
775
776     private static class PrivilegedGetField implements PrivilegedAction<Field> {
777
778         private final Class<?> clazz;
779         private final AnnotationCacheEntry entry;
780
781         public PrivilegedGetField(Class<?> clazz, AnnotationCacheEntry entry) {
782             this.clazz = clazz;
783             this.entry = entry;
784         }
785
786         @Override
787         public Field run() {
788             Field result = null;
789             try {
790                 result = clazz.getDeclaredField(entry.getAccessibleObjectName());
791             } catch (NoSuchFieldException e) {
792                 // Should never happen. On that basis don't log it.
793             }
794             return result;
795         }
796     }
797
798
799     private static class PrivilegedGetMethod implements PrivilegedAction<Method> {
800
801         private final Class<?> clazz;
802         private final AnnotationCacheEntry entry;
803
804         public PrivilegedGetMethod(Class<?> clazz, AnnotationCacheEntry entry) {
805             this.clazz = clazz;
806             this.entry = entry;
807         }
808
809         @Override
810         public Method run() {
811             Method result = null;
812             try {
813                 result = clazz.getDeclaredMethod(
814                         entry.getAccessibleObjectName(), entry.getParamTypes());
815             } catch (NoSuchMethodException e) {
816                 // Should never happen. On that basis don't log it.
817             }
818             return result;
819         }
820     }
821
822
823     private class PrivilegedLoadClass implements PrivilegedExceptionAction<Class<?>> {
824
825         private final String className;
826         private final ClassLoader classLoader;
827
828         public PrivilegedLoadClass(String className, ClassLoader classLoader) {
829             this.className = className;
830             this.classLoader = classLoader;
831         }
832
833         @Override
834         public Class<?> run() throws Exception {
835             return loadClass(className, classLoader);
836         }
837     }
838 }
839