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: [email protected]
For additional commands, e-mail: [email protected]