LOG4J2-1349 simplified: collapsed abstract with concrete thread context map 
classes


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4b166868
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4b166868
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4b166868

Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 4b1668684d92a41f26e185d942bd33550c4c5464
Parents: 65a551e
Author: rpopma <[email protected]>
Authored: Wed Aug 31 21:50:25 2016 +0900
Committer: rpopma <[email protected]>
Committed: Wed Aug 31 21:50:25 2016 +0900

----------------------------------------------------------------------
 ...AbstractCopyOnWriteMutableThreadContext.java | 191 ------------------
 ...AbstractGarbageFreeMutableThreadContext.java | 193 -------------------
 .../CopyOnWriteSortedArrayThreadContextMap.java | 183 +++++++++++++++++-
 .../GarbageFreeSortedArrayThreadContextMap.java | 186 +++++++++++++++++-
 .../CopyOnWriteOpenHashMapThreadContextMap.java |   4 +-
 .../GarbageFreeOpenHashMapThreadContextMap.java |   4 +-
 6 files changed, 361 insertions(+), 400 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java
deleted file mode 100644
index c71ed95..0000000
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.spi;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.logging.log4j.util.PropertiesUtil;
-
-/**
- * ThreadContextMap implementation backed by {@code MutableContextData}.
- * A new ThreadContext Map is created each time it is updated and the Map 
stored is always
- * immutable. This means the Map can be passed to other threads without 
concern that it will be updated. Since it is
- * expected that the Map will be passed to many more log events than the 
number of keys it contains the performance
- * should be much better than if the Map was copied for each event.
- *
- * @since 2.7
- */
-public abstract class AbstractCopyOnWriteMutableThreadContext implements 
ThreadContextMap, ThreadContextMap2,
-        CopyOnWrite, MutableContextDataSupplier {
-
-    /**
-     * The default initial capacity.
-     */
-    protected static final int DEFAULT_INITIAL_CAPACITY = 16;
-
-    /**
-     * System property name that can be used to control the data structure's 
initial capacity.
-     */
-    protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
-
-    /**
-     * Property name ({@value} ) for selecting {@code InheritableThreadLocal} 
(value "true") or plain
-     * {@code ThreadLocal} (value is not "true") in the implementation.
-     */
-    public static final String INHERITABLE_MAP = 
"isThreadContextMapInheritable";
-
-    private final ThreadLocal<MutableContextData> localMap;
-
-    public AbstractCopyOnWriteMutableThreadContext() {
-        this.localMap = createThreadLocalMap();
-    }
-
-    // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
-    // (This method is package protected for JUnit tests.)
-    private ThreadLocal<MutableContextData> createThreadLocalMap() {
-        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
-        final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
-        if (inheritable) {
-            return new InheritableThreadLocal<MutableContextData>() {
-                @Override
-                protected MutableContextData childValue(final 
MutableContextData parentValue) {
-                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
-                }
-            };
-        }
-        // if not inheritable, return plain ThreadLocal with null as initial 
value
-        return new ThreadLocal<>();
-    }
-
-    protected abstract MutableContextData createMutableContextData();
-
-    protected abstract MutableContextData createMutableContextData(final 
ContextData original);
-
-    @Override
-    public void put(final String key, final String value) {
-        MutableContextData map = localMap.get();
-        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
-        map.putValue(key, value);
-        localMap.set(map);
-    }
-
-    @Override
-    public void putAll(final Map<String, String> values) {
-        if (values == null || values.isEmpty()) {
-            return;
-        }
-        MutableContextData map = localMap.get();
-        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
-        for (final Map.Entry<String, String> entry : values.entrySet()) {
-            map.putValue(entry.getKey(), entry.getValue());
-        }
-        localMap.set(map);
-    }
-
-    @Override
-    public String get(final String key) {
-        final MutableContextData map = localMap.get();
-        return map == null ? null : (String) map.getValue(key);
-    }
-
-    @Override
-    public void remove(final String key) {
-        final MutableContextData map = localMap.get();
-        if (map != null) {
-            final MutableContextData copy = createMutableContextData(map);
-            copy.remove(key);
-            localMap.set(copy);
-        }
-    }
-
-    @Override
-    public void clear() {
-        localMap.remove();
-    }
-
-    @Override
-    public boolean containsKey(final String key) {
-        final MutableContextData map = localMap.get();
-        return map != null && map.containsKey(key);
-    }
-
-    @Override
-    public Map<String, String> getCopy() {
-        final MutableContextData map = localMap.get();
-        return map == null ? Collections.<String, String>emptyMap() : 
map.asMap();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public MutableContextData getMutableContextData() {
-        return localMap.get();
-    }
-
-    @Override
-    public Map<String, String> getImmutableMapOrNull() {
-        final MutableContextData map = localMap.get();
-        return map == null ? null : Collections.unmodifiableMap(map.asMap());
-    }
-
-    @Override
-    public boolean isEmpty() {
-        final MutableContextData map = localMap.get();
-        return map == null || map.size() == 0;
-    }
-
-    @Override
-    public String toString() {
-        final MutableContextData map = localMap.get();
-        return map == null ? "{}" : map.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        final MutableContextData map = this.localMap.get();
-        result = prime * result + ((map == null) ? 0 : map.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof ThreadContextMap)) {
-            return false;
-        }
-        final ThreadContextMap other = (ThreadContextMap) obj;
-        final Map<String, String> map = this.getImmutableMapOrNull();
-        final Map<String, String> otherMap = other.getImmutableMapOrNull();
-        if (map == null) {
-            if (otherMap != null) {
-                return false;
-            }
-        } else if (!map.equals(otherMap)) {
-            return false;
-        }
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java
deleted file mode 100644
index 4e86761..0000000
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.spi;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.logging.log4j.util.PropertiesUtil;
-
-/**
- * Garbage-free ThreadContextMap implementation backed by {@code 
MutableContextData}.
- * <p>
- * This implementation does <em>not</em> make a copy of its contents on every 
operation, so this data structure cannot
- * be passed to log events. It is advisable to provide a fast way to copy data 
from this data structure into log
- * events.
- * </p>
- * @since 2.7
- */
-public abstract class AbstractGarbageFreeMutableThreadContext implements 
ThreadContextMap, ThreadContextMap2,
-        MutableContextDataSupplier {
-
-    /**
-     * The default initial capacity.
-     */
-    protected static final int DEFAULT_INITIAL_CAPACITY = 16;
-
-    /**
-     * System property name that can be used to control the data structure's 
initial capacity.
-     */
-    protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
-
-    /**
-     * Property name ({@value} ) for selecting {@code InheritableThreadLocal} 
(value "true") or plain
-     * {@code ThreadLocal} (value is not "true") in the implementation.
-     */
-    public static final String INHERITABLE_MAP = 
"isThreadContextMapInheritable";
-
-    private final ThreadLocal<MutableContextData> localMap;
-
-    public AbstractGarbageFreeMutableThreadContext() {
-        this.localMap = createThreadLocalMap();
-    }
-
-    // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
-    // (This method is package protected for JUnit tests.)
-    private ThreadLocal<MutableContextData> createThreadLocalMap() {
-        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
-        final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
-        if (inheritable) {
-            return new InheritableThreadLocal<MutableContextData>() {
-                @Override
-                protected MutableContextData childValue(final 
MutableContextData parentValue) {
-                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
-                }
-            };
-        }
-        // if not inheritable, return plain ThreadLocal with null as initial 
value
-        return new ThreadLocal<>();
-    }
-
-    protected abstract MutableContextData createMutableContextData();
-
-    protected abstract MutableContextData createMutableContextData(final 
ContextData original);
-
-    private MutableContextData getThreadLocalMap() {
-        MutableContextData map = localMap.get();
-        if (map == null) {
-            map = createMutableContextData();
-            localMap.set(map);
-        }
-        return map;
-    }
-
-    @Override
-    public void put(final String key, final String value) {
-        getThreadLocalMap().putValue(key, value);
-    }
-
-    @Override
-    public void putAll(final Map<String, String> values) {
-        if (values == null || values.isEmpty()) {
-            return;
-        }
-        final MutableContextData map = getThreadLocalMap();
-        for (final Map.Entry<String, String> entry : values.entrySet()) {
-            map.putValue(entry.getKey(), entry.getValue());
-        }
-    }
-
-    @Override
-    public String get(final String key) {
-        final MutableContextData map = localMap.get();
-        return map == null ? null : (String) map.getValue(key);
-    }
-
-    @Override
-    public void remove(final String key) {
-        final MutableContextData map = localMap.get();
-        if (map != null) {
-            map.remove(key);
-        }
-    }
-
-    @Override
-    public void clear() {
-        localMap.remove();
-    }
-
-    @Override
-    public boolean containsKey(final String key) {
-        final MutableContextData map = localMap.get();
-        return map != null && map.containsKey(key);
-    }
-
-    @Override
-    public Map<String, String> getCopy() {
-        final MutableContextData map = localMap.get();
-        return map == null ? Collections.<String, String>emptyMap() : 
map.asMap();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public MutableContextData getMutableContextData() {
-        return localMap.get();
-    }
-
-    @Override
-    public Map<String, String> getImmutableMapOrNull() {
-        final MutableContextData map = localMap.get();
-        return map == null ? null : Collections.unmodifiableMap(map.asMap());
-    }
-
-    @Override
-    public boolean isEmpty() {
-        final MutableContextData map = localMap.get();
-        return map == null || map.size() == 0;
-    }
-
-    @Override
-    public String toString() {
-        final MutableContextData map = localMap.get();
-        return map == null ? "{}" : map.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        final MutableContextData map = this.localMap.get();
-        result = prime * result + ((map == null) ? 0 : map.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof ThreadContextMap)) {
-            return false;
-        }
-        final ThreadContextMap other = (ThreadContextMap) obj;
-        final Map<String, String> map = this.getImmutableMapOrNull();
-        final Map<String, String> otherMap = other.getImmutableMapOrNull();
-        if (map == null) {
-            if (otherMap != null) {
-                return false;
-            }
-        } else if (!map.equals(otherMap)) {
-            return false;
-        }
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
index f1bf509..afa9d99 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -16,24 +16,197 @@
  */
 package org.apache.logging.log4j.spi;
 
+import java.util.Collections;
+import java.util.Map;
+
 import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
- * {@code SortedArrayContextData}-based implementation of the {@code 
ThreadContextMap} interface that creates a copy of
+ * {@code ArrayContextData}-based implementation of the {@code 
ThreadContextMap} interface that creates a copy of
  * the data structure on every modification. Any particular instance of the 
data structure is a snapshot of the
- * ThreadContext at some point in time and can safely be passed off to other 
threads
+ * ThreadContext at some point in time and can safely be passed off to other 
threads.  Since it is
+ * expected that the Map will be passed to many more log events than the 
number of keys it contains the performance
+ * should be much better than if the Map was copied for each event.
  *
  * @since 2.7
  */
-public class CopyOnWriteSortedArrayThreadContextMap extends 
AbstractCopyOnWriteMutableThreadContext {
-    @Override
+public class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap, ThreadContextMap2,
+        CopyOnWrite, MutableContextDataSupplier {
+
+    /**
+     * The default initial capacity.
+     */
+    protected static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+    /**
+     * System property name that can be used to control the data structure's 
initial capacity.
+     */
+    protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
+
+    /**
+     * Property name ({@value} ) for selecting {@code InheritableThreadLocal} 
(value "true") or plain
+     * {@code ThreadLocal} (value is not "true") in the implementation.
+     */
+    public static final String INHERITABLE_MAP = 
"isThreadContextMapInheritable";
+
+    private final ThreadLocal<MutableContextData> localMap;
+
+    public CopyOnWriteSortedArrayThreadContextMap() {
+        this.localMap = createThreadLocalMap();
+    }
+
+    // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
+    // (This method is package protected for JUnit tests.)
+    private ThreadLocal<MutableContextData> createThreadLocalMap() {
+        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+        final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
+        if (inheritable) {
+            return new InheritableThreadLocal<MutableContextData>() {
+                @Override
+                protected MutableContextData childValue(final 
MutableContextData parentValue) {
+                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
+                }
+            };
+        }
+        // if not inheritable, return plain ThreadLocal with null as initial 
value
+        return new ThreadLocal<>();
+    }
+
+    /**
+     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     */
     protected MutableContextData createMutableContextData() {
         return new 
ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
                 PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
     }
 
-    @Override
+    /**
+     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map, pre-populated
+     * with the contents of the specified context data.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     * @param original the key-value pairs to initialize the returned context 
data with
+     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     */
     protected MutableContextData createMutableContextData(final ContextData 
original) {
         return new ArrayContextData(original);
     }
+
+    @Override
+    public void put(final String key, final String value) {
+        MutableContextData map = localMap.get();
+        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
+        map.putValue(key, value);
+        localMap.set(map);
+    }
+
+    @Override
+    public void putAll(final Map<String, String> values) {
+        if (values == null || values.isEmpty()) {
+            return;
+        }
+        MutableContextData map = localMap.get();
+        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
+        for (final Map.Entry<String, String> entry : values.entrySet()) {
+            map.putValue(entry.getKey(), entry.getValue());
+        }
+        localMap.set(map);
+    }
+
+    @Override
+    public String get(final String key) {
+        final MutableContextData map = localMap.get();
+        return map == null ? null : (String) map.getValue(key);
+    }
+
+    @Override
+    public void remove(final String key) {
+        final MutableContextData map = localMap.get();
+        if (map != null) {
+            final MutableContextData copy = createMutableContextData(map);
+            copy.remove(key);
+            localMap.set(copy);
+        }
+    }
+
+    @Override
+    public void clear() {
+        localMap.remove();
+    }
+
+    @Override
+    public boolean containsKey(final String key) {
+        final MutableContextData map = localMap.get();
+        return map != null && map.containsKey(key);
+    }
+
+    @Override
+    public Map<String, String> getCopy() {
+        final MutableContextData map = localMap.get();
+        return map == null ? Collections.<String, String>emptyMap() : 
map.asMap();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public MutableContextData getMutableContextData() {
+        return localMap.get();
+    }
+
+    @Override
+    public Map<String, String> getImmutableMapOrNull() {
+        final MutableContextData map = localMap.get();
+        return map == null ? null : Collections.unmodifiableMap(map.asMap());
+    }
+
+    @Override
+    public boolean isEmpty() {
+        final MutableContextData map = localMap.get();
+        return map == null || map.size() == 0;
+    }
+
+    @Override
+    public String toString() {
+        final MutableContextData map = localMap.get();
+        return map == null ? "{}" : map.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        final MutableContextData map = this.localMap.get();
+        result = prime * result + ((map == null) ? 0 : map.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof ThreadContextMap)) {
+            return false;
+        }
+        final ThreadContextMap other = (ThreadContextMap) obj;
+        final Map<String, String> map = this.getImmutableMapOrNull();
+        final Map<String, String> otherMap = other.getImmutableMapOrNull();
+        if (map == null) {
+            if (otherMap != null) {
+                return false;
+            }
+        } else if (!map.equals(otherMap)) {
+            return false;
+        }
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
index 241af5c..5b4a938 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
@@ -16,27 +16,199 @@
  */
 package org.apache.logging.log4j.spi;
 
+import java.util.Collections;
+import java.util.Map;
+
 import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
- * {@code SortedArrayContextData}-based implementation of the {@code 
ThreadContextMap} interface that attempts not to
+ * {@code ArrayContextData}-based implementation of the {@code 
ThreadContextMap} interface that attempts not to
  * create temporary objects. Adding and removing key-value pairs will not 
create temporary objects.
  * <p>
- * Since the underlying data structure is modified directly it is not suitable 
for passing by reference to other
- * threads. Instead, client code needs to copy the contents when interacting 
with another thread.
+ * This implementation does <em>not</em> make a copy of its contents on every 
operation, so this data structure cannot
+ * be passed to log events. Instead, client code needs to copy the contents 
when interacting with another thread.
  * </p>
- *
  * @since 2.7
  */
-public class GarbageFreeSortedArrayThreadContextMap extends 
AbstractGarbageFreeMutableThreadContext {
-    @Override
+public abstract class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap, ThreadContextMap2,
+        MutableContextDataSupplier {
+
+    /**
+     * The default initial capacity.
+     */
+    protected static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+    /**
+     * System property name that can be used to control the data structure's 
initial capacity.
+     */
+    protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
+
+    /**
+     * Property name ({@value} ) for selecting {@code InheritableThreadLocal} 
(value "true") or plain
+     * {@code ThreadLocal} (value is not "true") in the implementation.
+     */
+    public static final String INHERITABLE_MAP = 
"isThreadContextMapInheritable";
+
+    private final ThreadLocal<MutableContextData> localMap;
+
+    public GarbageFreeSortedArrayThreadContextMap() {
+        this.localMap = createThreadLocalMap();
+    }
+
+    // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
+    // (This method is package protected for JUnit tests.)
+    private ThreadLocal<MutableContextData> createThreadLocalMap() {
+        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+        final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
+        if (inheritable) {
+            return new InheritableThreadLocal<MutableContextData>() {
+                @Override
+                protected MutableContextData childValue(final 
MutableContextData parentValue) {
+                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
+                }
+            };
+        }
+        // if not inheritable, return plain ThreadLocal with null as initial 
value
+        return new ThreadLocal<>();
+    }
+
+    /**
+     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     */
     protected MutableContextData createMutableContextData() {
         return new 
ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
                 PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
     }
 
-    @Override
+    /**
+     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map, pre-populated
+     * with the contents of the specified context data.
+     * <p>
+     * Subclasses may override.
+     * </p>
+     * @param original the key-value pairs to initialize the returned context 
data with
+     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     */
     protected MutableContextData createMutableContextData(final ContextData 
original) {
         return new ArrayContextData(original);
     }
+
+    private MutableContextData getThreadLocalMap() {
+        MutableContextData map = localMap.get();
+        if (map == null) {
+            map = createMutableContextData();
+            localMap.set(map);
+        }
+        return map;
+    }
+
+    @Override
+    public void put(final String key, final String value) {
+        getThreadLocalMap().putValue(key, value);
+    }
+
+    @Override
+    public void putAll(final Map<String, String> values) {
+        if (values == null || values.isEmpty()) {
+            return;
+        }
+        final MutableContextData map = getThreadLocalMap();
+        for (final Map.Entry<String, String> entry : values.entrySet()) {
+            map.putValue(entry.getKey(), entry.getValue());
+        }
+    }
+
+    @Override
+    public String get(final String key) {
+        final MutableContextData map = localMap.get();
+        return map == null ? null : (String) map.getValue(key);
+    }
+
+    @Override
+    public void remove(final String key) {
+        final MutableContextData map = localMap.get();
+        if (map != null) {
+            map.remove(key);
+        }
+    }
+
+    @Override
+    public void clear() {
+        localMap.remove();
+    }
+
+    @Override
+    public boolean containsKey(final String key) {
+        final MutableContextData map = localMap.get();
+        return map != null && map.containsKey(key);
+    }
+
+    @Override
+    public Map<String, String> getCopy() {
+        final MutableContextData map = localMap.get();
+        return map == null ? Collections.<String, String>emptyMap() : 
map.asMap();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public MutableContextData getMutableContextData() {
+        return localMap.get();
+    }
+
+    @Override
+    public Map<String, String> getImmutableMapOrNull() {
+        final MutableContextData map = localMap.get();
+        return map == null ? null : Collections.unmodifiableMap(map.asMap());
+    }
+
+    @Override
+    public boolean isEmpty() {
+        final MutableContextData map = localMap.get();
+        return map == null || map.size() == 0;
+    }
+
+    @Override
+    public String toString() {
+        final MutableContextData map = localMap.get();
+        return map == null ? "{}" : map.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        final MutableContextData map = this.localMap.get();
+        result = prime * result + ((map == null) ? 0 : map.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof ThreadContextMap)) {
+            return false;
+        }
+        final ThreadContextMap other = (ThreadContextMap) obj;
+        final Map<String, String> map = this.getImmutableMapOrNull();
+        final Map<String, String> otherMap = other.getImmutableMapOrNull();
+        if (map == null) {
+            if (otherMap != null) {
+                return false;
+            }
+        } else if (!map.equals(otherMap)) {
+            return false;
+        }
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/CopyOnWriteOpenHashMapThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/CopyOnWriteOpenHashMapThreadContextMap.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/CopyOnWriteOpenHashMapThreadContextMap.java
index f219c90..e34c031 100644
--- 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/CopyOnWriteOpenHashMapThreadContextMap.java
+++ 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/CopyOnWriteOpenHashMapThreadContextMap.java
@@ -16,7 +16,7 @@
  */
 package org.apache.logging.log4j.perf.nogc;
 
-import org.apache.logging.log4j.spi.AbstractCopyOnWriteMutableThreadContext;
+import org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap;
 import org.apache.logging.log4j.spi.ContextData;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.util.PropertiesUtil;
@@ -28,7 +28,7 @@ import org.apache.logging.log4j.util.PropertiesUtil;
  *
  * @since 2.7
  */
-public class CopyOnWriteOpenHashMapThreadContextMap extends 
AbstractCopyOnWriteMutableThreadContext {
+public class CopyOnWriteOpenHashMapThreadContextMap extends 
CopyOnWriteSortedArrayThreadContextMap {
     @Override
     protected MutableContextData createMutableContextData() {
         return new 
OpenHashMapContextData<>(PropertiesUtil.getProperties().getIntegerProperty(

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4b166868/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/GarbageFreeOpenHashMapThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/GarbageFreeOpenHashMapThreadContextMap.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/GarbageFreeOpenHashMapThreadContextMap.java
index 400a76c..cb97936 100644
--- 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/GarbageFreeOpenHashMapThreadContextMap.java
+++ 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/nogc/GarbageFreeOpenHashMapThreadContextMap.java
@@ -16,7 +16,7 @@
  */
 package org.apache.logging.log4j.perf.nogc;
 
-import org.apache.logging.log4j.spi.AbstractGarbageFreeMutableThreadContext;
+import org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap;
 import org.apache.logging.log4j.spi.ContextData;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.util.PropertiesUtil;
@@ -31,7 +31,7 @@ import org.apache.logging.log4j.util.PropertiesUtil;
  *
  * @since 2.7
  */
-public class GarbageFreeOpenHashMapThreadContextMap extends 
AbstractGarbageFreeMutableThreadContext {
+public class GarbageFreeOpenHashMapThreadContextMap extends 
GarbageFreeSortedArrayThreadContextMap {
     @Override
     protected MutableContextData createMutableContextData() {
         return new 
OpenHashMapContextData<>(PropertiesUtil.getProperties().getIntegerProperty(

Reply via email to