You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2016/02/19 18:09:36 UTC
svn commit: r1731275 - in /tomcat/trunk/java/org/apache/catalina:
loader/ResourceEntry.java loader/WebappClassLoaderBase.java
webresources/Cache.java webresources/FileResource.java
Author: markt
Date: Fri Feb 19 17:09:35 2016
New Revision: 1731275
URL: http://svn.apache.org/viewvc?rev=1731275&view=rev
Log:
Refactoring to reduce the impact on the memory footprint of the resource cache within the web application class loader.
Main changes:
- WebResources caches everything apart from classes
- WebResources is responsible for EBCDIC conversion for properties files
- The class loader cache now only caches the last modified time of any resource loaded through the class loader and loaded classes.
Modified:
tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java
tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java
tomcat/trunk/java/org/apache/catalina/webresources/Cache.java
tomcat/trunk/java/org/apache/catalina/webresources/FileResource.java
Modified: tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java?rev=1731275&r1=1731274&r2=1731275&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java (original)
+++ tomcat/trunk/java/org/apache/catalina/loader/ResourceEntry.java Fri Feb 19 17:09:35 2016
@@ -16,10 +16,6 @@
*/
package org.apache.catalina.loader;
-import java.net.URL;
-
-import org.apache.catalina.WebResource;
-
/**
* Resource entry.
*
@@ -27,32 +23,16 @@ import org.apache.catalina.WebResource;
*/
public class ResourceEntry {
-
/**
- * The "last modified" time of the origin file at the time this class
+ * The "last modified" time of the origin file at the time this resource
* was loaded, in milliseconds since the epoch.
*/
public long lastModified = -1;
/**
- * Binary content of the resource.
- */
- public byte[] binaryContent = null;
-
-
- /**
* Loaded class.
*/
public volatile Class<?> loadedClass = null;
-
-
- /**
- * URL source from where the object was loaded.
- */
- public URL source = null;
-
-
- public WebResource webResource = null;
}
Modified: tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java?rev=1731275&r1=1731274&r2=1731275&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/loader/WebappClassLoaderBase.java Fri Feb 19 17:09:35 2016
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.loader;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
@@ -32,7 +31,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
-import java.nio.charset.StandardCharsets;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
@@ -139,7 +137,6 @@ public abstract class WebappClassLoaderB
private static final String JVM_THREAD_GROUP_SYSTEM = "system";
private static final String CLASS_FILE_SUFFIX = ".class";
- private static final String SERVICES_PREFIX = "/META-INF/services/";
static {
ClassLoader.registerAsParallelCapable();
@@ -147,20 +144,18 @@ public abstract class WebappClassLoaderB
JVM_THREAD_GROUP_NAMES.add("RMI Runtime");
}
- protected class PrivilegedFindResourceByName
- implements PrivilegedAction<ResourceEntry> {
+ protected class PrivilegedFindClassByName
+ implements PrivilegedAction<Class<?>> {
protected final String name;
- protected final String path;
- PrivilegedFindResourceByName(String name, String path) {
+ PrivilegedFindClassByName(String name) {
this.name = name;
- this.path = path;
}
@Override
- public ResourceEntry run() {
- return findResourceInternal(name, path);
+ public Class<?> run() {
+ return findClassInternal(name);
}
}
@@ -327,12 +322,6 @@ public abstract class WebappClassLoaderB
/**
- * need conversion for properties files
- */
- protected boolean needConvert = false;
-
-
- /**
* All permission.
*/
protected final Permission allPermission = new java.security.AllPermission();
@@ -688,7 +677,6 @@ public abstract class WebappClassLoaderB
base.resources = this.resources;
base.delegate = this.delegate;
base.state = LifecycleState.NEW;
- base.needConvert = this.needConvert;
base.clearReferencesStatic = this.clearReferencesStatic;
base.clearReferencesStopThreads = this.clearReferencesStopThreads;
base.clearReferencesStopTimerThreads = this.clearReferencesStopTimerThreads;
@@ -831,7 +819,13 @@ public abstract class WebappClassLoaderB
if (log.isTraceEnabled())
log.trace(" findClassInternal(" + name + ")");
try {
- clazz = findClassInternal(name);
+ if (securityManager != null) {
+ PrivilegedAction<Class<?>> dp =
+ new PrivilegedFindClassByName(name);
+ clazz = AccessController.doPrivileged(dp);
+ } else {
+ clazz = findClassInternal(name);
+ }
} catch(AccessControlException ace) {
log.warn("WebappClassLoader.findClassInternal(" + name
+ ") security exception: " + ace.getMessage(), ace);
@@ -903,19 +897,10 @@ public abstract class WebappClassLoaderB
String path = nameToPath(name);
- ResourceEntry entry = resourceEntries.get(path);
- if (entry == null) {
- if (securityManager != null) {
- PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path);
- entry = AccessController.doPrivileged(dp);
- } else {
- entry = findResourceInternal(name, path);
- }
- }
- if (entry != null) {
- url = entry.source;
- entry.webResource = null;
+ WebResource resource = resources.getClassLoaderResource(path);
+ if (resource.exists()) {
+ url = resource.getURL();
+ trackLastModified(path, resource);
}
if ((url == null) && hasExternalRepositories) {
@@ -929,7 +914,18 @@ public abstract class WebappClassLoaderB
log.debug(" --> Resource not found, returning null");
}
return url;
+ }
+
+ private void trackLastModified(String path, WebResource resource) {
+ if (resourceEntries.containsKey(path)) {
+ return;
+ }
+ ResourceEntry entry = new ResourceEntry();
+ entry.lastModified = resource.getLastModified();
+ synchronized(resourceEntries) {
+ resourceEntries.putIfAbsent(path, entry);
+ }
}
@@ -1064,14 +1060,6 @@ public abstract class WebappClassLoaderB
InputStream stream = null;
- // (0) Check for a cached copy of this resource
- stream = findLoadedResource(name);
- if (stream != null) {
- if (log.isDebugEnabled())
- log.debug(" --> Returning stream from cache");
- return (stream);
- }
-
boolean delegateFirst = delegate || filter(name, false);
// (1) Delegate to parent if requested
@@ -1080,30 +1068,33 @@ public abstract class WebappClassLoaderB
log.debug(" Delegating to parent classloader " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
- // FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
- return (stream);
+ return stream;
}
}
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
- URL url = findResource(name);
- if (url != null) {
- // FIXME - cache???
+ String path = nameToPath(name);
+ WebResource resource = resources.getClassLoaderResource(path);
+ if (resource.exists()) {
+ stream = resource.getInputStream();
+ trackLastModified(path, resource);
+ }
+ try {
+ if (hasExternalRepositories && stream == null) {
+ URL url = super.findResource(name);
+ stream = url.openStream();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ if (stream != null) {
if (log.isDebugEnabled())
log.debug(" --> Returning stream from local");
- stream = findLoadedResource(name);
- try {
- if (hasExternalRepositories && (stream == null))
- stream = url.openStream();
- } catch (IOException e) {
- // Ignore
- }
- if (stream != null)
- return (stream);
+ return stream;
}
// (3) Delegate to parent unconditionally
@@ -1112,18 +1103,16 @@ public abstract class WebappClassLoaderB
log.debug(" Delegating to parent classloader unconditionally " + parent);
stream = parent.getResourceAsStream(name);
if (stream != null) {
- // FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
- return (stream);
+ return stream;
}
}
// (4) Resource was not found
if (log.isDebugEnabled())
log.debug(" --> Resource not found, returning null");
- return (null);
-
+ return null;
}
@@ -1465,17 +1454,6 @@ public abstract class WebappClassLoaderB
}
}
- state = LifecycleState.STARTING;
-
- try {
- String encoding = System.getProperty("file.encoding");
- if (encoding.indexOf("EBCDIC") != -1) {
- needConvert = true;
- }
- } catch (SecurityException e) {
- // Ignore
- }
-
state = LifecycleState.STARTED;
}
@@ -2389,20 +2367,39 @@ public abstract class WebappClassLoaderB
*/
protected Class<?> findClassInternal(String name) {
- String path = binaryNameToPath(name, true);
+ checkStateForResourceLoading(name);
- ResourceEntry entry = null;
+ String path = binaryNameToPath(name, true);
- if (securityManager != null) {
- PrivilegedAction<ResourceEntry> dp =
- new PrivilegedFindResourceByName(name, path);
- entry = AccessController.doPrivileged(dp);
- } else {
- entry = findResourceInternal(name, path);
+ if (name == null || path == null) {
+ return null;
}
+ ResourceEntry entry = resourceEntries.get(path);
+ WebResource resource = null;
+
if (entry == null) {
- return null;
+ resource = resources.getClassLoaderResource(path);
+
+ if (!resource.exists()) {
+ return null;
+ }
+
+ entry = new ResourceEntry();
+ entry.lastModified = resource.getLastModified();
+
+ // Add the entry in the local resource repository
+ synchronized (resourceEntries) {
+ // Ensures that all the threads which may be in a race to load
+ // a particular class all end up with the same ResourceEntry
+ // instance
+ ResourceEntry entry2 = resourceEntries.get(path);
+ if (entry2 == null) {
+ resourceEntries.put(path, entry);
+ } else {
+ entry = entry2;
+ }
+ }
}
Class<?> clazz = entry.loadedClass;
@@ -2414,21 +2411,18 @@ public abstract class WebappClassLoaderB
if (clazz != null)
return clazz;
- WebResource webResource = entry.webResource;
- if (webResource == null) {
- webResource = resources.getClassLoaderResource(path);
- } else {
- entry.webResource = null;
+ if (resource == null) {
+ resource = resources.getClassLoaderResource(path);
}
- if (!webResource.exists()) {
+ if (!resource.exists()) {
return null;
}
- byte[] binaryContent = webResource.getContent();
- Manifest manifest = webResource.getManifest();
- URL codeBase = webResource.getCodeBase();
- Certificate[] certificates = webResource.getCertificates();
+ byte[] binaryContent = resource.getContent();
+ Manifest manifest = resource.getManifest();
+ URL codeBase = resource.getCodeBase();
+ Certificate[] certificates = resource.getCertificates();
if (transformers.size() > 0) {
// If the resource is a class just being loaded, decorate it
@@ -2503,11 +2497,7 @@ public abstract class WebappClassLoaderB
sm.getString("webappClassLoader.wrongVersion",
name));
}
- // Now the class has been defined, clear the elements of the local
- // resource cache that are no longer required.
entry.loadedClass = clazz;
- // Retain entry.source in case of a getResourceAsStream() call on
- // the class file after the class has been defined.
}
return clazz;
@@ -2539,90 +2529,6 @@ public abstract class WebappClassLoaderB
/**
- * Find specified resource in local repositories.
- *
- * @param name the resource name
- * @param path the resource path
- * @return the loaded resource, or null if the resource isn't found
- */
- protected ResourceEntry findResourceInternal(final String name, final String path) {
-
- checkStateForResourceLoading(name);
-
- if (name == null || path == null) {
- return null;
- }
-
- ResourceEntry entry = resourceEntries.get(path);
- if (entry != null) {
- return entry;
- }
-
- WebResource resource = resources.getClassLoaderResource(path);
-
- if (!resource.exists()) {
- return null;
- }
-
- entry = new ResourceEntry();
- entry.source = resource.getURL();
- entry.lastModified = resource.getLastModified();
- entry.webResource = resource;
-
- boolean fileNeedConvert = false;
- if (needConvert && path.endsWith(".properties")) {
- fileNeedConvert = true;
- }
-
- /* Only cache the binary content if there is some content
- * available and one of the following is true:
- * a) The file needs conversion to address encoding issues (see
- * below)
- * or
- * b) The resource is a service provider configuration file located
- * under META-INF/services
- *
- * In all other cases do not cache the content to prevent
- * excessive memory usage if large resources are present (see
- * https://bz.apache.org/bugzilla/show_bug.cgi?id=53081).
- */
- if (path.startsWith(SERVICES_PREFIX) || fileNeedConvert) {
- byte[] binaryContent = resource.getContent();
- if (binaryContent != null) {
- if (fileNeedConvert) {
- // Workaround for certain files on platforms that use
- // EBCDIC encoding, when they are read through FileInputStream.
- // See commit message of rev.303915 for details
- // http://svn.apache.org/viewvc?view=revision&revision=303915
- String str = new String(binaryContent);
- try {
- binaryContent = str.getBytes(StandardCharsets.UTF_8);
- } catch (Exception e) {
- return null;
- }
- }
- entry.binaryContent = binaryContent;
- }
- }
-
- // Add the entry in the local resource repository
- synchronized (resourceEntries) {
- // Ensures that all the threads which may be in a race to load
- // a particular class all end up with the same ResourceEntry
- // instance
- ResourceEntry entry2 = resourceEntries.get(path);
- if (entry2 == null) {
- resourceEntries.put(path, entry);
- } else {
- entry = entry2;
- }
- }
-
- return entry;
- }
-
-
- /**
* Returns true if the specified package name is sealed according to the
* given manifest.
*
@@ -2648,35 +2554,6 @@ public abstract class WebappClassLoaderB
}
- /**
- * Finds the resource with the given name if it has previously been
- * loaded and cached by this class loader, and return an input stream
- * to the resource data. If this resource has not been cached, return
- * <code>null</code>.
- *
- * @param name Name of the resource to return
- * @return a stream to the loaded resource
- */
- protected InputStream findLoadedResource(String name) {
-
- String path = nameToPath(name);
-
- ResourceEntry entry = resourceEntries.get(path);
- if (entry != null) {
- if (entry.binaryContent != null)
- return new ByteArrayInputStream(entry.binaryContent);
- else {
- try {
- return entry.source.openStream();
- } catch (IOException ioe) {
- // Ignore
- }
- }
- }
- return null;
- }
-
-
/**
* Finds the class with the given name if it has previously been
* loaded and cached by this class loader, and return the Class object.
Modified: tomcat/trunk/java/org/apache/catalina/webresources/Cache.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/Cache.java?rev=1731275&r1=1731274&r2=1731275&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/Cache.java (original)
+++ tomcat/trunk/java/org/apache/catalina/webresources/Cache.java Fri Feb 19 17:09:35 2016
@@ -203,9 +203,10 @@ public class Cache {
}
private boolean noCache(String path) {
- // Don't cache resources used by the class loader (it has its own cache)
- if (path.startsWith("/WEB-INF/classes/") ||
- path.startsWith("/WEB-INF/lib/")) {
+ // Don't cache classes. The class loader handles this.
+ if (path.endsWith(".class") &&
+ (path.startsWith("/WEB-INF/classes/") ||
+ path.startsWith("/WEB-INF/lib/"))) {
return true;
}
return false;
Modified: tomcat/trunk/java/org/apache/catalina/webresources/FileResource.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/webresources/FileResource.java?rev=1731275&r1=1731274&r2=1731275&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/webresources/FileResource.java (original)
+++ tomcat/trunk/java/org/apache/catalina/webresources/FileResource.java Fri Feb 19 17:09:35 2016
@@ -16,6 +16,7 @@
*/
package org.apache.catalina.webresources;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -23,6 +24,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.cert.Certificate;
@@ -40,10 +42,26 @@ public class FileResource extends Abstra
private static final Log log = LogFactory.getLog(FileResource.class);
+ private static final boolean PROPERTIES_NEED_CONVERT;
+ static {
+ boolean isEBCDIC = false;
+ try {
+ String encoding = System.getProperty("file.encoding");
+ if (encoding.indexOf("EBCDIC") != -1) {
+ isEBCDIC = true;
+ }
+ } catch (SecurityException e) {
+ // Ignore
+ }
+ PROPERTIES_NEED_CONVERT = isEBCDIC;
+ }
+
+
private final File resource;
private final String name;
private final boolean readOnly;
private final Manifest manifest;
+ private final boolean needConvert;
public FileResource(WebResourceRoot root, String webAppPath,
File resource, boolean readOnly, Manifest manifest) {
@@ -69,6 +87,7 @@ public class FileResource extends Abstra
this.readOnly = readOnly;
this.manifest = manifest;
+ this.needConvert = PROPERTIES_NEED_CONVERT && name.endsWith(".properties");
}
@Override
@@ -111,6 +130,14 @@ public class FileResource extends Abstra
@Override
public long getContentLength() {
+ if (needConvert) {
+ byte[] content = getContent();
+ if (content == null) {
+ return -1;
+ } else {
+ return content.length;
+ }
+ }
return resource.length();
}
@@ -134,6 +161,14 @@ public class FileResource extends Abstra
@Override
protected InputStream doGetInputStream() {
+ if (needConvert) {
+ byte[] content = getContent();
+ if (content == null) {
+ return null;
+ } else {
+ return new ByteArrayInputStream(content);
+ }
+ }
try {
return new FileInputStream(resource);
} catch (FileNotFoundException fnfe) {
@@ -172,6 +207,18 @@ public class FileResource extends Abstra
}
}
+ if (needConvert) {
+ // Workaround for certain files on platforms that use
+ // EBCDIC encoding, when they are read through FileInputStream.
+ // See commit message of rev.303915 for original details
+ // http://svn.apache.org/viewvc?view=revision&revision=303915
+ String str = new String(result);
+ try {
+ result = str.getBytes(StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ result = null;
+ }
+ }
return result;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org