001/**
002 * Copyright (c) 2004-2011 QOS.ch
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 *
024 */
025package org.slf4j.helpers;
026
027import org.slf4j.spi.MDCAdapter;
028
029import java.util.*;
030import java.util.Map;
031
032/**
033 * Basic MDC implementation, which can be used with logging systems that lack
034 * out-of-the-box MDC support.
035 *
036 * This code was initially inspired by  logback's LogbackMDCAdapter. However,
037 * LogbackMDCAdapter has evolved and is now considerably more sophisticated.
038 *
039 * @author Ceki Gulcu
040 * @author Maarten Bosteels
041 * @author Lukasz Cwik
042 * 
043 * @since 1.5.0
044 */
045public class BasicMDCAdapter implements MDCAdapter {
046
047    private InheritableThreadLocal<Map<String, String>> inheritableThreadLocal = new InheritableThreadLocal<Map<String, String>>() {
048        @Override
049        protected Map<String, String> childValue(Map<String, String> parentValue) {
050            if (parentValue == null) {
051                return null;
052            }
053            return new HashMap<String, String>(parentValue);
054        }
055    };
056
057    /**
058     * Put a context value (the <code>val</code> parameter) as identified with
059     * the <code>key</code> parameter into the current thread's context map.
060     * Note that contrary to log4j, the <code>val</code> parameter can be null.
061     *
062     * <p>
063     * If the current thread does not have a context map it is created as a side
064     * effect of this call.
065     *
066     * @throws IllegalArgumentException
067     *                 in case the "key" parameter is null
068     */
069    public void put(String key, String val) {
070        if (key == null) {
071            throw new IllegalArgumentException("key cannot be null");
072        }
073        Map<String, String> map = inheritableThreadLocal.get();
074        if (map == null) {
075            map = new HashMap<String, String>();
076            inheritableThreadLocal.set(map);
077        }
078        map.put(key, val);
079    }
080
081    /**
082     * Get the context identified by the <code>key</code> parameter.
083     */
084    public String get(String key) {
085        Map<String, String> map = inheritableThreadLocal.get();
086        if ((map != null) && (key != null)) {
087            return map.get(key);
088        } else {
089            return null;
090        }
091    }
092
093    /**
094     * Remove the the context identified by the <code>key</code> parameter.
095     */
096    public void remove(String key) {
097        Map<String, String> map = inheritableThreadLocal.get();
098        if (map != null) {
099            map.remove(key);
100        }
101    }
102
103    /**
104     * Clear all entries in the MDC.
105     */
106    public void clear() {
107        Map<String, String> map = inheritableThreadLocal.get();
108        if (map != null) {
109            map.clear();
110            inheritableThreadLocal.remove();
111        }
112    }
113
114    /**
115     * Returns the keys in the MDC as a {@link Set} of {@link String}s The
116     * returned value can be null.
117     *
118     * @return the keys in the MDC
119     */
120    public Set<String> getKeys() {
121        Map<String, String> map = inheritableThreadLocal.get();
122        if (map != null) {
123            return map.keySet();
124        } else {
125            return null;
126        }
127    }
128
129    /**
130     * Return a copy of the current thread's context map.
131     * Returned value may be null.
132     *
133     */
134    public Map<String, String> getCopyOfContextMap() {
135        Map<String, String> oldMap = inheritableThreadLocal.get();
136        if (oldMap != null) {
137            return new HashMap<String, String>(oldMap);
138        } else {
139            return null;
140        }
141    }
142
143    public void setContextMap(Map<String, String> contextMap) {
144        inheritableThreadLocal.set(new HashMap<String, String>(contextMap));
145    }
146}