001
014
015 package com.liferay.portal.kernel.util;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019
020 import java.lang.ref.Reference;
021 import java.lang.reflect.Field;
022 import java.lang.reflect.Method;
023
024
027 public class ClearThreadLocalUtil {
028
029 public static void clearThreadLocal() throws Exception {
030 if (!_INITIALIZED) {
031 return;
032 }
033
034 Thread[] threads = ThreadUtil.getThreads();
035
036 Thread currentThread = Thread.currentThread();
037
038 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
039
040 for (Thread thread : threads) {
041 _clearThreadLocal(thread, contextClassLoader);
042 }
043 }
044
045 private static void _clearThreadLocal(
046 Thread thread, ClassLoader classLoader)
047 throws Exception {
048
049 if (thread == null) {
050 return;
051 }
052
053 Object threadLocalMap = _threadLocalsField.get(thread);
054
055 Object inheritableThreadLocalMap = _inheritableThreadLocalsField.get(
056 thread);
057
058 _clearThreadLocalMap(threadLocalMap, classLoader);
059 _clearThreadLocalMap(inheritableThreadLocalMap, classLoader);
060 }
061
062 private static void _clearThreadLocalMap(
063 Object threadLocalMap, ClassLoader classLoader)
064 throws Exception {
065
066 if (threadLocalMap == null) {
067 return;
068 }
069
070 Object[] table = (Object[])_tableField.get(threadLocalMap);
071
072 if (table == null) {
073 return;
074 }
075
076 int staleEntriesCount = 0;
077
078 for (Object tableEntry : table) {
079 if (tableEntry == null) {
080 continue;
081 }
082
083 Object key = ((Reference<?>)tableEntry).get();
084 Object value = _valueField.get(tableEntry);
085
086 boolean remove = false;
087
088 if (key != null) {
089 Class<?> keyClass = key.getClass();
090
091 ClassLoader keyClassLoader = keyClass.getClassLoader();
092
093 if (keyClassLoader == classLoader) {
094 remove = true;
095 }
096 }
097
098 if (value != null) {
099 Class<?> valueClass = value.getClass();
100
101 ClassLoader valueClassLoader = valueClass.getClassLoader();
102
103 if (valueClassLoader == classLoader) {
104 remove = true;
105 }
106 }
107
108 if (remove) {
109 if (key != null) {
110 if (_log.isDebugEnabled()) {
111 Class<?> keyClass = key.getClass();
112
113 _log.debug(
114 "Clear a ThreadLocal with key of type " +
115 keyClass.getCanonicalName());
116 }
117
118 _removeMethod.invoke(threadLocalMap, key);
119 }
120 else {
121 staleEntriesCount++;
122 }
123 }
124 }
125
126 if (staleEntriesCount > 0) {
127 _expungeStaleEntriesMethod.invoke(threadLocalMap);
128 }
129 }
130
131 private static final boolean _INITIALIZED;
132
133 private static final Log _log = LogFactoryUtil.getLog(
134 ClearThreadLocalUtil.class);
135
136 private static final Method _expungeStaleEntriesMethod;
137 private static final Field _inheritableThreadLocalsField;
138 private static final Method _removeMethod;
139 private static final Field _tableField;
140 private static final Field _threadLocalsField;
141 private static final Field _valueField;
142
143 static {
144 boolean initialized = false;
145
146 Method expungeStaleEntriesMethod = null;
147 Field inheritableThreadLocalsField = null;
148 Method removeMethod = null;
149 Field tableField = null;
150 Field threadLocalsField = null;
151 Field valueField = null;
152
153 try {
154 inheritableThreadLocalsField = ReflectionUtil.getDeclaredField(
155 Thread.class, "inheritableThreadLocals");
156 threadLocalsField = ReflectionUtil.getDeclaredField(
157 Thread.class, "threadLocals");
158
159 Class<?> threadLocalMapClass = Class.forName(
160 "java.lang.ThreadLocal$ThreadLocalMap");
161
162 expungeStaleEntriesMethod = ReflectionUtil.getDeclaredMethod(
163 threadLocalMapClass, "expungeStaleEntries");
164 removeMethod = ReflectionUtil.getDeclaredMethod(
165 threadLocalMapClass, "remove", ThreadLocal.class);
166 tableField = ReflectionUtil.getDeclaredField(
167 threadLocalMapClass, "table");
168
169 Class<?> threadLocalMapEntryClass = Class.forName(
170 "java.lang.ThreadLocal$ThreadLocalMap$Entry");
171
172 valueField = ReflectionUtil.getDeclaredField(
173 threadLocalMapEntryClass, "value");
174
175 initialized = true;
176 }
177 catch (Throwable t) {
178 if (_log.isWarnEnabled()) {
179 _log.warn("Failed to initialize ClearThreadLocalUtil", t);
180 }
181 }
182
183 _expungeStaleEntriesMethod = expungeStaleEntriesMethod;
184 _inheritableThreadLocalsField = inheritableThreadLocalsField;
185 _removeMethod = removeMethod;
186 _tableField = tableField;
187 _threadLocalsField = threadLocalsField;
188 _valueField = valueField;
189
190 _INITIALIZED = initialized;
191 }
192
193 }