001: /**
002: *
003: */package clime.messadmin.providers.lifecycle;
004:
005: import java.lang.reflect.Method;
006: import java.util.Map;
007:
008: import javax.servlet.ServletContext;
009: import javax.servlet.http.HttpServletRequest;
010: import javax.servlet.http.HttpServletResponse;
011:
012: import clime.messadmin.providers.spi.RequestExceptionProvider;
013: import clime.messadmin.providers.spi.RequestLifeCycleProvider;
014:
015: /**
016: * Takes care of clean (some of) the ThreadLocals
017: * thereby avoiding (well, trying to avoid)
018: * OOM (java.lang.OutOfMemoryError) on hot restart...
019: *
020: * @see <a href="http://opensource2.atlassian.com/confluence/spring/pages/viewpage.action?pageId=2669">Memory leaks where the classloader cannot be garbage collected</a>
021: * @author Cédrik LIME
022: * @since 4.1
023: */
024: public class KnownThreadLocalsCleaner implements
025: RequestLifeCycleProvider, RequestExceptionProvider {
026: /* From ProviderUtils:
027: * "Providers are cached, keyed by its Interface, and by a ClassLoader.
028: * This enables different WebApps (different ClassLoaders) to have their own set of plugins (same Interface)."
029: *
030: * Thus we have a copy of plugins per webapp. Which means the ClassLoader is an invariant for each
031: * instance of this class.
032: */
033:
034: private Method log4jNDCRemoveMethod;
035: private Method log4jMDCContextMethod;
036: private Method logBackMDCClearMethod;
037:
038: /**
039: *
040: */
041: public KnownThreadLocalsCleaner() {
042: super ();
043: final ClassLoader this ClassLoader = Thread.currentThread()
044: .getContextClassLoader();//this.getClass().getClassLoader();
045: // Apache Logging Log4J 1.x
046: // org.apache.log4j.NDC.remove();
047: try {
048: Class log4jNdcClass = this ClassLoader
049: .loadClass("org.apache.log4j.NDC");//$NON-NLS-1$
050: log4jNDCRemoveMethod = log4jNdcClass.getMethod(
051: "remove", null);//$NON-NLS-1$
052: } catch (Throwable e) {
053: // ignore
054: }
055: // org.apache.log4j.MDC.getContext().clear();
056: try {
057: Class log4jMdcClass = this ClassLoader
058: .loadClass("org.apache.log4j.MDC");//$NON-NLS-1$
059: log4jMDCContextMethod = log4jMdcClass.getMethod(
060: "getContext", null);//$NON-NLS-1$
061: } catch (Throwable e) {
062: // ignore
063: }
064: // LOGBack
065: // ch.qos.logback.classic.MDC.clear();
066: try {
067: Class logBackMdcClass = this ClassLoader
068: .loadClass("ch.qos.logback.classic.MDC");//$NON-NLS-1$
069: logBackMDCClearMethod = logBackMdcClass.getMethod(
070: "clear", null);//$NON-NLS-1$
071: } catch (Throwable e) {
072: // ignore
073: }
074: }
075:
076: /* (non-Javadoc)
077: * @see clime.messadmin.providers.spi.BaseProvider#getPriority()
078: */
079: public int getPriority() {
080: return Integer.MAX_VALUE;
081: }
082:
083: /* (non-Javadoc)
084: * @see clime.messadmin.providers.spi.RequestLifeCycleProvider#requestInitialized(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.ServletContext)
085: */
086: public void requestInitialized(HttpServletRequest request,
087: HttpServletResponse response, ServletContext servletContext) {
088: // do nothing
089: }
090:
091: /* (non-Javadoc)
092: * @see clime.messadmin.providers.spi.RequestLifeCycleProvider#requestDestroyed(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.ServletContext)
093: */
094: public void requestDestroyed(HttpServletRequest request,
095: HttpServletResponse response, ServletContext servletContext) {
096: cleanThreadLocals();
097: }
098:
099: /* (non-Javadoc)
100: * @see clime.messadmin.providers.spi.RequestExceptionProvider#requestException(java.lang.Exception, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.ServletContext)
101: */
102: public void requestException(Exception e,
103: HttpServletRequest request, HttpServletResponse response,
104: ServletContext servletContext) {
105: cleanThreadLocals();
106: }
107:
108: protected void cleanThreadLocals() {
109: // ThreadLocals - With Great Power, comes Great Responsibility
110: // Can't do anything here. You need to manually set all your ThreadLocals to null yourself (myThreadLocal.remove() or myThreadLocal.set(null))...
111:
112: // Apache Logging Log4J 1.x
113: // org.apache.log4j.NDC.remove();
114: if (log4jNDCRemoveMethod != null) {
115: try {
116: log4jNDCRemoveMethod.invoke(null, null);
117: } catch (Exception e) {
118: // do nothing
119: }
120: }
121: // org.apache.log4j.MDC.getContext().clear();
122: if (log4jMDCContextMethod != null) {
123: try {
124: Map context = (Map) log4jMDCContextMethod.invoke(null,
125: null);
126: if (context != null) {
127: context.clear();
128: }
129: } catch (Exception e) {
130: // do nothing
131: }
132: }
133:
134: // LOGBack
135: // ch.qos.logback.classic.MDC.clear();
136: if (logBackMDCClearMethod != null) {
137: try {
138: logBackMDCClearMethod.invoke(null, null);
139: } catch (Exception e) {
140: // do nothing
141: }
142: }
143: }
144: }
|