View Javadoc

    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.logging.log4j.spi;
    18
    19  import java.util.Collections;
    20  import java.util.HashMap;
    21  import java.util.Map;
    22
    23  import org.apache.logging.log4j.util.PropertiesUtil;
    24
    25  /**
    26   * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored
    27   * is always immutable. This means the Map can be passed to other threads without concern that it will be updated.
    28   * Since it is expected that the Map will be passed to many more log events than the number of keys it contains
    29   * the performance should be much better than if the Map was copied for each event.
    30   */
    31  public class DefaultThreadContextMap implements ThreadContextMap {
    32      /**
    33       * Property name ({@value}) for selecting {@code InheritableThreadLocal} (value "true")
    34       * or plain {@code ThreadLocal} (value is not "true") in the implementation.
    35       */
    36      public static final String INHERITABLE_MAP = "isThreadContextMapInheritable";
    37
    38      private final boolean useMap;
    39      private final ThreadLocal<Map<String, String>> localMap;
    40
    41      public DefaultThreadContextMap(final boolean useMap) {
    42          this.useMap = useMap;
    43          this.localMap = createThreadLocalMap(useMap);
    44      }
    45
    46      // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
    47      // (This method is package protected for JUnit tests.)
    48      static ThreadLocal<Map<String, String>> createThreadLocalMap(final boolean isMapEnabled) {
    49          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
    50          final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
    51          if (inheritable) {
    52              return new InheritableThreadLocal<Map<String, String>>() {
    53                  @Override
    54                  protected Map<String, String> childValue(final Map<String, String> parentValue) {
    55                      return parentValue != null && isMapEnabled //
    56                              ? Collections.unmodifiableMap(new HashMap<String, String>(parentValue)) //
    57                              : null;
    58                  }
    59              };
    60          }
    61          // if not inheritable, return plain ThreadLocal with null as initial value
    62          return new ThreadLocal<Map<String, String>>();
    63      }
    64
    65      @Override
    66      public void put(final String key, final String value) {
    67          if (!useMap) {
    68              return;
    69          }
    70          Map<String, String> map = localMap.get();
    71          map = map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
    72          map.put(key, value);
    73          localMap.set(Collections.unmodifiableMap(map));
    74      }
    75
    76      @Override
    77      public String get(final String key) {
    78          final Map<String, String> map = localMap.get();
    79          return map == null ? null : map.get(key);
    80      }
    81
    82      @Override
    83      public void remove(final String key) {
    84          final Map<String, String> map = localMap.get();
    85          if (map != null) {
    86              final Map<String, String> copy = new HashMap<String, String>(map);
    87              copy.remove(key);
    88              localMap.set(Collections.unmodifiableMap(copy));
    89          }
    90      }
    91
    92      @Override
    93      public void clear() {
    94          localMap.remove();
    95      }
    96
    97      @Override
    98      public boolean containsKey(final String key) {
    99          final Map<String, String> map = localMap.get();
    100         return map != null && map.containsKey(key);
    101     }
    102
    103     @Override
    104     public Map<String, String> getCopy() {
    105         final Map<String, String> map = localMap.get();
    106         return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
    107     }
    108
    109     @Override
    110     public Map<String, String> getImmutableMapOrNull() {
    111         return localMap.get();
    112     }
    113
    114     @Override
    115     public boolean isEmpty() {
    116         final Map<String, String> map = localMap.get();
    117         return map == null || map.size() == 0;
    118     }
    119
    120     @Override
    121     public String toString() {
    122         final Map<String, String> map = localMap.get();
    123         return map == null ? "{}" : map.toString();
    124     }
    125
    126     @Override
    127     public int hashCode() {
    128         final int prime = 31;
    129         int result = 1;
    130         final Map<String, String> map = this.localMap.get();
    131         result = prime * result + ((map == null) ? 0 : map.hashCode());
    132         result = prime * result + (this.useMap ? 1231 : 1237);
    133         return result;
    134     }
    135
    136     @Override
    137     public boolean equals(final Object obj) {
    138         if (this == obj) {
    139             return true;
    140         }
    141         if (obj == null) {
    142             return false;
    143         }
    144         if (obj instanceof DefaultThreadContextMap) {
    145             final DefaultThreadContextMap other = (DefaultThreadContextMap) obj;
    146             if (this.useMap != other.useMap) {
    147                 return false;
    148             }
    149         }
    150         if (!(obj instanceof ThreadContextMap)) {
    151             return false;
    152         }
    153         final ThreadContextMap other = (ThreadContextMap) obj;
    154         final Map<String, String> map = this.localMap.get();
    155         final Map<String, String> otherMap = other.getImmutableMapOrNull();
    156         if (map == null) {
    157             if (otherMap != null) {
    158                 return false;
    159             }
    160         } else if (!map.equals(otherMap)) {
    161             return false;
    162         }
    163         return true;
    164     }
    165 }