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    package org.apache.logging.log4j.spi;
018    
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    /**
024     * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored
025     * is always immutable. This means the Map can be passed to other threads without concern that it will be updated.
026     * Since it is expected that the Map will be passed to many more log events than the number of keys it contains
027     * the performance should be much better than if the Map was copied for each event.
028     */
029    public class DefaultThreadContextMap implements ThreadContextMap {
030    
031        private final boolean useMap;
032    
033        private final ThreadLocal<Map<String, String>> localMap =
034            new InheritableThreadLocal<Map<String, String>>() {
035                @Override
036                protected Map<String, String> childValue(final Map<String, String> parentValue) {
037                    return parentValue == null || !useMap ? null :
038                        Collections.unmodifiableMap(new HashMap<String, String>(parentValue));
039                }
040            };
041    
042        public DefaultThreadContextMap(final boolean useMap) {
043            this.useMap = useMap;
044        }
045    
046        /**
047         * Put a context value (the <code>o</code> parameter) as identified
048         * with the <code>key</code> parameter into the current thread's
049         * context map.
050         * <p/>
051         * <p>If the current thread does not have a context map it is
052         * created as a side effect.
053         * @param key The key name.
054         * @param value The key value.
055         */
056        @Override
057        public void put(final String key, final String value) {
058            if (!useMap) {
059                return;
060            }
061            Map<String, String> map = localMap.get();
062            map = map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
063            map.put(key, value);
064            localMap.set(Collections.unmodifiableMap(map));
065        }
066    
067        /**
068         * Get the context identified by the <code>key</code> parameter.
069         * <p/>
070         * <p>This method has no side effects.
071         * @param key The key to locate.
072         * @return The value associated with the key or null.
073         */
074        @Override
075        public String get(final String key) {
076            final Map<String, String> map = localMap.get();
077            return map == null ? null : map.get(key);
078        }
079    
080        /**
081         * Remove the the context identified by the <code>key</code>
082         * parameter.
083         * @param key The key to remove.
084         */
085        @Override
086        public void remove(final String key) {
087            final Map<String, String> map = localMap.get();
088            if (map != null) {
089                final Map<String, String> copy = new HashMap<String, String>(map);
090                copy.remove(key);
091                localMap.set(Collections.unmodifiableMap(copy));
092            }
093        }
094    
095        /**
096         * Clear the context.
097         */
098        @Override
099        public void clear() {
100            localMap.remove();
101        }
102    
103        /**
104         * Determine if the key is in the context.
105         * @param key The key to locate.
106         * @return True if the key is in the context, false otherwise.
107         */
108        @Override
109        public boolean containsKey(final String key) {
110            final Map<String, String> map = localMap.get();
111            return map != null && map.containsKey(key);
112        }
113    
114        /**
115         * Returns a non-{@code null} mutable copy of the ThreadContext Map.
116         * @return a non-{@code null} mutable copy of the context.
117         */
118        @Override
119        public Map<String, String> getCopy() {
120            final Map<String, String> map = localMap.get();
121            return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
122        }
123    
124        /**
125         * Returns either {@code null} or an immutable view of the context Map.
126         * @return the Context Map.
127         */
128        @Override
129        public Map<String, String> getImmutableMapOrNull() {
130            return localMap.get();
131        }
132    
133        /**
134         * Returns true if the Map is empty.
135         * @return true if the Map is empty, false otherwise.
136         */
137        @Override
138        public boolean isEmpty() {
139            final Map<String, String> map = localMap.get();
140            return map == null || map.size() == 0;
141        }
142    }