Author: markt Date: Fri Feb 19 19:30:53 2016 New Revision: 1731291 URL: http://svn.apache.org/viewvc?rev=1731291&view=rev Log: Refactor to use a local variable rather than a field. Even after the cache is cleared it uses significant memory because of the size the Map has grown to.
Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java tomcat/trunk/webapps/docs/changelog.xml 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=1731291&r1=1731290&r2=1731291&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Fri Feb 19 19:30:53 2016 @@ -222,14 +222,6 @@ public class ContextConfig implements Li new HashMap<>(); /** - * 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<>(); - - /** * Flag that indicates if at least one {@link HandlesTypes} entry is present * that represents an annotation. */ @@ -1141,13 +1133,15 @@ public class ContextConfig implements Li if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) { // Step 4. Process /WEB-INF/classes for annotations and // @HandlesTypes matches + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); + if (ok) { WebResource[] webResources = context.getResources().listResources("/WEB-INF/classes"); for (WebResource webResource : webResources) { processAnnotationsWebResource(webResource, webXml, - webXml.isMetadataComplete()); + webXml.isMetadataComplete(), javaClassCache); } } @@ -1157,7 +1151,7 @@ public class ContextConfig implements Li // container fragments) if (ok) { processAnnotations( - orderedFragments, webXml.isMetadataComplete()); + orderedFragments, webXml.isMetadataComplete(), javaClassCache); } // Cache, if used, is no longer required so clear it @@ -1899,7 +1893,7 @@ public class ContextConfig implements Li } protected void processAnnotations(Set<WebXml> fragments, - boolean handlesTypesOnly) { + boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { for(WebXml fragment : fragments) { // Only need to scan for @HandlesTypes matches if any of the // following are true: @@ -1914,7 +1908,7 @@ public class ContextConfig implements Li // no impact on distributable annotations.setDistributable(true); URL url = fragment.getURL(); - processAnnotationsUrl(url, annotations, htOnly); + processAnnotationsUrl(url, annotations, htOnly, javaClassCache); Set<WebXml> set = new HashSet<>(); set.add(annotations); // Merge annotations into fragment - fragment takes priority @@ -1923,7 +1917,8 @@ public class ContextConfig implements Li } protected void processAnnotationsWebResource(WebResource webResource, - WebXml fragment, boolean handlesTypesOnly) { + WebXml fragment, boolean handlesTypesOnly, + Map<String,JavaClassCacheEntry> javaClassCache) { if (webResource.isDirectory()) { WebResource[] webResources = @@ -1936,13 +1931,13 @@ public class ContextConfig implements Li webResource.getURL())); } for (WebResource r : webResources) { - processAnnotationsWebResource(r, fragment, handlesTypesOnly); + processAnnotationsWebResource(r, fragment, handlesTypesOnly, javaClassCache); } } } else if (webResource.isFile() && webResource.getName().endsWith(".class")) { try (InputStream is = webResource.getInputStream()) { - processAnnotationsStream(is, fragment, handlesTypesOnly); + processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache); } catch (IOException e) { log.error(sm.getString("contextConfig.inputStreamWebResource", webResource.getWebappPath()),e); @@ -1955,16 +1950,16 @@ public class ContextConfig implements Li protected void processAnnotationsUrl(URL url, WebXml fragment, - boolean handlesTypesOnly) { + boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { if (url == null) { // Nothing to do. return; } else if ("jar".equals(url.getProtocol())) { - processAnnotationsJar(url, fragment, handlesTypesOnly); + processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache); } else if ("file".equals(url.getProtocol())) { try { processAnnotationsFile( - new File(url.toURI()), fragment, handlesTypesOnly); + new File(url.toURI()), fragment, handlesTypesOnly, javaClassCache); } catch (URISyntaxException e) { log.error(sm.getString("contextConfig.fileUrl", url), e); } @@ -1977,7 +1972,7 @@ public class ContextConfig implements Li protected void processAnnotationsJar(URL url, WebXml fragment, - boolean handlesTypesOnly) { + boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { try (Jar jar = JarFactory.newInstance(url)) { if (log.isDebugEnabled()) { @@ -1990,8 +1985,7 @@ public class ContextConfig implements Li while (entryName != null) { if (entryName.endsWith(".class")) { try (InputStream is = jar.getEntryInputStream()) { - processAnnotationsStream( - is, fragment, handlesTypesOnly); + processAnnotationsStream(is, fragment, handlesTypesOnly, javaClassCache); } catch (IOException e) { log.error(sm.getString("contextConfig.inputStreamJar", entryName, url),e); @@ -2010,7 +2004,7 @@ public class ContextConfig implements Li protected void processAnnotationsFile(File file, WebXml fragment, - boolean handlesTypesOnly) { + boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) { if (file.isDirectory()) { // Returns null if directory is not readable @@ -2022,12 +2016,12 @@ public class ContextConfig implements Li } for (String dir : dirs) { processAnnotationsFile( - new File(file,dir), fragment, handlesTypesOnly); + new File(file,dir), fragment, handlesTypesOnly, javaClassCache); } } } else if (file.getName().endsWith(".class") && file.canRead()) { try (FileInputStream fis = new FileInputStream(file)) { - processAnnotationsStream(fis, fragment, handlesTypesOnly); + processAnnotationsStream(fis, fragment, handlesTypesOnly, javaClassCache); } catch (IOException e) { log.error(sm.getString("contextConfig.inputStreamFile", file.getAbsolutePath()),e); @@ -2040,12 +2034,12 @@ public class ContextConfig implements Li protected void processAnnotationsStream(InputStream is, WebXml fragment, - boolean handlesTypesOnly) + boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache) throws ClassFormatException, IOException { ClassParser parser = new ClassParser(is); JavaClass clazz = parser.parse(); - checkHandlesTypes(clazz); + checkHandlesTypes(clazz, javaClassCache); if (handlesTypesOnly) { return; @@ -2075,7 +2069,8 @@ public class ContextConfig implements Li * for an annotation that matches {@link HandlesTypes}. * @param javaClass the class to check */ - protected void checkHandlesTypes(JavaClass javaClass) { + protected void checkHandlesTypes(JavaClass javaClass, + Map<String,JavaClassCacheEntry> javaClassCache) { // Skip this if we can if (typeInitializerMap.size() == 0) { @@ -2093,16 +2088,16 @@ public class ContextConfig implements Li Class<?> clazz = null; if (handlesTypesNonAnnotations) { // This *might* be match for a HandlesType. - populateJavaClassCache(className, javaClass); + populateJavaClassCache(className, javaClass, javaClassCache); JavaClassCacheEntry entry = javaClassCache.get(className); if (entry.getSciSet() == null) { try { - populateSCIsForCacheEntry(entry); + populateSCIsForCacheEntry(entry, javaClassCache); } catch (StackOverflowError soe) { throw new IllegalStateException(sm.getString( "contextConfig.annotationsStackOverflow", context.getName(), - classHierarchyToString(className, entry))); + classHierarchyToString(className, entry, javaClassCache))); } } if (!entry.getSciSet().isEmpty()) { @@ -2157,7 +2152,7 @@ public class ContextConfig implements Li private String classHierarchyToString(String className, - JavaClassCacheEntry entry) { + JavaClassCacheEntry entry, Map<String,JavaClassCacheEntry> javaClassCache) { JavaClassCacheEntry start = entry; StringBuilder msg = new StringBuilder(className); msg.append("->"); @@ -2180,7 +2175,8 @@ public class ContextConfig implements Li return msg.toString(); } - private void populateJavaClassCache(String className, JavaClass javaClass) { + private void populateJavaClassCache(String className, JavaClass javaClass, + Map<String,JavaClassCacheEntry> javaClassCache) { if (javaClassCache.containsKey(className)) { return; } @@ -2188,14 +2184,15 @@ public class ContextConfig implements Li // Add this class to the cache javaClassCache.put(className, new JavaClassCacheEntry(javaClass)); - populateJavaClassCache(javaClass.getSuperclassName()); + populateJavaClassCache(javaClass.getSuperclassName(), javaClassCache); for (String iterface : javaClass.getInterfaceNames()) { - populateJavaClassCache(iterface); + populateJavaClassCache(iterface, javaClassCache); } } - private void populateJavaClassCache(String className) { + private void populateJavaClassCache(String className, + Map<String,JavaClassCacheEntry> javaClassCache) { if (!javaClassCache.containsKey(className)) { String name = className.replace('.', '/') + ".class"; try (InputStream is = context.getLoader().getClassLoader().getResourceAsStream(name)) { @@ -2204,7 +2201,7 @@ public class ContextConfig implements Li } ClassParser parser = new ClassParser(is); JavaClass clazz = parser.parse(); - populateJavaClassCache(clazz.getClassName(), clazz); + populateJavaClassCache(clazz.getClassName(), clazz, javaClassCache); } catch (ClassFormatException e) { log.debug(sm.getString("contextConfig.invalidSciHandlesTypes", className), e); @@ -2215,7 +2212,8 @@ public class ContextConfig implements Li } } - private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry) { + private void populateSCIsForCacheEntry(JavaClassCacheEntry cacheEntry, + Map<String,JavaClassCacheEntry> javaClassCache) { Set<ServletContainerInitializer> result = new HashSet<>(); // Super class @@ -2232,7 +2230,7 @@ public class ContextConfig implements Li // May be null of the class is not present or could not be loaded. if (superClassCacheEntry != null) { if (superClassCacheEntry.getSciSet() == null) { - populateSCIsForCacheEntry(superClassCacheEntry); + populateSCIsForCacheEntry(superClassCacheEntry, javaClassCache); } result.addAll(superClassCacheEntry.getSciSet()); } @@ -2247,7 +2245,7 @@ public class ContextConfig implements Li // so move along if (interfaceEntry != null) { if (interfaceEntry.getSciSet() == null) { - populateSCIsForCacheEntry(interfaceEntry); + populateSCIsForCacheEntry(interfaceEntry, javaClassCache); } result.addAll(interfaceEntry.getSciSet()); } @@ -2598,7 +2596,7 @@ public class ContextConfig implements Li } } - private static class JavaClassCacheEntry { + static class JavaClassCacheEntry { public final String superclassName; public final String[] interfaceNames; 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=1731291&r1=1731290&r2=1731291&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java (original) +++ tomcat/trunk/test/org/apache/catalina/startup/TestContextConfigAnnotation.java Fri Feb 19 19:30:53 2016 @@ -19,7 +19,9 @@ package org.apache.catalina.startup; import java.beans.PropertyChangeListener; import java.io.File; import java.net.URL; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.servlet.DispatcherType; @@ -39,6 +41,7 @@ import org.junit.Test; import org.apache.catalina.Context; import org.apache.catalina.Loader; import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.ContextConfig.JavaClassCacheEntry; import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap; import org.apache.tomcat.util.descriptor.web.ServletDef; @@ -55,11 +58,12 @@ public class TestContextConfigAnnotation @Test public void testAnnotation() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); File pFile = paramClassResource( "org/apache/catalina/startup/ParamServlet"); assertTrue(pFile.exists()); - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); ServletDef servletDef = webxml.getServlets().get("param"); assertNotNull(servletDef); assertEquals("Hello", servletDef.getParameterMap().get("foo")); @@ -81,6 +85,7 @@ public class TestContextConfigAnnotation @Test public void testOverwriteAnnotation() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ServletDef servletDef = new ServletDef(); servletDef.setServletName("param"); servletDef.setServletClass("org.apache.catalina.startup.ParamServlet"); @@ -98,7 +103,7 @@ public class TestContextConfigAnnotation File pFile = paramClassResource( "org/apache/catalina/startup/ParamServlet"); assertTrue(pFile.exists()); - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); assertEquals(servletDef, webxml.getServlets().get("param")); @@ -121,16 +126,17 @@ public class TestContextConfigAnnotation @Test public void testNoMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); File pFile = paramClassResource( "org/apache/catalina/startup/NoMappingParamServlet"); assertTrue(pFile.exists()); - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); ServletDef servletDef = webxml.getServlets().get("param1"); assertNull(servletDef); webxml.addServletMapping("/param", "param1"); - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); servletDef = webxml.getServlets().get("param1"); assertNull(servletDef); @@ -139,6 +145,7 @@ public class TestContextConfigAnnotation @Test public void testSetupWebXMLNoMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ServletDef servletDef = new ServletDef(); servletDef.setServletName("param1"); servletDef.setServletClass( @@ -151,7 +158,7 @@ public class TestContextConfigAnnotation File pFile = paramClassResource( "org/apache/catalina/startup/NoMappingParamServlet"); assertTrue(pFile.exists()); - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); assertEquals("tomcat", servletDef.getParameterMap().get("foo")); assertEquals("World!", servletDef.getParameterMap().get("bar")); ServletDef servletDef1 = webxml.getServlets().get("param1"); @@ -162,12 +169,13 @@ public class TestContextConfigAnnotation @Test public void testDuplicateMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); File pFile = paramClassResource( "org/apache/catalina/startup/DuplicateMappingParamServlet"); assertTrue(pFile.exists()); try { - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); fail(); } catch (IllegalArgumentException ex) { // ignore @@ -179,13 +187,14 @@ public class TestContextConfigAnnotation @Test public void testFilterMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); File sFile = paramClassResource( "org/apache/catalina/startup/ParamServlet"); - config.processAnnotationsFile(sFile, webxml, false); + config.processAnnotationsFile(sFile, webxml, false, javaClassCache); File fFile = paramClassResource( "org/apache/catalina/startup/ParamFilter"); - config.processAnnotationsFile(fFile, webxml, false); + config.processAnnotationsFile(fFile, webxml, false, javaClassCache); FilterDef fdef = webxml.getFilters().get("paramFilter"); assertNotNull(fdef); assertEquals("Servlet says: ",fdef.getParameterMap().get("message")); @@ -194,6 +203,7 @@ public class TestContextConfigAnnotation @Test public void testOverwriteFilterMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); FilterDef filterDef = new FilterDef(); filterDef.setFilterName("paramFilter"); filterDef.setFilterClass("org.apache.catalina.startup.ParamFilter"); @@ -214,10 +224,10 @@ public class TestContextConfigAnnotation ContextConfig config = new ContextConfig(); File sFile = paramClassResource( "org/apache/catalina/startup/ParamServlet"); - config.processAnnotationsFile(sFile, webxml, false); + config.processAnnotationsFile(sFile, webxml, false, javaClassCache); File fFile = paramClassResource( "org/apache/catalina/startup/ParamFilter"); - config.processAnnotationsFile(fFile, webxml, false); + config.processAnnotationsFile(fFile, webxml, false, javaClassCache); FilterDef fdef = webxml.getFilters().get("paramFilter"); assertNotNull(fdef); assertEquals(filterDef,fdef); @@ -249,12 +259,13 @@ public class TestContextConfigAnnotation @Test public void testDuplicateFilterMapping() throws Exception { WebXml webxml = new WebXml(); + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); File pFile = paramClassResource( "org/apache/catalina/startup/DuplicateMappingParamFilter"); assertTrue(pFile.exists()); try { - config.processAnnotationsFile(pFile, webxml, false); + config.processAnnotationsFile(pFile, webxml, false, javaClassCache); fail(); } catch (IllegalArgumentException ex) { // ignore @@ -265,6 +276,7 @@ public class TestContextConfigAnnotation @Test public void testCheckHandleTypes() throws Exception { + Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>(); ContextConfig config = new ContextConfig(); config.handlesTypesAnnotations = true; config.handlesTypesNonAnnotations = true; @@ -296,13 +308,13 @@ public class TestContextConfigAnnotation WebXml ignore = new WebXml(); File file = paramClassResource( "org/apache/catalina/startup/ParamServlet"); - config.processAnnotationsFile(file, ignore, false); + config.processAnnotationsFile(file, ignore, false, javaClassCache); file = paramClassResource("org/apache/catalina/startup/ParamFilter"); - config.processAnnotationsFile(file, ignore, false); + config.processAnnotationsFile(file, ignore, false, javaClassCache); file = paramClassResource("org/apache/catalina/startup/TesterServlet"); - config.processAnnotationsFile(file, ignore, false); + config.processAnnotationsFile(file, ignore, false, javaClassCache); file = paramClassResource("org/apache/catalina/startup/TestListener"); - config.processAnnotationsFile(file, ignore, false); + config.processAnnotationsFile(file, ignore, false, javaClassCache); // Check right number of classes were noted to be handled assertEquals(0, config.initializerClassMap.get(sciNone).size()); Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1731291&r1=1731290&r2=1731291&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Fri Feb 19 19:30:53 2016 @@ -108,6 +108,9 @@ effective web.xml. Components needing access to configuration information may access it via the Servlet API. (markt) </fix> + <fix> + Refactor JAR scanning to reduce memory footprint. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org