Fields Summary |
---|
private static boolean | firstFirst load of the class. |
private WebappClassLoader | classLoaderThe class loader being managed by this Loader component. |
private org.apache.catalina.Container | containerThe Container with which this Loader has been associated. |
private boolean | delegateThe "follow standard delegation model" flag that will be used to
configure our ClassLoader. |
private static final String | infoThe descriptive information about this Loader implementation. |
protected org.apache.catalina.util.LifecycleSupport | lifecycleThe lifecycle event support for this component. |
private String | loaderClassThe Java class name of the ClassLoader implementation to be used.
This class should extend WebappClassLoader, otherwise, a different
loader implementation must be used. |
private ClassLoader | parentClassLoaderThe parent class loader of the class loader we will create. |
private boolean | reloadableThe reloadable flag for this Loader. |
private String[] | repositoriesThe set of repositories associated with this class loader. |
protected static final org.apache.catalina.util.StringManager | smThe string manager for this package. |
private boolean | startedHas this component been started? |
protected PropertyChangeSupport | supportThe property change support for this component. |
private String | classpathClasspath set in the loader. |
private ArrayList | loaderRepositoriesRepositories that are set in the loader, for JMX. |
private boolean | initialized |
private static org.apache.juli.logging.Log | log |
private ObjectName | oname |
private MBeanServer | mserver |
private String | domain |
private ObjectName | controller |
Methods Summary |
---|
public void | addLifecycleListener(org.apache.catalina.LifecycleListener listener)Add a lifecycle event listener to this component.
lifecycle.addLifecycleListener(listener);
|
public void | addPropertyChangeListener(java.beans.PropertyChangeListener listener)Add a property change listener to this component.
support.addPropertyChangeListener(listener);
|
public void | addRepository(java.lang.String repository)Add a new repository to the set of repositories for this class loader.
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();
}
|
public void | backgroundProcess()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.
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);
}
|
public void | closeJARs(boolean force)Used to periodically signal to the classloader to release JAR resources.
if (classLoader !=null){
classLoader.closeJARs(force);
}
|
private boolean | copy(java.io.InputStream is, java.io.OutputStream os)Copy a file to the specified temp directory. This is required only
because Jasper depends on it.
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 boolean | copyDir(javax.naming.directory.DirContext srcDir, java.io.File destDir)Copy directory.
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;
|
private WebappClassLoader | createClassLoader()Create associated classLoader.
Class clazz = Class.forName(loaderClass);
WebappClassLoader classLoader = null;
if (parentClassLoader == null) {
parentClassLoader = container.getParentClassLoader();
}
Class[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoader) constr.newInstance(args);
return classLoader;
|
public void | destroy()
if( controller==oname ) {
// Self-registration, undo it
Registry.getRegistry(null, null).unregisterComponent(oname);
oname = null;
}
initialized = false;
|
public org.apache.catalina.LifecycleListener[] | findLifecycleListeners()Get the lifecycle listeners associated with this lifecycle. If this
Lifecycle has no listeners registered, a zero-length array is returned.
return lifecycle.findLifecycleListeners();
|
public java.lang.String[] | findRepositories()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).
return ((String[])repositories.clone());
|
public java.lang.ClassLoader | getClassLoader()Return the Java class loader to be used by this Container.
// ------------------------------------------------------------- Properties
return ((ClassLoader) classLoader);
|
public java.lang.String | getClasspath()Classpath, as set in org.apache.catalina.jsp_classpath context
property
return classpath;
|
private java.lang.String | getClasspath(java.lang.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;
|
public org.apache.catalina.Container | getContainer()Return the Container with which this Logger has been associated.
return (container);
|
public javax.management.ObjectName | getController()
return controller;
|
public boolean | getDelegate()Return the "follow standard delegation model" flag used to configure
our ClassLoader.
return (this.delegate);
|
public java.lang.String | getInfo()Return descriptive information about this Loader implementation and
the corresponding version number, in the format
<description>/<version> .
return (info);
|
public java.lang.String | getLoaderClass()Return the ClassLoader class name.
return (this.loaderClass);
|
public java.lang.String[] | getLoaderRepositories()
if( loaderRepositories==null ) return null;
String res[]=new String[ loaderRepositories.size()];
loaderRepositories.toArray(res);
return res;
|
public java.lang.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();
|
public boolean | getReloadable()Return the reloadable flag for this Loader.
return (this.reloadable);
|
public java.lang.String[] | getRepositories()
return ((String[])repositories.clone());
|
public java.lang.String | getRepositoriesString()Extra repositories for this loader
StringBuffer sb=new StringBuffer();
for( int i=0; i<repositories.length ; i++ ) {
sb.append( repositories[i]).append(":");
}
return sb.toString();
|
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 boolean | modified()Has the internal repository associated with this Loader been modified,
such that the loaded classes should be reloaded?
return (classLoader.modified());
|
public void | postDeregister()
|
public void | postRegister(java.lang.Boolean registrationDone)
|
public void | preDeregister()
|
public javax.management.ObjectName | preRegister(javax.management.MBeanServer server, javax.management.ObjectName name)
oname=name;
mserver=server;
domain=name.getDomain();
return name;
|
public void | propertyChange(java.beans.PropertyChangeEvent event)Process property change events from our associated Context.
// 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()));
}
}
|
public void | removeLifecycleListener(org.apache.catalina.LifecycleListener listener)Remove a lifecycle event listener from this component.
lifecycle.removeLifecycleListener(listener);
|
public void | removePropertyChangeListener(java.beans.PropertyChangeListener listener)Remove a property change listener from this component.
support.removePropertyChangeListener(listener);
|
private void | setClassPath()Set the appropriate context attribute for our class path. This
is required only because Jasper depends on it.
// 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());
|
public void | setContainer(org.apache.catalina.Container container)Set the Container with which this Logger has been associated.
// 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);
}
|
public void | setController(javax.management.ObjectName controller)
this.controller = controller;
|
public void | setDelegate(boolean delegate)Set the "follow standard delegation model" flag used to configure
our ClassLoader.
boolean oldDelegate = this.delegate;
this.delegate = delegate;
support.firePropertyChange("delegate", new Boolean(oldDelegate),
new Boolean(this.delegate));
|
public void | setLoaderClass(java.lang.String loaderClass)Set the ClassLoader class name.
this.loaderClass = loaderClass;
|
private void | setPermissions()Configure associated class loader permissions.
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) {
}
|
public void | setReloadable(boolean reloadable)Set the reloadable flag for this Loader.
// Process this property change
boolean oldReloadable = this.reloadable;
this.reloadable = reloadable;
support.firePropertyChange("reloadable",
new Boolean(oldReloadable),
new Boolean(this.reloadable));
|
private void | setRepositories()Configure the repositories for our class loader, based on the
associated Context.
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(classesPath + "/" );
}
// 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()));
Resource jarResource = (Resource) binding.getObject();
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( filename );
}
} catch (NamingException e) {
// Silent catch: it's valid that no /WEB-INF/lib directory
// exists
} catch (IOException e) {
e.printStackTrace();
}
}
|
public void | start()Start this component, initializing our associated class loader.
// 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
log.info("Dual registration of jndi stream handler: "
+ t.getMessage());
}
}
// 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=WebappClassLoader,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);
}
|
public void | stop()Stop this component, finalizing our associated class loader.
// 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=WebappClassLoader,path="
+ path + ",host=" + ctx.getParent().getName());
Registry.getRegistry(null, null).unregisterComponent(cloname);
} catch (Throwable t) {
log.error( "LifecycleException ", t );
}
classLoader = null;
destroy();
|
public java.lang.String | toString()Return a String representation of this component.
StringBuffer sb = new StringBuffer("WebappLoader[");
if (container != null)
sb.append(container.getName());
sb.append("]");
return (sb.toString());
|