Author: remy.maucherat(a)jboss.com
Date: 2009-06-10 18:40:29 -0400 (Wed, 10 Jun 2009)
New Revision: 1100
Added:
trunk/java/org/apache/catalina/loader/LegacyWebappClassLoader.java
trunk/java/org/apache/catalina/loader/LegacyWebappLoader.java
Modified:
trunk/java/org/apache/catalina/core/StandardContext.java
trunk/java/org/apache/catalina/loader/WebappClassLoader.java
trunk/java/org/apache/catalina/loader/WebappLoader.java
Log:
- Add the basics for the new CL. Not that happy with the result (but at least it's
simpler).
Modified: trunk/java/org/apache/catalina/core/StandardContext.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardContext.java 2009-06-10 12:42:02 UTC (rev
1099)
+++ trunk/java/org/apache/catalina/core/StandardContext.java 2009-06-10 22:40:29 UTC (rev
1100)
@@ -4278,12 +4278,6 @@
broadcaster.sendNotification(notification);
}
- // Close all JARs right away to avoid always opening a peak number
- // of files on startup
- if (getLoader() instanceof WebappLoader) {
- ((WebappLoader) getLoader()).closeJARs(true);
- }
-
// Reinitializing if something went wrong
if (!ok && started) {
stop();
Added: trunk/java/org/apache/catalina/loader/LegacyWebappClassLoader.java
===================================================================
--- trunk/java/org/apache/catalina/loader/LegacyWebappClassLoader.java
(rev 0)
+++ trunk/java/org/apache/catalina/loader/LegacyWebappClassLoader.java 2009-06-10 22:40:29
UTC (rev 1100)
@@ -0,0 +1,2354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.jar.Attributes.Name;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.StringManager;
+import org.apache.naming.JndiPermission;
+import org.apache.naming.resources.Resource;
+import org.apache.naming.resources.ResourceAttributes;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/**
+ * Specialized web application class loader.
+ * <p>
+ * This class loader is a full reimplementation of the
+ * <code>URLClassLoader</code> from the JDK. It is desinged to be fully
+ * compatible with a normal <code>URLClassLoader</code>, although its
internal
+ * behavior may be completely different.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - This class loader faithfully
follows
+ * the delegation model recommended in the specification. The system class
+ * loader will be queried first, then the local repositories, and only then
+ * delegation to the parent class loader will occur. This allows the web
+ * application to override any shared class except the classes from J2SE.
+ * Special handling is provided from the JAXP XML parser interfaces, the JNDI
+ * interfaces, and the classes from the servlet API, which are never loaded
+ * from the webapp repository.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper
+ * compilation technology, any repository which contains classes from
+ * the servlet API will be ignored by the class loader.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
+ * URLs which include the full JAR URL when a class is loaded from a JAR file,
+ * which allows setting security permission at the class level, even when a
+ * class is contained inside a JAR.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
+ * the order they are added via the initial constructor and/or any subsequent
+ * calls to <code>addRepository()</code> or
<code>addJar()</code>.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
+ * security is made unless a security manager is present.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision: 1061 $ $Date: 2009-05-25 16:32:03 +0200 (Mon, 25 May 2009) $
+ */
+public class LegacyWebappClassLoader
+ extends URLClassLoader
+ implements Reloader, Lifecycle
+ {
+
+ protected static org.jboss.logging.Logger log=
+ org.jboss.logging.Logger.getLogger( LegacyWebappClassLoader.class );
+
+ public static final boolean ENABLE_CLEAR_REFERENCES =
+
Boolean.valueOf(System.getProperty("org.apache.catalina.loader.LegacyWebappClassLoader.ENABLE_CLEAR_REFERENCES",
"true")).booleanValue();
+
+ public static final boolean SYSTEM_CL_DELEGATION =
+
Boolean.valueOf(System.getProperty("org.apache.catalina.loader.LegacyWebappClassLoader.SYSTEM_CL_DELEGATION",
"true")).booleanValue();
+
+ protected class PrivilegedFindResource
+ implements PrivilegedAction {
+
+ protected File file;
+ protected String path;
+
+ PrivilegedFindResource(File file, String path) {
+ this.file = file;
+ this.path = path;
+ }
+
+ public Object run() {
+ return findResourceInternal(file, path);
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Static Variables
+
+
+ /**
+ * The set of trigger classes that will cause a proposed repository not
+ * to be added if this class is visible to the class loader that loaded
+ * this factory class. Typically, trigger classes will be listed for
+ * components that have been integrated into the JDK for later versions,
+ * but where the corresponding JAR files are required to run on
+ * earlier versions.
+ */
+ protected static final String[] triggers = {
+ "javax.servlet.Servlet" // Servlet API
+ };
+
+
+ /**
+ * Set of package names which are not allowed to be loaded from a webapp
+ * class loader without delegating first.
+ */
+ protected static final String[] packageTriggers = {
+ };
+
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ /**
+ * Use anti JAR locking code, which does URL rerouting when accessing
+ * resources.
+ */
+ boolean antiJARLocking = false;
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Construct a new ClassLoader with no defined repositories and no
+ * parent ClassLoader.
+ */
+ public LegacyWebappClassLoader() {
+
+ super(new URL[0]);
+ this.parent = getParent();
+ system = getSystemClassLoader();
+ securityManager = System.getSecurityManager();
+
+ if (securityManager != null) {
+ refreshPolicy();
+ }
+
+ }
+
+
+ /**
+ * Construct a new ClassLoader with no defined repositories and no
+ * parent ClassLoader.
+ */
+ public LegacyWebappClassLoader(ClassLoader parent) {
+
+ super(new URL[0], parent);
+
+ this.parent = getParent();
+
+ system = getSystemClassLoader();
+ securityManager = System.getSecurityManager();
+
+ if (securityManager != null) {
+ refreshPolicy();
+ }
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * Associated directory context giving access to the resources in this
+ * webapp.
+ */
+ protected DirContext resources = null;
+
+
+ /**
+ * The cache of ResourceEntry for classes and resources we have loaded,
+ * keyed by resource name.
+ */
+ protected HashMap resourceEntries = new HashMap();
+
+
+ /**
+ * The list of not found resources.
+ */
+ protected HashMap notFoundResources = new HashMap();
+
+
+ /**
+ * Should this class loader delegate to the parent class loader
+ * <strong>before</strong> searching its own repositories (i.e. the
+ * usual Java2 delegation model)? If set to <code>false</code>,
+ * this class loader will search its own repositories first, and
+ * delegate to the parent only if the class or resource is not
+ * found locally.
+ */
+ protected boolean delegate = false;
+
+
+ /**
+ * Last time a JAR was accessed.
+ */
+ protected long lastJarAccessed = 0L;
+
+
+ /**
+ * The list of local repositories, in the order they should be searched
+ * for locally loaded classes or resources.
+ */
+ protected String[] repositories = new String[0];
+
+
+ /**
+ * Repositories URLs, used to cache the result of getURLs.
+ */
+ protected URL[] repositoryURLs = null;
+
+
+ /**
+ * Repositories translated as path in the work directory (for Jasper
+ * originally), but which is used to generate fake URLs should getURLs be
+ * called.
+ */
+ protected File[] files = new File[0];
+
+
+ /**
+ * The list of JARs, in the order they should be searched
+ * for locally loaded classes or resources.
+ */
+ protected JarFile[] jarFiles = new JarFile[0];
+
+
+ /**
+ * The list of JARs, in the order they should be searched
+ * for locally loaded classes or resources.
+ */
+ protected File[] jarRealFiles = new File[0];
+
+
+ /**
+ * The path which will be monitored for added Jar files.
+ */
+ protected String jarPath = null;
+
+
+ /**
+ * The list of JARs, in the order they should be searched
+ * for locally loaded classes or resources.
+ */
+ protected String[] jarNames = new String[0];
+
+
+ /**
+ * The list of JARs last modified dates, in the order they should be
+ * searched for locally loaded classes or resources.
+ */
+ protected long[] lastModifiedDates = new long[0];
+
+
+ /**
+ * The list of resources which should be checked when checking for
+ * modifications.
+ */
+ protected String[] paths = new String[0];
+
+
+ /**
+ * A list of read File and Jndi Permission's required if this loader
+ * is for a web application context.
+ */
+ protected ArrayList permissionList = new ArrayList();
+
+
+ /**
+ * Path where resources loaded from JARs will be extracted.
+ */
+ protected File loaderDir = null;
+
+
+ /**
+ * The PermissionCollection for each CodeSource for a web
+ * application context.
+ */
+ protected HashMap loaderPC = new HashMap();
+
+
+ /**
+ * Instance of the SecurityManager installed.
+ */
+ protected SecurityManager securityManager = null;
+
+
+ /**
+ * The parent class loader.
+ */
+ protected ClassLoader parent = null;
+
+
+ /**
+ * The system class loader.
+ */
+ protected ClassLoader system = null;
+
+
+ /**
+ * Has this component been started?
+ */
+ protected boolean started = false;
+
+
+ /**
+ * Has external repositories.
+ */
+ protected boolean hasExternalRepositories = false;
+
+ /**
+ * need conversion for properties files
+ */
+ protected boolean needConvert = false;
+
+
+ /**
+ * All permission.
+ */
+ protected Permission allPermission = new java.security.AllPermission();
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Get associated resources.
+ */
+ public DirContext getResources() {
+
+ return this.resources;
+
+ }
+
+
+ /**
+ * Set associated resources.
+ */
+ public void setResources(DirContext resources) {
+
+ this.resources = resources;
+
+ }
+
+
+ /**
+ * Return the "delegate first" flag for this class loader.
+ */
+ public boolean getDelegate() {
+
+ return (this.delegate);
+
+ }
+
+
+ /**
+ * Set the "delegate first" flag for this class loader.
+ *
+ * @param delegate The new "delegate first" flag
+ */
+ public void setDelegate(boolean delegate) {
+
+ this.delegate = delegate;
+
+ }
+
+
+ /**
+ * @return Returns the antiJARLocking.
+ */
+ public boolean getAntiJARLocking() {
+ return antiJARLocking;
+ }
+
+
+ /**
+ * @param antiJARLocking The antiJARLocking to set.
+ */
+ public void setAntiJARLocking(boolean antiJARLocking) {
+ this.antiJARLocking = antiJARLocking;
+ }
+
+
+ /**
+ * If there is a Java SecurityManager create a read FilePermission
+ * or JndiPermission for the file directory path.
+ *
+ * @param path file directory path
+ */
+ public void addPermission(String path) {
+ if (path == null) {
+ return;
+ }
+
+ if (securityManager != null) {
+ Permission permission = null;
+ if( path.startsWith("jndi:") ||
path.startsWith("jar:jndi:") ) {
+ if (!path.endsWith("/")) {
+ path = path + "/";
+ }
+ permission = new JndiPermission(path + "*");
+ addPermission(permission);
+ } else {
+ if (!path.endsWith(File.separator)) {
+ permission = new FilePermission(path, "read");
+ addPermission(permission);
+ path = path + File.separator;
+ }
+ permission = new FilePermission(path + "-", "read");
+ addPermission(permission);
+ }
+ }
+ }
+
+
+ /**
+ * If there is a Java SecurityManager create a read FilePermission
+ * or JndiPermission for URL.
+ *
+ * @param url URL for a file or directory on local system
+ */
+ public void addPermission(URL url) {
+ if (url != null) {
+ addPermission(url.toString());
+ }
+ }
+
+
+ /**
+ * If there is a Java SecurityManager create a Permission.
+ *
+ * @param permission The permission
+ */
+ public void addPermission(Permission permission) {
+ if ((securityManager != null) && (permission != null)) {
+ permissionList.add(permission);
+ }
+ }
+
+
+ /**
+ * Return the JAR path.
+ */
+ public String getJarPath() {
+
+ return this.jarPath;
+
+ }
+
+
+ /**
+ * Change the Jar path.
+ */
+ public void setJarPath(String jarPath) {
+
+ this.jarPath = jarPath;
+
+ }
+
+
+ /**
+ * Change the work directory.
+ */
+ public void setWorkDir(File workDir) {
+ this.loaderDir = new File(workDir, "loader");
+ }
+
+ /**
+ * Utility method for use in subclasses.
+ * Must be called before Lifecycle methods to have any effect.
+ */
+ protected void setParentClassLoader(ClassLoader pcl) {
+ parent = pcl;
+ }
+
+ // ------------------------------------------------------- Reloader Methods
+
+
+ /**
+ * Add a new repository to the set of places this ClassLoader can look for
+ * classes to be loaded.
+ *
+ * @param repository Name of a source of classes to be loaded, such as a
+ * directory pathname, a JAR file pathname, or a ZIP file pathname
+ *
+ * @exception IllegalArgumentException if the specified repository is
+ * invalid or does not exist
+ */
+ public void addRepository(String repository) {
+
+ // Ignore any of the standard repositories, as they are set up using
+ // either addJar or addRepository
+ if (repository.startsWith("/WEB-INF/lib")
+ || repository.startsWith("/WEB-INF/classes"))
+ return;
+
+ // Add this repository to our underlying class loader
+ try {
+ URL url = new URL(repository);
+ super.addURL(url);
+ hasExternalRepositories = true;
+ repositoryURLs = null;
+ } catch (MalformedURLException e) {
+ IllegalArgumentException iae = new IllegalArgumentException
+ ("Invalid repository: " + repository);
+ iae.initCause(e);
+ throw iae;
+ }
+
+ }
+
+
+ /**
+ * Add a new repository to the set of places this ClassLoader can look for
+ * classes to be loaded.
+ *
+ * @param repository Name of a source of classes to be loaded, such as a
+ * directory pathname, a JAR file pathname, or a ZIP file pathname
+ *
+ * @exception IllegalArgumentException if the specified repository is
+ * invalid or does not exist
+ */
+ synchronized void addRepository(String repository, File file) {
+
+ // Note : There should be only one (of course), but I think we should
+ // keep this a bit generic
+
+ if (repository == null)
+ return;
+
+ if (log.isDebugEnabled())
+ log.debug("addRepository(" + repository + ")");
+
+ int i;
+
+ // Add this repository to our internal list
+ String[] result = new String[repositories.length + 1];
+ for (i = 0; i < repositories.length; i++) {
+ result[i] = repositories[i];
+ }
+ result[repositories.length] = repository;
+ repositories = result;
+
+ // Add the file to the list
+ File[] result2 = new File[files.length + 1];
+ for (i = 0; i < files.length; i++) {
+ result2[i] = files[i];
+ }
+ result2[files.length] = file;
+ files = result2;
+
+ }
+
+
+ synchronized void addJar(String jar, JarFile jarFile, File file)
+ throws IOException {
+
+ if (jar == null)
+ return;
+ if (jarFile == null)
+ return;
+ if (file == null)
+ return;
+
+ if (log.isDebugEnabled())
+ log.debug("addJar(" + jar + ")");
+
+ int i;
+
+ if ((jarPath != null) && (jar.startsWith(jarPath))) {
+
+ String jarName = jar.substring(jarPath.length());
+ while (jarName.startsWith("/"))
+ jarName = jarName.substring(1);
+
+ String[] result = new String[jarNames.length + 1];
+ for (i = 0; i < jarNames.length; i++) {
+ result[i] = jarNames[i];
+ }
+ result[jarNames.length] = jarName;
+ jarNames = result;
+
+ }
+
+ try {
+
+ // Register the JAR for tracking
+
+ long lastModified =
+ ((ResourceAttributes) resources.getAttributes(jar))
+ .getLastModified();
+
+ String[] result = new String[paths.length + 1];
+ for (i = 0; i < paths.length; i++) {
+ result[i] = paths[i];
+ }
+ result[paths.length] = jar;
+ paths = result;
+
+ long[] result3 = new long[lastModifiedDates.length + 1];
+ for (i = 0; i < lastModifiedDates.length; i++) {
+ result3[i] = lastModifiedDates[i];
+ }
+ result3[lastModifiedDates.length] = lastModified;
+ lastModifiedDates = result3;
+
+ } catch (NamingException e) {
+ // Ignore
+ }
+
+ // If the JAR currently contains invalid classes, don't actually use it
+ // for classloading
+ if (!validateJarFile(file))
+ return;
+
+ JarFile[] result2 = new JarFile[jarFiles.length + 1];
+ for (i = 0; i < jarFiles.length; i++) {
+ result2[i] = jarFiles[i];
+ }
+ result2[jarFiles.length] = jarFile;
+ jarFiles = result2;
+
+ // Add the file to the list
+ File[] result4 = new File[jarRealFiles.length + 1];
+ for (i = 0; i < jarRealFiles.length; i++) {
+ result4[i] = jarRealFiles[i];
+ }
+ result4[jarRealFiles.length] = file;
+ jarRealFiles = result4;
+ }
+
+
+ /**
+ * Return a String array of the current repositories for this class
+ * loader. If there are no repositories, a zero-length array is
+ * returned.For security reason, returns a clone of the Array (since
+ * String are immutable).
+ */
+ public String[] findRepositories() {
+
+ return ((String[])repositories.clone());
+
+ }
+
+
+ /**
+ * Have one or more classes or resources been modified so that a reload
+ * is appropriate?
+ */
+ public boolean modified() {
+
+ if (log.isDebugEnabled())
+ log.debug("modified()");
+
+ // Checking for modified loaded resources
+ int length = paths.length;
+
+ // A rare race condition can occur in the updates of the two arrays
+ // It's totally ok if the latest class added is not checked (it will
+ // be checked the next time
+ int length2 = lastModifiedDates.length;
+ if (length > length2)
+ length = length2;
+
+ for (int i = 0; i < length; i++) {
+ try {
+ long lastModified =
+ ((ResourceAttributes) resources.getAttributes(paths[i]))
+ .getLastModified();
+ if (lastModified != lastModifiedDates[i]) {
+ if( log.isDebugEnabled() )
+ log.debug(" Resource '" + paths[i]
+ + "' was modified; Date is now: "
+ + new java.util.Date(lastModified) + " Was:
"
+ + new java.util.Date(lastModifiedDates[i]));
+ return (true);
+ }
+ } catch (NamingException e) {
+ log.error(" Resource '" + paths[i] + "' is
missing");
+ return (true);
+ }
+ }
+
+ length = jarNames.length;
+
+ // Check if JARs have been added or removed
+ if (getJarPath() != null) {
+
+ try {
+ NamingEnumeration enumeration = resources.list(getJarPath());
+ int i = 0;
+ while (enumeration.hasMoreElements() && (i < length)) {
+ NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
+ String name = ncPair.getName();
+ // Ignore non JARs present in the lib folder
+ if (!name.endsWith(".jar"))
+ continue;
+ if (!name.equals(jarNames[i])) {
+ // Missing JAR
+ log.info(" Additional JARs have been added : '"
+ + name + "'");
+ return (true);
+ }
+ i++;
+ }
+ if (enumeration.hasMoreElements()) {
+ while (enumeration.hasMoreElements()) {
+ NameClassPair ncPair =
+ (NameClassPair) enumeration.nextElement();
+ String name = ncPair.getName();
+ // Additional non-JAR files are allowed
+ if (name.endsWith(".jar")) {
+ // There was more JARs
+ log.info(" Additional JARs have been added");
+ return (true);
+ }
+ }
+ } else if (i < jarNames.length) {
+ // There was less JARs
+ log.info(" Additional JARs have been added");
+ return (true);
+ }
+ } catch (NamingException e) {
+ if (log.isDebugEnabled())
+ log.debug(" Failed tracking modifications of '"
+ + getJarPath() + "'");
+ } catch (ClassCastException e) {
+ log.error(" Failed tracking modifications of '"
+ + getJarPath() + "' : " + e.getMessage());
+ }
+
+ }
+
+ // No classes have been modified
+ return (false);
+
+ }
+
+
+ /**
+ * Render a String representation of this object.
+ */
+ public String toString() {
+
+ StringBuffer sb = new StringBuffer("LegacyWebappClassLoader\r\n");
+ sb.append(" delegate: ");
+ sb.append(delegate);
+ sb.append("\r\n");
+ sb.append(" repositories:\r\n");
+ if (repositories != null) {
+ for (int i = 0; i < repositories.length; i++) {
+ sb.append(" ");
+ sb.append(repositories[i]);
+ sb.append("\r\n");
+ }
+ }
+ if (this.parent != null) {
+ sb.append("----------> Parent Classloader:\r\n");
+ sb.append(this.parent.toString());
+ sb.append("\r\n");
+ }
+ return (sb.toString());
+
+ }
+
+
+ // ---------------------------------------------------- ClassLoader Methods
+
+
+ /**
+ * Add the specified URL to the classloader.
+ */
+ protected void addURL(URL url) {
+ super.addURL(url);
+ hasExternalRepositories = true;
+ repositoryURLs = null;
+ }
+
+
+ /**
+ * Find the specified class in our local repositories, if possible. If
+ * not found, throw <code>ClassNotFoundException</code>.
+ *
+ * @param name Name of the class to be loaded
+ *
+ * @exception ClassNotFoundException if the class was not found
+ */
+ public Class findClass(String name) throws ClassNotFoundException {
+
+ if (log.isDebugEnabled())
+ log.debug(" findClass(" + name + ")");
+
+ // Cannot load anything from local repositories if class loader is stopped
+ if (!started) {
+ throw new ClassNotFoundException(name);
+ }
+
+ // (1) Permission to define this class when using a SecurityManager
+ if (securityManager != null) {
+ int i = name.lastIndexOf('.');
+ if (i >= 0) {
+ try {
+ if (log.isTraceEnabled())
+ log.trace("
securityManager.checkPackageDefinition");
+ securityManager.checkPackageDefinition(name.substring(0,i));
+ } catch (Exception se) {
+ if (log.isTraceEnabled())
+ log.trace("
-->Exception-->ClassNotFoundException", se);
+ throw new ClassNotFoundException(name, se);
+ }
+ }
+ }
+
+ // Ask our superclass to locate this class, if possible
+ // (throws ClassNotFoundException if it is not found)
+ Class clazz = null;
+ try {
+ if (log.isTraceEnabled())
+ log.trace(" findClassInternal(" + name + ")");
+ try {
+ clazz = findClassInternal(name);
+ } catch(ClassNotFoundException cnfe) {
+ if (!hasExternalRepositories) {
+ throw cnfe;
+ }
+ } catch(AccessControlException ace) {
+ throw new ClassNotFoundException(name, ace);
+ } catch (RuntimeException e) {
+ if (log.isTraceEnabled())
+ log.trace(" -->RuntimeException Rethrown", e);
+ throw e;
+ }
+ if ((clazz == null) && hasExternalRepositories) {
+ try {
+ synchronized (this) {
+ clazz = super.findClass(name);
+ }
+ } catch(AccessControlException ace) {
+ throw new ClassNotFoundException(name, ace);
+ } catch (RuntimeException e) {
+ if (log.isTraceEnabled())
+ log.trace(" -->RuntimeException Rethrown", e);
+ throw e;
+ }
+ }
+ if (clazz == null) {
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning ClassNotFoundException");
+ throw new ClassNotFoundException(name);
+ }
+ } catch (ClassNotFoundException e) {
+ if (log.isTraceEnabled())
+ log.trace(" --> Passing on ClassNotFoundException");
+ throw e;
+ }
+
+ // Return the class we have located
+ if (log.isTraceEnabled())
+ log.debug(" Returning class " + clazz);
+ if ((log.isTraceEnabled()) && (clazz != null))
+ log.debug(" Loaded by " + clazz.getClassLoader());
+ return (clazz);
+
+ }
+
+
+ /**
+ * Find the specified resource in our local repository, and return a
+ * <code>URL</code> refering to it, or <code>null</code> if
this resource
+ * cannot be found.
+ *
+ * @param name Name of the resource to be found
+ */
+ public URL findResource(final String name) {
+
+ if (log.isDebugEnabled())
+ log.debug(" findResource(" + name + ")");
+
+ URL url = null;
+
+ ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+ if (entry == null) {
+ entry = findResourceInternal(name, name);
+ }
+ if (entry != null) {
+ url = entry.source;
+ }
+
+ if ((url == null) && hasExternalRepositories)
+ url = super.findResource(name);
+
+ if (log.isDebugEnabled()) {
+ if (url != null)
+ log.debug(" --> Returning '" + url.toString() +
"'");
+ else
+ log.debug(" --> Resource not found, returning null");
+ }
+ return (url);
+
+ }
+
+
+ /**
+ * Return an enumeration of <code>URLs</code> representing all of the
+ * resources with the given name. If no resources with this name are
+ * found, return an empty enumeration.
+ *
+ * @param name Name of the resources to be found
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public Enumeration findResources(String name) throws IOException {
+
+ if (log.isDebugEnabled())
+ log.debug(" findResources(" + name + ")");
+
+ Vector result = new Vector();
+
+ int jarFilesLength = jarFiles.length;
+ int repositoriesLength = repositories.length;
+
+ int i;
+
+ // Looking at the repositories
+ for (i = 0; i < repositoriesLength; i++) {
+ try {
+ String fullPath = repositories[i] + name;
+ resources.lookup(fullPath);
+ // Note : Not getting an exception here means the resource was
+ // found
+ try {
+ result.addElement(getURI(new File(files[i], name)));
+ } catch (MalformedURLException e) {
+ // Ignore
+ }
+ } catch (NamingException e) {
+ }
+ }
+
+ // Looking at the JAR files
+ synchronized (jarFiles) {
+ if (openJARs()) {
+ for (i = 0; i < jarFilesLength; i++) {
+ JarEntry jarEntry = jarFiles[i].getJarEntry(name);
+ if (jarEntry != null) {
+ try {
+ String jarFakeUrl = getURI(jarRealFiles[i]).toString();
+ jarFakeUrl = "jar:" + jarFakeUrl + "!/" +
name;
+ result.addElement(new URL(jarFakeUrl));
+ } catch (MalformedURLException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+ }
+
+ // Adding the results of a call to the superclass
+ if (hasExternalRepositories) {
+
+ Enumeration otherResourcePaths = super.findResources(name);
+
+ while (otherResourcePaths.hasMoreElements()) {
+ result.addElement(otherResourcePaths.nextElement());
+ }
+
+ }
+
+ return result.elements();
+
+ }
+
+
+ /**
+ * Find the resource with the given name. A resource is some data
+ * (images, audio, text, etc.) that can be accessed by class code in a
+ * way that is independent of the location of the code. The name of a
+ * resource is a "/"-separated path name that identifies the resource.
+ * If the resource cannot be found, return <code>null</code>.
+ * <p>
+ * This method searches according to the following algorithm, returning
+ * as soon as it finds the appropriate URL. If the resource cannot be
+ * found, returns <code>null</code>.
+ * <ul>
+ * <li>If the <code>delegate</code> property is set to
<code>true</code>,
+ * call the <code>getResource()</code> method of the parent class
+ * loader, if any.</li>
+ * <li>Call <code>findResource()</code> to find this resource in
our
+ * locally defined repositories.</li>
+ * <li>Call the <code>getResource()</code> method of the parent
class
+ * loader, if any.</li>
+ * </ul>
+ *
+ * @param name Name of the resource to return a URL for
+ */
+ public URL getResource(String name) {
+
+ if (log.isDebugEnabled())
+ log.debug("getResource(" + name + ")");
+ URL url = null;
+
+ // (1) Delegate to parent if requested
+ if (delegate) {
+ if (log.isDebugEnabled())
+ log.debug(" Delegating to parent classloader " + parent);
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ url = loader.getResource(name);
+ if (url != null) {
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning '" + url.toString() +
"'");
+ return (url);
+ }
+ }
+
+ // (2) Search local repositories
+ url = findResource(name);
+ if (url != null) {
+ // Locating the repository for special handling in the case
+ // of a JAR
+ if (antiJARLocking) {
+ ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+ try {
+ String repository = entry.codeBase.toString();
+ if ((repository.endsWith(".jar"))
+ && (!(name.endsWith(".class")))) {
+ // Copy binary content to the work directory if not present
+ File resourceFile = new File(loaderDir, name);
+ url = getURI(resourceFile);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning '" + url.toString() +
"'");
+ return (url);
+ }
+
+ // (3) Delegate to parent unconditionally if not already attempted
+ if( !delegate ) {
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ url = loader.getResource(name);
+ if (url != null) {
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning '" + url.toString() +
"'");
+ return (url);
+ }
+ }
+
+ // (4) Resource was not found
+ if (log.isDebugEnabled())
+ log.debug(" --> Resource not found, returning null");
+ return (null);
+
+ }
+
+
+ /**
+ * Find the resource with the given name, and return an input stream
+ * that can be used for reading it. The search order is as described
+ * for <code>getResource()</code>, after checking to see if the resource
+ * data has been previously cached. If the resource cannot be found,
+ * return <code>null</code>.
+ *
+ * @param name Name of the resource to return an input stream for
+ */
+ public InputStream getResourceAsStream(String name) {
+
+ if (log.isDebugEnabled())
+ log.debug("getResourceAsStream(" + name + ")");
+ 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);
+ }
+
+ // (1) Delegate to parent if requested
+ if (delegate) {
+ if (log.isDebugEnabled())
+ log.debug(" Delegating to parent classloader " + parent);
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ stream = loader.getResourceAsStream(name);
+ if (stream != null) {
+ // FIXME - cache???
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning stream from parent");
+ return (stream);
+ }
+ }
+
+ // (2) Search local repositories
+ if (log.isDebugEnabled())
+ log.debug(" Searching local repositories");
+ URL url = findResource(name);
+ if (url != null) {
+ // FIXME - cache???
+ 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);
+ }
+
+ // (3) Delegate to parent unconditionally
+ if (!delegate) {
+ if (log.isDebugEnabled())
+ log.debug(" Delegating to parent classloader unconditionally "
+ parent);
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ stream = loader.getResourceAsStream(name);
+ if (stream != null) {
+ // FIXME - cache???
+ if (log.isDebugEnabled())
+ log.debug(" --> Returning stream from parent");
+ return (stream);
+ }
+ }
+
+ // (4) Resource was not found
+ if (log.isDebugEnabled())
+ log.debug(" --> Resource not found, returning null");
+ return (null);
+
+ }
+
+
+ /**
+ * Load the class with the specified name. This method searches for
+ * classes in the same manner as <code>loadClass(String, boolean)</code>
+ * with <code>false</code> as the second argument.
+ *
+ * @param name Name of the class to be loaded
+ *
+ * @exception ClassNotFoundException if the class was not found
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+
+ return (loadClass(name, false));
+
+ }
+
+
+ /**
+ * Load the class with the specified name, searching using the following
+ * algorithm until it finds and returns the class. If the class cannot
+ * be found, returns <code>ClassNotFoundException</code>.
+ * <ul>
+ * <li>Call <code>findLoadedClass(String)</code> to check if the
+ * class has already been loaded. If it has, the same
+ * <code>Class</code> object is returned.</li>
+ * <li>If the <code>delegate</code> property is set to
<code>true</code>,
+ * call the <code>loadClass()</code> method of the parent class
+ * loader, if any.</li>
+ * <li>Call <code>findClass()</code> to find this class in our
locally
+ * defined repositories.</li>
+ * <li>Call the <code>loadClass()</code> method of our parent
+ * class loader, if any.</li>
+ * </ul>
+ * If the class was found using the above steps, and the
+ * <code>resolve</code> flag is <code>true</code>, this
method will then
+ * call <code>resolveClass(Class)</code> on the resulting Class object.
+ *
+ * @param name Name of the class to be loaded
+ * @param resolve If <code>true</code> then resolve the class
+ *
+ * @exception ClassNotFoundException if the class was not found
+ */
+ public Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+
+ if (log.isDebugEnabled())
+ log.debug("loadClass(" + name + ", " + resolve +
")");
+ Class clazz = null;
+
+ // Log access to stopped classloader
+ if (!started) {
+ try {
+ throw new IllegalStateException();
+ } catch (IllegalStateException e) {
+ log.info(sm.getString("WebappClassLoader.stopped", name), e);
+ }
+ }
+
+ // (0) Check our previously loaded local class cache
+ clazz = findLoadedClass0(name);
+ if (clazz != null) {
+ if (log.isDebugEnabled())
+ log.debug(" Returning class from cache");
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+
+ // (0.1) Check our previously loaded class cache
+ clazz = findLoadedClass(name);
+ if (clazz != null) {
+ if (log.isDebugEnabled())
+ log.debug(" Returning class from cache");
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+
+ // (0.2) Try loading the class with the system class loader, to prevent
+ // the webapp from overriding J2SE classes
+ if (SYSTEM_CL_DELEGATION) {
+ try {
+ clazz = Class.forName(name, false, system);
+ if (clazz != null) {
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ // Ignore
+ }
+ }
+
+ // (0.5) Permission to access this class when using a SecurityManager
+ if (securityManager != null) {
+ int i = name.lastIndexOf('.');
+ if (i >= 0) {
+ try {
+ securityManager.checkPackageAccess(name.substring(0,i));
+ } catch (SecurityException se) {
+ String error = "Security Violation, attempt to use " +
+ "Restricted Class: " + name;
+ log.info(error, se);
+ throw new ClassNotFoundException(error, se);
+ }
+ }
+ }
+
+ boolean delegateLoad = delegate || filter(name);
+
+ // (1) Delegate to our parent if requested
+ if (delegateLoad) {
+ if (log.isDebugEnabled())
+ log.debug(" Delegating to parent classloader1 " + parent);
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ try {
+ clazz = Class.forName(name, false, loader);
+ if (clazz != null) {
+ if (log.isDebugEnabled())
+ log.debug(" Loading class from parent");
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ ;
+ }
+ }
+
+ // (2) Search local repositories
+ if (log.isDebugEnabled())
+ log.debug(" Searching local repositories");
+ try {
+ clazz = findClass(name);
+ if (clazz != null) {
+ if (log.isDebugEnabled())
+ log.debug(" Loading class from local repository");
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ ;
+ }
+
+ // (3) Delegate to parent unconditionally
+ if (!delegateLoad) {
+ if (log.isDebugEnabled())
+ log.debug(" Delegating to parent classloader at end: " +
parent);
+ ClassLoader loader = parent;
+ if (loader == null)
+ loader = system;
+ try {
+ clazz = Class.forName(name, false, loader);
+ if (clazz != null) {
+ if (log.isDebugEnabled())
+ log.debug(" Loading class from parent");
+ if (resolve)
+ resolveClass(clazz);
+ return (clazz);
+ }
+ } catch (ClassNotFoundException e) {
+ ;
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+
+ /**
+ * Get the Permissions for a CodeSource. If this instance
+ * of LegacyWebappClassLoader is for a web application context,
+ * add read FilePermission or JndiPermissions for the base
+ * directory (if unpacked),
+ * the context URL, and jar file resources.
+ *
+ * @param codeSource where the code was loaded from
+ * @return PermissionCollection for CodeSource
+ */
+ protected PermissionCollection getPermissions(CodeSource codeSource) {
+
+ String codeUrl = codeSource.getLocation().toString();
+ PermissionCollection pc;
+ if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
+ pc = super.getPermissions(codeSource);
+ if (pc != null) {
+ Iterator perms = permissionList.iterator();
+ while (perms.hasNext()) {
+ Permission p = (Permission)perms.next();
+ pc.add(p);
+ }
+ loaderPC.put(codeUrl,pc);
+ }
+ }
+ return (pc);
+
+ }
+
+
+ /**
+ * Returns the search path of URLs for loading classes and resources.
+ * This includes the original list of URLs specified to the constructor,
+ * along with any URLs subsequently appended by the addURL() method.
+ * @return the search path of URLs for loading classes and resources.
+ */
+ public URL[] getURLs() {
+
+ if (repositoryURLs != null) {
+ return repositoryURLs;
+ }
+
+ URL[] external = super.getURLs();
+
+ int filesLength = files.length;
+ int jarFilesLength = jarRealFiles.length;
+ int length = filesLength + jarFilesLength + external.length;
+ int i;
+
+ try {
+
+ URL[] urls = new URL[length];
+ for (i = 0; i < length; i++) {
+ if (i < filesLength) {
+ urls[i] = getURL(files[i], true);
+ } else if (i < filesLength + jarFilesLength) {
+ urls[i] = getURL(jarRealFiles[i - filesLength], true);
+ } else {
+ urls[i] = external[i - filesLength - jarFilesLength];
+ }
+ }
+
+ repositoryURLs = urls;
+
+ } catch (MalformedURLException e) {
+ repositoryURLs = new URL[0];
+ }
+
+ return repositoryURLs;
+
+ }
+
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+ return new LifecycleListener[0];
+ }
+
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+ }
+
+
+ /**
+ * Start the class loader.
+ *
+ * @exception LifecycleException if a lifecycle error occurs
+ */
+ public void start() throws LifecycleException {
+
+ started = true;
+ String encoding = null;
+ try {
+ encoding = System.getProperty("file.encoding");
+ } catch (Exception e) {
+ return;
+ }
+ if (encoding.indexOf("EBCDIC")!=-1) {
+ needConvert = true;
+ }
+
+ }
+
+
+ /**
+ * Stop the class loader.
+ *
+ * @exception LifecycleException if a lifecycle error occurs
+ */
+ public void stop() throws LifecycleException {
+
+ // Clearing references should be done before setting started to
+ // false, due to possible side effects
+ clearReferences();
+
+ started = false;
+
+ int length = files.length;
+ for (int i = 0; i < length; i++) {
+ files[i] = null;
+ }
+
+ length = jarFiles.length;
+ for (int i = 0; i < length; i++) {
+ try {
+ if (jarFiles[i] != null) {
+ jarFiles[i].close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ jarFiles[i] = null;
+ }
+
+ notFoundResources.clear();
+ resourceEntries.clear();
+ resources = null;
+ repositories = null;
+ repositoryURLs = null;
+ files = null;
+ jarFiles = null;
+ jarRealFiles = null;
+ jarPath = null;
+ jarNames = null;
+ lastModifiedDates = null;
+ paths = null;
+ hasExternalRepositories = false;
+ parent = null;
+
+ permissionList.clear();
+ loaderPC.clear();
+
+ if (loaderDir != null) {
+ deleteDir(loaderDir);
+ }
+
+ }
+
+
+ /**
+ * Used to periodically signal to the classloader to release
+ * JAR resources.
+ */
+ public void closeJARs(boolean force) {
+ if (jarFiles.length > 0) {
+ synchronized (jarFiles) {
+ if (force || (System.currentTimeMillis()
+ > (lastJarAccessed + 90000))) {
+ for (int i = 0; i < jarFiles.length; i++) {
+ try {
+ if (jarFiles[i] != null) {
+ jarFiles[i].close();
+ jarFiles[i] = null;
+ }
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to close JAR", e);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Clear references.
+ */
+ protected void clearReferences() {
+
+ // Unregister any JDBC drivers loaded by this classloader
+ Enumeration drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ Driver driver = (Driver) drivers.nextElement();
+ if (driver.getClass().getClassLoader() == this) {
+ try {
+ DriverManager.deregisterDriver(driver);
+ } catch (SQLException e) {
+ log.warn("SQL driver deregistration failed", e);
+ }
+ }
+ }
+
+ // Null out any static or final fields from loaded classes,
+ // as a workaround for apparent garbage collection bugs
+ if (ENABLE_CLEAR_REFERENCES) {
+ Iterator loadedClasses = ((HashMap)
resourceEntries.clone()).values().iterator();
+ while (loadedClasses.hasNext()) {
+ ResourceEntry entry = (ResourceEntry) loadedClasses.next();
+ if (entry.loadedClass != null) {
+ Class clazz = entry.loadedClass;
+ try {
+ Field[] fields = clazz.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ int mods = field.getModifiers();
+ if (field.getType().isPrimitive()
+ || (field.getName().indexOf("$") != -1)) {
+ continue;
+ }
+ if (Modifier.isStatic(mods)) {
+ try {
+ field.setAccessible(true);
+ if (Modifier.isFinal(mods)) {
+ if
(!((field.getType().getName().startsWith("java."))
+ ||
(field.getType().getName().startsWith("javax.")))) {
+ nullInstance(field.get(null));
+ }
+ } else {
+ field.set(null, null);
+ if (log.isDebugEnabled()) {
+ log.debug("Set field " +
field.getName()
+ + " to null in class " +
clazz.getName());
+ }
+ }
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Could not set field " +
field.getName()
+ + " to null in class " +
clazz.getName(), t);
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Could not clean fields for class " +
clazz.getName(), t);
+ }
+ }
+ }
+ }
+ }
+
+ // Clear the IntrospectionUtils cache.
+ IntrospectionUtils.clear();
+
+ // Clear the classloader reference in the VM's bean introspector
+ java.beans.Introspector.flushCaches();
+
+ }
+
+
+ protected void nullInstance(Object instance) {
+ if (instance == null) {
+ return;
+ }
+ Field[] fields = instance.getClass().getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ int mods = field.getModifiers();
+ if (field.getType().isPrimitive()
+ || (field.getName().indexOf("$") != -1)) {
+ continue;
+ }
+ try {
+ field.setAccessible(true);
+ if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
+ // Doing something recursively is too risky
+ continue;
+ } else {
+ Object value = field.get(instance);
+ if (null != value) {
+ Class valueClass = value.getClass();
+ if (!loadedByThisOrChild(valueClass)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Not setting field " +
field.getName() +
+ " to null in object of class " +
+ instance.getClass().getName() +
+ " because the referenced object was of type
" +
+ valueClass.getName() +
+ " which was not loaded by this
LegacyWebappClassLoader.");
+ }
+ } else {
+ field.set(instance, null);
+ if (log.isDebugEnabled()) {
+ log.debug("Set field " + field.getName()
+ + " to null in class " +
instance.getClass().getName());
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Could not set field " + field.getName()
+ + " to null in object instance of class "
+ + instance.getClass().getName(), t);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Determine whether a class was loaded by this class loader or one of
+ * its child class loaders.
+ */
+ protected boolean loadedByThisOrChild(Class clazz)
+ {
+ boolean result = false;
+ for (ClassLoader classLoader = clazz.getClassLoader();
+ null != classLoader; classLoader = classLoader.getParent()) {
+ if (classLoader.equals(this)) {
+ result = true;
+ break;
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Used to periodically signal to the classloader to release JAR resources.
+ */
+ protected boolean openJARs() {
+ if (started && (jarFiles.length > 0)) {
+ lastJarAccessed = System.currentTimeMillis();
+ if (jarFiles[0] == null) {
+ for (int i = 0; i < jarFiles.length; i++) {
+ try {
+ jarFiles[i] = new JarFile(jarRealFiles[i]);
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to open JAR", e);
+ }
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Find specified class in local repositories.
+ *
+ * @return the loaded class, or null if the class isn't found
+ */
+ protected Class findClassInternal(String name)
+ throws ClassNotFoundException {
+
+ if (!validate(name))
+ throw new ClassNotFoundException(name);
+
+ String tempPath = name.replace('.', '/');
+ String classPath = tempPath + ".class";
+
+ ResourceEntry entry = null;
+
+ entry = findResourceInternal(name, classPath);
+
+ if (entry == null)
+ throw new ClassNotFoundException(name);
+
+ Class clazz = entry.loadedClass;
+ if (clazz != null)
+ return clazz;
+
+ synchronized (this) {
+ clazz = entry.loadedClass;
+ if (clazz != null)
+ return clazz;
+
+ if (entry.binaryContent == null)
+ throw new ClassNotFoundException(name);
+
+ // Looking up the package
+ String packageName = null;
+ int pos = name.lastIndexOf('.');
+ if (pos != -1)
+ packageName = name.substring(0, pos);
+
+ Package pkg = null;
+
+ if (packageName != null) {
+ pkg = getPackage(packageName);
+ // Define the package (if null)
+ if (pkg == null) {
+ try {
+ if (entry.manifest == null) {
+ definePackage(packageName, null, null, null, null,
+ null, null, null);
+ } else {
+ definePackage(packageName, entry.manifest,
+ entry.codeBase);
+ }
+ } catch (IllegalArgumentException e) {
+ // Ignore: normal error due to dual definition of package
+ }
+ pkg = getPackage(packageName);
+ }
+ }
+
+ if (securityManager != null) {
+
+ // Checking sealing
+ if (pkg != null) {
+ boolean sealCheck = true;
+ if (pkg.isSealed()) {
+ sealCheck = pkg.isSealed(entry.codeBase);
+ } else {
+ sealCheck = (entry.manifest == null)
+ || !isPackageSealed(packageName, entry.manifest);
+ }
+ if (!sealCheck)
+ throw new SecurityException
+ ("Sealing violation loading " + name + " :
Package "
+ + packageName + " is sealed.");
+ }
+
+ }
+
+ clazz = defineClass(name, entry.binaryContent, 0,
+ entry.binaryContent.length,
+ new CodeSource(entry.codeBase, entry.certificates));
+ entry.loadedClass = clazz;
+ entry.binaryContent = null;
+ entry.source = null;
+ entry.codeBase = null;
+ entry.manifest = null;
+ entry.certificates = null;
+ }
+
+ return clazz;
+
+ }
+
+ /**
+ * Find specified resource in local repositories. This block
+ * will execute under an AccessControl.doPrivilege block.
+ *
+ * @return the loaded resource, or null if the resource isn't found
+ */
+ protected ResourceEntry findResourceInternal(File file, String path){
+ ResourceEntry entry = new ResourceEntry();
+ try {
+ entry.source = getURI(new File(file, path));
+ entry.codeBase = getURL(new File(file, path), false);
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ return entry;
+ }
+
+
+ /**
+ * Find specified resource in local repositories.
+ *
+ * @return the loaded resource, or null if the resource isn't found
+ */
+ protected ResourceEntry findResourceInternal(String name, String path) {
+
+ if (!started) {
+ log.info(sm.getString("webappClassLoader.stopped", name));
+ return null;
+ }
+
+ if ((name == null) || (path == null))
+ return null;
+
+ ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+ if (entry != null)
+ return entry;
+
+ int contentLength = -1;
+ InputStream binaryStream = null;
+
+ int jarFilesLength = jarFiles.length;
+ int repositoriesLength = repositories.length;
+
+ int i;
+
+ Resource resource = null;
+
+ boolean fileNeedConvert = false;
+
+ for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
+ try {
+
+ String fullPath = repositories[i] + path;
+
+ Object lookupResult = resources.lookup(fullPath);
+ if (lookupResult instanceof Resource) {
+ resource = (Resource) lookupResult;
+ }
+
+ // Note : Not getting an exception here means the resource was
+ // found
+ if (securityManager != null) {
+ PrivilegedAction dp =
+ new PrivilegedFindResource(files[i], path);
+ entry = (ResourceEntry)AccessController.doPrivileged(dp);
+ } else {
+ entry = findResourceInternal(files[i], path);
+ }
+
+ ResourceAttributes attributes =
+ (ResourceAttributes) resources.getAttributes(fullPath);
+ contentLength = (int) attributes.getContentLength();
+ entry.lastModified = attributes.getLastModified();
+
+ if (resource != null) {
+
+
+ try {
+ binaryStream = resource.streamContent();
+ } catch (IOException e) {
+ return null;
+ }
+
+ if (needConvert) {
+ if (path.endsWith(".properties")) {
+ fileNeedConvert = true;
+ }
+ }
+
+ // Register the full path for modification checking
+ // Note: Only syncing on a 'constant' object is needed
+ synchronized (allPermission) {
+
+ int j;
+
+ long[] result2 =
+ new long[lastModifiedDates.length + 1];
+ for (j = 0; j < lastModifiedDates.length; j++) {
+ result2[j] = lastModifiedDates[j];
+ }
+ result2[lastModifiedDates.length] = entry.lastModified;
+ lastModifiedDates = result2;
+
+ String[] result = new String[paths.length + 1];
+ for (j = 0; j < paths.length; j++) {
+ result[j] = paths[j];
+ }
+ result[paths.length] = fullPath;
+ paths = result;
+
+ }
+
+ }
+
+ } catch (NamingException e) {
+ }
+ }
+
+ if ((entry == null) && (notFoundResources.containsKey(name)))
+ return null;
+
+ JarEntry jarEntry = null;
+
+ synchronized (jarFiles) {
+
+ if (!openJARs()) {
+ return null;
+ }
+ for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
+
+ jarEntry = jarFiles[i].getJarEntry(path);
+
+ if (jarEntry != null) {
+
+ entry = new ResourceEntry();
+ try {
+ entry.codeBase = getURL(jarRealFiles[i], false);
+ String jarFakeUrl = getURI(jarRealFiles[i]).toString();
+ jarFakeUrl = "jar:" + jarFakeUrl + "!/" +
path;
+ entry.source = new URL(jarFakeUrl);
+ entry.lastModified = jarRealFiles[i].lastModified();
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ contentLength = (int) jarEntry.getSize();
+ try {
+ entry.manifest = jarFiles[i].getManifest();
+ binaryStream = jarFiles[i].getInputStream(jarEntry);
+ } catch (IOException e) {
+ return null;
+ }
+
+ // Extract resources contained in JAR to the workdir
+ if (antiJARLocking && !(path.endsWith(".class")))
{
+ byte[] buf = new byte[1024];
+ File resourceFile = new File
+ (loaderDir, jarEntry.getName());
+ if (!resourceFile.exists()) {
+ Enumeration entries = jarFiles[i].entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry2 =
+ (JarEntry) entries.nextElement();
+ if (!(jarEntry2.isDirectory())
+ && (!jarEntry2.getName().endsWith
+ (".class"))) {
+ resourceFile = new File
+ (loaderDir, jarEntry2.getName());
+ resourceFile.getParentFile().mkdirs();
+ FileOutputStream os = null;
+ InputStream is = null;
+ try {
+ is = jarFiles[i].getInputStream
+ (jarEntry2);
+ os = new FileOutputStream
+ (resourceFile);
+ while (true) {
+ int n = is.read(buf);
+ if (n <= 0) {
+ break;
+ }
+ os.write(buf, 0, n);
+ }
+ } catch (IOException e) {
+ // Ignore
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ }
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ if (entry == null) {
+ synchronized (notFoundResources) {
+ notFoundResources.put(name, name);
+ }
+ return null;
+ }
+
+ if (binaryStream != null) {
+
+ byte[] binaryContent = new byte[contentLength];
+
+ int pos = 0;
+ try {
+
+ while (true) {
+ int n = binaryStream.read(binaryContent, pos,
+ binaryContent.length - pos);
+ if (n <= 0)
+ break;
+ pos += n;
+ }
+ } catch (IOException e) {
+ log.error(sm.getString("webappClassLoader.readError",
name), e);
+ return null;
+ } finally {
+ try {
+ binaryStream.close();
+ } catch (IOException e) {}
+ }
+
+ if (fileNeedConvert) {
+ String str = new String(binaryContent,0,pos);
+ try {
+ binaryContent = str.getBytes("UTF-8");
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ entry.binaryContent = binaryContent;
+
+ // The certificates are only available after the JarEntry
+ // associated input stream has been fully read
+ if (jarEntry != null) {
+ entry.certificates = jarEntry.getCertificates();
+ }
+
+ }
+
+ }
+
+ // 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 = (ResourceEntry) resourceEntries.get(name);
+ if (entry2 == null) {
+ resourceEntries.put(name, entry);
+ } else {
+ entry = entry2;
+ }
+ }
+
+ return entry;
+
+ }
+
+
+ /**
+ * Returns true if the specified package name is sealed according to the
+ * given manifest.
+ */
+ protected boolean isPackageSealed(String name, Manifest man) {
+
+ String path = name.replace('.', '/') + '/';
+ Attributes attr = man.getAttributes(path);
+ String sealed = null;
+ if (attr != null) {
+ sealed = attr.getValue(Name.SEALED);
+ }
+ if (sealed == null) {
+ if ((attr = man.getMainAttributes()) != null) {
+ sealed = attr.getValue(Name.SEALED);
+ }
+ }
+ return "true".equalsIgnoreCase(sealed);
+
+ }
+
+
+ /**
+ * 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
+ */
+ protected InputStream findLoadedResource(String name) {
+
+ ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+ if (entry != null) {
+ if (entry.binaryContent != null)
+ return new ByteArrayInputStream(entry.binaryContent);
+ }
+ 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.
+ * If this class has not been cached, return <code>null</code>.
+ *
+ * @param name Name of the resource to return
+ */
+ protected Class findLoadedClass0(String name) {
+
+ ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
+ if (entry != null) {
+ return entry.loadedClass;
+ }
+ return (null); // FIXME - findLoadedResource()
+
+ }
+
+
+ /**
+ * Refresh the system policy file, to pick up eventual changes.
+ */
+ protected void refreshPolicy() {
+
+ try {
+ // The policy file may have been modified to adjust
+ // permissions, so we're reloading it when loading or
+ // reloading a Context
+ Policy policy = Policy.getPolicy();
+ policy.refresh();
+ } catch (AccessControlException e) {
+ // Some policy files may restrict this, even for the core,
+ // so this exception is ignored
+ }
+
+ }
+
+
+ /**
+ * Filter classes.
+ *
+ * @param name class name
+ * @return true if the class should be filtered
+ */
+ protected boolean filter(String name) {
+
+ if (name == null)
+ return false;
+
+ // Looking up the package
+ String packageName = null;
+ int pos = name.lastIndexOf('.');
+ if (pos != -1)
+ packageName = name.substring(0, pos);
+ else
+ return false;
+
+ for (int i = 0; i < packageTriggers.length; i++) {
+ if (packageName.startsWith(packageTriggers[i]))
+ return true;
+ }
+
+ return false;
+
+ }
+
+
+ /**
+ * Validate a classname. As per SRV.9.7.2, we must restict loading of
+ * classes from J2SE (java.*) and classes of the servlet API
+ * (javax.servlet.*). That should enhance robustness and prevent a number
+ * of user error (where an older version of servlet.jar would be present
+ * in /WEB-INF/lib).
+ *
+ * @param name class name
+ * @return true if the name is valid
+ */
+ protected boolean validate(String name) {
+
+ if (name == null)
+ return false;
+ if (name.startsWith("java."))
+ return false;
+
+ return true;
+
+ }
+
+
+ /**
+ * Check the specified JAR file, and return <code>true</code> if it does
+ * not contain any of the trigger classes.
+ *
+ * @param jarfile The JAR file to be checked
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ protected boolean validateJarFile(File jarfile)
+ throws IOException {
+
+ if (triggers == null)
+ return (true);
+ JarFile jarFile = new JarFile(jarfile);
+ for (int i = 0; i < triggers.length; i++) {
+ Class clazz = null;
+ try {
+ if (parent != null) {
+ clazz = parent.loadClass(triggers[i]);
+ } else {
+ clazz = Class.forName(triggers[i]);
+ }
+ } catch (Throwable t) {
+ clazz = null;
+ }
+ if (clazz == null)
+ continue;
+ String name = triggers[i].replace('.', '/') +
".class";
+ if (log.isDebugEnabled())
+ log.debug(" Checking for " + name);
+ JarEntry jarEntry = jarFile.getJarEntry(name);
+ if (jarEntry != null) {
+ log.info("validateJarFile(" + jarfile +
+ ") - jar not loaded. See Servlet Spec 2.3, "
+ + "section 9.7.2. Offending class: " + name);
+ jarFile.close();
+ return (false);
+ }
+ }
+ jarFile.close();
+ return (true);
+
+ }
+
+
+ /**
+ * Get URL.
+ */
+ protected URL getURL(File file, boolean encoded)
+ throws MalformedURLException {
+
+ File realFile = file;
+ try {
+ realFile = realFile.getCanonicalFile();
+ } catch (IOException e) {
+ // Ignore
+ }
+ if(encoded) {
+ return getURI(realFile);
+ } else {
+ return realFile.toURL();
+ }
+
+ }
+
+
+ /**
+ * Get URL.
+ */
+ protected URL getURI(File file)
+ throws MalformedURLException {
+
+
+ File realFile = file;
+ try {
+ realFile = realFile.getCanonicalFile();
+ } catch (IOException e) {
+ // Ignore
+ }
+ return realFile.toURI().toURL();
+
+ }
+
+
+ /**
+ * Delete the specified directory, including all of its contents and
+ * subdirectories recursively.
+ *
+ * @param dir File object representing the directory to be deleted
+ */
+ protected static void deleteDir(File dir) {
+
+ String files[] = dir.list();
+ if (files == null) {
+ files = new String[0];
+ }
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(dir, files[i]);
+ if (file.isDirectory()) {
+ deleteDir(file);
+ } else {
+ file.delete();
+ }
+ }
+ dir.delete();
+
+ }
+
+
+}
+
Added: trunk/java/org/apache/catalina/loader/LegacyWebappLoader.java
===================================================================
--- trunk/java/org/apache/catalina/loader/LegacyWebappLoader.java
(rev 0)
+++ trunk/java/org/apache/catalina/loader/LegacyWebappLoader.java 2009-06-10 22:40:29 UTC
(rev 1100)
@@ -0,0 +1,1234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.catalina.loader;
+
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.util.ArrayList;
+import java.util.jar.JarFile;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.naming.Binding;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
+import org.apache.naming.resources.Resource;
+import org.apache.tomcat.util.modeler.Registry;
+
+
+/**
+ * Classloader implementation which is specialized for handling web
+ * applications in the most efficient way, while being Catalina aware (all
+ * accesses to resources are made through the DirContext interface).
+ * This class loader supports detection of modified
+ * Java classes, which can be used to implement auto-reload support.
+ * <p>
+ * This class loader is configured by adding the pathnames of directories,
+ * JAR files, and ZIP files with the <code>addRepository()</code> method,
+ * prior to calling <code>start()</code>. When a new class is required,
+ * these repositories will be consulted first to locate the class. If it
+ * is not present, the system class loader will be used instead.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 914 $ $Date: 2009-01-29 17:56:25 +0100 (Thu, 29 Jan 2009) $
+ */
+
+public class LegacyWebappLoader
+ implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration {
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Construct a new WebappLoader with no defined parent class loader
+ * (so that the actual parent will be the system class loader).
+ */
+ public LegacyWebappLoader() {
+
+ this(null);
+
+ }
+
+
+ /**
+ * Construct a new WebappLoader with the specified class loader
+ * to be defined as the parent of the ClassLoader we ultimately create.
+ *
+ * @param parent The parent class loader
+ */
+ public LegacyWebappLoader(ClassLoader parent) {
+ super();
+ this.parentClassLoader = parent;
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * First load of the class.
+ */
+ private static boolean first = true;
+
+
+ /**
+ * The class loader being managed by this Loader component.
+ */
+ private LegacyWebappClassLoader classLoader = null;
+
+
+ /**
+ * The Container with which this Loader has been associated.
+ */
+ private Container container = null;
+
+
+ /**
+ * The "follow standard delegation model" flag that will be used to
+ * configure our ClassLoader.
+ */
+ private boolean delegate = false;
+
+
+ /**
+ * The descriptive information about this Loader implementation.
+ */
+ private static final String info =
+ "org.apache.catalina.loader.LegacyWebappLoader/1.0";
+
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+ /**
+ * The Java class name of the ClassLoader implementation to be used.
+ * This class should extend LegacyWebappClassLoader, otherwise, a different
+ * loader implementation must be used.
+ */
+ private String loaderClass =
+ "org.apache.catalina.loader.LegacyWebappClassLoader";
+
+
+ /**
+ * The parent class loader of the class loader we will create.
+ */
+ private ClassLoader parentClassLoader = null;
+
+
+ /**
+ * The reloadable flag for this Loader.
+ */
+ private boolean reloadable = false;
+
+
+ /**
+ * The set of repositories associated with this class loader.
+ */
+ private String repositories[] = new String[0];
+
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ /**
+ * Has this component been started?
+ */
+ private boolean started = false;
+
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+
+ /**
+ * Classpath set in the loader.
+ */
+ private String classpath = null;
+
+
+ /**
+ * Repositories that are set in the loader, for JMX.
+ */
+ private ArrayList loaderRepositories = null;
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Return the Java class loader to be used by this Container.
+ */
+ public ClassLoader getClassLoader() {
+
+ return ((ClassLoader) classLoader);
+
+ }
+
+
+ /**
+ * Return the Container with which this Logger has been associated.
+ */
+ public Container getContainer() {
+
+ return (container);
+
+ }
+
+
+ /**
+ * Set the Container with which this Logger has been associated.
+ *
+ * @param container The associated Container
+ */
+ public void setContainer(Container container) {
+
+ // Deregister from the old Container (if any)
+ if ((this.container != null) && (this.container instanceof Context))
+ ((Context) this.container).removePropertyChangeListener(this);
+
+ // Process this property change
+ Container oldContainer = this.container;
+ this.container = container;
+ support.firePropertyChange("container", oldContainer, this.container);
+
+ // Register with the new Container (if any)
+ if ((this.container != null) && (this.container instanceof Context)) {
+ setReloadable( ((Context) this.container).getReloadable() );
+ ((Context) this.container).addPropertyChangeListener(this);
+ }
+
+ }
+
+
+ /**
+ * Return the "follow standard delegation model" flag used to configure
+ * our ClassLoader.
+ */
+ public boolean getDelegate() {
+
+ return (this.delegate);
+
+ }
+
+
+ /**
+ * Set the "follow standard delegation model" flag used to configure
+ * our ClassLoader.
+ *
+ * @param delegate The new flag
+ */
+ public void setDelegate(boolean delegate) {
+
+ boolean oldDelegate = this.delegate;
+ this.delegate = delegate;
+ support.firePropertyChange("delegate", new Boolean(oldDelegate),
+ new Boolean(this.delegate));
+
+ }
+
+
+ /**
+ * Return descriptive information about this Loader implementation and
+ * the corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+
+ /**
+ * Return the ClassLoader class name.
+ */
+ public String getLoaderClass() {
+
+ return (this.loaderClass);
+
+ }
+
+
+ /**
+ * Set the ClassLoader class name.
+ *
+ * @param loaderClass The new ClassLoader class name
+ */
+ public void setLoaderClass(String loaderClass) {
+
+ this.loaderClass = loaderClass;
+
+ }
+
+
+ /**
+ * Return the reloadable flag for this Loader.
+ */
+ public boolean getReloadable() {
+
+ return (this.reloadable);
+
+ }
+
+
+ /**
+ * Set the reloadable flag for this Loader.
+ *
+ * @param reloadable The new reloadable flag
+ */
+ public void setReloadable(boolean reloadable) {
+
+ // Process this property change
+ boolean oldReloadable = this.reloadable;
+ this.reloadable = reloadable;
+ support.firePropertyChange("reloadable",
+ new Boolean(oldReloadable),
+ new Boolean(this.reloadable));
+
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Add a property change listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+
+ support.addPropertyChangeListener(listener);
+
+ }
+
+
+ /**
+ * Add a new repository to the set of repositories for this class loader.
+ *
+ * @param repository Repository to be added
+ */
+ public void addRepository(String repository) {
+
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.addRepository", repository));
+
+ for (int i = 0; i < repositories.length; i++) {
+ if (repository.equals(repositories[i]))
+ return;
+ }
+ String results[] = new String[repositories.length + 1];
+ for (int i = 0; i < repositories.length; i++)
+ results[i] = repositories[i];
+ results[repositories.length] = repository;
+ repositories = results;
+
+ if (started && (classLoader != null)) {
+ classLoader.addRepository(repository);
+ if( loaderRepositories != null ) loaderRepositories.add(repository);
+ setClassPath();
+ }
+
+ }
+
+
+ /**
+ * Execute a periodic task, such as reloading, etc. This method will be
+ * invoked inside the classloading context of this container. Unexpected
+ * throwables will be caught and logged.
+ */
+ public void backgroundProcess() {
+ if (reloadable && modified()) {
+ try {
+ Thread.currentThread().setContextClassLoader
+ (LegacyWebappLoader.class.getClassLoader());
+ if (container instanceof StandardContext) {
+ ((StandardContext) container).reload();
+ }
+ } finally {
+ if (container.getLoader() != null) {
+ Thread.currentThread().setContextClassLoader
+ (container.getLoader().getClassLoader());
+ }
+ }
+ } else {
+ closeJARs(false);
+ }
+ }
+
+
+ /**
+ * Return the set of repositories defined for this class loader.
+ * If none are defined, a zero-length array is returned.
+ * For security reason, returns a clone of the Array (since
+ * String are immutable).
+ */
+ public String[] findRepositories() {
+
+ return ((String[])repositories.clone());
+
+ }
+
+ public String[] getRepositories() {
+ return ((String[])repositories.clone());
+ }
+
+ /** Extra repositories for this loader
+ */
+ public String getRepositoriesString() {
+ StringBuffer sb=new StringBuffer();
+ for( int i=0; i<repositories.length ; i++ ) {
+ sb.append( repositories[i]).append(":");
+ }
+ return sb.toString();
+ }
+
+ public String[] findLoaderRepositories() {
+ return getLoaderRepositories();
+ }
+
+ public String[] getLoaderRepositories() {
+ if( loaderRepositories==null ) return null;
+ String res[]=new String[ loaderRepositories.size()];
+ loaderRepositories.toArray(res);
+ return res;
+ }
+
+ public String getLoaderRepositoriesString() {
+ String repositories[]=getLoaderRepositories();
+ StringBuffer sb=new StringBuffer();
+ for( int i=0; i<repositories.length ; i++ ) {
+ sb.append( repositories[i]).append(":");
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Classpath, as set in org.apache.catalina.jsp_classpath context
+ * property
+ *
+ * @return The classpath
+ */
+ public String getClasspath() {
+ return classpath;
+ }
+
+
+ /**
+ * Has the internal repository associated with this Loader been modified,
+ * such that the loaded classes should be reloaded?
+ */
+ public boolean modified() {
+
+ return (classLoader.modified());
+
+ }
+
+
+ /**
+ * Used to periodically signal to the classloader to release JAR resources.
+ */
+ public void closeJARs(boolean force) {
+ if (classLoader !=null){
+ classLoader.closeJARs(force);
+ }
+ }
+
+
+ /**
+ * Remove a property change listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+
+ support.removePropertyChangeListener(listener);
+
+ }
+
+
+ /**
+ * Return a String representation of this component.
+ */
+ public String toString() {
+
+ StringBuffer sb = new StringBuffer("WebappLoader[");
+ if (container != null)
+ sb.append(container.getName());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+
+ lifecycle.addLifecycleListener(listener);
+
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+
+ return lifecycle.findLifecycleListeners();
+
+ }
+
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+
+ lifecycle.removeLifecycleListener(listener);
+
+ }
+
+ private boolean initialized=false;
+
+ public void init() {
+ initialized=true;
+
+ if( oname==null ) {
+ // not registered yet - standalone or API
+ if( container instanceof StandardContext) {
+ // Register ourself. The container must be a webapp
+ try {
+ StandardContext ctx=(StandardContext)container;
+ Engine eng=(Engine)ctx.getParent().getParent();
+ String path = ctx.getPath();
+ if (path.equals("")) {
+ path = "/";
+ }
+ oname=new ObjectName(ctx.getEngineName() +
":type=Loader,path=" +
+ path + ",host=" + ctx.getParent().getName());
+ Registry.getRegistry(null, null).registerComponent(this, oname,
null);
+ controller=oname;
+ } catch (Exception e) {
+ log.error("Error registering loader", e );
+ }
+ }
+ }
+
+ if( container == null ) {
+ // JMX created the loader
+ // TODO
+
+ }
+ }
+
+ public void destroy() {
+ if( controller==oname ) {
+ // Self-registration, undo it
+ Registry.getRegistry(null, null).unregisterComponent(oname);
+ oname = null;
+ }
+ initialized = false;
+
+ }
+
+ /**
+ * Start this component, initializing our associated class loader.
+ *
+ * @exception LifecycleException if a lifecycle error occurs
+ */
+ public void start() throws LifecycleException {
+ // Validate and update our current component state
+ if( ! initialized ) init();
+ if (started)
+ throw new LifecycleException
+ (sm.getString("webappLoader.alreadyStarted"));
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.starting"));
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+
+ if (container.getResources() == null) {
+ log.info("No resources for " + container);
+ return;
+ }
+ // Register a stream handler factory for the JNDI protocol
+ URLStreamHandlerFactory streamHandlerFactory =
+ new DirContextURLStreamHandlerFactory();
+ if (first) {
+ first = false;
+ try {
+ URL.setURLStreamHandlerFactory(streamHandlerFactory);
+ } catch (Exception e) {
+ // Log and continue anyway, this is not critical
+ log.error("Error registering jndi stream handler", e);
+ } catch (Throwable t) {
+ // This is likely a dual registration, ignore
+ }
+ }
+
+ // Construct a class loader based on our current repositories list
+ try {
+
+ classLoader = createClassLoader();
+ classLoader.setResources(container.getResources());
+ classLoader.setDelegate(this.delegate);
+ if (container instanceof StandardContext)
+ classLoader.setAntiJARLocking(((StandardContext)
container).getAntiJARLocking());
+
+ for (int i = 0; i < repositories.length; i++) {
+ classLoader.addRepository(repositories[i]);
+ }
+
+ // Configure our repositories
+ setRepositories();
+ setClassPath();
+
+ setPermissions();
+
+ if (classLoader instanceof Lifecycle)
+ ((Lifecycle) classLoader).start();
+
+ // Binding the Webapp class loader to the directory context
+ DirContextURLStreamHandler.bind
+ ((ClassLoader) classLoader, this.container.getResources());
+
+ StandardContext ctx=(StandardContext)container;
+ Engine eng=(Engine)ctx.getParent().getParent();
+ String path = ctx.getPath();
+ if (path.equals("")) {
+ path = "/";
+ }
+ ObjectName cloname = new ObjectName
+ (ctx.getEngineName() + ":type=LegacyWebappClassLoader,path="
+ + path + ",host=" + ctx.getParent().getName());
+ Registry.getRegistry(null, null)
+ .registerComponent(classLoader, cloname, null);
+
+ } catch (Throwable t) {
+ log.error( "LifecycleException ", t );
+ throw new LifecycleException("start: ", t);
+ }
+
+ }
+
+
+ /**
+ * Stop this component, finalizing our associated class loader.
+ *
+ * @exception LifecycleException if a lifecycle error occurs
+ */
+ public void stop() throws LifecycleException {
+
+ // Validate and update our current component state
+ if (!started)
+ throw new LifecycleException
+ (sm.getString("webappLoader.notStarted"));
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.stopping"));
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+
+ // Remove context attributes as appropriate
+ if (container instanceof Context) {
+ ServletContext servletContext =
+ ((Context) container).getServletContext();
+ servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
+ }
+
+ // Throw away our current class loader
+ if (classLoader instanceof Lifecycle)
+ ((Lifecycle) classLoader).stop();
+ DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
+
+ try {
+ StandardContext ctx=(StandardContext)container;
+ Engine eng=(Engine)ctx.getParent().getParent();
+ String path = ctx.getPath();
+ if (path.equals("")) {
+ path = "/";
+ }
+ ObjectName cloname = new ObjectName
+ (ctx.getEngineName() + ":type=LegacyWebappClassLoader,path="
+ + path + ",host=" + ctx.getParent().getName());
+ Registry.getRegistry(null, null).unregisterComponent(cloname);
+ } catch (Throwable t) {
+ log.error( "LifecycleException ", t );
+ }
+
+ classLoader = null;
+
+ destroy();
+
+ }
+
+
+ // ----------------------------------------- PropertyChangeListener Methods
+
+
+ /**
+ * Process property change events from our associated Context.
+ *
+ * @param event The property change event that has occurred
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+
+ // Validate the source of this event
+ if (!(event.getSource() instanceof Context))
+ return;
+ Context context = (Context) event.getSource();
+
+ // Process a relevant property change
+ if (event.getPropertyName().equals("reloadable")) {
+ try {
+ setReloadable
+ ( ((Boolean) event.getNewValue()).booleanValue() );
+ } catch (NumberFormatException e) {
+ log.error(sm.getString("webappLoader.reloadable",
+ event.getNewValue().toString()));
+ }
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Private Methods
+
+
+ /**
+ * Create associated classLoader.
+ */
+ private LegacyWebappClassLoader createClassLoader()
+ throws Exception {
+
+ Class clazz = Class.forName(loaderClass);
+ LegacyWebappClassLoader classLoader = null;
+
+ if (parentClassLoader == null) {
+ parentClassLoader = container.getParentClassLoader();
+ }
+ Class[] argTypes = { ClassLoader.class };
+ Object[] args = { parentClassLoader };
+ Constructor constr = clazz.getConstructor(argTypes);
+ classLoader = (LegacyWebappClassLoader) constr.newInstance(args);
+
+ return classLoader;
+
+ }
+
+
+ /**
+ * Configure associated class loader permissions.
+ */
+ private void setPermissions() {
+
+ if (!Globals.IS_SECURITY_ENABLED)
+ return;
+ if (!(container instanceof Context))
+ return;
+
+ // Tell the class loader the root of the context
+ ServletContext servletContext =
+ ((Context) container).getServletContext();
+
+ // Assigning permissions for the work directory
+ File workDir =
+ (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+ if (workDir != null) {
+ try {
+ String workDirPath = workDir.getCanonicalPath();
+ classLoader.addPermission
+ (new FilePermission(workDirPath, "read,write"));
+ classLoader.addPermission
+ (new FilePermission(workDirPath + File.separator + "-",
+ "read,write,delete"));
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ try {
+
+ URL rootURL = servletContext.getResource("/");
+ classLoader.addPermission(rootURL);
+
+ String contextRoot = servletContext.getRealPath("/");
+ if (contextRoot != null) {
+ try {
+ contextRoot = (new File(contextRoot)).getCanonicalPath();
+ classLoader.addPermission(contextRoot);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ URL classesURL = servletContext.getResource("/WEB-INF/classes/");
+ classLoader.addPermission(classesURL);
+ URL libURL = servletContext.getResource("/WEB-INF/lib/");
+ classLoader.addPermission(libURL);
+
+ if (contextRoot != null) {
+
+ if (libURL != null) {
+ File rootDir = new File(contextRoot);
+ File libDir = new File(rootDir, "WEB-INF/lib/");
+ try {
+ String path = libDir.getCanonicalPath();
+ classLoader.addPermission(path);
+ } catch (IOException e) {
+ }
+ }
+
+ } else {
+
+ if (workDir != null) {
+ if (libURL != null) {
+ File libDir = new File(workDir, "WEB-INF/lib/");
+ try {
+ String path = libDir.getCanonicalPath();
+ classLoader.addPermission(path);
+ } catch (IOException e) {
+ }
+ }
+ if (classesURL != null) {
+ File classesDir = new File(workDir,
"WEB-INF/classes/");
+ try {
+ String path = classesDir.getCanonicalPath();
+ classLoader.addPermission(path);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ }
+
+ } catch (MalformedURLException e) {
+ }
+
+ }
+
+
+ /**
+ * Configure the repositories for our class loader, based on the
+ * associated Context.
+ */
+ private void setRepositories() {
+
+ if (!(container instanceof Context))
+ return;
+ ServletContext servletContext =
+ ((Context) container).getServletContext();
+ if (servletContext == null)
+ return;
+
+ loaderRepositories=new ArrayList();
+ // Loading the work directory
+ File workDir =
+ (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+ if (workDir == null) {
+ log.info("No work dir for " + servletContext);
+ }
+
+ if( log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.deploy",
workDir.getAbsolutePath()));
+
+ classLoader.setWorkDir(workDir);
+
+ DirContext resources = container.getResources();
+
+ // Setting up the class repository (/WEB-INF/classes), if it exists
+
+ String classesPath = "/WEB-INF/classes";
+ DirContext classes = null;
+
+ try {
+ Object object = resources.lookup(classesPath);
+ if (object instanceof DirContext) {
+ classes = (DirContext) object;
+ }
+ } catch(NamingException e) {
+ // Silent catch: it's valid that no /WEB-INF/classes collection
+ // exists
+ }
+
+ if (classes != null) {
+
+ File classRepository = null;
+
+ String absoluteClassesPath =
+ servletContext.getRealPath(classesPath);
+
+ if (absoluteClassesPath != null) {
+
+ classRepository = new File(absoluteClassesPath);
+
+ } else {
+
+ classRepository = new File(workDir, classesPath);
+ classRepository.mkdirs();
+ copyDir(classes, classRepository);
+
+ }
+
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.classDeploy",
classesPath,
+ classRepository.getAbsolutePath()));
+
+
+ // Adding the repository to the class loader
+ classLoader.addRepository(classesPath + "/", classRepository);
+ loaderRepositories.add(classRepository.getAbsolutePath());
+
+ }
+
+ // Setting up the JAR repository (/WEB-INF/lib), if it exists
+
+ String libPath = "/WEB-INF/lib";
+
+ classLoader.setJarPath(libPath);
+
+ DirContext libDir = null;
+ // Looking up directory /WEB-INF/lib in the context
+ try {
+ Object object = resources.lookup(libPath);
+ if (object instanceof DirContext)
+ libDir = (DirContext) object;
+ } catch(NamingException e) {
+ // Silent catch: it's valid that no /WEB-INF/lib collection
+ // exists
+ }
+
+ if (libDir != null) {
+
+ boolean copyJars = false;
+ String absoluteLibPath = servletContext.getRealPath(libPath);
+
+ File destDir = null;
+
+ if (absoluteLibPath != null) {
+ destDir = new File(absoluteLibPath);
+ } else {
+ copyJars = true;
+ destDir = new File(workDir, libPath);
+ destDir.mkdirs();
+ }
+
+ // Looking up directory /WEB-INF/lib in the context
+ try {
+ NamingEnumeration enumeration = resources.listBindings(libPath);
+ while (enumeration.hasMoreElements()) {
+
+ Binding binding = (Binding) enumeration.nextElement();
+ String filename = libPath + "/" + binding.getName();
+ if (!filename.endsWith(".jar"))
+ continue;
+
+ // Copy JAR in the work directory, always (the JAR file
+ // would get locked otherwise, which would make it
+ // impossible to update it or remove it at runtime)
+ File destFile = new File(destDir, binding.getName());
+
+ if( log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.jarDeploy", filename,
+ destFile.getAbsolutePath()));
+
+ Object obj = binding.getObject();
+ if (!(obj instanceof Resource))
+ continue;
+
+ Resource jarResource = (Resource) obj;
+ if (copyJars) {
+ if (!copy(jarResource.streamContent(),
+ new FileOutputStream(destFile)))
+ continue;
+ }
+
+ try {
+ JarFile jarFile = new JarFile(destFile);
+ classLoader.addJar(filename, jarFile, destFile);
+ } catch (Exception ex) {
+ // Catch the exception if there is an empty jar file
+ // Should ignore and continute loading other jar files
+ // in the dir
+ }
+
+ loaderRepositories.add(destFile.getAbsolutePath());
+
+ }
+ } catch (NamingException e) {
+ // Silent catch: it's valid that no /WEB-INF/lib directory
+ // exists
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Set the appropriate context attribute for our class path. This
+ * is required only because Jasper depends on it.
+ */
+ private void setClassPath() {
+
+ // Validate our current state information
+ if (!(container instanceof Context))
+ return;
+ ServletContext servletContext =
+ ((Context) container).getServletContext();
+ if (servletContext == null)
+ return;
+
+ if (container instanceof StandardContext) {
+ String baseClasspath =
+ ((StandardContext) container).getCompilerClasspath();
+ if (baseClasspath != null) {
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ baseClasspath);
+ return;
+ }
+ }
+
+ StringBuffer classpath = new StringBuffer();
+
+ // Assemble the class path information from our class loader chain
+ ClassLoader loader = getClassLoader();
+ int layers = 0;
+ int n = 0;
+ while (loader != null) {
+ if (!(loader instanceof URLClassLoader)) {
+ String cp=getClasspath( loader );
+ if( cp==null ) {
+ log.info( "Unknown loader " + loader + " " +
loader.getClass());
+ break;
+ } else {
+ if (n > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(cp);
+ n++;
+ }
+ break;
+ //continue;
+ }
+ URL repositories[] =
+ ((URLClassLoader) loader).getURLs();
+ for (int i = 0; i < repositories.length; i++) {
+ String repository = repositories[i].toString();
+ if (repository.startsWith("file://"))
+ repository = repository.substring(7);
+ else if (repository.startsWith("file:"))
+ repository = repository.substring(5);
+ else if (repository.startsWith("jndi:"))
+ repository =
+ servletContext.getRealPath(repository.substring(5));
+ else
+ continue;
+ if (repository == null)
+ continue;
+ if (n > 0)
+ classpath.append(File.pathSeparator);
+ classpath.append(repository);
+ n++;
+ }
+ loader = loader.getParent();
+ layers++;
+ }
+
+ this.classpath=classpath.toString();
+
+ // Store the assembled class path as a servlet context attribute
+ servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
+ classpath.toString());
+
+ }
+
+ // try to extract the classpath from a loader that is not URLClassLoader
+ private String getClasspath( ClassLoader loader ) {
+ try {
+ Method m=loader.getClass().getMethod("getClasspath", new Class[]
{});
+ if( log.isTraceEnabled())
+ log.trace("getClasspath " + m );
+ if( m==null ) return null;
+ Object o=m.invoke( loader, new Object[] {} );
+ if( log.isDebugEnabled() )
+ log.debug("gotClasspath " + o);
+ if( o instanceof String )
+ return (String)o;
+ return null;
+ } catch( Exception ex ) {
+ if (log.isDebugEnabled())
+ log.debug("getClasspath ", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Copy directory.
+ */
+ private boolean copyDir(DirContext srcDir, File destDir) {
+
+ try {
+
+ NamingEnumeration enumeration = srcDir.list("");
+ while (enumeration.hasMoreElements()) {
+ NameClassPair ncPair =
+ (NameClassPair) enumeration.nextElement();
+ String name = ncPair.getName();
+ Object object = srcDir.lookup(name);
+ File currentFile = new File(destDir, name);
+ if (object instanceof Resource) {
+ InputStream is = ((Resource) object).streamContent();
+ OutputStream os = new FileOutputStream(currentFile);
+ if (!copy(is, os))
+ return false;
+ } else if (object instanceof InputStream) {
+ OutputStream os = new FileOutputStream(currentFile);
+ if (!copy((InputStream) object, os))
+ return false;
+ } else if (object instanceof DirContext) {
+ currentFile.mkdir();
+ copyDir((DirContext) object, currentFile);
+ }
+ }
+
+ } catch (NamingException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ /**
+ * Copy a file to the specified temp directory. This is required only
+ * because Jasper depends on it.
+ */
+ private boolean copy(InputStream is, OutputStream os) {
+
+ try {
+ byte[] buf = new byte[4096];
+ while (true) {
+ int len = is.read(buf);
+ if (len < 0)
+ break;
+ os.write(buf, 0, len);
+ }
+ is.close();
+ os.close();
+ } catch (IOException e) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ private static org.jboss.logging.Logger log=
+ org.jboss.logging.Logger.getLogger( LegacyWebappLoader.class );
+
+ private ObjectName oname;
+ private MBeanServer mserver;
+ private String domain;
+ private ObjectName controller;
+
+ public ObjectName preRegister(MBeanServer server,
+ ObjectName name) throws Exception {
+ oname=name;
+ mserver=server;
+ domain=name.getDomain();
+
+ return name;
+ }
+
+ public void postRegister(Boolean registrationDone) {
+ }
+
+ public void preDeregister() throws Exception {
+ }
+
+ public void postDeregister() {
+ }
+
+ public ObjectName getController() {
+ return controller;
+ }
+
+ public void setController(ObjectName controller) {
+ this.controller = controller;
+ }
+
+}
Modified: trunk/java/org/apache/catalina/loader/WebappClassLoader.java
===================================================================
--- trunk/java/org/apache/catalina/loader/WebappClassLoader.java 2009-06-10 12:42:02 UTC
(rev 1099)
+++ trunk/java/org/apache/catalina/loader/WebappClassLoader.java 2009-06-10 22:40:29 UTC
(rev 1100)
@@ -1,18 +1,46 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
*
- *
http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 1999-2009 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
*/
@@ -20,6 +48,7 @@
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.IOException;
@@ -42,6 +71,7 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import java.util.jar.Attributes;
@@ -50,17 +80,15 @@
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;
-import javax.naming.NameClassPair;
-import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
+import org.apache.catalina.JarRepository;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.util.StringManager;
import org.apache.naming.JndiPermission;
-import org.apache.naming.resources.Resource;
import org.apache.naming.resources.ResourceAttributes;
import org.apache.tomcat.util.IntrospectionUtils;
@@ -91,8 +119,7 @@
* class is contained inside a JAR.
* <p>
* <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
- * the order they are added via the initial constructor and/or any subsequent
- * calls to <code>addRepository()</code> or
<code>addJar()</code>.
+ * the order they are specified in the JarRepository.
* <p>
* <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
* security is made unless a security manager is present.
@@ -154,6 +181,7 @@
* class loader without delegating first.
*/
protected static final String[] packageTriggers = {
+ "javax.servlet."
};
@@ -215,6 +243,12 @@
/**
+ * Associated Jar repository.
+ */
+ protected JarRepository repository = null;
+
+
+ /**
* Associated directory context giving access to the resources in this
* webapp.
*/
@@ -225,13 +259,13 @@
* The cache of ResourceEntry for classes and resources we have loaded,
* keyed by resource name.
*/
- protected HashMap resourceEntries = new HashMap();
+ protected HashMap<String, ResourceEntry> resourceEntries = new
HashMap<String, ResourceEntry>();
/**
* The list of not found resources.
*/
- protected HashMap notFoundResources = new HashMap();
+ protected HashSet<String> notFoundResources = new HashSet<String>();
/**
@@ -248,14 +282,14 @@
/**
* Last time a JAR was accessed.
*/
- protected long lastJarAccessed = 0L;
+ //protected long lastJarAccessed = 0L;
/**
* The list of local repositories, in the order they should be searched
* for locally loaded classes or resources.
*/
- protected String[] repositories = new String[0];
+ //protected String[] repositories = new String[0];
/**
@@ -269,34 +303,34 @@
* originally), but which is used to generate fake URLs should getURLs be
* called.
*/
- protected File[] files = new File[0];
+ //protected File[] files = new File[0];
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
- protected JarFile[] jarFiles = new JarFile[0];
+ //protected JarFile[] jarFiles = new JarFile[0];
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
- protected File[] jarRealFiles = new File[0];
+ //protected File[] jarRealFiles = new File[0];
/**
* The path which will be monitored for added Jar files.
*/
- protected String jarPath = null;
+ //protected String jarPath = null;
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
- protected String[] jarNames = new String[0];
+ //protected String[] jarNames = new String[0];
/**
@@ -360,7 +394,7 @@
/**
* Has external repositories.
*/
- protected boolean hasExternalRepositories = false;
+ //protected boolean hasExternalRepositories = false;
/**
* need conversion for properties files
@@ -377,23 +411,23 @@
// ------------------------------------------------------------- Properties
- /**
- * Get associated resources.
- */
- public DirContext getResources() {
+ public JarRepository getRepository() {
+ return repository;
+ }
- return this.resources;
+ public void setRepository(JarRepository repository) {
+ this.repository = repository;
}
- /**
- * Set associated resources.
- */
- public void setResources(DirContext resources) {
+ public DirContext getResources() {
+ return resources;
+ }
- this.resources = resources;
+ public void setResources(DirContext resources) {
+ this.resources = resources;
}
@@ -493,40 +527,21 @@
/**
- * Return the JAR path.
+ * Change the work directory.
*/
- public String getJarPath() {
-
- return this.jarPath;
-
+ public void setWorkDir(File workDir) {
+ this.loaderDir = new File(workDir, "loader");
}
-
/**
- * Change the Jar path.
+ * Utility method for use in subclasses.
+ * Must be called before Lifecycle methods to have any effect.
*/
- public void setJarPath(String jarPath) {
-
- this.jarPath = jarPath;
-
+ protected void setParentClassLoader(ClassLoader pcl) {
+ parent = pcl;
}
- /**
- * Change the work directory.
- */
- public void setWorkDir(File workDir) {
- this.loaderDir = new File(workDir, "loader");
- }
-
- /**
- * Utility method for use in subclasses.
- * Must be called before Lifecycle methods to have any effect.
- */
- protected void setParentClassLoader(ClassLoader pcl) {
- parent = pcl;
- }
-
// ------------------------------------------------------- Reloader Methods
@@ -542,158 +557,24 @@
*/
public void addRepository(String repository) {
+ // FIXME: remove
// Ignore any of the standard repositories, as they are set up using
// either addJar or addRepository
if (repository.startsWith("/WEB-INF/lib")
|| repository.startsWith("/WEB-INF/classes"))
return;
- // Add this repository to our underlying class loader
- try {
- URL url = new URL(repository);
- super.addURL(url);
- hasExternalRepositories = true;
- repositoryURLs = null;
- } catch (MalformedURLException e) {
- IllegalArgumentException iae = new IllegalArgumentException
- ("Invalid repository: " + repository);
- iae.initCause(e);
- throw iae;
- }
-
}
/**
- * Add a new repository to the set of places this ClassLoader can look for
- * classes to be loaded.
- *
- * @param repository Name of a source of classes to be loaded, such as a
- * directory pathname, a JAR file pathname, or a ZIP file pathname
- *
- * @exception IllegalArgumentException if the specified repository is
- * invalid or does not exist
- */
- synchronized void addRepository(String repository, File file) {
-
- // Note : There should be only one (of course), but I think we should
- // keep this a bit generic
-
- if (repository == null)
- return;
-
- if (log.isDebugEnabled())
- log.debug("addRepository(" + repository + ")");
-
- int i;
-
- // Add this repository to our internal list
- String[] result = new String[repositories.length + 1];
- for (i = 0; i < repositories.length; i++) {
- result[i] = repositories[i];
- }
- result[repositories.length] = repository;
- repositories = result;
-
- // Add the file to the list
- File[] result2 = new File[files.length + 1];
- for (i = 0; i < files.length; i++) {
- result2[i] = files[i];
- }
- result2[files.length] = file;
- files = result2;
-
- }
-
-
- synchronized void addJar(String jar, JarFile jarFile, File file)
- throws IOException {
-
- if (jar == null)
- return;
- if (jarFile == null)
- return;
- if (file == null)
- return;
-
- if (log.isDebugEnabled())
- log.debug("addJar(" + jar + ")");
-
- int i;
-
- if ((jarPath != null) && (jar.startsWith(jarPath))) {
-
- String jarName = jar.substring(jarPath.length());
- while (jarName.startsWith("/"))
- jarName = jarName.substring(1);
-
- String[] result = new String[jarNames.length + 1];
- for (i = 0; i < jarNames.length; i++) {
- result[i] = jarNames[i];
- }
- result[jarNames.length] = jarName;
- jarNames = result;
-
- }
-
- try {
-
- // Register the JAR for tracking
-
- long lastModified =
- ((ResourceAttributes) resources.getAttributes(jar))
- .getLastModified();
-
- String[] result = new String[paths.length + 1];
- for (i = 0; i < paths.length; i++) {
- result[i] = paths[i];
- }
- result[paths.length] = jar;
- paths = result;
-
- long[] result3 = new long[lastModifiedDates.length + 1];
- for (i = 0; i < lastModifiedDates.length; i++) {
- result3[i] = lastModifiedDates[i];
- }
- result3[lastModifiedDates.length] = lastModified;
- lastModifiedDates = result3;
-
- } catch (NamingException e) {
- // Ignore
- }
-
- // If the JAR currently contains invalid classes, don't actually use it
- // for classloading
- if (!validateJarFile(file))
- return;
-
- JarFile[] result2 = new JarFile[jarFiles.length + 1];
- for (i = 0; i < jarFiles.length; i++) {
- result2[i] = jarFiles[i];
- }
- result2[jarFiles.length] = jarFile;
- jarFiles = result2;
-
- // Add the file to the list
- File[] result4 = new File[jarRealFiles.length + 1];
- for (i = 0; i < jarRealFiles.length; i++) {
- result4[i] = jarRealFiles[i];
- }
- result4[jarRealFiles.length] = file;
- jarRealFiles = result4;
- }
-
-
- /**
* Return a String array of the current repositories for this class
* loader. If there are no repositories, a zero-length array is
* returned.For security reason, returns a clone of the Array (since
* String are immutable).
*/
public String[] findRepositories() {
-
- return ((String[])repositories.clone());
-
+ return null;
}
@@ -706,16 +587,8 @@
if (log.isDebugEnabled())
log.debug("modified()");
- // Checking for modified loaded resources
- int length = paths.length;
+ int length = lastModifiedDates.length;
- // A rare race condition can occur in the updates of the two arrays
- // It's totally ok if the latest class added is not checked (it will
- // be checked the next time
- int length2 = lastModifiedDates.length;
- if (length > length2)
- length = length2;
-
for (int i = 0; i < length; i++) {
try {
long lastModified =
@@ -735,56 +608,6 @@
}
}
- length = jarNames.length;
-
- // Check if JARs have been added or removed
- if (getJarPath() != null) {
-
- try {
- NamingEnumeration enumeration = resources.list(getJarPath());
- int i = 0;
- while (enumeration.hasMoreElements() && (i < length)) {
- NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
- String name = ncPair.getName();
- // Ignore non JARs present in the lib folder
- if (!name.endsWith(".jar"))
- continue;
- if (!name.equals(jarNames[i])) {
- // Missing JAR
- log.info(" Additional JARs have been added : '"
- + name + "'");
- return (true);
- }
- i++;
- }
- if (enumeration.hasMoreElements()) {
- while (enumeration.hasMoreElements()) {
- NameClassPair ncPair =
- (NameClassPair) enumeration.nextElement();
- String name = ncPair.getName();
- // Additional non-JAR files are allowed
- if (name.endsWith(".jar")) {
- // There was more JARs
- log.info(" Additional JARs have been added");
- return (true);
- }
- }
- } else if (i < jarNames.length) {
- // There was less JARs
- log.info(" Additional JARs have been added");
- return (true);
- }
- } catch (NamingException e) {
- if (log.isDebugEnabled())
- log.debug(" Failed tracking modifications of '"
- + getJarPath() + "'");
- } catch (ClassCastException e) {
- log.error(" Failed tracking modifications of '"
- + getJarPath() + "' : " + e.getMessage());
- }
-
- }
-
// No classes have been modified
return (false);
@@ -800,14 +623,6 @@
sb.append(" delegate: ");
sb.append(delegate);
sb.append("\r\n");
- sb.append(" repositories:\r\n");
- if (repositories != null) {
- for (int i = 0; i < repositories.length; i++) {
- sb.append(" ");
- sb.append(repositories[i]);
- sb.append("\r\n");
- }
- }
if (this.parent != null) {
sb.append("----------> Parent Classloader:\r\n");
sb.append(this.parent.toString());
@@ -821,16 +636,6 @@
// ---------------------------------------------------- ClassLoader Methods
- /**
- * Add the specified URL to the classloader.
- */
- protected void addURL(URL url) {
- super.addURL(url);
- hasExternalRepositories = true;
- repositoryURLs = null;
- }
-
-
/**
* Find the specified class in our local repositories, if possible. If
* not found, throw <code>ClassNotFoundException</code>.
@@ -868,43 +673,17 @@
// Ask our superclass to locate this class, if possible
// (throws ClassNotFoundException if it is not found)
Class clazz = null;
+ if (log.isTraceEnabled())
+ log.trace(" findClassInternal(" + name + ")");
try {
+ clazz = findClassInternal(name);
+ } catch(ClassNotFoundException cnfe) {
+ throw cnfe;
+ } catch(AccessControlException ace) {
+ throw new ClassNotFoundException(name, ace);
+ } catch (RuntimeException e) {
if (log.isTraceEnabled())
- log.trace(" findClassInternal(" + name + ")");
- try {
- clazz = findClassInternal(name);
- } catch(ClassNotFoundException cnfe) {
- if (!hasExternalRepositories) {
- throw cnfe;
- }
- } catch(AccessControlException ace) {
- throw new ClassNotFoundException(name, ace);
- } catch (RuntimeException e) {
- if (log.isTraceEnabled())
- log.trace(" -->RuntimeException Rethrown", e);
- throw e;
- }
- if ((clazz == null) && hasExternalRepositories) {
- try {
- synchronized (this) {
- clazz = super.findClass(name);
- }
- } catch(AccessControlException ace) {
- throw new ClassNotFoundException(name, ace);
- } catch (RuntimeException e) {
- if (log.isTraceEnabled())
- log.trace(" -->RuntimeException Rethrown", e);
- throw e;
- }
- }
- if (clazz == null) {
- if (log.isDebugEnabled())
- log.debug(" --> Returning ClassNotFoundException");
- throw new ClassNotFoundException(name);
- }
- } catch (ClassNotFoundException e) {
- if (log.isTraceEnabled())
- log.trace(" --> Passing on ClassNotFoundException");
+ log.trace(" -->RuntimeException Rethrown", e);
throw e;
}
@@ -940,9 +719,6 @@
url = entry.source;
}
- if ((url == null) && hasExternalRepositories)
- url = super.findResource(name);
-
if (log.isDebugEnabled()) {
if (url != null)
log.debug(" --> Returning '" + url.toString() +
"'");
@@ -970,56 +746,32 @@
Vector result = new Vector();
- int jarFilesLength = jarFiles.length;
- int repositoriesLength = repositories.length;
+ File[] repositories = repository.findExplodedJars();
+ JarFile[] jarFiles = repository.findJars();
- int i;
-
// Looking at the repositories
- for (i = 0; i < repositoriesLength; i++) {
- try {
- String fullPath = repositories[i] + name;
- resources.lookup(fullPath);
- // Note : Not getting an exception here means the resource was
- // found
- try {
- result.addElement(getURI(new File(files[i], name)));
- } catch (MalformedURLException e) {
- // Ignore
- }
- } catch (NamingException e) {
+ for (int i = 0; i < repositories.length; i++) {
+ File resource = new File(repositories[i], name);
+ if (resource.exists()) {
+ result.addElement(getURI(resource));
}
}
// Looking at the JAR files
- synchronized (jarFiles) {
- if (openJARs()) {
- for (i = 0; i < jarFilesLength; i++) {
- JarEntry jarEntry = jarFiles[i].getJarEntry(name);
- if (jarEntry != null) {
- try {
- String jarFakeUrl = getURI(jarRealFiles[i]).toString();
- jarFakeUrl = "jar:" + jarFakeUrl + "!/" +
name;
- result.addElement(new URL(jarFakeUrl));
- } catch (MalformedURLException e) {
- // Ignore
- }
- }
+ for (int i = 0; i < jarFiles.length; i++) {
+ JarEntry jarEntry = jarFiles[i].getJarEntry(name);
+ if (jarEntry != null) {
+ File jarFile = new File(jarFiles[i].getName());
+ try {
+ String jarFakeUrl = getURI(jarFile).toString();
+ jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
+ result.addElement(new URL(jarFakeUrl));
+ } catch (MalformedURLException e) {
+ // Ignore
}
}
}
- // Adding the results of a call to the superclass
- if (hasExternalRepositories) {
-
- Enumeration otherResourcePaths = super.findResources(name);
-
- while (otherResourcePaths.hasMoreElements()) {
- result.addElement(otherResourcePaths.nextElement());
- }
-
- }
-
return result.elements();
}
@@ -1161,12 +913,6 @@
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);
}
@@ -1196,22 +942,6 @@
/**
- * Load the class with the specified name. This method searches for
- * classes in the same manner as <code>loadClass(String, boolean)</code>
- * with <code>false</code> as the second argument.
- *
- * @param name Name of the class to be loaded
- *
- * @exception ClassNotFoundException if the class was not found
- */
- public Class loadClass(String name) throws ClassNotFoundException {
-
- return (loadClass(name, false));
-
- }
-
-
- /**
* Load the class with the specified name, searching using the following
* algorithm until it finds and returns the class. If the class cannot
* be found, returns <code>ClassNotFoundException</code>.
@@ -1236,7 +966,7 @@
*
* @exception ClassNotFoundException if the class was not found
*/
- public Class loadClass(String name, boolean resolve)
+ public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (log.isDebugEnabled())
@@ -1410,21 +1140,19 @@
URL[] external = super.getURLs();
- int filesLength = files.length;
- int jarFilesLength = jarRealFiles.length;
- int length = filesLength + jarFilesLength + external.length;
- int i;
+ File[] repositories = repository.findExplodedJars();
+ JarFile[] jarFiles = repository.findJars();
+ int length = repositories.length + jarFiles.length;
+
try {
URL[] urls = new URL[length];
- for (i = 0; i < length; i++) {
- if (i < filesLength) {
- urls[i] = getURL(files[i], true);
- } else if (i < filesLength + jarFilesLength) {
- urls[i] = getURL(jarRealFiles[i - filesLength], true);
+ for (int i = 0; i < length; i++) {
+ if (i < repositories.length) {
+ urls[i] = getURL(repositories[i], true);
} else {
- urls[i] = external[i - filesLength - jarFilesLength];
+ urls[i] = getURL(new File(jarFiles[i -
repositories.length].getName()), true);
}
}
@@ -1503,37 +1231,14 @@
started = false;
- int length = files.length;
- for (int i = 0; i < length; i++) {
- files[i] = null;
- }
-
- length = jarFiles.length;
- for (int i = 0; i < length; i++) {
- try {
- if (jarFiles[i] != null) {
- jarFiles[i].close();
- }
- } catch (IOException e) {
- // Ignore
- }
- jarFiles[i] = null;
- }
-
notFoundResources.clear();
resourceEntries.clear();
resources = null;
- repositories = null;
repositoryURLs = null;
- files = null;
- jarFiles = null;
- jarRealFiles = null;
- jarPath = null;
- jarNames = null;
lastModifiedDates = null;
paths = null;
- hasExternalRepositories = false;
parent = null;
+ repository = null;
permissionList.clear();
loaderPC.clear();
@@ -1545,33 +1250,6 @@
}
- /**
- * Used to periodically signal to the classloader to release
- * JAR resources.
- */
- public void closeJARs(boolean force) {
- if (jarFiles.length > 0) {
- synchronized (jarFiles) {
- if (force || (System.currentTimeMillis()
- > (lastJarAccessed + 90000))) {
- for (int i = 0; i < jarFiles.length; i++) {
- try {
- if (jarFiles[i] != null) {
- jarFiles[i].close();
- jarFiles[i] = null;
- }
- } catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.debug("Failed to close JAR", e);
- }
- }
- }
- }
- }
- }
- }
-
-
// ------------------------------------------------------ Protected Methods
@@ -1720,29 +1398,6 @@
/**
- * Used to periodically signal to the classloader to release JAR resources.
- */
- protected boolean openJARs() {
- if (started && (jarFiles.length > 0)) {
- lastJarAccessed = System.currentTimeMillis();
- if (jarFiles[0] == null) {
- for (int i = 0; i < jarFiles.length; i++) {
- try {
- jarFiles[i] = new JarFile(jarRealFiles[i]);
- } catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.debug("Failed to open JAR", e);
- }
- return false;
- }
- }
- }
- }
- return true;
- }
-
-
- /**
* Find specified class in local repositories.
*
* @return the loaded class, or null if the class isn't found
@@ -1876,219 +1531,198 @@
int contentLength = -1;
InputStream binaryStream = null;
- int jarFilesLength = jarFiles.length;
- int repositoriesLength = repositories.length;
+ File[] repositories = repository.findExplodedJars();
+ JarFile[] jarFiles = repository.findJars();
int i;
- Resource resource = null;
+ //Resource resource = null;
boolean fileNeedConvert = false;
- for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
- try {
+ for (i = 0; (entry == null) && (i < repositories.length); i++) {
- String fullPath = repositories[i] + path;
+ File resource = new File(repositories[i], path);
+ if (!resource.exists()) {
+ continue;
+ }
- Object lookupResult = resources.lookup(fullPath);
- if (lookupResult instanceof Resource) {
- resource = (Resource) lookupResult;
- }
+ // Note : Not getting an exception here means the resource was
+ // found
+ if (securityManager != null) {
+ PrivilegedAction dp =
+ new PrivilegedFindResource(repositories[i], path);
+ entry = (ResourceEntry)AccessController.doPrivileged(dp);
+ } else {
+ entry = findResourceInternal(repositories[i], path);
+ }
- // Note : Not getting an exception here means the resource was
- // found
- if (securityManager != null) {
- PrivilegedAction dp =
- new PrivilegedFindResource(files[i], path);
- entry = (ResourceEntry)AccessController.doPrivileged(dp);
- } else {
- entry = findResourceInternal(files[i], path);
- }
+ contentLength = (int) resource.length();
+ entry.lastModified = resource.lastModified();
- ResourceAttributes attributes =
- (ResourceAttributes) resources.getAttributes(fullPath);
- contentLength = (int) attributes.getContentLength();
- entry.lastModified = attributes.getLastModified();
+ try {
+ binaryStream = new FileInputStream(resource);
+ } catch (IOException e) {
+ return null;
+ }
- if (resource != null) {
+ if (needConvert) {
+ if (path.endsWith(".properties")) {
+ fileNeedConvert = true;
+ }
+ }
+ // Register the full path for modification checking
+ // Note: Only syncing on a 'constant' object is needed
+ synchronized (allPermission) {
- try {
- binaryStream = resource.streamContent();
- } catch (IOException e) {
- return null;
- }
+ int j;
- if (needConvert) {
- if (path.endsWith(".properties")) {
- fileNeedConvert = true;
- }
- }
+ long[] result2 =
+ new long[lastModifiedDates.length + 1];
+ for (j = 0; j < lastModifiedDates.length; j++) {
+ result2[j] = lastModifiedDates[j];
+ }
+ result2[lastModifiedDates.length] = entry.lastModified;
+ lastModifiedDates = result2;
- // Register the full path for modification checking
- // Note: Only syncing on a 'constant' object is needed
- synchronized (allPermission) {
-
- int j;
-
- long[] result2 =
- new long[lastModifiedDates.length + 1];
- for (j = 0; j < lastModifiedDates.length; j++) {
- result2[j] = lastModifiedDates[j];
- }
- result2[lastModifiedDates.length] = entry.lastModified;
- lastModifiedDates = result2;
-
- String[] result = new String[paths.length + 1];
- for (j = 0; j < paths.length; j++) {
- result[j] = paths[j];
- }
- result[paths.length] = fullPath;
- paths = result;
-
- }
-
+ String[] result = new String[paths.length + 1];
+ for (j = 0; j < paths.length; j++) {
+ result[j] = paths[j];
}
+ result[paths.length] = resource.getPath();
+ paths = result;
- } catch (NamingException e) {
}
+
}
- if ((entry == null) && (notFoundResources.containsKey(name)))
+ if ((entry == null) && (notFoundResources.contains(name)))
return null;
JarEntry jarEntry = null;
- synchronized (jarFiles) {
+ for (i = 0; (entry == null) && (i < jarFiles.length); i++) {
- if (!openJARs()) {
- return null;
- }
- for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
+ jarEntry = jarFiles[i].getJarEntry(path);
- jarEntry = jarFiles[i].getJarEntry(path);
+ if (jarEntry != null) {
- if (jarEntry != null) {
+ entry = new ResourceEntry();
+ try {
+ File jarFile = new File(jarFiles[i].getName());
+ entry.codeBase = getURL(jarFile, false);
+ String jarFakeUrl = getURI(jarFile).toString();
+ jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
+ entry.source = new URL(jarFakeUrl);
+ entry.lastModified = jarFile.lastModified();
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ contentLength = (int) jarEntry.getSize();
+ try {
+ entry.manifest = jarFiles[i].getManifest();
+ binaryStream = jarFiles[i].getInputStream(jarEntry);
+ } catch (IOException e) {
+ return null;
+ }
- entry = new ResourceEntry();
- try {
- entry.codeBase = getURL(jarRealFiles[i], false);
- String jarFakeUrl = getURI(jarRealFiles[i]).toString();
- jarFakeUrl = "jar:" + jarFakeUrl + "!/" +
path;
- entry.source = new URL(jarFakeUrl);
- entry.lastModified = jarRealFiles[i].lastModified();
- } catch (MalformedURLException e) {
- return null;
- }
- contentLength = (int) jarEntry.getSize();
- try {
- entry.manifest = jarFiles[i].getManifest();
- binaryStream = jarFiles[i].getInputStream(jarEntry);
- } catch (IOException e) {
- return null;
- }
-
- // Extract resources contained in JAR to the workdir
- if (antiJARLocking && !(path.endsWith(".class")))
{
- byte[] buf = new byte[1024];
- File resourceFile = new File
- (loaderDir, jarEntry.getName());
- if (!resourceFile.exists()) {
- Enumeration entries = jarFiles[i].entries();
- while (entries.hasMoreElements()) {
- JarEntry jarEntry2 =
- (JarEntry) entries.nextElement();
- if (!(jarEntry2.isDirectory())
- && (!jarEntry2.getName().endsWith
- (".class"))) {
- resourceFile = new File
- (loaderDir, jarEntry2.getName());
- resourceFile.getParentFile().mkdirs();
- FileOutputStream os = null;
- InputStream is = null;
+ // Extract resources contained in JAR to the workdir
+ if (antiJARLocking && !(path.endsWith(".class"))) {
+ byte[] buf = new byte[1024];
+ File resourceFile = new File
+ (loaderDir, jarEntry.getName());
+ if (!resourceFile.exists()) {
+ Enumeration entries = jarFiles[i].entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry2 =
+ (JarEntry) entries.nextElement();
+ if (!(jarEntry2.isDirectory())
+ &&
(!jarEntry2.getName().endsWith(".class"))) {
+ resourceFile = new File
+ (loaderDir, jarEntry2.getName());
+ resourceFile.getParentFile().mkdirs();
+ FileOutputStream os = null;
+ InputStream is = null;
+ try {
+ is = jarFiles[i].getInputStream(jarEntry2);
+ os = new FileOutputStream(resourceFile);
+ while (true) {
+ int n = is.read(buf);
+ if (n <= 0) {
+ break;
+ }
+ os.write(buf, 0, n);
+ }
+ } catch (IOException e) {
+ // Ignore
+ } finally {
try {
- is = jarFiles[i].getInputStream
- (jarEntry2);
- os = new FileOutputStream
- (resourceFile);
- while (true) {
- int n = is.read(buf);
- if (n <= 0) {
- break;
- }
- os.write(buf, 0, n);
+ if (is != null) {
+ is.close();
}
} catch (IOException e) {
- // Ignore
- } finally {
- try {
- if (is != null) {
- is.close();
- }
- } catch (IOException e) {
+ }
+ try {
+ if (os != null) {
+ os.close();
}
- try {
- if (os != null) {
- os.close();
- }
- } catch (IOException e) {
- }
+ } catch (IOException e) {
}
}
}
}
}
-
}
}
+
+ }
- if (entry == null) {
- synchronized (notFoundResources) {
- notFoundResources.put(name, name);
- }
- return null;
+ if (entry == null) {
+ synchronized (notFoundResources) {
+ notFoundResources.add(name);
}
+ return null;
+ }
- if (binaryStream != null) {
+ if (binaryStream != null) {
- byte[] binaryContent = new byte[contentLength];
+ byte[] binaryContent = new byte[contentLength];
- int pos = 0;
+ int pos = 0;
+ try {
+
+ while (true) {
+ int n = binaryStream.read(binaryContent, pos,
+ binaryContent.length - pos);
+ if (n <= 0)
+ break;
+ pos += n;
+ }
+ } catch (IOException e) {
+ log.error(sm.getString("webappClassLoader.readError", name),
e);
+ return null;
+ } finally {
try {
+ binaryStream.close();
+ } catch (IOException e) {}
+ }
- while (true) {
- int n = binaryStream.read(binaryContent, pos,
- binaryContent.length - pos);
- if (n <= 0)
- break;
- pos += n;
- }
- } catch (IOException e) {
- log.error(sm.getString("webappClassLoader.readError",
name), e);
+ if (fileNeedConvert) {
+ String str = new String(binaryContent,0,pos);
+ try {
+ binaryContent = str.getBytes("UTF-8");
+ } catch (Exception e) {
return null;
- } finally {
- try {
- binaryStream.close();
- } catch (IOException e) {}
}
+ }
+ entry.binaryContent = binaryContent;
- if (fileNeedConvert) {
- String str = new String(binaryContent,0,pos);
- try {
- binaryContent = str.getBytes("UTF-8");
- } catch (Exception e) {
- return null;
- }
- }
- entry.binaryContent = binaryContent;
-
- // The certificates are only available after the JarEntry
- // associated input stream has been fully read
- if (jarEntry != null) {
- entry.certificates = jarEntry.getCertificates();
- }
-
+ // The certificates are only available after the JarEntry
+ // associated input stream has been fully read
+ if (jarEntry != null) {
+ entry.certificates = jarEntry.getCertificates();
}
}
Modified: trunk/java/org/apache/catalina/loader/WebappLoader.java
===================================================================
--- trunk/java/org/apache/catalina/loader/WebappLoader.java 2009-06-10 12:42:02 UTC (rev
1099)
+++ trunk/java/org/apache/catalina/loader/WebappLoader.java 2009-06-10 22:40:29 UTC (rev
1100)
@@ -1,18 +1,46 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
*
- *
http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 1999-2009 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
*/
@@ -23,28 +51,16 @@
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.FilePermission;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
-import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
-import java.util.ArrayList;
-import java.util.jar.JarFile;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;
-import javax.naming.Binding;
-import javax.naming.NameClassPair;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.DirContext;
import javax.servlet.ServletContext;
import org.apache.catalina.Container;
@@ -60,7 +76,6 @@
import org.apache.catalina.util.StringManager;
import org.apache.naming.resources.DirContextURLStreamHandler;
import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
-import org.apache.naming.resources.Resource;
import org.apache.tomcat.util.modeler.Registry;
@@ -85,6 +100,13 @@
public class WebappLoader
implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration {
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
// ----------------------------------------------------------- Constructors
@@ -174,19 +196,6 @@
/**
- * The set of repositories associated with this class loader.
- */
- private String repositories[] = new String[0];
-
-
- /**
- * The string manager for this package.
- */
- protected static final StringManager sm =
- StringManager.getManager(Constants.Package);
-
-
- /**
* Has this component been started?
*/
private boolean started = false;
@@ -198,18 +207,6 @@
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
- /**
- * Classpath set in the loader.
- */
- private String classpath = null;
-
-
- /**
- * Repositories that are set in the loader, for JMX.
- */
- private ArrayList loaderRepositories = null;
-
-
// ------------------------------------------------------------- Properties
@@ -371,22 +368,6 @@
if (log.isDebugEnabled())
log.debug(sm.getString("webappLoader.addRepository", repository));
- for (int i = 0; i < repositories.length; i++) {
- if (repository.equals(repositories[i]))
- return;
- }
- String results[] = new String[repositories.length + 1];
- for (int i = 0; i < repositories.length; i++)
- results[i] = repositories[i];
- results[repositories.length] = repository;
- repositories = results;
-
- if (started && (classLoader != null)) {
- classLoader.addRepository(repository);
- if( loaderRepositories != null ) loaderRepositories.add(repository);
- setClassPath();
- }
-
}
@@ -409,8 +390,6 @@
(container.getLoader().getClassLoader());
}
}
- } else {
- closeJARs(false);
}
}
@@ -422,87 +401,31 @@
* String are immutable).
*/
public String[] findRepositories() {
-
- return ((String[])repositories.clone());
-
+ return null;
}
- public String[] getRepositories() {
- return ((String[])repositories.clone());
- }
- /** Extra repositories for this loader
- */
- public String getRepositoriesString() {
- StringBuffer sb=new StringBuffer();
- for( int i=0; i<repositories.length ; i++ ) {
- sb.append( repositories[i]).append(":");
- }
- return sb.toString();
- }
-
public String[] findLoaderRepositories() {
- return getLoaderRepositories();
+ return null;
}
-
- public String[] getLoaderRepositories() {
- if( loaderRepositories==null ) return null;
- String res[]=new String[ loaderRepositories.size()];
- loaderRepositories.toArray(res);
- return res;
- }
- public String getLoaderRepositoriesString() {
- String repositories[]=getLoaderRepositories();
- StringBuffer sb=new StringBuffer();
- for( int i=0; i<repositories.length ; i++ ) {
- sb.append( repositories[i]).append(":");
- }
- return sb.toString();
- }
-
- /**
- * Classpath, as set in org.apache.catalina.jsp_classpath context
- * property
- *
- * @return The classpath
- */
- public String getClasspath() {
- return classpath;
- }
-
-
/**
* Has the internal repository associated with this Loader been modified,
* such that the loaded classes should be reloaded?
*/
public boolean modified() {
-
return (classLoader.modified());
-
}
-
+
/**
- * Used to periodically signal to the classloader to release JAR resources.
- */
- public void closeJARs(boolean force) {
- if (classLoader !=null){
- classLoader.closeJARs(force);
- }
- }
-
-
- /**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
-
support.removePropertyChangeListener(listener);
-
}
@@ -644,14 +567,25 @@
if (container instanceof StandardContext)
classLoader.setAntiJARLocking(((StandardContext)
container).getAntiJARLocking());
- for (int i = 0; i < repositories.length; i++) {
- classLoader.addRepository(repositories[i]);
+ // Configure our repositories
+ if (!(container instanceof Context))
+ throw new IllegalStateException("This loader only supports a
Context");
+ ServletContext servletContext = ((Context) container).getServletContext();
+ if (servletContext == null)
+ throw new IllegalStateException("No servlet context
available");
+
+ File workDir =
+ (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
+ if (workDir == null) {
+ log.info("No work dir for " + servletContext);
}
- // Configure our repositories
- setRepositories();
- setClassPath();
+ if( log.isDebugEnabled())
+ log.debug(sm.getString("webappLoader.deploy",
workDir.getAbsolutePath()));
+ classLoader.setWorkDir(workDir);
+ classLoader.setRepository(container.getJarRepository());
+
setPermissions();
if (classLoader instanceof Lifecycle)
@@ -666,7 +600,7 @@
String path = ctx.getPath();
if (path.equals("")) {
path = "/";
- }
+ }
ObjectName cloname = new ObjectName
(ctx.getEngineName() + ":type=WebappClassLoader,path="
+ path + ",host=" + ctx.getParent().getName());
@@ -876,327 +810,6 @@
}
- /**
- * Configure the repositories for our class loader, based on the
- * associated Context.
- */
- private void setRepositories() {
-
- if (!(container instanceof Context))
- return;
- ServletContext servletContext =
- ((Context) container).getServletContext();
- if (servletContext == null)
- return;
-
- loaderRepositories=new ArrayList();
- // Loading the work directory
- File workDir =
- (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
- if (workDir == null) {
- log.info("No work dir for " + servletContext);
- }
-
- if( log.isDebugEnabled())
- log.debug(sm.getString("webappLoader.deploy",
workDir.getAbsolutePath()));
-
- classLoader.setWorkDir(workDir);
-
- DirContext resources = container.getResources();
-
- // Setting up the class repository (/WEB-INF/classes), if it exists
-
- String classesPath = "/WEB-INF/classes";
- DirContext classes = null;
-
- try {
- Object object = resources.lookup(classesPath);
- if (object instanceof DirContext) {
- classes = (DirContext) object;
- }
- } catch(NamingException e) {
- // Silent catch: it's valid that no /WEB-INF/classes collection
- // exists
- }
-
- if (classes != null) {
-
- File classRepository = null;
-
- String absoluteClassesPath =
- servletContext.getRealPath(classesPath);
-
- if (absoluteClassesPath != null) {
-
- classRepository = new File(absoluteClassesPath);
-
- } else {
-
- classRepository = new File(workDir, classesPath);
- classRepository.mkdirs();
- copyDir(classes, classRepository);
-
- }
-
- if(log.isDebugEnabled())
- log.debug(sm.getString("webappLoader.classDeploy",
classesPath,
- classRepository.getAbsolutePath()));
-
-
- // Adding the repository to the class loader
- classLoader.addRepository(classesPath + "/", classRepository);
- loaderRepositories.add(classRepository.getAbsolutePath());
-
- }
-
- // Setting up the JAR repository (/WEB-INF/lib), if it exists
-
- String libPath = "/WEB-INF/lib";
-
- classLoader.setJarPath(libPath);
-
- DirContext libDir = null;
- // Looking up directory /WEB-INF/lib in the context
- try {
- Object object = resources.lookup(libPath);
- if (object instanceof DirContext)
- libDir = (DirContext) object;
- } catch(NamingException e) {
- // Silent catch: it's valid that no /WEB-INF/lib collection
- // exists
- }
-
- if (libDir != null) {
-
- boolean copyJars = false;
- String absoluteLibPath = servletContext.getRealPath(libPath);
-
- File destDir = null;
-
- if (absoluteLibPath != null) {
- destDir = new File(absoluteLibPath);
- } else {
- copyJars = true;
- destDir = new File(workDir, libPath);
- destDir.mkdirs();
- }
-
- // Looking up directory /WEB-INF/lib in the context
- try {
- NamingEnumeration enumeration = resources.listBindings(libPath);
- while (enumeration.hasMoreElements()) {
-
- Binding binding = (Binding) enumeration.nextElement();
- String filename = libPath + "/" + binding.getName();
- if (!filename.endsWith(".jar"))
- continue;
-
- // Copy JAR in the work directory, always (the JAR file
- // would get locked otherwise, which would make it
- // impossible to update it or remove it at runtime)
- File destFile = new File(destDir, binding.getName());
-
- if( log.isDebugEnabled())
- log.debug(sm.getString("webappLoader.jarDeploy", filename,
- destFile.getAbsolutePath()));
-
- Object obj = binding.getObject();
- if (!(obj instanceof Resource))
- continue;
-
- Resource jarResource = (Resource) obj;
- if (copyJars) {
- if (!copy(jarResource.streamContent(),
- new FileOutputStream(destFile)))
- continue;
- }
-
- try {
- JarFile jarFile = new JarFile(destFile);
- classLoader.addJar(filename, jarFile, destFile);
- } catch (Exception ex) {
- // Catch the exception if there is an empty jar file
- // Should ignore and continute loading other jar files
- // in the dir
- }
-
- loaderRepositories.add(destFile.getAbsolutePath());
-
- }
- } catch (NamingException e) {
- // Silent catch: it's valid that no /WEB-INF/lib directory
- // exists
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- }
-
-
- /**
- * Set the appropriate context attribute for our class path. This
- * is required only because Jasper depends on it.
- */
- private void setClassPath() {
-
- // Validate our current state information
- if (!(container instanceof Context))
- return;
- ServletContext servletContext =
- ((Context) container).getServletContext();
- if (servletContext == null)
- return;
-
- if (container instanceof StandardContext) {
- String baseClasspath =
- ((StandardContext) container).getCompilerClasspath();
- if (baseClasspath != null) {
- servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
- baseClasspath);
- return;
- }
- }
-
- StringBuffer classpath = new StringBuffer();
-
- // Assemble the class path information from our class loader chain
- ClassLoader loader = getClassLoader();
- int layers = 0;
- int n = 0;
- while (loader != null) {
- if (!(loader instanceof URLClassLoader)) {
- String cp=getClasspath( loader );
- if( cp==null ) {
- log.info( "Unknown loader " + loader + " " +
loader.getClass());
- break;
- } else {
- if (n > 0)
- classpath.append(File.pathSeparator);
- classpath.append(cp);
- n++;
- }
- break;
- //continue;
- }
- URL repositories[] =
- ((URLClassLoader) loader).getURLs();
- for (int i = 0; i < repositories.length; i++) {
- String repository = repositories[i].toString();
- if (repository.startsWith("file://"))
- repository = repository.substring(7);
- else if (repository.startsWith("file:"))
- repository = repository.substring(5);
- else if (repository.startsWith("jndi:"))
- repository =
- servletContext.getRealPath(repository.substring(5));
- else
- continue;
- if (repository == null)
- continue;
- if (n > 0)
- classpath.append(File.pathSeparator);
- classpath.append(repository);
- n++;
- }
- loader = loader.getParent();
- layers++;
- }
-
- this.classpath=classpath.toString();
-
- // Store the assembled class path as a servlet context attribute
- servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
- classpath.toString());
-
- }
-
- // try to extract the classpath from a loader that is not URLClassLoader
- private String getClasspath( ClassLoader loader ) {
- try {
- Method m=loader.getClass().getMethod("getClasspath", new Class[]
{});
- if( log.isTraceEnabled())
- log.trace("getClasspath " + m );
- if( m==null ) return null;
- Object o=m.invoke( loader, new Object[] {} );
- if( log.isDebugEnabled() )
- log.debug("gotClasspath " + o);
- if( o instanceof String )
- return (String)o;
- return null;
- } catch( Exception ex ) {
- if (log.isDebugEnabled())
- log.debug("getClasspath ", ex);
- }
- return null;
- }
-
- /**
- * Copy directory.
- */
- private boolean copyDir(DirContext srcDir, File destDir) {
-
- try {
-
- NamingEnumeration enumeration = srcDir.list("");
- while (enumeration.hasMoreElements()) {
- NameClassPair ncPair =
- (NameClassPair) enumeration.nextElement();
- String name = ncPair.getName();
- Object object = srcDir.lookup(name);
- File currentFile = new File(destDir, name);
- if (object instanceof Resource) {
- InputStream is = ((Resource) object).streamContent();
- OutputStream os = new FileOutputStream(currentFile);
- if (!copy(is, os))
- return false;
- } else if (object instanceof InputStream) {
- OutputStream os = new FileOutputStream(currentFile);
- if (!copy((InputStream) object, os))
- return false;
- } else if (object instanceof DirContext) {
- currentFile.mkdir();
- copyDir((DirContext) object, currentFile);
- }
- }
-
- } catch (NamingException e) {
- return false;
- } catch (IOException e) {
- return false;
- }
-
- return true;
-
- }
-
-
- /**
- * Copy a file to the specified temp directory. This is required only
- * because Jasper depends on it.
- */
- private boolean copy(InputStream is, OutputStream os) {
-
- try {
- byte[] buf = new byte[4096];
- while (true) {
- int len = is.read(buf);
- if (len < 0)
- break;
- os.write(buf, 0, len);
- }
- is.close();
- os.close();
- } catch (IOException e) {
- return false;
- }
-
- return true;
-
- }
-
-
private static org.jboss.logging.Logger log=
org.jboss.logging.Logger.getLogger( WebappLoader.class );