Author: markt
Date: Mon Jan 30 11:14:06 2012
New Revision: 1237604

URL: http://svn.apache.org/viewvc?rev=1237604&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=52444
Remove need to load classes unless they actually match an HandlesTypes
entry
Modified:
    tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
    tomcat/trunk/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java
    
tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java

Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=1237604&r1=1237603&r2=1237604&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Mon Jan 30 
11:14:06 2012
@@ -30,6 +30,7 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -183,14 +184,34 @@ public class ContextConfig implements Li
      * Map of ServletContainerInitializer to classes they expressed interest 
in.
      */
     protected final Map<ServletContainerInitializer, Set<Class<?>>> 
initializerClassMap =
-        new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();
+            new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();
 
     /**
      * Map of Types to ServletContainerInitializer that are interested in those
      * types.
      */
     protected final Map<Class<?>, Set<ServletContainerInitializer>> 
typeInitializerMap =
-        new HashMap<Class<?>, Set<ServletContainerInitializer>>();
+            new HashMap<Class<?>, Set<ServletContainerInitializer>>();
+
+    /**
+     * Cache of JavaClass objects (byte code) by fully qualified class name.
+     * Only populated if it is necessary to scan the super types and interfaces
+     * as part of the processing for {@link HandlesTypes}.
+     */
+    protected final Map<String,JavaClassCacheEntry> javaClassCache =
+            new HashMap<String,JavaClassCacheEntry>();
+
+    /**
+     * Flag that indicates if at least one {@link HandlesTypes} entry is 
present
+     * that represents an annotation.
+     */
+    protected boolean handlesTypesAnnotations = false;
+
+    /**
+     * Flag that indicates if at least one {@link HandlesTypes} entry is 
present
+     * that represents a non-annotation.
+     */
+    protected boolean handlesTypesNonAnnotations = false;
 
     /**
      * The <code>Digester</code> we will use to process web application
@@ -1196,6 +1217,9 @@ public class ContextConfig implements Li
                     processAnnotations(orderedFragments);
                 }
 
+                // Cache, if used, is no longer required so clear it
+                javaClassCache.clear();
+
                 // Step 6. Merge web-fragment.xml files into the main web.xml
                 // file.
                 if (ok) {
@@ -1473,6 +1497,11 @@ public class ContextConfig implements Li
                 Class<?>[] types = ht.value();
                 if (types != null) {
                     for (Class<?> type : types) {
+                        if (type.isAnnotation()) {
+                            handlesTypesAnnotations = true;
+                        } else {
+                            handlesTypesNonAnnotations = true;
+                        }
                         Set<ServletContainerInitializer> scis =
                             typeInitializerMap.get(type);
                         if (scis == null) {
@@ -1993,59 +2022,180 @@ public class ContextConfig implements Li
             return;
         }
 
-        // No choice but to load the class
         String className = javaClass.getClassName();
 
         Class<?> clazz = null;
+        if (handlesTypesNonAnnotations) {
+            // This *might* be match for a HandlesType.
+            populateJavaClassCache(className, javaClass);
+            JavaClassCacheEntry entry = javaClassCache.get(className);
+            if (entry.getSciSet() == null) {
+                populateSCIsForCacheEntry(entry);
+            }
+            if (entry.getSciSet().size() > 0) {
+                // Need to try and load the class
+                clazz = loadClass(className);
+                if (clazz == null) {
+                    // Can't load the class so no point continuing
+                    return;
+                }
+
+                for (ServletContainerInitializer sci :
+                        entry.getSciSet()) {
+                    Set<Class<?>> classes = initializerClassMap.get(sci);
+                    if (classes == null) {
+                        classes = new HashSet<Class<?>>();
+                        initializerClassMap.put(sci, classes);
+                    }
+                    classes.add(clazz);
+                }
+            }
+        }
+
+        if (handlesTypesAnnotations) {
+            for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
+                    typeInitializerMap.entrySet()) {
+                if (entry.getKey().isAnnotation()) {
+                    AnnotationEntry[] annotationEntries =
+                            javaClass.getAnnotationEntries();
+                    for (AnnotationEntry annotationEntry : annotationEntries) {
+                        if (entry.getKey().getName().equals(
+                                
getClassName(annotationEntry.getAnnotationType()))) {
+                            if (clazz == null) {
+                                clazz = loadClass(className);
+                                if (clazz == null) {
+                                    // Can't load the class so no point
+                                    // continuing
+                                    return;
+                                }
+                            }
+                            for (ServletContainerInitializer sci : 
entry.getValue()) {
+                                initializerClassMap.get(sci).add(clazz);
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    private void populateJavaClassCache(String className, JavaClass javaClass) 
{
+        if (javaClassCache.containsKey(className)) {
+            return;
+        }
+
+        // Add this class to the cache
+        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));
+
+        populateJavaClassCache(javaClass.getSuperclassName());
+
+        for (String iterface : javaClass.getInterfaceNames()) {
+            populateJavaClassCache(iterface);
+        }
+    }
+
+    private void populateJavaClassCache(String className) {
+        if (!javaClassCache.containsKey(className)) {
+            String name = className.replace('.', '/') + ".class";
+            InputStream is =
+                    
context.getLoader().getClassLoader().getResourceAsStream(name);
+            ClassParser parser = new ClassParser(is, null);
+            try {
+                JavaClass clazz = parser.parse();
+                populateJavaClassCache(clazz.getClassName(), clazz);
+            } catch (ClassFormatException e) {
+                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
+                        className), e);
+            } catch (IOException e) {
+                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
+                        className), e);
+            }
+        }
+    }
+
+    private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry) {
+        Set<ServletContainerInitializer> result =
+                new HashSet<ServletContainerInitializer>();
+
+        JavaClass javaClass = cacheEntry.getJavaClass();
+
+        // Super class
+        String superClassName = javaClass.getSuperclassName();
+        JavaClassCacheEntry superClassCacheEntry =
+                javaClassCache.get(superClassName);
+
+        // Avoid an infinite loop with java.lang.Object
+        if (cacheEntry.equals(superClassCacheEntry)) {
+            cacheEntry.setSciSet(new HashSet<ServletContainerInitializer>());
+            return;
+        }
+
+        // May be null of the class is not present or could not be loaded.
+        if (superClassCacheEntry != null) {
+            if (superClassCacheEntry.getSciSet() == null) {
+                populateSCIsForCacheEntry(superClassCacheEntry);
+            }
+            result.addAll(superClassCacheEntry.getSciSet());
+        }
+        result.addAll(getSCIsForClass(superClassName));
+
+        // Interfaces
+        for (String interfaceName : javaClass.getInterfaceNames()) {
+            JavaClassCacheEntry interfaceEntry =
+                    javaClassCache.get(interfaceName);
+            // A null could mean that the class not present in application or
+            // that there is nothing of interest. Either way, nothing to do 
here
+            // so move along
+            if (interfaceEntry != null) {
+                if (interfaceEntry.getSciSet() == null) {
+                    populateSCIsForCacheEntry(interfaceEntry);
+                }
+                result.addAll(interfaceEntry.getSciSet());
+            }
+            result.addAll(getSCIsForClass(interfaceName));
+        }
+
+        cacheEntry.setSciSet(result);
+    }
+
+    private Set<ServletContainerInitializer> getSCIsForClass(String className) 
{
+        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
+                typeInitializerMap.entrySet()) {
+            Class<?> clazz = entry.getKey();
+            if (!clazz.isAnnotation()) {
+                if (clazz.getName().equals(className)) {
+                    return entry.getValue();
+                }
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    private Class<?> loadClass(String className) {
+        Class<?> clazz = null;
         try {
             clazz = context.getLoader().getClassLoader().loadClass(className);
         } catch (NoClassDefFoundError e) {
             log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                     className), e);
-            return;
+            return null;
         } catch (ClassNotFoundException e) {
             log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                     className), e);
-            return;
+            return null;
         } catch (ClassFormatError e) {
             log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                     className), e);
-            return;
+            return null;
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
             log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                     className), t);
-            return;
-        }
-
-        if (clazz.isAnnotation()) {
-            // Skip
-            return;
-        }
-
-        boolean match = false;
-
-        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
-                typeInitializerMap.entrySet()) {
-            if (entry.getKey().isAnnotation()) {
-                AnnotationEntry[] annotationEntries = 
javaClass.getAnnotationEntries();
-                for (AnnotationEntry annotationEntry : annotationEntries) {
-                    if (entry.getKey().getName().equals(
-                        getClassName(annotationEntry.getAnnotationType()))) {
-                        match = true;
-                        break;
-                    }
-                }
-            } else if (entry.getKey().isAssignableFrom(clazz)) {
-                match = true;
-            }
-            if (match) {
-                for (ServletContainerInitializer sci : entry.getValue()) {
-                    initializerClassMap.get(sci).add(clazz);
-                }
-                match = false;
-            }
+            return null;
         }
+        return clazz;
     }
 
     private static final String getClassName(String internalForm) {
@@ -2455,4 +2605,25 @@ public class ContextConfig implements Li
             return hostTimeStamp;
         }
     }
+
+    private static class JavaClassCacheEntry {
+        private final JavaClass javaClass;
+        private Set<ServletContainerInitializer> sciSet = null;
+
+        public JavaClassCacheEntry(JavaClass javaClass) {
+            this.javaClass = javaClass;
+        }
+
+        public JavaClass getJavaClass() {
+            return javaClass;
+        }
+
+        public Set<ServletContainerInitializer> getSciSet() {
+            return sciSet;
+        }
+
+        public void setSciSet(Set<ServletContainerInitializer> sciSet) {
+            this.sciSet = sciSet;
+        }
+    }
 }

Modified: tomcat/trunk/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java?rev=1237604&r1=1237603&r2=1237604&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/bcel/classfile/JavaClass.java Mon 
Jan 30 11:14:06 2012
@@ -180,6 +180,26 @@ public class JavaClass extends AccessFla
 
 
     /**
+     * @return Names of implemented interfaces.
+     */
+    public String[] getInterfaceNames() {
+        return interface_names;
+    }
+
+
+    /**
+     * returns the super class name of this class. In the case that this class 
is
+     * java.lang.Object, it will return itself (java.lang.Object). This is 
probably incorrect
+     * but isn't fixed at this time to not break existing clients.
+     *
+     * @return Superclass name.
+     */
+    public String getSuperclassName() {
+        return superclass_name;
+    }
+
+
+    /**
      * @return String representing class contents.
      */
     @Override

Modified: 
tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java?rev=1237604&r1=1237603&r2=1237604&view=diff
==============================================================================
--- 
tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 
(original)
+++ 
tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 
Mon Jan 30 11:14:06 2012
@@ -267,6 +267,8 @@ public class TestContextConfigAnnotation
     @Test
     public void testCheckHandleTypes() throws Exception {
         ContextConfig config = new ContextConfig();
+        config.handlesTypesAnnotations = true;
+        config.handlesTypesNonAnnotations = true;
 
         // Need a Context, Loader and ClassLoader for checkHandleTypes
         StandardContext context = new StandardContext();



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to