This is an insideous bug because without this the MDC becomes tainted with
previous execution values. Evil.
http://code.google.com/p/parfait/source/detail?r=4c76f4ebd8
Modified:
/parfait-core/src/main/java/com/custardsource/parfait/timing/ThreadContext.java
/parfait-core/src/test/java/com/custardsource/parfait/timing/ThreadContextTest.java
=======================================
---
/parfait-core/src/main/java/com/custardsource/parfait/timing/ThreadContext.java
Thu Dec 16 00:06:37 2010
+++
/parfait-core/src/main/java/com/custardsource/parfait/timing/ThreadContext.java
Thu Mar 10 01:52:25 2011
@@ -40,8 +40,17 @@
private final ConcurrentMap<Thread, Map<String, Object>>
PER_THREAD_CONTEXTS = new MapMaker()
.weakKeys().makeComputingMap(NEW_CONTEXT_CREATOR);
- private static volatile MdcBridge mdcBridge = new NullMdcBridge();
-
+ private volatile MdcBridge mdcBridge = new NullMdcBridge();
+
+ public ThreadContext() {
+ this(new NullMdcBridge());
+ }
+
+ public ThreadContext(MdcBridge mdcBridge) {
+ // TODO should that be a static variable..?
+ this.mdcBridge=mdcBridge;
+ }
+
/**
* Adds the given key/value pair to the current thread's context, and
updates {@link MDC} with
* same.
@@ -71,6 +80,15 @@
* Clears all values for the current thread.
*/
public void clear() {
+
+ /**
+ * Unfortunately log4j's MDC historically never had a mechanism to
block remove keys,
+ * so we're forced to do this one by one.
+ */
+ for (String key : allKeys()) {
+ mdcBridge.remove(key);
+ }
+
PER_THREAD_CONTEXTS.get(Thread.currentThread()).clear();
}
@@ -92,7 +110,21 @@
public Object getForThread(Thread thread, String key) {
return PER_THREAD_CONTEXTS.get(thread).get(key);
}
-
+
+ /**
+ * Factory method that creates a new ThreadContext initialized to also
update Log4j's MDC.
+ */
+ public static ThreadContext newMDCEnabledContext() {
+ return new ThreadContext(new Log4jMdcBridge());
+ }
+
+ /**
+ * Factory method that creates a new ThreadContext initialised to also
update SLF4J's MDC
+ */
+ public static ThreadContext newSLF4JEnabledContext() {
+ return new ThreadContext(new Slf4jMDCBridge());
+ }
+
public interface MdcBridge {
void put(String key, Object object);
=======================================
---
/parfait-core/src/test/java/com/custardsource/parfait/timing/ThreadContextTest.java
Tue Feb 16 20:21:06 2010
+++
/parfait-core/src/test/java/com/custardsource/parfait/timing/ThreadContextTest.java
Thu Mar 10 01:52:25 2011
@@ -1,8 +1,11 @@
package com.custardsource.parfait.timing;
+import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
+import org.apache.log4j.MDC;
+
import junit.framework.TestCase;
public class ThreadContextTest extends TestCase {
@@ -10,7 +13,14 @@
public void setUp() {
context = new ThreadContext();
- }
+
+ Hashtable hashtable = MDC.getContext();
+ if (hashtable != null) {
+ hashtable.clear();
+ }
+ }
+
+
public void testGetOfUnusedKeyReturnsNull() {
final String testKey = "handy";
@@ -59,4 +69,25 @@
context.clear();
assertNull("get() after clear should return null",
context.get(testKey));
}
-}
+
+ public void testClearRemovesMDCValue(){
+
+ ThreadContext log4jThreadContext =
ThreadContext.newMDCEnabledContext();
+
+ Hashtable mdcContext = MDC.getContext();
+ assertNull(mdcContext);
+
+ final String testKey = "painter";
+ log4jThreadContext.put(testKey, 7);
+
+ mdcContext = MDC.getContext();
+ assertEquals(1, mdcContext.size());
+
+
+ log4jThreadContext.clear();
+
+ assertEquals(0, mdcContext.size());
+
+ assertNull("get() after clear should return null",
log4jThreadContext.get(testKey));
+ }
+}