1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j;
18  
19  import java.lang.ref.Reference;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import junit.framework.TestCase;
23  
24  /**
25   * Test for MDC
26   * 
27   *  @author Maarten Bosteels
28   */
29  public class MDCTestCase extends TestCase {
30  
31    public void setUp() {
32      MDC.clear();
33    }
34  
35    public void tearDown() {
36      MDC.clear();
37    }
38  
39    public void testPut() throws Exception {
40      MDC.put("key", "some value");
41      assertEquals("some value", MDC.get("key"));
42      assertEquals(1, MDC.getContext().size());
43    }
44    
45    public void testRemoveLastKey() throws Exception {
46      MDC.put("key", "some value");
47  
48      MDC.remove("key");
49      checkThreadLocalsForLeaks();
50    }
51  
52    private void checkThreadLocalsForLeaks() throws Exception {
53  
54        // this code is heavily based on code in org.apache.catalina.loader.WebappClassLoader
55  
56        // Make the fields in the Thread class that store ThreadLocals accessible    
57        Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
58        threadLocalsField.setAccessible(true);
59        Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
60        inheritableThreadLocalsField.setAccessible(true);
61        // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects accessible
62        Class tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
63        Field tableField = tlmClass.getDeclaredField("table");
64        tableField.setAccessible(true);
65  
66        Thread thread = Thread.currentThread();
67  
68        Object threadLocalMap;
69        threadLocalMap = threadLocalsField.get(thread);
70        // Check the first map
71        checkThreadLocalMapForLeaks(threadLocalMap, tableField);
72        // Check the second map
73        threadLocalMap = inheritableThreadLocalsField.get(thread);
74        checkThreadLocalMapForLeaks(threadLocalMap, tableField);
75  
76    }
77  
78    private void checkThreadLocalMapForLeaks(Object map, Field internalTableField) 
79            throws IllegalAccessException, NoSuchFieldException {
80      if (map != null) {
81        Object[] table = (Object[]) internalTableField.get(map);
82        if (table != null) {
83          for (int j =0; j < table.length; j++) {
84            if (table[j] != null) {
85  
86              // Check the key
87              Object key = ((Reference) table[j]).get();
88              String keyClassName = key.getClass().getName();
89  
90              if (key.getClass() == org.apache.log4j.helpers.ThreadLocalMap.class) {
91                fail("Found a ThreadLocal with key of type [" + keyClassName + "]");
92              }
93            }
94          }
95        }
96      }
97    }
98  }