/* * 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.FilePermission; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; import javax.management.ObjectName; import javax.servlet.ServletContext; import org.apache.catalina.Context; import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.Loader; import org.apache.catalina.util.LifecycleMBeanBase; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; /** * 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 * {@link org.apache.catalina.WebResourceRoot}). * This class loader supports detection of modified * Java classes, which can be used to implement auto-reload support. *
* This class loader is configured via the Resources children of its Context
* prior to calling start(). When a new class is required,
* these Resources 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
*/
public class WebappLoader extends LifecycleMBeanBase
implements Loader, PropertyChangeListener {
// ----------------------------------------------------------- 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
/**
* The class loader being managed by this Loader component.
*/
private WebappClassLoaderBase classLoader = null;
/**
* The Context with which this Loader has been associated.
*/
private Context context = null;
/**
* The "follow standard delegation model" flag that will be used to
* configure our ClassLoader.
*/
private boolean delegate = false;
/**
* The Java class name of the ClassLoader implementation to be used.
* This class should extend WebappClassLoaderBase, otherwise, a different
* loader implementation must be used.
*/
private String loaderClass = ParallelWebappClassLoader.class.getName();
/**
* 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 string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The property change support for this component.
*/
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
/**
* Classpath set in the loader.
*/
private String classpath = null;
// ------------------------------------------------------------- Properties
/**
* Return the Java class loader to be used by this Container.
*/
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public Context getContext() {
return context;
}
@Override
public void setContext(Context context) {
if (this.context == context) {
return;
}
if (getState().isAvailable()) {
throw new IllegalStateException(
sm.getString("webappLoader.setContext.ise"));
}
// Deregister from the old Context (if any)
if (this.context != null) {
this.context.removePropertyChangeListener(this);
}
// Process this property change
Context oldContext = this.context;
this.context = context;
support.firePropertyChange("context", oldContext, this.context);
// Register with the new Container (if any)
if (this.context != null) {
setReloadable(this.context.getReloadable());
this.context.addPropertyChangeListener(this);
}
}
/**
* Return the "follow standard delegation model" flag used to configure
* our ClassLoader.
*/
@Override
public boolean getDelegate() {
return this.delegate;
}
/**
* Set the "follow standard delegation model" flag used to configure
* our ClassLoader.
*
* @param delegate The new flag
*/
@Override
public void setDelegate(boolean delegate) {
boolean oldDelegate = this.delegate;
this.delegate = delegate;
support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate),
Boolean.valueOf(this.delegate));
}
/**
* @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.
*/
@Override
public boolean getReloadable() {
return this.reloadable;
}
/**
* Set the reloadable flag for this Loader.
*
* @param reloadable The new reloadable flag
*/
@Override
public void setReloadable(boolean reloadable) {
// Process this property change
boolean oldReloadable = this.reloadable;
this.reloadable = reloadable;
support.firePropertyChange("reloadable",
Boolean.valueOf(oldReloadable),
Boolean.valueOf(this.reloadable));
}
// --------------------------------------------------------- Public Methods
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* 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.
*/
@Override
public void backgroundProcess() {
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (context != null) {
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
}
}
public String[] getLoaderRepositories() {
if (classLoader == null) {
return new String[0];
}
URL[] urls = classLoader.getURLs();
String[] result = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
result[i] = urls[i].toExternalForm();
}
return result;
}
public String getLoaderRepositoriesString() {
String repositories[]=getLoaderRepositories();
StringBuilder sb=new StringBuilder();
for( int i=0; i