/* * 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. *
* This class loader is configured by adding the pathnames of directories,
* JAR files, and ZIP files with the addRepository()
method,
* prior to calling start()
. 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$ $Date$
*/
public class WebappLoader
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 WebappLoader() {
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 WebappLoader(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 WebappClassLoader 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.WebappLoader/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 WebappClassLoader, otherwise, a different
* loader implementation must be used.
*/
private String loaderClass =
"org.apache.catalina.loader.WebappClassLoader";
/**
* 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
* <description>/<version>
.
*/
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
(WebappLoader.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