001: /*
002: * Copyright 2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.logging.impl;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021:
022: import javax.servlet.ServletContextEvent;
023: import javax.servlet.ServletContextListener;
024:
025: import org.apache.commons.logging.LogFactory;
026:
027: /**
028: * This class is capable of receiving notifications about the undeployment of
029: * a webapp, and responds by ensuring that commons-logging releases all
030: * memory associated with the undeployed webapp.
031: * <p>
032: * In general, the WeakHashtable support added in commons-logging release 1.1
033: * ensures that logging classes do not hold references that prevent an
034: * undeployed webapp's memory from being garbage-collected even when multiple
035: * copies of commons-logging are deployed via multiple classloaders (a
036: * situation that earlier versions had problems with). However there are
037: * some rare cases where the WeakHashtable approach does not work; in these
038: * situations specifying this class as a listener for the web application will
039: * ensure that all references held by commons-logging are fully released.
040: * <p>
041: * To use this class, configure the webapp deployment descriptor to call
042: * this class on webapp undeploy; the contextDestroyed method will tell
043: * every accessable LogFactory class that the entry in its map for the
044: * current webapp's context classloader should be cleared.
045: *
046: * @since 1.1
047: */
048:
049: public class ServletContextCleaner implements ServletContextListener {
050:
051: private Class[] RELEASE_SIGNATURE = { ClassLoader.class };
052:
053: /**
054: * Invoked when a webapp is undeployed, this tells the LogFactory
055: * class to release any logging information related to the current
056: * contextClassloader.
057: */
058: public void contextDestroyed(ServletContextEvent sce) {
059: ClassLoader tccl = Thread.currentThread()
060: .getContextClassLoader();
061:
062: Object[] params = new Object[1];
063: params[0] = tccl;
064:
065: // Walk up the tree of classloaders, finding all the available
066: // LogFactory classes and releasing any objects associated with
067: // the tccl (ie the webapp).
068: //
069: // When there is only one LogFactory in the classpath, and it
070: // is within the webapp being undeployed then there is no problem;
071: // garbage collection works fine.
072: //
073: // When there are multiple LogFactory classes in the classpath but
074: // parent-first classloading is used everywhere, this loop is really
075: // short. The first instance of LogFactory found will
076: // be the highest in the classpath, and then no more will be found.
077: // This is ok, as with this setup this will be the only LogFactory
078: // holding any data associated with the tccl being released.
079: //
080: // When there are multiple LogFactory classes in the classpath and
081: // child-first classloading is used in any classloader, then multiple
082: // LogFactory instances may hold info about this TCCL; whenever the
083: // webapp makes a call into a class loaded via an ancestor classloader
084: // and that class calls LogFactory the tccl gets registered in
085: // the LogFactory instance that is visible from the ancestor
086: // classloader. However the concrete logging library it points
087: // to is expected to have been loaded via the TCCL, so the
088: // underlying logging lib is only initialised/configured once.
089: // These references from ancestor LogFactory classes down to
090: // TCCL classloaders are held via weak references and so should
091: // be released but there are circumstances where they may not.
092: // Walking up the classloader ancestry ladder releasing
093: // the current tccl at each level tree, though, will definitely
094: // clear any problem references.
095: ClassLoader loader = tccl;
096: while (loader != null) {
097: // Load via the current loader. Note that if the class is not accessable
098: // via this loader, but is accessable via some ancestor then that class
099: // will be returned.
100: try {
101: Class logFactoryClass = loader
102: .loadClass("org.apache.commons.logging.LogFactory");
103: Method releaseMethod = logFactoryClass.getMethod(
104: "release", RELEASE_SIGNATURE);
105: releaseMethod.invoke(null, params);
106: loader = logFactoryClass.getClassLoader().getParent();
107: } catch (ClassNotFoundException ex) {
108: // Neither the current classloader nor any of its ancestors could find
109: // the LogFactory class, so we can stop now.
110: loader = null;
111: } catch (NoSuchMethodException ex) {
112: // This is not expected; every version of JCL has this method
113: System.err
114: .println("LogFactory instance found which does not support release method!");
115: loader = null;
116: } catch (IllegalAccessException ex) {
117: // This is not expected; every ancestor class should be accessable
118: System.err
119: .println("LogFactory instance found which is not accessable!");
120: loader = null;
121: } catch (InvocationTargetException ex) {
122: // This is not expected
123: System.err
124: .println("LogFactory instance release method failed!");
125: loader = null;
126: }
127: }
128:
129: // Just to be sure, invoke release on the LogFactory that is visible from
130: // this ServletContextCleaner class too. This should already have been caught
131: // by the above loop but just in case...
132: LogFactory.release(tccl);
133: }
134:
135: /**
136: * Invoked when a webapp is deployed. Nothing needs to be done here.
137: */
138: public void contextInitialized(ServletContextEvent sce) {
139: // do nothing
140: }
141: }
|