001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.log4j;
019
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.util.Hashtable;
023import org.apache.log4j.helpers.Loader;
024import org.apache.log4j.helpers.ThreadLocalMap;
025
026/**
027   The MDC class is similar to the {@link NDC} class except that it is
028   based on a map instead of a stack. It provides <em>mapped
029   diagnostic contexts</em>. A <em>Mapped Diagnostic Context</em>, or
030   MDC in short, is an instrument for distinguishing interleaved log
031   output from different sources. Log output is typically interleaved
032   when a server handles multiple clients near-simultaneously.
033
034   <p><b><em>The MDC is managed on a per thread basis</em></b>. A
035   child thread automatically inherits a <em>copy</em> of the mapped
036   diagnostic context of its parent.
037  
038   <p>The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC
039   will always return empty values but otherwise will not affect or
040   harm your application.
041   
042   @since 1.2
043
044   @author Ceki G&uuml;lc&uuml; 
045*/
046public class MDC {
047  
048  final static MDC mdc = new MDC();
049  
050  static final int HT_SIZE = 7;
051
052  boolean java1;
053  
054  Object tlm;
055
056  private Method removeMethod;
057
058  private
059  MDC() {
060    java1 = Loader.isJava1();
061    if(!java1) {
062      tlm = new ThreadLocalMap();
063    }
064
065    try {
066      removeMethod = ThreadLocal.class.getMethod("remove", null);
067    } catch (NoSuchMethodException e) {
068      // don't do anything - java prior 1.5
069    }
070  }
071
072  /**
073     Put a context value (the <code>o</code> parameter) as identified
074     with the <code>key</code> parameter into the current thread's
075     context map.
076
077     <p>If the current thread does not have a context map it is
078     created as a side effect.
079    
080   */
081  static
082  public
083  void put(String key, Object o) {
084     if (mdc != null) {
085         mdc.put0(key, o);
086     }
087  }
088  
089  /**
090     Get the context identified by the <code>key</code> parameter.
091
092     <p>This method has no side effects.
093   */
094  static 
095  public
096  Object get(String key) {
097    if (mdc != null) {
098        return mdc.get0(key);
099    }
100    return null;
101  }
102
103  /**
104     Remove the the context identified by the <code>key</code>
105     parameter.
106
107  */
108  static 
109  public
110  void remove(String key) {
111    if (mdc != null) {
112        mdc.remove0(key);
113    }
114  }
115
116
117  /**
118   * Get the current thread's MDC as a hashtable. This method is
119   * intended to be used internally.  
120   * */
121  public static Hashtable getContext() {
122    if (mdc != null) {
123        return mdc.getContext0();
124    } else {
125        return null;
126    }
127  }
128
129  /**
130   *  Remove all values from the MDC.
131   *  @since 1.2.16
132  */
133  public static void clear() {
134    if (mdc != null) {
135        mdc.clear0();
136    }
137  }
138
139
140  private
141  void put0(String key, Object o) {
142    if(java1 || tlm == null) {
143      return;
144    } else {
145      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
146      if(ht == null) {
147        ht = new Hashtable(HT_SIZE);
148        ((ThreadLocalMap)tlm).set(ht);
149      }    
150      ht.put(key, o);
151    }
152  }
153  
154  private
155  Object get0(String key) {
156    if(java1 || tlm == null) {
157      return null;
158    } else {       
159      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
160      if(ht != null && key != null) {
161        return ht.get(key);
162      } else {
163        return null;
164      }
165    }
166  }
167
168  private
169  void remove0(String key) {
170    if(!java1 && tlm != null) {
171      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
172      if(ht != null) {
173        ht.remove(key);
174        // clean up if this was the last key
175        if (ht.isEmpty()) {
176          clear0();
177        }
178      } 
179    }
180  }
181
182
183  private
184  Hashtable getContext0() {
185     if(java1 || tlm == null) {
186      return null;
187    } else {       
188      return (Hashtable) ((ThreadLocalMap)tlm).get();
189    }
190  }
191
192  private
193  void clear0() {
194    if(!java1 && tlm != null) {
195      Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
196      if(ht != null) {
197        ht.clear();
198      }
199      if(removeMethod != null) {
200          // java 1.3/1.4 does not have remove - will suffer from a memory leak
201          try {
202            removeMethod.invoke(tlm, null);
203          } catch (IllegalAccessException e) {
204            // should not happen
205          } catch (InvocationTargetException e) {
206            // should not happen
207          }
208      }
209    }
210  }
211
212}