You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/08/22 15:40:15 UTC
[01/16] logging-log4j2 git commit: LOG4J2-1349 GC-free ThreadContext
initial commit
Repository: logging-log4j2
Updated Branches:
refs/heads/LOG4J2-1349-gcfree-threadcontext [created] 167b281e8
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/OpenHashMapContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/OpenHashMapContextDataTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/OpenHashMapContextDataTest.java
deleted file mode 100644
index dd76cc3..0000000
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/OpenHashMapContextDataTest.java
+++ /dev/null
@@ -1,506 +0,0 @@
-package org.apache.logging.log4j.core.impl;/*
- * 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.
- */
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.TriConsumer;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests the OpenHashMapContextData class.
- */
-public class OpenHashMapContextDataTest {
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsNegativeCapacity() throws Exception {
- new OpenHashMapContextData(-1);
- }
-
- @Test
- public void testConstructorAllowsZeroCapacity() throws Exception {
- OpenHashMapContextData data = new OpenHashMapContextData(0);
- assertEquals(2, data.arraySize);
- }
-
- @Test(expected = NullPointerException.class)
- @SuppressWarnings("unchecked")
- public void testConstructorDisallowsNullMap() throws Exception {
- assertEquals(0, new OpenHashMapContextData((Map) null).size());
- }
-
- @Test(expected = NullPointerException.class)
- @SuppressWarnings("unchecked")
- public void testConstructorDisallowsNullContextData() throws Exception {
- assertEquals(0, new OpenHashMapContextData((ContextData) null).size());
- }
-
- @Test
- public void testToString() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals("{B=Bvalue, a=avalue, 3=3value}", original.toString());
- }
-
- @Test
- public void testSerialization() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final byte[] binary = serialize(original);
- final OpenHashMapContextData copy = deserialize(binary);
- assertEquals(original, copy);
- }
-
- private byte[] serialize(final OpenHashMapContextData data) throws IOException {
- final ByteArrayOutputStream arr = new ByteArrayOutputStream();
- final ObjectOutputStream out = new ObjectOutputStream(arr);
- out.writeObject(data);
- return arr.toByteArray();
- }
-
- private OpenHashMapContextData deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
- final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
- final ObjectInputStream in = new ObjectInputStream(inArr);
- final OpenHashMapContextData result = (OpenHashMapContextData) in.readObject();
- return result;
- }
-
- @Test
- public void testPutAll() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final OpenHashMapContextData other = new OpenHashMapContextData();
- other.putAll(original);
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testEquals() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(original, original); // equal to itself
-
- final OpenHashMapContextData other = new OpenHashMapContextData();
- other.putValue("a", "avalue");
- assertNotEquals(original, other);
-
- other.putValue("B", "Bvalue");
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testAsMap() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final Map<String, Object> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
-
- assertEquals(expected, original.asMap());
- }
-
- @Test
- public void testGetCopyDelegatesToAsMap() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- assertEquals(original.getCopy(), original.asMap());
-
- original.putValue("B", "Bvalue");
- assertEquals(original.getCopy(), original.asMap());
-
- original.putValue("3", "3value");
- assertEquals(original.getCopy(), original.asMap());
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void testGetImmutableMapOrNull() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- original.putValue("B", "Bvalue");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- original.putValue("3", "3value");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- try {
- original.getImmutableMapOrNull().put("abc", "xyz");
- fail("Expected map to be immutable");
- } catch (final UnsupportedOperationException ok) {
- //ok
- }
- }
-
- @Test
- public void testPutInsertsInAlphabeticOrder() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.put("a", "avalue");
- original.put("B", "Bvalue");
- original.put("3", "3value");
- original.put("c", "cvalue");
- original.put("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("3value", original.getValue("3"));
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("dvalue", original.getValue("d"));
- }
-
- @Test
- public void testPutValueInsertsInAlphabeticOrder() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("3value", original.getValue("3"));
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("dvalue", original.getValue("d"));
- }
-
- @Test
- public void testNullKeysAllowed() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
- assertEquals("{B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
-
- original.putValue(null, "otherNullvalue");
- assertEquals("{null=otherNullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
- assertEquals(6, original.size());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
-
- original.putValue(null, "abc");
- assertEquals(6, original.size());
- assertEquals("{null=abc, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
- }
-
- @Test
- public void testNullKeysCopiedToAsMap() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
-
- HashMap<String, String> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
- expected.put("c", "cvalue");
- expected.put("d", "dvalue");
- assertEquals("initial", expected, original.asMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key", expected, original.asMap());
-
- original.putValue(null, "otherNullvalue");
- expected.put(null, "otherNullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value2", expected, original.asMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value1 again", expected, original.asMap());
-
- original.putValue(null, "abc");
- expected.put(null, "abc");
- assertEquals(6, original.size());
- assertEquals("with null key value3", expected, original.asMap());
- }
-
- @Test
- public void testRemove() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.remove("a");
- assertEquals(0, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.remove("B");
- assertEquals(0, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testNullValuesArePreserved() {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.putValue("a", null);
- assertEquals(1, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.putValue("B", null);
- assertEquals(2, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testGet() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.put("a", "avalue");
- original.put("B", "Bvalue");
- original.put("3", "3value");
-
- assertEquals("avalue", original.get("a"));
- assertEquals("Bvalue", original.get("B"));
- assertEquals("3value", original.get("3"));
-
- original.putValue("0", "0value");
- assertEquals("0value", original.get("0"));
- assertEquals("3value", original.get("3"));
- assertEquals("Bvalue", original.get("B"));
- assertEquals("avalue", original.get("a"));
- }
-
- @Test
- public void testGetValue_GetValueAt() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("3value", original.getValue("3"));
- original.putValue("0", "0value");
- assertEquals("0value", original.getValue("0"));
- assertEquals("3value", original.getValue("3"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("avalue", original.getValue("a"));
- }
-
- @Test
- public void testClear() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(3, original.size());
-
- original.clear();
- assertEquals(0, original.size());
-
- // ensure slots in the values array are nulled out
- Field f = OpenHashMapContextData.class.getDeclaredField("values");
- f.setAccessible(true);
- Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
- }
-
- @Test
- public void testContainsKey() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- assertFalse("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("a", "avalue");
- assertTrue("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("B", "Bvalue");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("3", "3value");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("A", "AAA");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertTrue("A", original.containsKey("A"));
- }
-
- @Test
- public void testSizeAndIsEmpty() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- assertEquals(0, original.size());
- assertTrue("initial", original.isEmpty());
-
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("B", "Bvalue");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("3", "3value");
- assertEquals(3, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("B");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("3");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("a");
- assertEquals(0, original.size());
- assertTrue("size=" + original.size(), original.isEmpty());
- }
-
- @Test
- public void testForEachBiConsumer() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- final List<String> keys = new ArrayList<>(Arrays.asList("a", "B", "3", null));
- final List<String> values = new ArrayList<>(Arrays.asList("aValue", "Bvalue", "3Value", "nullValue"));
- for (int i = 0; i < keys.size(); i++) {
- original.put(keys.get(i), values.get(i));
- }
-
- original.forEach(new BiConsumer<String, String>() {
- int count = 0;
- @Override
- public void accept(final String key, final String value) {
- assertTrue("key exists", keys.remove(key));
- assertTrue("val exists", values.remove(value));
- assertEquals("val", value, original.getValue(key));
- count++;
- assertTrue("count should not exceed size but was " + count, count <= original.size());
- }
- });
- }
-
- static class State<K, V> {
- OpenHashMapContextData data;
- int count;
- List<K> keys;
- List<V> values;
- }
- private static TriConsumer<String, String, State> COUNTER = new TriConsumer<String, String, State>() {
- @Override
- public void accept(final String key, final String value, final State state) {
- assertTrue("key exists", state.keys.remove(key));
- assertTrue("val exists", state.values.remove(value));
- assertEquals("val", value, state.data.getValue(key));
- state.count++;
- assertTrue("count should not exceed size but was " + state.count,
- state.count <= state.data.size());
- }
- };
-
- @Test
- public void testForEachTriConsumer() throws Exception {
- final OpenHashMapContextData original = new OpenHashMapContextData();
- final List<String> keys = Arrays.asList("a", "B", "3", null);
- final List<String> values = Arrays.asList("aValue", "Bvalue", "3Value", "nullValue");
- for (int i = 0; i < keys.size(); i++) {
- original.put(keys.get(i), values.get(i));
- }
- final State state = new State();
- state.data = original;
- state.keys = new ArrayList(keys);
- state.values = new ArrayList(values);
-
- original.forEach(COUNTER, state);
- assertEquals(state.count, original.size());
- assertTrue("all keys", state.keys.isEmpty());
- assertTrue("all values", state.values.isEmpty());
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
----------------------------------------------------------------------
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
index a56b4e7..7753748 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEvent.java
@@ -29,7 +29,7 @@ import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LoggingException;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.util.Patterns;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
index 9587e2d..ada45b4 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
@@ -22,10 +22,10 @@ import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
-import org.apache.logging.log4j.core.impl.ArrayContextData;
-import org.apache.logging.log4j.core.impl.OpenHashMapContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.TriConsumer;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.OpenHashMapContextData;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
[13/16] logging-log4j2 git commit: LOG4J2-1010, LOG4J2-1447,
LOG4J2-1349 use ThreadContextMap for fine-grained context data
injection
Posted by rp...@apache.org.
LOG4J2-1010, LOG4J2-1447, LOG4J2-1349 use ThreadContextMap for fine-grained context data injection
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/24cbcc6a
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/24cbcc6a
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/24cbcc6a
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 24cbcc6a1d2bae5607f6db511f28a00968e7d4aa
Parents: fcb58f1
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 10:39:30 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:15 2016 +0900
----------------------------------------------------------------------
...AbstractCopyOnWriteMutableThreadContext.java | 2 +-
...AbstractGarbageFreeMutableThreadContext.java | 2 +-
.../core/impl/ContextDataInjectorFactory.java | 17 +++++----
.../core/impl/ThreadContextDataInjector.java | 37 ++++++++++----------
4 files changed, 30 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24cbcc6a/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
index 9837ba8..ffc09ac 100644
--- 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
@@ -129,7 +129,7 @@ public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadC
return map == null ? Collections.<String, String>emptyMap() : map.asMap();
}
- public ContextData getContextData() {
+ public MutableContextData getContextData() {
return localMap.get();
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24cbcc6a/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
index 7a0e6f8..95dd30a 100644
--- 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
@@ -131,7 +131,7 @@ public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadC
return map == null ? Collections.<String, String>emptyMap() : map.asMap();
}
- public ContextData getContextData() {
+ public MutableContextData getContextData() {
return localMap.get();
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24cbcc6a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
index d3da505..fa1c28f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
@@ -17,7 +17,10 @@
package org.apache.logging.log4j.core.impl;
import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.ThreadContextAccess;
import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.spi.AbstractCopyOnWriteMutableThreadContext;
+import org.apache.logging.log4j.spi.AbstractGarbageFreeMutableThreadContext;
import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.spi.ThreadContextMap;
import org.apache.logging.log4j.status.StatusLogger;
@@ -69,13 +72,13 @@ public class ContextDataInjectorFactory {
}
private static ContextDataInjector createDefaultInjector() {
-// final ThreadContextMap threadContextMap = null; // ThreadContext.getThreadContextMap(); TODO LOG4J2-1349
-// if (threadContextMap instanceof AbstractCopyOnWriteMutableThreadContext) {
-// return new ThreadContextDataInjector.ForCopyOnWriteMutableThreadContextMap();
-// }
-// if (threadContextMap instanceof AbstractGarbageFreeMutableThreadContext) {
-// return new ThreadContextDataInjector.ForGarbageFreeMutableThreadContextMap();
-// }
+ final ThreadContextMap threadContextMap = ThreadContextAccess.getThreadContextMap();
+ if (threadContextMap instanceof AbstractCopyOnWriteMutableThreadContext) {
+ return new ThreadContextDataInjector.ForCopyOnWriteMutableThreadContextMap();
+ }
+ if (threadContextMap instanceof AbstractGarbageFreeMutableThreadContext) {
+ return new ThreadContextDataInjector.ForGarbageFreeMutableThreadContextMap();
+ }
return new ThreadContextDataInjector.ForDefaultThreadContextMap();
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/24cbcc6a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
index 786d23f..6c56520 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
@@ -20,10 +20,12 @@ import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.ThreadContextAccess;
import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.spi.AbstractCopyOnWriteMutableThreadContext;
+import org.apache.logging.log4j.spi.AbstractGarbageFreeMutableThreadContext;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.spi.MutableContextData;
-import org.apache.logging.log4j.spi.ThreadContextMap;
/**
* {@code ThreadContextDataInjector} contains a number of strategies for copying key-value pairs from the various
@@ -105,10 +107,9 @@ public class ThreadContextDataInjector {
// modified.
copyProperties(props, reusable);
- // TODO LOG4J2-1349
-// final MutableContextData immutableCopy = ((AbstractGarbageFreeMutableThreadContext)
-// ThreadContext.getThreadContextMap()).getContextData();
-// reusable.putAll(immutableCopy);
+ final ContextData immutableCopy = ((AbstractGarbageFreeMutableThreadContext)
+ ThreadContextAccess.getThreadContextMap()).getContextData();
+ reusable.putAll(immutableCopy);
return reusable;
}
}
@@ -133,19 +134,17 @@ public class ThreadContextDataInjector {
*/
@Override
public MutableContextData injectContextData(final List<Property> props, final MutableContextData reusable) {
- // TODO LOG4J2-1349
-
-// // If there are no configuration properties we want to just return the ThreadContext's MutableContextData:
-// // it is a copy-on-write data structure so we are sure ThreadContext changes will not affect our copy.
-// final MutableContextData immutableCopy = ((AbstractCopyOnWriteMutableThreadContext)
-// ThreadContext.getThreadContextMap()).getContextData();
-// if (props == null || props.isEmpty()) {
-// return immutableCopy;
-// }
-// // However, if the list of Properties is non-empty we need to combine the properties and the ThreadContext
-// // data. In that case we will copy the key-value pairs into the specified reusable MutableContextData.
-// copyProperties(props, reusable);
-// reusable.putAll(immutableCopy);
+ // If there are no configuration properties we want to just return the ThreadContext's MutableContextData:
+ // it is a copy-on-write data structure so we are sure ThreadContext changes will not affect our copy.
+ final MutableContextData immutableCopy = ((AbstractCopyOnWriteMutableThreadContext)
+ ThreadContextAccess.getThreadContextMap()).getContextData();
+ if (props == null || props.isEmpty()) {
+ return immutableCopy;
+ }
+ // However, if the list of Properties is non-empty we need to combine the properties and the ThreadContext
+ // data. In that case we will copy the key-value pairs into the specified reusable MutableContextData.
+ copyProperties(props, reusable);
+ reusable.putAll(immutableCopy);
return reusable;
}
}
[16/16] logging-log4j2 git commit: LOG4J2-1349 benchmark
Posted by rp...@apache.org.
LOG4J2-1349 benchmark
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/167b281e
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/167b281e
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/167b281e
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 167b281e8c57cca996f5bd316e75747a34f72a3b
Parents: eb0d5ed
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 20:10:20 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:19 2016 +0900
----------------------------------------------------------------------
.../log4j/perf/jmh/ThreadContextBenchmark.java | 165 +++++++++++++++++++
1 file changed, 165 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/167b281e/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
new file mode 100644
index 0000000..a8d15b8
--- /dev/null
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
@@ -0,0 +1,165 @@
+/*
+ * 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.perf.jmh;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.ThreadContextBenchmarkAccess;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.impl.ContextDataInjector;
+import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.CopyOnWriteOpenHashMapThreadContextMap;
+import org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap;
+import org.apache.logging.log4j.spi.DefaultThreadContextMap;
+import org.apache.logging.log4j.spi.GarbageFreeOpenHashMapThreadContextMap;
+import org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap;
+import org.apache.logging.log4j.spi.MutableContextData;
+import org.apache.logging.log4j.spi.OpenHashMapContextData;
+import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+
+/**
+ * Compares performance of ThreadContextMap implementations.
+ */
+// ============================== HOW TO RUN THIS TEST: ====================================
+// (Quick build: mvn -DskipTests=true clean package -pl log4j-perf -am )
+//
+// single thread:
+// java -jar log4j-perf/target/benchmarks.jar ".*ThreadContextBench.*"
+//
+// four threads:
+// java -jar log4j-perf/target/benchmarks.jar ".*ThreadContextBench.*" -f 1 -wi 10 -i 20 -tu ns -bm sample -t 4
+//
+// Usage help:
+// java -jar log4j-perf/target/benchmarks.jar -help
+//
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class ThreadContextBenchmark {
+ private static final String DEFAULT_CONTEXT_MAP = "Default";
+ private static final String COPY_OPENHASH_MAP = "CopyOpenHash";
+ private static final String COPY_ARRAY_MAP = "CopySortedArray";
+ private static final String NO_GC_OPENHASH_MAP = "NoGcOpenHash";
+ private static final String NO_GC_ARRAY_MAP = "NoGcSortedArray";
+ private static final Map<String, Class<? extends ThreadContextMap>> IMPLEMENTATIONS = new HashMap<>();
+ static {
+ IMPLEMENTATIONS.put(DEFAULT_CONTEXT_MAP, DefaultThreadContextMap.class);
+ IMPLEMENTATIONS.put(COPY_OPENHASH_MAP, CopyOnWriteOpenHashMapThreadContextMap.class);
+ IMPLEMENTATIONS.put(COPY_ARRAY_MAP, CopyOnWriteSortedArrayThreadContextMap.class);
+ IMPLEMENTATIONS.put(NO_GC_OPENHASH_MAP, GarbageFreeOpenHashMapThreadContextMap.class);
+ IMPLEMENTATIONS.put(NO_GC_ARRAY_MAP, GarbageFreeSortedArrayThreadContextMap.class);
+ }
+
+ @Param({ "Default", "CopyOpenHash", "CopySortedArray", "NoGcOpenHash", "NoGcSortedArray"})
+ public String threadContextMapAlias;
+
+ @Param({"5", "50", "500"})
+ public int count;
+
+ private final int KEY_LENGTH = 16;
+ private String[] keys;
+ private String[] values;
+ private List<Property> propertyList;
+
+ private ContextDataInjector injector;
+ private MutableContextData reusableContextData;
+
+ @Setup
+ public void setup() {
+ System.setProperty("log4j2.threadContextMap", IMPLEMENTATIONS.get(threadContextMapAlias).getName());
+ ThreadContextBenchmarkAccess.init();
+
+ injector = ContextDataInjectorFactory.createInjector();
+ System.out.println(threadContextMapAlias + ": Injector = " + injector);
+
+ reusableContextData = threadContextMapAlias.contains("Array")
+ ? new ArrayContextData()
+ : new OpenHashMapContextData<>();
+
+ keys = new String[count];
+ values = new String[count];
+ Random r = new Random();
+ for (int j = 0; j < keys.length; j++) {
+ char[] str = new char[KEY_LENGTH];
+ for (int i = 0; i < str.length; i++) {
+ str[i] = (char) r.nextInt();
+ }
+ keys[j] = new String(str);
+ values[j] = new String(str);
+ }
+ final int PROPERTIES_COUNT = 5; // count
+ propertyList = new ArrayList<>(PROPERTIES_COUNT);
+ for (int j = 0; j < PROPERTIES_COUNT; j++) {
+ char[] str = new char[KEY_LENGTH];
+ for (int i = 0; i < str.length; i++) {
+ str[i] = (char) r.nextInt();
+ }
+ propertyList.add(Property.createProperty(new String(str), new String(str)));
+ }
+
+ clearAndPut(); // ensure ThreadContext contains values
+ }
+
+ @TearDown
+ public void tearDown() {
+ System.clearProperty("log4j2.threadContextMap");
+ ThreadContextBenchmarkAccess.init();
+ }
+
+ @Benchmark
+ public void clearAndPut() {
+ ThreadContext.clearMap();
+ for (int i = 0; i < count; i++) {
+ ThreadContext.put(keys[i], values[i]);
+ }
+ }
+
+ @Benchmark
+ public MutableContextData injectWithoutProperties() {
+ reusableContextData.clear();
+ return injector.injectContextData(null, reusableContextData);
+ }
+
+ @Benchmark
+ public MutableContextData injectWithProperties() {
+ reusableContextData.clear();
+ return injector.injectContextData(propertyList, reusableContextData);
+ }
+}
\ No newline at end of file
[04/16] logging-log4j2 git commit: LOG4J2-1349 GC-free ThreadContext
initial commit
Posted by rp...@apache.org.
LOG4J2-1349 GC-free ThreadContext initial commit
- move ArrayContextData and OpenHashMapContextData to spi package in log4j-api module
- move BiConsumer/TriConsumer to util package in log4j-api module
- added CopyOnWriteSortedArrayThreadContext and SortedArrayThreadContext to support garbage-free and classic thread context strategies
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/1118b27f
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/1118b27f
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/1118b27f
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 1118b27fafe76c4d6e36cd71ffa2eee90991710f
Parents: 88d2066
Author: rpopma <rp...@apache.org>
Authored: Fri Aug 19 09:10:15 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:03 2016 +0900
----------------------------------------------------------------------
.../logging/log4j/spi/ArrayContextData.java | 448 ++++++++++
.../apache/logging/log4j/spi/ContextData.java | 115 +++
.../CopyOnWriteSortedArrayThreadContext.java | 156 ++++
.../logging/log4j/spi/MutableContextData.java | 68 ++
.../log4j/spi/OpenHashMapContextData.java | 883 ++++++++++++++++++
.../log4j/spi/SortedArrayThreadContext.java | 156 ++++
.../apache/logging/log4j/util/BiConsumer.java | 19 +
.../apache/logging/log4j/util/TriConsumer.java | 21 +
.../logging/log4j/spi/ArrayContextDataTest.java | 569 ++++++++++++
.../log4j/spi/OpenHashMapContextDataTest.java | 507 +++++++++++
.../logging/log4j/core/AbstractLogEvent.java | 1 +
.../apache/logging/log4j/core/ContextData.java | 119 ---
.../org/apache/logging/log4j/core/LogEvent.java | 1 +
.../db/jpa/AbstractLogEventWrapperEntity.java | 2 +-
.../ContextDataAttributeConverter.java | 2 +-
.../ContextDataJsonAttributeConverter.java | 6 +-
.../logging/log4j/core/async/AsyncLogger.java | 2 +-
.../log4j/core/async/RingBufferLogEvent.java | 4 +-
.../async/RingBufferLogEventTranslator.java | 2 +-
.../log4j/core/impl/ArrayContextData.java | 438 ---------
.../log4j/core/impl/ContextDataFactory.java | 3 +
.../log4j/core/impl/ContextDataInjector.java | 3 +
.../core/impl/ContextDataInjectorFactory.java | 2 +
.../logging/log4j/core/impl/Log4jLogEvent.java | 4 +-
.../log4j/core/impl/MutableContextData.java | 68 --
.../log4j/core/impl/MutableLogEvent.java | 3 +-
.../log4j/core/impl/OpenHashMapContextData.java | 887 -------------------
.../core/impl/ReusableLogEventFactory.java | 1 +
.../core/impl/ThreadContextDataInjector.java | 4 +-
.../ContextDataAsEntryListDeserializer.java | 2 +-
.../ContextDataAsEntryListSerializer.java | 4 +-
.../core/jackson/ContextDataDeserializer.java | 2 +-
.../core/jackson/ContextDataSerializer.java | 4 +-
.../log4j/core/jackson/LogEventJsonMixIn.java | 2 +-
.../jackson/LogEventWithContextListMixIn.java | 2 +-
.../logging/log4j/core/layout/GelfLayout.java | 2 +-
.../log4j/core/pattern/MdcPatternConverter.java | 4 +-
.../logging/log4j/core/util/BiConsumer.java | 19 -
.../logging/log4j/core/util/TriConsumer.java | 21 -
.../core/appender/db/jpa/TestBaseEntity.java | 2 +-
.../ContextDataAttributeConverterTest.java | 4 +-
.../ContextDataJsonAttributeConverterTest.java | 6 +-
.../core/async/RingBufferLogEventTest.java | 2 +-
.../log4j/core/impl/ArrayContextDataTest.java | 568 ------------
.../log4j/core/impl/Log4jLogEventTest.java | 2 +
.../log4j/core/impl/MutableLogEventTest.java | 2 +
.../core/impl/OpenHashMapContextDataTest.java | 506 -----------
.../log4j/flume/appender/FlumeEvent.java | 2 +-
.../jmh/ArrayContextDataVsHashMapBenchmark.java | 8 +-
49 files changed, 2997 insertions(+), 2661 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ArrayContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ArrayContextData.java
new file mode 100644
index 0000000..d934a3d
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ArrayContextData.java
@@ -0,0 +1,448 @@
+/*
+ * 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.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Array-based implementation of the {@code ContextData} interface. Keys are held in a sorted array.
+ * <p>
+ * This is not a generic collection, but makes some trade-offs to optimize for the Log4j ContextData use case:
+ * </p>
+ * <ul>
+ * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
+ * <li>Fast copy. If the ThreadContextMap is also an instance of {@code ArrayContextData}, the full thread context
+ * data can be transferred with two array copies and two field updates.</li>
+ * <li>Acceptable performance for small data sets. The current implementation stores keys in a sorted array, values
+ * are stored in a separate array at the same index.
+ * Worst-case performance of {@code get} and {@code containsKey} is O(log N),
+ * worst-case performance of {@code put} and {@code remove} is O(N log N).
+ * The expectation is that for the small values of {@code N} (less than 100) that are the vast majority of
+ * ThreadContext use cases, the constants dominate performance more than the asymptotic performance of the
+ * algorithms used.
+ * </li>
+ * <li>Compact representation.</li>
+ * </ul>
+ *
+ * @since 2.7
+ */
+public class ArrayContextData implements MutableContextData, ThreadContextMap {
+
+ /**
+ * The default initial capacity.
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 4;
+ private static final long serialVersionUID = -5748905872274478116L;
+ private static final int HASHVAL = 31;
+
+ private static final TriConsumer<String, Object, MutableContextData> PUT_ALL = new TriConsumer<String, Object, MutableContextData>() {
+ @Override
+ public void accept(final String key, final Object value, final MutableContextData contextData) {
+ contextData.putValue(key, value);
+ }
+ };
+
+ /**
+ * An empty array instance to share when the table is not inflated.
+ */
+ private static final String[] EMPTY = {};
+
+ private transient String[] keys = EMPTY;
+ private transient Object[] values = EMPTY;
+
+ /**
+ * The number of key-value mappings contained in this map.
+ */
+ private transient int size;
+
+ /**
+ * The next size value at which to resize (capacity * load factor).
+ * @serial
+ */
+ // If table == EMPTY_TABLE then this is the initial capacity at which the
+ // table will be created when inflated.
+ private int threshold;
+
+ public ArrayContextData() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ public ArrayContextData(final int initialCapacity) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("Initial capacity must be at least one but was " + initialCapacity);
+ }
+ threshold = ceilingNextPowerOfTwo(initialCapacity);
+ }
+
+ public ArrayContextData(final ContextData other) {
+ if (other instanceof ArrayContextData) {
+ initFrom0((ArrayContextData) other);
+ } else if (other != null) {
+ resize(ceilingNextPowerOfTwo(other.size()));
+ other.forEach(PUT_ALL, this);
+ }
+ }
+
+ @Override
+ public void clear() {
+ Arrays.fill(keys, 0, size, null);
+ Arrays.fill(values, 0, size, null);
+ size = 0;
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ return indexOfKey(key) >= 0;
+ }
+
+ @Override
+ public Map<String, String> asMap() {
+ final Map<String, String> result = new HashMap<>(size());
+ for (int i = 0; i < size(); i++) {
+ final Object value = getValueAt(i);
+ result.put(getKeyAt(i), value == null ? null : String.valueOf(value));
+ }
+ return result;
+ }
+
+ @Override
+ public Map<String, String> getCopy() {
+ return asMap();
+ }
+
+ @Override
+ public Map<String, String> getImmutableMapOrNull() {
+ return isEmpty() ? null : Collections.unmodifiableMap(asMap());
+ }
+
+ @Override
+ public String get(final String key) {
+ final Object result = getValue(key);
+ return result == null ? null : String.valueOf(result);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V> V getValue(final String key) {
+ final int index = indexOfKey(key);
+ if (index < 0) {
+ return null;
+ }
+ return (V) values[index];
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ int indexOfKey(final String key) {
+ if (keys == EMPTY) {
+ return -1;
+ }
+ if (key == null) { // null key is located at the start of the array
+ return nullKeyIndex(); // insert at index zero
+ }
+ final int start = size > 0 && keys[0] == null ? 1 : 0;
+ return Arrays.binarySearch(keys, start, size, key);
+ }
+
+ private int nullKeyIndex() {
+ return size > 0 && keys[0] == null ? 0 : ~0;
+ }
+
+ @Override
+ public void put(final String key, final String value) {
+ putValue(key, value);
+ }
+
+ @Override
+ public void putValue(final String key, final Object value) {
+ if (keys == EMPTY) {
+ inflateTable(threshold);
+ }
+ final int index = indexOfKey(key);
+ if (index >= 0) {
+ keys[index] = key;
+ values[index] = value;
+ } else { // not found, so insert.
+ insertAt(~index, key, value);
+ }
+ }
+
+ private void insertAt(final int index, final String key, final Object value) {
+ ensureCapacity();
+ System.arraycopy(keys, index, keys, index + 1, size - index);
+ System.arraycopy(values, index, values, index + 1, size - index);
+ keys[index] = key;
+ values[index] = value;
+ size++;
+ }
+
+ @Override
+ public void putAll(final ContextData source) {
+ if (source instanceof ArrayContextData) {
+ initFrom0((ArrayContextData) source);
+ } else if (source != null) {
+ source.forEach(PUT_ALL, this);
+ }
+ }
+
+ public void initFrom(final ArrayContextData other) {
+ initFrom0(other);
+ }
+
+ private void initFrom0(final ArrayContextData other) {
+ if (keys.length < other.size) {
+ keys = new String[other.threshold];
+ values = new Object[other.threshold];
+ }
+ System.arraycopy(other.keys, 0, keys, 0, other.size);
+ System.arraycopy(other.values, 0, values, 0, other.size);
+
+ size = other.size;
+ threshold = other.threshold;
+ }
+
+ private void ensureCapacity() {
+ if (size >= threshold) {
+ resize(threshold * 2);
+ }
+ }
+
+ private void resize(final int newCapacity) {
+ final String[] oldKeys = keys;
+ final Object[] oldValues = values;
+
+ keys = new String[newCapacity];
+ values = new Object[newCapacity];
+
+ System.arraycopy(oldKeys, 0, keys, 0, size);
+ System.arraycopy(oldValues, 0, values, 0, size);
+
+ threshold = newCapacity;
+ }
+
+ /**
+ * Inflates the table.
+ */
+ private void inflateTable(int toSize) {
+ threshold = toSize;
+ keys = new String[toSize];
+ values = new Object[toSize];
+ }
+
+ @Override
+ public void remove(final String key) {
+ if (keys == EMPTY) {
+ return;
+ }
+ final int index = indexOfKey(key);
+ if (index >= 0) {
+ System.arraycopy(keys, index + 1, keys, index, size - index);
+ System.arraycopy(values, index + 1, values, index, size - index);
+ size--;
+ }
+ }
+
+ String getKeyAt(final int index) {
+ if (index < 0 || index >= size) {
+ return null;
+ }
+ return keys[index];
+ }
+
+ @SuppressWarnings("unchecked")
+ <V> V getValueAt(final int index) {
+ if (index < 0 || index >= size) {
+ return null;
+ }
+ return (V) values[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V> void forEach(BiConsumer<String, ? super V> action) {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i]);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V, T> void forEach(TriConsumer<String, ? super V, T> action, T state) {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i], state);
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof ArrayContextData)) {
+ return false;
+ }
+ ArrayContextData other = (ArrayContextData) obj;
+ if (this.size() != other.size()) {
+ return false;
+ }
+ for (int i = 0; i < size(); i++) {
+ if (!Objects.equals(keys[i], other.keys[i])) {
+ return false;
+ }
+ if (!Objects.equals(values[i], other.values[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 37;
+ result = HASHVAL * result + size;
+ result = HASHVAL * result + hashCode(keys, size);
+ result = HASHVAL * result + hashCode(values, size);
+ return result;
+ }
+
+ private static int hashCode(Object[] values, int length) {
+ int result = 1;
+ for (int i = 0; i < length; i++) {
+ result = HASHVAL * result + (values[i] == null ? 0 : values[i].hashCode());
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(256);
+ sb.append('{');
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(keys[i]).append('=');
+ sb.append(values[i] == this ? "(this map)" : values[i]);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Save the state of the {@code ArrayContextData} instance to a stream (i.e.,
+ * serialize it).
+ *
+ * @serialData The <i>capacity</i> of the ArrayContextData (the length of the
+ * bucket array) is emitted (int), followed by the
+ * <i>size</i> (an int, the number of key-value
+ * mappings), followed by the key (Object) and value (Object)
+ * for each key-value mapping. The key-value mappings are
+ * emitted in no particular order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s) throws IOException {
+ // Write out the threshold, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out number of buckets
+ if (keys == EMPTY) {
+ s.writeInt(ceilingNextPowerOfTwo(threshold));
+ } else {
+ s.writeInt(keys.length);
+ }
+
+ // Write out size (number of Mappings)
+ s.writeInt(size);
+
+ // Write out keys and values (alternating)
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ s.writeObject(keys[i]);
+ s.writeObject(values[i]);
+ }
+ }
+ }
+
+
+ /**
+ * Calculate the next power of 2, greater than or equal to x.
+ * <p>
+ * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
+ *
+ * @param x Value to round up
+ * @return The next power of 2 from x inclusive
+ */
+ private static int ceilingNextPowerOfTwo(final int x) {
+ final int BITS_PER_INT = 32;
+ return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1));
+ }
+
+ /**
+ * Reconstitute the {@code ArrayContextData} instance from a stream (i.e.,
+ * deserialize it).
+ */
+ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+ // Read in the threshold (ignored), and any hidden stuff
+ s.defaultReadObject();
+
+ // set other fields that need values
+ keys = EMPTY;
+ values = EMPTY;
+
+ // Read in number of buckets
+ int capacity = s.readInt();
+ if (capacity < 0) {
+ throw new InvalidObjectException("Illegal capacity: " + capacity);
+ }
+
+ // Read number of mappings
+ int mappings = s.readInt();
+ if (mappings < 0) {
+ throw new InvalidObjectException("Illegal mappings count: " + mappings);
+ }
+
+ // allocate the bucket array;
+ if (mappings > 0) {
+ inflateTable(capacity);
+ } else {
+ threshold = capacity;
+ }
+
+ // Read the keys and values, and put the mappings in the arrays
+ for (int i = 0; i < mappings; i++) {
+ keys[i] = (String) s.readObject();
+ values[i] = s.readObject();
+ }
+ size = mappings;
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/ContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ContextData.java
new file mode 100644
index 0000000..d62b3f4
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ContextData.java
@@ -0,0 +1,115 @@
+/*
+ * 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.io.Serializable;
+import java.util.Map;
+
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * A read-only collection of context data. Context data items are String keys and values of arbitrary type that are
+ * set by the application to be included in all subsequent log events. A typical source of context data is the
+ * {@code ThreadContextMap} and the {@code Properties} defined in the configuration.
+ * <p>
+ * Applications can put custom data in this collection by installing a custom {@code ContextDataInjector}.
+ * </p>
+ *
+ * @see org.apache.logging.log4j.spi.ThreadContextMap
+ * @since 2.7
+ */
+public interface ContextData extends Serializable {
+ /**
+ * Returns a {@code Map<String, String>} view of this context data.
+ *
+ * @return a map view of this context data
+ */
+ Map<String, String> asMap();
+
+ /**
+ * Returns {@code true} if this context data contains the specified key, {@code false} otherwise.
+ *
+ * @param key the key whose presence to check. May be {@code null}.
+ * @return {@code true} if this context data contains the specified key, {@code false} otherwise
+ */
+ boolean containsKey(String key);
+
+ /**
+ * Performs the given action for each key-value pair in this data structure
+ * until all entries have been processed or the action throws an exception.
+ * <p>
+ * Some implementations may not support structural modifications (adding new elements or removing elements) while
+ * iterating over the contents. In such implementations, attempts to add or remove elements from the
+ * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} method may cause a
+ * {@code ConcurrentModificationException} to be thrown.
+ * </p>
+ *
+ * @param action The action to be performed for each key-value pair in this collection
+ * @param <V> type of the value
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ <V> void forEach(final BiConsumer<String, ? super V> action);
+
+ /**
+ * Performs the given action for each key-value pair in this data structure
+ * until all entries have been processed or the action throws an exception.
+ * <p>
+ * The third parameter lets callers pass in a stateful object to be modified with the key-value pairs,
+ * so the TriConsumer implementation itself can be stateless and potentially reusable.
+ * </p>
+ * <p>
+ * Some implementations may not support structural modifications (adding new elements or removing elements) while
+ * iterating over the contents. In such implementations, attempts to add or remove elements from the
+ * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) accept} method may cause a
+ * {@code ConcurrentModificationException} to be thrown.
+ * </p>
+ *
+ * @param action The action to be performed for each key-value pair in this collection
+ * @param state the object to be passed as the third parameter to each invocation on the specified
+ * triconsumer
+ * @param <V> type of the value
+ * @param <S> type of the third parameter
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state);
+
+ /**
+ * Returns the value for the specified key, or {@code null} if the specified key does not exist in this collection.
+ *
+ * @param key the key whose value to return
+ * @return the value for the specified key or {@code null}
+ */
+ <V> V getValue(final String key);
+
+ /**
+ * Returns {@code true} if this collection is empty (size is zero), {@code false} otherwise.
+ * @return {@code true} if this collection is empty (size is zero)
+ */
+ boolean isEmpty();
+
+ /**
+ * Returns the number of key-value pairs in this collection.
+ *
+ * @return the number of key-value pairs in this collection
+ */
+ int size();
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
new file mode 100644
index 0000000..be172b4
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
@@ -0,0 +1,156 @@
+/*
+ * 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 SortedArrayContextData}.
+ * 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.
+ */
+public class CopyOnWriteSortedArrayThreadContext implements ThreadContextMap {
+ /**
+ * 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<ArrayContextData> localMap;
+
+ public CopyOnWriteSortedArrayThreadContext() {
+ this.localMap = createThreadLocalMap();
+ }
+
+ // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
+ // (This method is package protected for JUnit tests.)
+ static ThreadLocal<ArrayContextData> createThreadLocalMap() {
+ final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+ final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
+ if (inheritable) {
+ return new InheritableThreadLocal<ArrayContextData>() {
+ @Override
+ protected ArrayContextData childValue(final ArrayContextData parentValue) {
+ return parentValue != null ? new ArrayContextData(parentValue) : null;
+ }
+ };
+ }
+ // if not inheritable, return plain ThreadLocal with null as initial value
+ return new ThreadLocal<>();
+ }
+
+ @Override
+ public void put(final String key, final String value) {
+ ArrayContextData map = localMap.get();
+ map = map == null ? new ArrayContextData() : new ArrayContextData(map);
+ map.put(key, value);
+ localMap.set(map);
+ }
+
+ @Override
+ public String get(final String key) {
+ final ArrayContextData map = localMap.get();
+ return map == null ? null : map.get(key);
+ }
+
+ @Override
+ public void remove(final String key) {
+ final ArrayContextData map = localMap.get();
+ if (map != null) {
+ final ArrayContextData copy = new ArrayContextData(map);
+ copy.remove(key);
+ localMap.set(copy);
+ }
+ }
+
+ @Override
+ public void clear() {
+ localMap.remove();
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ final ArrayContextData map = localMap.get();
+ return map != null && map.containsKey(key);
+ }
+
+ @Override
+ public Map<String, String> getCopy() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? Collections.<String, String>emptyMap() : map.asMap();
+ }
+
+ public ContextData getContextData() {
+ return localMap.get();
+ }
+
+ @Override
+ public Map<String, String> getImmutableMapOrNull() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? null : Collections.unmodifiableMap(map.asMap());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ final ArrayContextData map = localMap.get();
+ return map == null || map.size() == 0;
+ }
+
+ @Override
+ public String toString() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? "{}" : map.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ final ArrayContextData 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/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
new file mode 100644
index 0000000..8bc64e8
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MutableContextData.java
@@ -0,0 +1,68 @@
+/*
+ * 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 org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Exposes methods to add and remove key-value pairs to and from {@code ContextData}.
+ *
+ * @see ContextData
+ * @since 2.7
+ */
+public interface MutableContextData extends ContextData {
+
+ /**
+ * Removes all key-value pairs from this collection.
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ void clear();
+
+ /**
+ * Puts the specified key-value pair into the collection.
+ *
+ * @param key the key to add or remove. Keys may be {@code null}.
+ * @param value the value to add. Values may be {@code null}.
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ void putValue(final String key, final Object value);
+
+ /**
+ * Copy all key-value pairs from the specified {@code ContextData} into this {@code MutableContextData}.
+ * @param source the {@code ContextData} to copy key-value pairs from
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ void putAll(final ContextData source);
+
+ /**
+ * Removes the key-value pair for the specified key from this context data collection.
+ *
+ * @param key the key to remove. May be {@code null}.
+ * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
+ * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
+ * {@link #forEach(TriConsumer, Object)}.
+ */
+ void remove(final String key);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
new file mode 100644
index 0000000..9f27a6c
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
@@ -0,0 +1,883 @@
+/*
+ * 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Open hash map-based implementation of the {@code ContextData} interface.
+ * Implementation based on <a href="http://fastutil.di.unimi.it/">fastutil</a>'s
+ * <a href="http://fastutil.di.unimi.it/docs/it/unimi/dsi/fastutil/objects/Object2ObjectOpenHashMap.html">Object2ObjectOpenHashMap</a>.
+ * <p>
+ * A type-specific hash map with a fast, small-footprint implementation.
+ *
+ * <P>
+ * Instances of this class use a hash table to represent a map. The table is
+ * filled up to a specified <em>load factor</em>, and then doubled in size to
+ * accommodate new entries. If the table is emptied below <em>one fourth</em> of
+ * the load factor, it is halved in size. However, halving is not performed when
+ * deleting entries from an iterator, as it would interfere with the iteration
+ * process.
+ *
+ * <p>
+ * Note that {@link #clear()} does not modify the hash table size. Rather, the
+ * {@link #trim(int)} method lets you control the size of
+ * the table; this is particularly useful if you reuse instances of this class.
+ * <p>
+ * <ul>
+ * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
+ * <li>Fast copy. If the ThreadContextMap is also an instance of {@code OpenHashMapContextData},
+ * the full thread context data can be transferred with two array copies and five field updates.</li>
+ * </ul>
+ *
+ * @since 2.7
+ */
+public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadContextMap {
+ /** The initial default size of a hash table. */
+ public static final int DEFAULT_INITIAL_SIZE = 16;
+
+ /** The default load factor of a hash table. */
+ public static final float DEFAULT_LOAD_FACTOR = .75f;
+
+ private static final long serialVersionUID = -1486744623338827187L;
+
+ /** The array of keys. */
+ protected transient K[] keys;
+ /** The array of values. */
+ protected transient V[] values;
+ /** The mask for wrapping a position counter. */
+ protected transient int mask;
+ /** Whether this set contains the key zero. */
+ protected transient boolean containsNullKey;
+ /** The current table size. */
+ protected transient int arraySize;
+ /**
+ * Threshold after which we rehash. It must be the table size times {@link #loadFactor}.
+ */
+ protected transient int maxFill;
+ /** Number of entries in the set (including the key zero, if present). */
+ protected int size;
+ /** The acceptable load factor. */
+ protected final float loadFactor;
+
+ private V defRetValue = null;
+
+ /**
+ * Creates a new hash map with initial expected
+ * {@link #DEFAULT_INITIAL_SIZE} entries and
+ * {@link #DEFAULT_LOAD_FACTOR} as load factor.
+ */
+ public OpenHashMapContextData() {
+ this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR);
+ }
+ /**
+ * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load factor.
+ *
+ * @param expected
+ * the expected number of elements in the hash map.
+ */
+ public OpenHashMapContextData(final int expected) {
+ this(expected, DEFAULT_LOAD_FACTOR);
+ }
+ /**
+ * Creates a new hash map.
+ *
+ * <p>
+ * The actual table size will be the least power of two greater than
+ * <code>expected</code>/<code>f</code>.
+ *
+ * @param expected
+ * the expected number of elements in the hash set.
+ * @param f
+ * the load factor.
+ */
+ @SuppressWarnings("unchecked")
+ public OpenHashMapContextData(final int expected, final float f) {
+ if (f <= 0 || f > 1) {
+ throw new IllegalArgumentException(
+ "Load factor must be greater than 0 and smaller than or equal to 1");
+ }
+ if (expected < 0){
+ throw new IllegalArgumentException(
+ "The expected number of elements must be nonnegative");
+ }
+ this.loadFactor = f;
+ arraySize = HashCommon.arraySize(expected, f);
+ mask = arraySize - 1;
+ maxFill = HashCommon.maxFill(arraySize, f);
+ keys = (K[]) new Object[arraySize + 1];
+ values = (V[]) new Object[arraySize + 1];
+ }
+ /**
+ * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load
+ * factor copying a given one.
+ *
+ * @param map
+ * a {@link Map} to be copied into the new hash map.
+ */
+ public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
+ this(map, DEFAULT_LOAD_FACTOR);
+ }
+ /**
+ * Creates a new hash map copying a given one.
+ *
+ * @param map
+ * a {@link Map} to be copied into the new hash map.
+ * @param f
+ * the load factor.
+ */
+ public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
+ this(map.size(), f);
+ putAll(map);
+ }
+
+ /**
+ * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load
+ * factor copying a given type-specific one.
+ *
+ * @param contextData
+ * a type-specific map to be copied into the new hash map.
+ */
+ public OpenHashMapContextData(final ContextData contextData) {
+ this(contextData, DEFAULT_LOAD_FACTOR);
+ }
+ /**
+ * Creates a new hash map copying a given type-specific one.
+ *
+ * @param contextData
+ * a type-specific map to be copied into the new hash map.
+ * @param f
+ * the load factor.
+ */
+ public OpenHashMapContextData(final ContextData contextData, final float f) {
+ this(contextData.size(), f);
+ if (contextData instanceof OpenHashMapContextData) {
+ initFrom0((OpenHashMapContextData) contextData);
+ } else {
+ contextData.forEach(PUT_ALL, this);
+ }
+ }
+ private static final TriConsumer<String, Object, MutableContextData> PUT_ALL =
+ new TriConsumer<String, Object, MutableContextData>() {
+ @Override
+ public void accept(final String key, final Object value, final MutableContextData contextData) {
+ contextData.putValue(key, value);
+ }
+ };
+
+ @SuppressWarnings("unchecked")
+ private void initFrom0(final OpenHashMapContextData other) {
+ // this.loadFactor = other.loadFactor; // final field
+ this.arraySize = other.arraySize;
+ this.size = other.size;
+ this.containsNullKey = other.containsNullKey;
+ this.mask = other.mask;
+ this.maxFill = other.maxFill;
+ keys = (K[]) Arrays.copyOf(other.keys, arraySize + 1);
+ values = (V[]) Arrays.copyOf(other.values, arraySize + 1);
+ }
+
+ private int realSize() {
+ return containsNullKey ? size - 1 : size;
+ }
+
+ private void ensureCapacity(final int capacity) {
+ final int needed = HashCommon.arraySize(capacity, loadFactor);
+ if (needed > arraySize) {
+ rehash(needed);
+ }
+ }
+
+ private void tryCapacity(final long capacity) {
+ final int needed = (int) Math.min(
+ 1 << 30, Math.max(2, HashCommon.nextPowerOfTwo((int) Math.ceil(capacity / loadFactor))));
+ if (needed > arraySize) {
+ rehash(needed);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void putAll(Map<? extends K, ? extends V> map) {
+ if (loadFactor <= .5) {
+ // The resulting map will be sized for m.size() elements
+ ensureCapacity(map.size());
+ } else {
+ // The resulting map will be tentatively sized for size() + m.size() elements
+ tryCapacity(size() + map.size());
+ }
+ for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+ putObjectValue(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public Map<String, String> asMap() {
+ final Map<String, String> result = new HashMap<>(size);
+ forEach(COPY_INTO_MAP, result);
+ return result;
+ }
+
+ private static final TriConsumer<String, Object, Map<String, String>> COPY_INTO_MAP =
+ new TriConsumer<String, Object, Map<String, String>>() {
+ @Override
+ public void accept(final String k, final Object v, final Map<String, String> map) {
+ map.put(k, v == null ? null : v.toString());
+ }
+ };
+
+ /*
+ * Removes all elements from this map.
+ *
+ * <P>To increase object reuse, this method does not change the table size.
+ * If you want to reduce the table size, you must use {@link #trim()}.
+ */
+ @Override
+ public void clear() {
+ if (size == 0) {
+ return;
+ }
+ size = 0;
+ containsNullKey = false;
+ Arrays.fill(keys, (null));
+ Arrays.fill(values, null);
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ return containsObjectKey((Object) key);
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean containsObjectKey(final Object k) {
+ if (k == null) {
+ return containsNullKey;
+ }
+ K curr;
+ final K[] key = this.keys;
+ int pos;
+ // The starting point.
+ if ((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null) {
+ return false;
+ }
+ if (k.equals(curr)) {
+ return true;
+ }
+ // There's always an unused entry.
+ while (true) {
+ if ((curr = key[pos = (pos + 1) & mask]) == null) {
+ return false;
+ }
+ if (k.equals(curr)) {
+ return true;
+ }
+ }
+ }
+
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof ContextData)) {
+ return false;
+ }
+ final ContextData other = (ContextData) obj;
+ if (other.size() != size()) {
+ return false;
+ }
+ int pos = arraySize;
+ if (containsNullKey) {
+ if (!Objects.equals(getObjectValue(null), other.getValue(null))) {
+ return false;
+ }
+ }
+ --pos;
+ final K myKeys[] = this.keys;
+ for (; pos >= 0; pos--) {
+ K k;
+ if ((k = myKeys[pos]) != null) {
+ if (!Objects.equals(values[pos], other.getValue((String) k))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <VAL> void forEach(final BiConsumer<String, ? super VAL> action) {
+ final int startSize = size;
+ final K myKeys[] = this.keys;
+ int pos = arraySize;
+ if (containsNullKey) {
+ action.accept((String) myKeys[pos], (VAL) values[pos]);
+ if (size != startSize) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ --pos;
+ for (; pos >= 0; pos--) {
+ if (myKeys[pos] != null) {
+ action.accept((String) myKeys[pos], (VAL) values[pos]);
+ if (size != startSize) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <VAL, STATE> void forEach(final TriConsumer<String, ? super VAL, STATE> action, final STATE state) {
+ final int startSize = size;
+ final K myKeys[] = this.keys;
+ int pos = arraySize;
+ if (containsNullKey) {
+ action.accept((String) myKeys[pos], (VAL) values[pos], state);
+ if (size != startSize) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ --pos;
+ for (; pos >= 0; pos--) {
+ if (myKeys[pos] != null) {
+ action.accept((String) myKeys[pos], (VAL) values[pos], state);
+ if (size != startSize) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
+
+ @Override
+ public String get(final String key) {
+ return (String) getObjectValue(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ private V getObjectValue(final Object k) {
+ if (k == null) {
+ return containsNullKey ? values[arraySize] : defRetValue;
+ }
+ K curr;
+ final K[] key = this.keys;
+ int pos;
+ // The starting point.
+ if ((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null) {
+ return defRetValue;
+ }
+ if (k.equals(curr)) {
+ return values[pos];
+ }
+ // There's always an unused entry.
+ while (true) {
+ if (((curr = key[pos = (pos + 1) & mask]) == null)) {
+ return defRetValue;
+ }
+ if (k.equals(curr)) {
+ return values[pos];
+ }
+ }
+ }
+
+ @Override
+ public Map<String, String> getCopy() {
+ return asMap();
+ }
+
+ @Override
+ public Map<String, String> getImmutableMapOrNull() {
+ return isEmpty() ? null : Collections.unmodifiableMap(asMap());
+ }
+
+ @Override
+ public <VAL> VAL getValue(final String key) {
+ return (VAL) getObjectValue((Object) key);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void put(final String key, final String value) {
+ putObjectValue((K) key, (V) value);
+ }
+
+ private int insert(final K k, final V v) {
+ int pos;
+ if (k == null) {
+ if (containsNullKey) {
+ return arraySize;
+ }
+ containsNullKey = true;
+ pos = arraySize;
+ } else {
+ K curr;
+ final K[] key = this.keys;
+ // The starting point.
+ if (!((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null)) {
+ if (curr.equals(k)) {
+ return pos;
+ }
+ while (!((curr = key[pos = (pos + 1) & mask]) == null)) {
+ if (curr.equals(k)) {
+ return pos;
+ }
+ }
+ }
+ }
+ keys[pos] = k;
+ values[pos] = v;
+ if (size++ >= maxFill) {
+ rehash(HashCommon.arraySize(size + 1, loadFactor));
+ }
+ return -1;
+ }
+
+ @Override
+ public void putAll(final ContextData source) {
+ if (size() == 0 && source instanceof OpenHashMapContextData) {
+ initFrom0((OpenHashMapContextData) source);
+ } else if (source != null) {
+ source.forEach(PUT_ALL, this);
+ }
+ }
+
+ private V putObjectValue(final K k, final V v) {
+ final int pos = insert(k, v);
+ if (pos < 0) {
+ return defRetValue;
+ }
+ final V oldValue = values[pos];
+ values[pos] = v;
+ return oldValue;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void putValue(final String key, final Object value) {
+ putObjectValue((K) key, (V) value);
+ }
+
+ @Override
+ public void remove(final String key) {
+ removeObjectKey((Object) key);
+ }
+
+ @SuppressWarnings("unchecked")
+ private V removeObjectKey(final Object k) {
+ if (k == null) {
+ if (containsNullKey) {
+ return removeNullEntry();
+ }
+ return defRetValue;
+ }
+ final K[] key = this.keys;
+ int pos = HashCommon.mix(k.hashCode()) & mask;
+ K curr = key[pos & mask];
+ // The starting point.
+ if (curr == null) {
+ return defRetValue;
+ }
+ if (k.equals(curr)) {
+ return removeEntry(pos);
+ }
+ while (true) {
+ if ((curr = key[pos = (pos + 1) & mask]) == null) {
+ return defRetValue;
+ }
+ if (k.equals(curr)) {
+ return removeEntry(pos);
+ }
+ }
+ }
+ private V removeEntry(final int pos) {
+ final V oldValue = values[pos];
+ values[pos] = null;
+ size--;
+ shiftKeys(pos);
+ if (size < maxFill / 4 && arraySize > DEFAULT_INITIAL_SIZE) {
+ rehash(arraySize / 2);
+ }
+ return oldValue;
+ }
+ private V removeNullEntry() {
+ containsNullKey = false;
+ keys[arraySize] = null;
+ final V oldValue = values[arraySize];
+ values[arraySize] = null;
+ size--;
+ if (size < maxFill / 4 && arraySize > DEFAULT_INITIAL_SIZE) {
+ rehash(arraySize / 2);
+ }
+ return oldValue;
+ }
+ /**
+ * Shifts left entries with the specified hash code, starting at the
+ * specified position, and empties the resulting free entry.
+ *
+ * @param pos
+ * a starting position.
+ */
+ private void shiftKeys(int pos) {
+ // Shift entries with the same hash.
+ int last, slot;
+ K curr;
+ final K[] myKeys = this.keys;
+ for (;;) {
+ pos = ((last = pos) + 1) & mask;
+ for (;;) {
+ if (((curr = myKeys[pos]) == null)) {
+ myKeys[last] = (null);
+ values[last] = null;
+ return;
+ }
+ slot = HashCommon.mix(curr.hashCode()) & mask;
+ if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
+ break;
+ }
+ pos = (pos + 1) & mask;
+ }
+ myKeys[last] = curr;
+ values[last] = values[pos];
+ }
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Rehashes this map if the table is too large.
+ *
+ * <P>
+ * Let <var>N</var> be the smallest table size that can hold
+ * <code>max(n,{@link #size()})</code> entries, still satisfying the load
+ * factor. If the current table size is smaller than or equal to
+ * <var>N</var>, this method does nothing. Otherwise, it rehashes this map
+ * in a table of size <var>N</var>.
+ *
+ * <P>
+ * This method is useful when reusing maps. {@linkplain #clear() Clearing a
+ * map} leaves the table size untouched. If you are reusing a map many times,
+ * you can call this method with a typical size to avoid keeping around a
+ * very large table just because of a few large transient maps.
+ *
+ * @param n
+ * the threshold for the trimming.
+ * @return true if there was enough memory to trim the map.
+ */
+ public boolean trim(final int n) {
+ final int l = HashCommon.nextPowerOfTwo((int) Math.ceil(n / loadFactor));
+ if (l >= n || size > HashCommon.maxFill(l, loadFactor))
+ return true;
+ try {
+ rehash(l);
+ } catch (OutOfMemoryError cantDoIt) { // unusual to catch OOME but in this case appropriate
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Rehashes the map.
+ *
+ * <P>
+ * This method implements the basic rehashing strategy, and may be overriden
+ * by subclasses implementing different rehashing strategies (e.g.,
+ * disk-based rehashing). However, you should not override this method
+ * unless you understand the internal workings of this class.
+ *
+ * @param newN
+ * the new size
+ */
+ @SuppressWarnings("unchecked")
+ protected void rehash(final int newN) {
+ final K myKeys[] = this.keys;
+ final V myValues[] = this.values;
+ final int mask = newN - 1; // Note that this is used by the hashing
+ // macro
+ final K newKey[] = (K[]) new Object[newN + 1];
+ final V newValue[] = (V[]) new Object[newN + 1];
+ int i = arraySize, pos;
+ for (int j = realSize(); j-- != 0;) {
+ while (myKeys[--i] == null) {
+ // advance i until we find an existing key
+ }
+ if (newKey[pos = HashCommon.mix(myKeys[i].hashCode()) & mask] != null) { // rehash & check slot availability
+ while (newKey[pos = (pos + 1) & mask] != null) {
+ // find available slot at (or immediately following) pos
+ }
+ }
+ newKey[pos] = myKeys[i];
+ newValue[pos] = myValues[i];
+ }
+ newValue[newN] = myValues[arraySize];
+ arraySize = newN;
+ this.mask = mask;
+ maxFill = HashCommon.maxFill(arraySize, loadFactor);
+ this.keys = newKey;
+ this.values = newValue;
+ }
+
+ /**
+ * Returns a hash code for this map.
+ *
+ * @return a hash code for this map.
+ */
+ public int hashCode() {
+ int result = 0;
+ for (int j = realSize(), i = 0, t = 0; j-- != 0;) {
+ while (keys[i] == null) {
+ i++;
+ }
+ if (this != keys[i]) {
+ t = keys[i].hashCode();
+ }
+ if (this != values[i]) {
+ t ^= (values[i] == null ? 0 : values[i].hashCode());
+ }
+ result += t;
+ i++;
+ }
+ // Zero / null keys have hash zero.
+ if (containsNullKey) {
+ result += (values[arraySize] == null ? 0 : values[arraySize].hashCode());
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ arraySize = HashCommon.arraySize(size, loadFactor);
+ maxFill = HashCommon.maxFill(arraySize, loadFactor);
+ mask = arraySize - 1;
+ final K key[] = this.keys = (K[]) new Object[arraySize + 1];
+ final V value[] = this.values = (V[]) new Object[arraySize + 1];
+ K k;
+ V v;
+ for (int i = size, pos; i-- != 0;) {
+ k = (K) s.readObject();
+ v = (V) s.readObject();
+ if (k == null) {
+ pos = arraySize;
+ containsNullKey = true;
+ } else {
+ pos = HashCommon.mix(k.hashCode()) & mask;
+ while (key[pos] != null) {
+ pos = (pos + 1) & mask;
+ }
+ }
+ key[pos] = k;
+ value[pos] = v;
+ }
+ }
+
+ private void writeObject(final ObjectOutputStream s) throws IOException {
+ s.defaultWriteObject();
+ try {
+ forEach(SERIALIZER, s);
+ } catch (final RuntimeException runex) {
+ if (runex.getCause() instanceof IOException) {
+ throw (IOException) runex.getCause();
+ }
+ throw runex;
+ }
+ }
+
+ private static final TriConsumer<String, Object, ObjectOutputStream> SERIALIZER =
+ new TriConsumer<String, Object, ObjectOutputStream>() {
+ @Override
+ public void accept(final String k, final Object v, final ObjectOutputStream objectOutputStream) {
+ try {
+ objectOutputStream.writeObject(k);
+ objectOutputStream.writeObject(v);
+ } catch (final IOException ioex) {
+ throw new IllegalStateException(ioex);
+ }
+ }
+ };
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(256);
+ sb.append('{');
+ final K myKeys[] = this.keys;
+ int pos = arraySize;
+ boolean first = true;
+ if (containsNullKey) {
+ sb.append(myKeys[pos] == this ? "(this map)" : myKeys[pos]);
+ sb.append('=');
+ sb.append(values[pos] == this ? "(this map)" : values[pos]);
+ first = false;
+ }
+ --pos;
+ for (; pos >= 0; pos--) {
+ if (myKeys[pos] != null) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(myKeys[pos] == this ? "(this map)" : myKeys[pos]);
+ sb.append('=');
+ sb.append(values[pos] == this ? "(this map)" : values[pos]);
+ }
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ private static class HashCommon {
+ private HashCommon() {}
+
+ /** 2<sup>32</sup> · φ, φ = (√5 − 1)/2. */
+ private static final int INT_PHI = 0x9E3779B9;
+
+ /** The reciprocal of {@link #INT_PHI} modulo 2<sup>32</sup>. */
+ private static final int INV_INT_PHI = 0x144cbc89;
+
+ /** Avalanches the bits of an integer by applying the finalisation step of MurmurHash3.
+ *
+ * <p>This method implements the finalisation step of Austin Appleby's
+ * <a href="http://code.google.com/p/smhasher/">MurmurHash3</a>.
+ * Its purpose is to avalanche the bits of the argument to within 0.25% bias.
+ *
+ * @param x an integer.
+ * @return a hash value with good avalanching properties.
+ */
+ public static int murmurHash3(int x) {
+ x ^= x >>> 16;
+ x *= 0x85ebca6b;
+ x ^= x >>> 13;
+ x *= 0xc2b2ae35;
+ x ^= x >>> 16;
+ return x;
+ }
+
+ /**
+ * Quickly mixes the bits of an integer.
+ *
+ * <p>This method mixes the bits of the argument by multiplying by the golden ratio and
+ * xorshifting the result. It is borrowed from <a href="https://github.com/OpenHFT/Koloboke">Koloboke</a>, and
+ * it has slightly worse behaviour than {@link #murmurHash3(int)} (in open-addressing hash tables the average
+ * number of probes is slightly larger), but it's much faster.
+ *
+ * @param x an integer.
+ * @return a hash value obtained by mixing the bits of {@code x}.
+ * @see #invMix(int)
+ */
+ public static int mix(final int x) {
+ final int h = x * INT_PHI;
+ return h ^ (h >>> 16);
+ }
+
+ /** The inverse of {@link #mix(int)}. This method is mainly useful to create unit tests.
+ *
+ * @param x an integer.
+ * @return a value that passed through {@link #mix(int)} would give {@code x}.
+ */
+ public static int invMix(final int x) {
+ return (x ^ x >>> 16) * INV_INT_PHI;
+ }
+
+ /** Return the least power of two greater than or equal to the specified value.
+ *
+ * <p>Note that this function will return 1 when the argument is 0.
+ *
+ * @param x an integer smaller than or equal to 2<sup>30</sup>.
+ * @return the least power of two greater than or equal to the specified value.
+ */
+ public static int nextPowerOfTwo(int x) {
+ if (x == 0) {
+ return 1;
+ }
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ return (x | x >> 16) + 1;
+ }
+
+ /** Return the least power of two greater than or equal to the specified value.
+ *
+ * <p>Note that this function will return 1 when the argument is 0.
+ *
+ * @param x a long integer smaller than or equal to 2<sup>62</sup>.
+ * @return the least power of two greater than or equal to the specified value.
+ */
+ public static long nextPowerOfTwo(long x) {
+ if (x == 0) {
+ return 1;
+ }
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return (x | x >> 32) + 1;
+ }
+
+
+ /** Returns the maximum number of entries that can be filled before rehashing.
+ *
+ * @param n the size of the backing array.
+ * @param f the load factor.
+ * @return the maximum number of entries before rehashing.
+ */
+ public static int maxFill(final int n, final float f) {
+ /* We must guarantee that there is always at least
+ * one free entry (even with pathological load factors). */
+ return Math.min((int) Math.ceil(n * f), n - 1);
+ }
+
+ /**
+ * Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or equal to
+ * <code>Math.ceil( expected / f )</code>.
+ *
+ * @param expected the expected number of elements in a hash table.
+ * @param f the load factor.
+ * @return the minimum possible size for a backing array.
+ * @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>.
+ */
+ public static int arraySize(final int expected, final float f) {
+ final long result = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f)));
+ if (result > (1 << 30)) {
+ throw new IllegalArgumentException("Too large (" + expected +
+ " expected elements with load factor " + f + ")");
+ }
+ return (int) result;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
new file mode 100644
index 0000000..f3438c5
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
@@ -0,0 +1,156 @@
+/*
+ * 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 SortedArrayContextData}.
+ */
+public class SortedArrayThreadContext implements ThreadContextMap {
+ /**
+ * 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<ArrayContextData> localMap;
+
+ public SortedArrayThreadContext() {
+ this.localMap = createThreadLocalMap();
+ }
+
+ // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
+ // (This method is package protected for JUnit tests.)
+ static ThreadLocal<ArrayContextData> createThreadLocalMap() {
+ final PropertiesUtil managerProps = PropertiesUtil.getProperties();
+ final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
+ if (inheritable) {
+ return new InheritableThreadLocal<ArrayContextData>() {
+ @Override
+ protected ArrayContextData childValue(final ArrayContextData parentValue) {
+ return parentValue != null ? new ArrayContextData(parentValue) : null;
+ }
+ };
+ }
+ // if not inheritable, return plain ThreadLocal with null as initial value
+ return new ThreadLocal<>();
+ }
+
+ private ArrayContextData getThreadLocalMap() {
+ ArrayContextData map = localMap.get();
+ if (map == null) {
+ map = new ArrayContextData();
+ localMap.set(map);
+ }
+ return map;
+ }
+
+ @Override
+ public void put(final String key, final String value) {
+ getThreadLocalMap().put(key, value);
+ }
+
+ @Override
+ public String get(final String key) {
+ final ArrayContextData map = localMap.get();
+ return map == null ? null : map.get(key);
+ }
+
+ @Override
+ public void remove(final String key) {
+ final ArrayContextData map = localMap.get();
+ if (map != null) {
+ map.remove(key);
+ }
+ }
+
+ @Override
+ public void clear() {
+ localMap.remove();
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ final ArrayContextData map = localMap.get();
+ return map != null && map.containsKey(key);
+ }
+
+ @Override
+ public Map<String, String> getCopy() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? Collections.<String, String>emptyMap() : map.asMap();
+ }
+
+ public ContextData getContextData() {
+ return localMap.get();
+ }
+
+ @Override
+ public Map<String, String> getImmutableMapOrNull() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? null : Collections.unmodifiableMap(map.asMap());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ final ArrayContextData map = localMap.get();
+ return map == null || map.size() == 0;
+ }
+
+ @Override
+ public String toString() {
+ final ArrayContextData map = localMap.get();
+ return map == null ? "{}" : map.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ final ArrayContextData 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/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
new file mode 100644
index 0000000..3dad0e4
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
@@ -0,0 +1,19 @@
+package org.apache.logging.log4j.util;
+
+/**
+ * An operation that accepts two input arguments and returns no result.
+ *
+ * @param <K> type of the first argument
+ * @param <V> type of the second argument
+ * @see org.apache.logging.log4j.core.ContextData
+ * @since 2.7
+ */
+public interface BiConsumer<K, V> {
+
+ /**
+ * Performs the operation given the specified arguments.
+ * @param k the first input argument
+ * @param v the second input argument
+ */
+ void accept(K k, V v);
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
new file mode 100644
index 0000000..63960be
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
@@ -0,0 +1,21 @@
+package org.apache.logging.log4j.util;
+
+/**
+ * An operation that accepts three input arguments and returns no result.
+ *
+ * @param <K> type of the first argument
+ * @param <V> type of the second argument
+ * @param <S> type of the third argument
+ * @see org.apache.logging.log4j.core.ContextData
+ * @since 2.7
+ */
+public interface TriConsumer<K, V, S> {
+
+ /**
+ * Performs the operation given the specified arguments.
+ * @param k the first input argument
+ * @param v the second input argument
+ * @param s the third input argument
+ */
+ void accept(K k, V v, S s);
+}
[11/16] logging-log4j2 git commit: LOG4J2-1349 add package-protected
method getThreadContextMap
Posted by rp...@apache.org.
LOG4J2-1349 add package-protected method getThreadContextMap
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/ffdd2d9c
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/ffdd2d9c
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/ffdd2d9c
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: ffdd2d9c0f76e3720775c5ff338616e23b99e2dc
Parents: 0e9aace
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 10:29:37 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:13 2016 +0900
----------------------------------------------------------------------
.../java/org/apache/logging/log4j/ThreadContext.java | 12 ++++++++++++
1 file changed, 12 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/ffdd2d9c/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
index c932746..e0a88dc 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/ThreadContext.java
@@ -356,6 +356,18 @@ public final class ThreadContext {
}
/**
+ * Returns the internal data structure used to store thread context key-value pairs.
+ * <p><em>
+ * This data structure is not intended to be used directly by applications. This method is package protected for
+ * internal log4j2 usage.
+ * </em></p>
+ * @return the internal data structure used to store thread context key-value pairs
+ */
+ static ThreadContextMap getThreadContextMap() {
+ return contextMap;
+ }
+
+ /**
* Returns true if the Map is empty.
*
* @return true if the Map is empty, false otherwise.
[08/16] logging-log4j2 git commit: revert to putAll(Map extends K,
? extends V>), and don't implement ThreadContextMap2
Posted by rp...@apache.org.
revert to putAll(Map<? extends K, ? extends V>), and don't implement ThreadContextMap2
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/2ab025ec
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/2ab025ec
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/2ab025ec
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 2ab025ecfe4840043870a877e4f79e6ba12ee428
Parents: 4182abd
Author: rpopma <rp...@apache.org>
Authored: Sat Aug 20 11:15:24 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:08 2016 +0900
----------------------------------------------------------------------
.../logging/log4j/spi/OpenHashMapContextData.java | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/2ab025ec/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
index 00712cc..72f5622 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
@@ -140,8 +140,7 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
* @param map
* a {@link Map} to be copied into the new hash map.
*/
- // TODO public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
- public OpenHashMapContextData(final Map<String, String> map) {
+ public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
this(map, DEFAULT_LOAD_FACTOR);
}
/**
@@ -152,8 +151,7 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
* @param f
* the load factor.
*/
- // TODO public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
- public OpenHashMapContextData(final Map<String, String> map, final float f) {
+ public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
this(map.size(), f);
putAll(map);
}
@@ -460,8 +458,7 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
}
/** {@inheritDoc} */
- //TODO public void putAll(Map<? extends K, ? extends V> map) {
- public void putAll(Map<String, String> map) {
+ public void putAll(Map<? extends K, ? extends V> map) {
if (loadFactor <= .5) {
// The resulting map will be sized for m.size() elements
ensureCapacity(map.size());
@@ -469,8 +466,8 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
// The resulting map will be tentatively sized for size() + m.size() elements
tryCapacity(size() + map.size());
}
- for (Map.Entry<String, String> entry : map.entrySet()) {
- putValue(entry.getKey(), entry.getValue());
+ for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
+ putObjectValue(entry.getKey(), entry.getValue());
}
}
[15/16] logging-log4j2 git commit: LOG4J2-1349 add
ThreadContextBenchmarkAccess for calling init() from benchmark
Posted by rp...@apache.org.
LOG4J2-1349 add ThreadContextBenchmarkAccess for calling init() from benchmark
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/eb0d5ed8
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/eb0d5ed8
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/eb0d5ed8
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: eb0d5ed8d6c568bba9d2dc38e0a4ed0355ededd3
Parents: bdee385
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 20:08:13 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:18 2016 +0900
----------------------------------------------------------------------
.../log4j/ThreadContextBenchmarkAccess.java | 34 ++++++++++++++++++++
1 file changed, 34 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/eb0d5ed8/log4j-perf/src/main/java/org/apache/logging/log4j/ThreadContextBenchmarkAccess.java
----------------------------------------------------------------------
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/ThreadContextBenchmarkAccess.java b/log4j-perf/src/main/java/org/apache/logging/log4j/ThreadContextBenchmarkAccess.java
new file mode 100644
index 0000000..129b863
--- /dev/null
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/ThreadContextBenchmarkAccess.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * <p>
+ * Utility class to access package protected methods in {@code ThreadContext}.
+ * </p>
+ *
+ * @see ThreadContext
+ * @since 2.7
+ */
+public final class ThreadContextBenchmarkAccess {
+ private ThreadContextBenchmarkAccess() { // prevent instantiation
+ }
+
+ public static void init() {
+ ThreadContext.init();
+ }
+}
[10/16] logging-log4j2 git commit: LOG4J2-1010, LOG4J2-1447,
LOG4J2-1349 ContextDataInjector::injectContextData now returns a
MutableContextData object to allow returning copy-on-write ThreadContext
internal data structures. This allows us to avoid unnec
Posted by rp...@apache.org.
LOG4J2-1010, LOG4J2-1447, LOG4J2-1349 ContextDataInjector::injectContextData now returns a MutableContextData object to allow returning copy-on-write ThreadContext internal data structures. This allows us to avoid unnecessary data copies in non-garbage free configurations.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/0e9aaceb
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/0e9aaceb
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/0e9aaceb
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 0e9aaceba2a3ce416941093ad8dec2d6593d3c13
Parents: 5c6acbf
Author: rpopma <rp...@apache.org>
Authored: Sat Aug 20 22:48:40 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:11 2016 +0900
----------------------------------------------------------------------
.../org/apache/logging/log4j/core/impl/ContextDataInjector.java | 1 -
.../apache/logging/log4j/core/impl/ContextDataInjectorFactory.java | 1 -
2 files changed, 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0e9aaceb/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
index 0c77b9c..30d099e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
@@ -36,7 +36,6 @@ import org.apache.logging.log4j.spi.MutableContextData;
*
* @see ContextData
* @see ContextDataInjectorFactory
- * @see org.apache.logging.log4j.core.ContextData
* @see org.apache.logging.log4j.ThreadContext
* @see ThreadContextDataInjector
* @since 2.7
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/0e9aaceb/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
index 51b17cd..d3da505 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
@@ -34,7 +34,6 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* @see ContextDataInjector
* @see ContextData
* @see ThreadContextDataInjector
- * @see org.apache.logging.log4j.core.ContextData
* @see LogEvent#getContextData()
* @since 2.7
*/
[07/16] logging-log4j2 git commit: LOG4J2-1516 moved putAll(Map)
method into separate ThreadContextMap2 interface
Posted by rp...@apache.org.
LOG4J2-1516 moved putAll(Map) method into separate ThreadContextMap2 interface
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4182abd0
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4182abd0
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4182abd0
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 4182abd080e2f1fd8bf4d866823555b410fc5bc4
Parents: 4a0962b
Author: rpopma <rp...@apache.org>
Authored: Sat Aug 20 09:15:43 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:07 2016 +0900
----------------------------------------------------------------------
.../log4j/spi/AbstractCopyOnWriteMutableThreadContext.java | 4 +++-
.../log4j/spi/AbstractGarbageFreeMutableThreadContext.java | 8 +++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4182abd0/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
index fe6c367..42529f7 100644
--- 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
@@ -27,8 +27,10 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* 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 {
+public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadContextMap, ThreadContextMap2 {
/**
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
* {@code ThreadLocal} (value is not "true") in the implementation.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4182abd0/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
index 45010a0..73636a9 100644
--- 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
@@ -23,8 +23,14 @@ 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 {
+public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadContextMap, ThreadContextMap2 {
/**
* Property name ({@value} ) for selecting {@code InheritableThreadLocal} (value "true") or plain
* {@code ThreadLocal} (value is not "true") in the implementation.
[05/16] logging-log4j2 git commit: LOG4J2-1349 made ThreadContextMap
classes abstract
Posted by rp...@apache.org.
LOG4J2-1349 made ThreadContextMap classes abstract
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/efb22938
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/efb22938
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/efb22938
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: efb2293850576385684b6ace9e9f3dbb3e3107a2
Parents: 1118b27
Author: rpopma <rp...@apache.org>
Authored: Fri Aug 19 23:14:00 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:04 2016 +0900
----------------------------------------------------------------------
...AbstractCopyOnWriteMutableThreadContext.java | 173 +++++++++++++++++++
...AbstractGarbageFreeMutableThreadContext.java | 171 ++++++++++++++++++
.../CopyOnWriteSortedArrayThreadContext.java | 156 -----------------
.../log4j/spi/SortedArrayThreadContext.java | 156 -----------------
4 files changed, 344 insertions(+), 312 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/efb22938/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
new file mode 100644
index 0000000..fe6c367
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractCopyOnWriteMutableThreadContext.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadContextMap {
+ /**
+ * 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 MutableContextData 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();
+ }
+
+ public ContextData getContextData() {
+ 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/efb22938/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
new file mode 100644
index 0000000..45010a0
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractGarbageFreeMutableThreadContext.java
@@ -0,0 +1,171 @@
+/*
+ * 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}.
+ */
+public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadContextMap {
+ /**
+ * 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 MutableContextData 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();
+ }
+
+ public ContextData getContextData() {
+ 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/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
deleted file mode 100644
index be172b4..0000000
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContext.java
+++ /dev/null
@@ -1,156 +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 SortedArrayContextData}.
- * 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.
- */
-public class CopyOnWriteSortedArrayThreadContext implements ThreadContextMap {
- /**
- * 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<ArrayContextData> localMap;
-
- public CopyOnWriteSortedArrayThreadContext() {
- this.localMap = createThreadLocalMap();
- }
-
- // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
- // (This method is package protected for JUnit tests.)
- static ThreadLocal<ArrayContextData> createThreadLocalMap() {
- final PropertiesUtil managerProps = PropertiesUtil.getProperties();
- final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
- if (inheritable) {
- return new InheritableThreadLocal<ArrayContextData>() {
- @Override
- protected ArrayContextData childValue(final ArrayContextData parentValue) {
- return parentValue != null ? new ArrayContextData(parentValue) : null;
- }
- };
- }
- // if not inheritable, return plain ThreadLocal with null as initial value
- return new ThreadLocal<>();
- }
-
- @Override
- public void put(final String key, final String value) {
- ArrayContextData map = localMap.get();
- map = map == null ? new ArrayContextData() : new ArrayContextData(map);
- map.put(key, value);
- localMap.set(map);
- }
-
- @Override
- public String get(final String key) {
- final ArrayContextData map = localMap.get();
- return map == null ? null : map.get(key);
- }
-
- @Override
- public void remove(final String key) {
- final ArrayContextData map = localMap.get();
- if (map != null) {
- final ArrayContextData copy = new ArrayContextData(map);
- copy.remove(key);
- localMap.set(copy);
- }
- }
-
- @Override
- public void clear() {
- localMap.remove();
- }
-
- @Override
- public boolean containsKey(final String key) {
- final ArrayContextData map = localMap.get();
- return map != null && map.containsKey(key);
- }
-
- @Override
- public Map<String, String> getCopy() {
- final ArrayContextData map = localMap.get();
- return map == null ? Collections.<String, String>emptyMap() : map.asMap();
- }
-
- public ContextData getContextData() {
- return localMap.get();
- }
-
- @Override
- public Map<String, String> getImmutableMapOrNull() {
- final ArrayContextData map = localMap.get();
- return map == null ? null : Collections.unmodifiableMap(map.asMap());
- }
-
- @Override
- public boolean isEmpty() {
- final ArrayContextData map = localMap.get();
- return map == null || map.size() == 0;
- }
-
- @Override
- public String toString() {
- final ArrayContextData map = localMap.get();
- return map == null ? "{}" : map.toString();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- final ArrayContextData 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/efb22938/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
deleted file mode 100644
index f3438c5..0000000
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/SortedArrayThreadContext.java
+++ /dev/null
@@ -1,156 +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 SortedArrayContextData}.
- */
-public class SortedArrayThreadContext implements ThreadContextMap {
- /**
- * 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<ArrayContextData> localMap;
-
- public SortedArrayThreadContext() {
- this.localMap = createThreadLocalMap();
- }
-
- // LOG4J2-479: by default, use a plain ThreadLocal, only use InheritableThreadLocal if configured.
- // (This method is package protected for JUnit tests.)
- static ThreadLocal<ArrayContextData> createThreadLocalMap() {
- final PropertiesUtil managerProps = PropertiesUtil.getProperties();
- final boolean inheritable = managerProps.getBooleanProperty(INHERITABLE_MAP);
- if (inheritable) {
- return new InheritableThreadLocal<ArrayContextData>() {
- @Override
- protected ArrayContextData childValue(final ArrayContextData parentValue) {
- return parentValue != null ? new ArrayContextData(parentValue) : null;
- }
- };
- }
- // if not inheritable, return plain ThreadLocal with null as initial value
- return new ThreadLocal<>();
- }
-
- private ArrayContextData getThreadLocalMap() {
- ArrayContextData map = localMap.get();
- if (map == null) {
- map = new ArrayContextData();
- localMap.set(map);
- }
- return map;
- }
-
- @Override
- public void put(final String key, final String value) {
- getThreadLocalMap().put(key, value);
- }
-
- @Override
- public String get(final String key) {
- final ArrayContextData map = localMap.get();
- return map == null ? null : map.get(key);
- }
-
- @Override
- public void remove(final String key) {
- final ArrayContextData map = localMap.get();
- if (map != null) {
- map.remove(key);
- }
- }
-
- @Override
- public void clear() {
- localMap.remove();
- }
-
- @Override
- public boolean containsKey(final String key) {
- final ArrayContextData map = localMap.get();
- return map != null && map.containsKey(key);
- }
-
- @Override
- public Map<String, String> getCopy() {
- final ArrayContextData map = localMap.get();
- return map == null ? Collections.<String, String>emptyMap() : map.asMap();
- }
-
- public ContextData getContextData() {
- return localMap.get();
- }
-
- @Override
- public Map<String, String> getImmutableMapOrNull() {
- final ArrayContextData map = localMap.get();
- return map == null ? null : Collections.unmodifiableMap(map.asMap());
- }
-
- @Override
- public boolean isEmpty() {
- final ArrayContextData map = localMap.get();
- return map == null || map.size() == 0;
- }
-
- @Override
- public String toString() {
- final ArrayContextData map = localMap.get();
- return map == null ? "{}" : map.toString();
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- final ArrayContextData 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;
- }
-}
[03/16] logging-log4j2 git commit: LOG4J2-1349 GC-free ThreadContext
initial commit
Posted by rp...@apache.org.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/test/java/org/apache/logging/log4j/spi/ArrayContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/ArrayContextDataTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/spi/ArrayContextDataTest.java
new file mode 100644
index 0000000..f85db8a
--- /dev/null
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/spi/ArrayContextDataTest.java
@@ -0,0 +1,569 @@
+package org.apache.logging.log4j.spi;/*
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the ArrayContextData class.
+ */
+public class ArrayContextDataTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructorDisallowsNegativeCapacity() throws Exception {
+ new ArrayContextData(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructorDisallowsZeroCapacity() throws Exception {
+ new ArrayContextData(0);
+ }
+
+ @Test
+ public void testConstructorIgnoresNull() throws Exception {
+ assertEquals(0, new ArrayContextData(null).size());
+ }
+
+ @Test
+ public void testToString() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals("{3=3value, B=Bvalue, a=avalue}", original.toString());
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final byte[] binary = serialize(original);
+ final ArrayContextData copy = deserialize(binary);
+ assertEquals(original, copy);
+ }
+
+ private byte[] serialize(final ArrayContextData data) throws IOException {
+ final ByteArrayOutputStream arr = new ByteArrayOutputStream();
+ final ObjectOutputStream out = new ObjectOutputStream(arr);
+ out.writeObject(data);
+ return arr.toByteArray();
+ }
+
+ private ArrayContextData deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
+ final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
+ final ObjectInputStream in = new ObjectInputStream(inArr);
+ final ArrayContextData result = (ArrayContextData) in.readObject();
+ return result;
+ }
+
+ @Test
+ public void testPutAll() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final ArrayContextData other = new ArrayContextData();
+ other.putAll(original);
+ assertEquals(original, other);
+
+ other.putValue("3", "otherValue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", null);
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+ }
+
+ @Test
+ public void testEquals() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals(original, original); // equal to itself
+
+ final ArrayContextData other = new ArrayContextData();
+ other.putValue("a", "avalue");
+ assertNotEquals(original, other);
+
+ other.putValue("B", "Bvalue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+
+ other.putValue("3", "otherValue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", null);
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+ }
+
+ @Test
+ public void testAsMap() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final Map<String, Object> expected = new HashMap<>();
+ expected.put("a", "avalue");
+ expected.put("B", "Bvalue");
+ expected.put("3", "3value");
+
+ assertEquals(expected, original.asMap());
+ }
+
+ @Test
+ public void testGetCopyDelegatesToAsMap() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals(original.getCopy(), original.asMap());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(original.getCopy(), original.asMap());
+
+ original.putValue("3", "3value");
+ assertEquals(original.getCopy(), original.asMap());
+ }
+
+ @Test
+ public void testGetImmutableMapOrNull() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ original.putValue("3", "3value");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ try {
+ original.getImmutableMapOrNull().put("abc", "xyz");
+ fail("Expected map to be immutable");
+ } catch (final UnsupportedOperationException ok) {
+ //ok
+ }
+ }
+
+ @Test
+ public void testPutInsertsInAlphabeticOrder() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "avalue");
+ original.put("B", "Bvalue");
+ original.put("3", "3value");
+ original.put("c", "cvalue");
+ original.put("d", "dvalue");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("avalue", original.getValueAt(2));
+
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("Bvalue", original.getValueAt(1));
+
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("3value", original.getValueAt(0));
+
+ assertEquals("cvalue", original.getValue("c"));
+ assertEquals("cvalue", original.getValueAt(3));
+
+ assertEquals("dvalue", original.getValue("d"));
+ assertEquals("dvalue", original.getValueAt(4));
+ }
+
+ @Test
+ public void testPutValueInsertsInAlphabeticOrder() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("avalue", original.getValueAt(2));
+
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("Bvalue", original.getValueAt(1));
+
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("3value", original.getValueAt(0));
+
+ assertEquals("cvalue", original.getValue("c"));
+ assertEquals("cvalue", original.getValueAt(3));
+
+ assertEquals("dvalue", original.getValue("d"));
+ assertEquals("dvalue", original.getValueAt(4));
+ }
+
+ @Test
+ public void testNullKeysAllowed() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+ assertEquals(5, original.size());
+ assertEquals("{3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
+
+ original.putValue(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
+
+ original.putValue(null, "otherNullvalue");
+ assertEquals("{null=otherNullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
+ assertEquals(6, original.size());
+
+ original.putValue(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
+
+ original.putValue(null, "abc");
+ assertEquals(6, original.size());
+ assertEquals("{null=abc, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
+ }
+
+ @Test
+ public void testNullKeysCopiedToAsMap() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+ assertEquals(5, original.size());
+
+ HashMap<String, String> expected = new HashMap<>();
+ expected.put("a", "avalue");
+ expected.put("B", "Bvalue");
+ expected.put("3", "3value");
+ expected.put("c", "cvalue");
+ expected.put("d", "dvalue");
+ assertEquals("initial", expected, original.asMap());
+
+ original.putValue(null, "nullvalue");
+ expected.put(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key", expected, original.asMap());
+
+ original.putValue(null, "otherNullvalue");
+ expected.put(null, "otherNullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key value2", expected, original.asMap());
+
+ original.putValue(null, "nullvalue");
+ expected.put(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key value1 again", expected, original.asMap());
+
+ original.putValue(null, "abc");
+ expected.put(null, "abc");
+ assertEquals(6, original.size());
+ assertEquals("with null key value3", expected, original.asMap());
+ }
+
+ @Test
+ public void testRemove() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertEquals("avalue", original.getValue("a"));
+
+ original.remove("a");
+ assertEquals(0, original.size());
+ assertNull("no a val", original.getValue("a"));
+
+ original.remove("B");
+ assertEquals(0, original.size());
+ assertNull("no B val", original.getValue("B"));
+ }
+
+ @Test
+ public void testNullValuesArePreserved() {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertEquals("avalue", original.getValue("a"));
+
+ original.putValue("a", null);
+ assertEquals(1, original.size());
+ assertNull("no a val", original.getValue("a"));
+
+ original.putValue("B", null);
+ assertEquals(2, original.size());
+ assertNull("no B val", original.getValue("B"));
+ }
+
+ @Test
+ public void testGet() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "avalue");
+ original.put("B", "Bvalue");
+ original.put("3", "3value");
+
+ assertEquals("avalue", original.get("a"));
+ assertEquals("Bvalue", original.get("B"));
+ assertEquals("3value", original.get("3"));
+
+ original.putValue("0", "0value");
+ assertEquals("0value", original.get("0"));
+ assertEquals("3value", original.get("3"));
+ assertEquals("Bvalue", original.get("B"));
+ assertEquals("avalue", original.get("a"));
+ }
+
+ @Test
+ public void testGetValue_GetValueAt() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("avalue", original.getValueAt(2));
+
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("Bvalue", original.getValueAt(1));
+
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("3value", original.getValueAt(0));
+
+ original.putValue("0", "0value");
+ assertEquals("0value", original.getValue("0"));
+ assertEquals("0value", original.getValueAt(0));
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("3value", original.getValueAt(1));
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("Bvalue", original.getValueAt(2));
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("avalue", original.getValueAt(3));
+ }
+
+ @Test
+ public void testClear() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals(3, original.size());
+
+ original.clear();
+ assertEquals(0, original.size());
+
+ // ensure slots in the values array are nulled out
+ Field f = ArrayContextData.class.getDeclaredField("values");
+ f.setAccessible(true);
+ Object[] values = (Object[]) f.get(original);
+ for (int i = 0; i < values.length; i++) {
+ assertNull(values[i]);
+ }
+ }
+
+ @Test
+ public void testIndexOfKey() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals(0, original.indexOfKey("a"));
+
+ original.putValue("B", "Bvalue");
+ assertEquals(1, original.indexOfKey("a"));
+ assertEquals(0, original.indexOfKey("B"));
+
+ original.putValue("3", "3value");
+ assertEquals(2, original.indexOfKey("a"));
+ assertEquals(1, original.indexOfKey("B"));
+ assertEquals(0, original.indexOfKey("3"));
+
+ original.putValue("A", "AAA");
+ assertEquals(3, original.indexOfKey("a"));
+ assertEquals(2, original.indexOfKey("B"));
+ assertEquals(1, original.indexOfKey("A"));
+ assertEquals(0, original.indexOfKey("3"));
+
+ original.putValue("C", "CCC");
+ assertEquals(4, original.indexOfKey("a"));
+ assertEquals(3, original.indexOfKey("C"));
+ assertEquals(2, original.indexOfKey("B"));
+ assertEquals(1, original.indexOfKey("A"));
+ assertEquals(0, original.indexOfKey("3"));
+
+ original.putValue("2", "222");
+ assertEquals(5, original.indexOfKey("a"));
+ assertEquals(4, original.indexOfKey("C"));
+ assertEquals(3, original.indexOfKey("B"));
+ assertEquals(2, original.indexOfKey("A"));
+ assertEquals(1, original.indexOfKey("3"));
+ assertEquals(0, original.indexOfKey("2"));
+ }
+
+ @Test
+ public void testContainsKey() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ assertFalse("a", original.containsKey("a"));
+ assertFalse("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("a", "avalue");
+ assertTrue("a", original.containsKey("a"));
+ assertFalse("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("B", "Bvalue");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("3", "3value");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertTrue("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("A", "AAA");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertTrue("3", original.containsKey("3"));
+ assertTrue("A", original.containsKey("A"));
+ }
+
+ @Test
+ public void testGetValueAt() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ assertEquals("a", original.getKeyAt(0));
+ assertEquals("avalue", original.getValueAt(0));
+
+ original.putValue("B", "Bvalue");
+ assertEquals("B", original.getKeyAt(0));
+ assertEquals("Bvalue", original.getValueAt(0));
+ assertEquals("a", original.getKeyAt(1));
+ assertEquals("avalue", original.getValueAt(1));
+
+ original.putValue("3", "3value");
+ assertEquals("3", original.getKeyAt(0));
+ assertEquals("3value", original.getValueAt(0));
+ assertEquals("B", original.getKeyAt(1));
+ assertEquals("Bvalue", original.getValueAt(1));
+ assertEquals("a", original.getKeyAt(2));
+ assertEquals("avalue", original.getValueAt(2));
+ }
+
+ @Test
+ public void testSizeAndIsEmpty() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ assertEquals(0, original.size());
+ assertTrue("initial", original.isEmpty());
+
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(2, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.putValue("3", "3value");
+ assertEquals(3, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("B");
+ assertEquals(2, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("3");
+ assertEquals(1, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("a");
+ assertEquals(0, original.size());
+ assertTrue("size=" + original.size(), original.isEmpty());
+ }
+
+ @Test
+ public void testForEachBiConsumer() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ original.forEach(new BiConsumer<String, String>() {
+ int count = 0;
+ @Override
+ public void accept(final String key, final String value) {
+ assertEquals("key", key, original.getKeyAt(count));
+ assertEquals("val", value, original.getValueAt(count));
+ count++;
+ assertTrue("count should not exceed size but was " + count, count <= original.size());
+ }
+ });
+ }
+
+ static class State {
+ ArrayContextData data;
+ int count;
+ }
+ static TriConsumer<String, String, State> COUNTER = new TriConsumer<String, String, State>() {
+ @Override
+ public void accept(final String key, final String value, final State state) {
+ assertEquals("key", key, state.data.getKeyAt(state.count));
+ assertEquals("val", value, state.data.getValueAt(state.count));
+ state.count++;
+ assertTrue("count should not exceed size but was " + state.count,
+ state.count <= state.data.size());
+ }
+ };
+
+ @Test
+ public void testForEachTriConsumer() throws Exception {
+ final ArrayContextData original = new ArrayContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final State state = new State();
+ state.data = original;
+ original.forEach(COUNTER, state);
+ assertEquals(state.count, original.size());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-api/src/test/java/org/apache/logging/log4j/spi/OpenHashMapContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/OpenHashMapContextDataTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/spi/OpenHashMapContextDataTest.java
new file mode 100644
index 0000000..a49ca81
--- /dev/null
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/spi/OpenHashMapContextDataTest.java
@@ -0,0 +1,507 @@
+package org.apache.logging.log4j.spi;/*
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.spi.OpenHashMapContextData;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the OpenHashMapContextData class.
+ */
+public class OpenHashMapContextDataTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructorDisallowsNegativeCapacity() throws Exception {
+ new OpenHashMapContextData(-1);
+ }
+
+ @Test
+ public void testConstructorAllowsZeroCapacity() throws Exception {
+ OpenHashMapContextData data = new OpenHashMapContextData(0);
+ assertEquals(2, data.arraySize);
+ }
+
+ @Test(expected = NullPointerException.class)
+ @SuppressWarnings("unchecked")
+ public void testConstructorDisallowsNullMap() throws Exception {
+ assertEquals(0, new OpenHashMapContextData((Map) null).size());
+ }
+
+ @Test(expected = NullPointerException.class)
+ @SuppressWarnings("unchecked")
+ public void testConstructorDisallowsNullContextData() throws Exception {
+ assertEquals(0, new OpenHashMapContextData((ContextData) null).size());
+ }
+
+ @Test
+ public void testToString() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals("{B=Bvalue, a=avalue, 3=3value}", original.toString());
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final byte[] binary = serialize(original);
+ final OpenHashMapContextData copy = deserialize(binary);
+ assertEquals(original, copy);
+ }
+
+ private byte[] serialize(final OpenHashMapContextData data) throws IOException {
+ final ByteArrayOutputStream arr = new ByteArrayOutputStream();
+ final ObjectOutputStream out = new ObjectOutputStream(arr);
+ out.writeObject(data);
+ return arr.toByteArray();
+ }
+
+ private OpenHashMapContextData deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
+ final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
+ final ObjectInputStream in = new ObjectInputStream(inArr);
+ final OpenHashMapContextData result = (OpenHashMapContextData) in.readObject();
+ return result;
+ }
+
+ @Test
+ public void testPutAll() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final OpenHashMapContextData other = new OpenHashMapContextData();
+ other.putAll(original);
+ assertEquals(original, other);
+
+ other.putValue("3", "otherValue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", null);
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+ }
+
+ @Test
+ public void testEquals() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals(original, original); // equal to itself
+
+ final OpenHashMapContextData other = new OpenHashMapContextData();
+ other.putValue("a", "avalue");
+ assertNotEquals(original, other);
+
+ other.putValue("B", "Bvalue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+
+ other.putValue("3", "otherValue");
+ assertNotEquals(original, other);
+
+ other.putValue("3", null);
+ assertNotEquals(original, other);
+
+ other.putValue("3", "3value");
+ assertEquals(original, other);
+ }
+
+ @Test
+ public void testAsMap() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ final Map<String, Object> expected = new HashMap<>();
+ expected.put("a", "avalue");
+ expected.put("B", "Bvalue");
+ expected.put("3", "3value");
+
+ assertEquals(expected, original.asMap());
+ }
+
+ @Test
+ public void testGetCopyDelegatesToAsMap() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ assertEquals(original.getCopy(), original.asMap());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(original.getCopy(), original.asMap());
+
+ original.putValue("3", "3value");
+ assertEquals(original.getCopy(), original.asMap());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testGetImmutableMapOrNull() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ original.putValue("3", "3value");
+ assertEquals(original.getImmutableMapOrNull(), original.asMap());
+
+ try {
+ original.getImmutableMapOrNull().put("abc", "xyz");
+ fail("Expected map to be immutable");
+ } catch (final UnsupportedOperationException ok) {
+ //ok
+ }
+ }
+
+ @Test
+ public void testPutInsertsInAlphabeticOrder() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.put("a", "avalue");
+ original.put("B", "Bvalue");
+ original.put("3", "3value");
+ original.put("c", "cvalue");
+ original.put("d", "dvalue");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("cvalue", original.getValue("c"));
+ assertEquals("dvalue", original.getValue("d"));
+ }
+
+ @Test
+ public void testPutValueInsertsInAlphabeticOrder() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("cvalue", original.getValue("c"));
+ assertEquals("dvalue", original.getValue("d"));
+ }
+
+ @Test
+ public void testNullKeysAllowed() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+ assertEquals(5, original.size());
+ assertEquals("{B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
+
+ original.putValue(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("{null=nullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
+
+ original.putValue(null, "otherNullvalue");
+ assertEquals("{null=otherNullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
+ assertEquals(6, original.size());
+
+ original.putValue(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("{null=nullvalue, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
+
+ original.putValue(null, "abc");
+ assertEquals(6, original.size());
+ assertEquals("{null=abc, B=Bvalue, a=avalue, 3=3value, d=dvalue, c=cvalue}", original.toString());
+ }
+
+ @Test
+ public void testNullKeysCopiedToAsMap() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ original.putValue("c", "cvalue");
+ original.putValue("d", "dvalue");
+ assertEquals(5, original.size());
+
+ HashMap<String, String> expected = new HashMap<>();
+ expected.put("a", "avalue");
+ expected.put("B", "Bvalue");
+ expected.put("3", "3value");
+ expected.put("c", "cvalue");
+ expected.put("d", "dvalue");
+ assertEquals("initial", expected, original.asMap());
+
+ original.putValue(null, "nullvalue");
+ expected.put(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key", expected, original.asMap());
+
+ original.putValue(null, "otherNullvalue");
+ expected.put(null, "otherNullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key value2", expected, original.asMap());
+
+ original.putValue(null, "nullvalue");
+ expected.put(null, "nullvalue");
+ assertEquals(6, original.size());
+ assertEquals("with null key value1 again", expected, original.asMap());
+
+ original.putValue(null, "abc");
+ expected.put(null, "abc");
+ assertEquals(6, original.size());
+ assertEquals("with null key value3", expected, original.asMap());
+ }
+
+ @Test
+ public void testRemove() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertEquals("avalue", original.getValue("a"));
+
+ original.remove("a");
+ assertEquals(0, original.size());
+ assertNull("no a val", original.getValue("a"));
+
+ original.remove("B");
+ assertEquals(0, original.size());
+ assertNull("no B val", original.getValue("B"));
+ }
+
+ @Test
+ public void testNullValuesArePreserved() {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertEquals("avalue", original.getValue("a"));
+
+ original.putValue("a", null);
+ assertEquals(1, original.size());
+ assertNull("no a val", original.getValue("a"));
+
+ original.putValue("B", null);
+ assertEquals(2, original.size());
+ assertNull("no B val", original.getValue("B"));
+ }
+
+ @Test
+ public void testGet() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.put("a", "avalue");
+ original.put("B", "Bvalue");
+ original.put("3", "3value");
+
+ assertEquals("avalue", original.get("a"));
+ assertEquals("Bvalue", original.get("B"));
+ assertEquals("3value", original.get("3"));
+
+ original.putValue("0", "0value");
+ assertEquals("0value", original.get("0"));
+ assertEquals("3value", original.get("3"));
+ assertEquals("Bvalue", original.get("B"));
+ assertEquals("avalue", original.get("a"));
+ }
+
+ @Test
+ public void testGetValue_GetValueAt() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+
+ assertEquals("avalue", original.getValue("a"));
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("3value", original.getValue("3"));
+ original.putValue("0", "0value");
+ assertEquals("0value", original.getValue("0"));
+ assertEquals("3value", original.getValue("3"));
+ assertEquals("Bvalue", original.getValue("B"));
+ assertEquals("avalue", original.getValue("a"));
+ }
+
+ @Test
+ public void testClear() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ original.putValue("a", "avalue");
+ original.putValue("B", "Bvalue");
+ original.putValue("3", "3value");
+ assertEquals(3, original.size());
+
+ original.clear();
+ assertEquals(0, original.size());
+
+ // ensure slots in the values array are nulled out
+ Field f = OpenHashMapContextData.class.getDeclaredField("values");
+ f.setAccessible(true);
+ Object[] values = (Object[]) f.get(original);
+ for (int i = 0; i < values.length; i++) {
+ assertNull(values[i]);
+ }
+ }
+
+ @Test
+ public void testContainsKey() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ assertFalse("a", original.containsKey("a"));
+ assertFalse("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("a", "avalue");
+ assertTrue("a", original.containsKey("a"));
+ assertFalse("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("B", "Bvalue");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertFalse("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("3", "3value");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertTrue("3", original.containsKey("3"));
+ assertFalse("A", original.containsKey("A"));
+
+ original.putValue("A", "AAA");
+ assertTrue("a", original.containsKey("a"));
+ assertTrue("B", original.containsKey("B"));
+ assertTrue("3", original.containsKey("3"));
+ assertTrue("A", original.containsKey("A"));
+ }
+
+ @Test
+ public void testSizeAndIsEmpty() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ assertEquals(0, original.size());
+ assertTrue("initial", original.isEmpty());
+
+ original.putValue("a", "avalue");
+ assertEquals(1, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.putValue("B", "Bvalue");
+ assertEquals(2, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.putValue("3", "3value");
+ assertEquals(3, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("B");
+ assertEquals(2, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("3");
+ assertEquals(1, original.size());
+ assertFalse("size=" + original.size(), original.isEmpty());
+
+ original.remove("a");
+ assertEquals(0, original.size());
+ assertTrue("size=" + original.size(), original.isEmpty());
+ }
+
+ @Test
+ public void testForEachBiConsumer() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ final List<String> keys = new ArrayList<>(Arrays.asList("a", "B", "3", null));
+ final List<String> values = new ArrayList<>(Arrays.asList("aValue", "Bvalue", "3Value", "nullValue"));
+ for (int i = 0; i < keys.size(); i++) {
+ original.put(keys.get(i), values.get(i));
+ }
+
+ original.forEach(new BiConsumer<String, String>() {
+ int count = 0;
+ @Override
+ public void accept(final String key, final String value) {
+ assertTrue("key exists", keys.remove(key));
+ assertTrue("val exists", values.remove(value));
+ assertEquals("val", value, original.getValue(key));
+ count++;
+ assertTrue("count should not exceed size but was " + count, count <= original.size());
+ }
+ });
+ }
+
+ static class State<K, V> {
+ OpenHashMapContextData data;
+ int count;
+ List<K> keys;
+ List<V> values;
+ }
+ private static TriConsumer<String, String, State> COUNTER = new TriConsumer<String, String, State>() {
+ @Override
+ public void accept(final String key, final String value, final State state) {
+ assertTrue("key exists", state.keys.remove(key));
+ assertTrue("val exists", state.values.remove(value));
+ assertEquals("val", value, state.data.getValue(key));
+ state.count++;
+ assertTrue("count should not exceed size but was " + state.count,
+ state.count <= state.data.size());
+ }
+ };
+
+ @Test
+ public void testForEachTriConsumer() throws Exception {
+ final OpenHashMapContextData original = new OpenHashMapContextData();
+ final List<String> keys = Arrays.asList("a", "B", "3", null);
+ final List<String> values = Arrays.asList("aValue", "Bvalue", "3Value", "nullValue");
+ for (int i = 0; i < keys.size(); i++) {
+ original.put(keys.get(i), values.get(i));
+ }
+ final State state = new State();
+ state.data = original;
+ state.keys = new ArrayList(keys);
+ state.values = new ArrayList(values);
+
+ original.forEach(COUNTER, state);
+ assertEquals(state.count, original.size());
+ assertTrue("all keys", state.keys.isEmpty());
+ assertTrue("all values", state.values.isEmpty());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
index d2a3b0a..01b2883 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/AbstractLogEvent.java
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.ThreadContext.ContextStack;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.spi.ContextData;
/**
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextData.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextData.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextData.java
deleted file mode 100644
index 9c50255..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/ContextData.java
+++ /dev/null
@@ -1,119 +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.core;
-
-import java.io.Serializable;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.TriConsumer;
-
-/**
- * A read-only collection of context data. Context data items are String keys and values of arbitrary type that are
- * set by the application to be included in all subsequent log events. A typical source of context data is the
- * {@code ThreadContextMap} and the {@code Properties} defined in the configuration.
- * <p>
- * Applications can put custom data in this collection by installing a custom {@code ContextDataInjector}.
- * </p>
- *
- * @see org.apache.logging.log4j.spi.ThreadContextMap
- * @see org.apache.logging.log4j.core.config.Property
- * @see org.apache.logging.log4j.core.impl.ContextDataInjector
- * @see org.apache.logging.log4j.core.impl.ContextDataInjectorFactory
- * @since 2.7
- */
-public interface ContextData extends Serializable {
- /**
- * Returns a map view of this context data.
- * Called to implement {@link LogEvent#getContextMap()}.
- *
- * @return a map view of this context data
- */
- Map<String, String> asMap();
-
- /**
- * Returns {@code true} if this context data contains the specified key, {@code false} otherwise.
- *
- * @param key the key whose presence to check. May be {@code null}.
- * @return {@code true} if this context data contains the specified key, {@code false} otherwise
- */
- boolean containsKey(String key);
-
- /**
- * Performs the given action for each key-value pair in this data structure
- * until all entries have been processed or the action throws an exception.
- * <p>
- * Some implementations may not support structural modifications (adding new elements or removing elements) while
- * iterating over the contents. In such implementations, attempts to add or remove elements from the
- * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} method may cause a
- * {@code ConcurrentModificationException} to be thrown.
- * </p>
- *
- * @param action The action to be performed for each key-value pair in this collection
- * @param <V> type of the value
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- <V> void forEach(final BiConsumer<String, ? super V> action);
-
- /**
- * Performs the given action for each key-value pair in this data structure
- * until all entries have been processed or the action throws an exception.
- * <p>
- * The third parameter lets callers pass in a stateful object to be modified with the key-value pairs,
- * so the TriConsumer implementation itself can be stateless and potentially reusable.
- * </p>
- * <p>
- * Some implementations may not support structural modifications (adding new elements or removing elements) while
- * iterating over the contents. In such implementations, attempts to add or remove elements from the
- * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) accept} method may cause a
- * {@code ConcurrentModificationException} to be thrown.
- * </p>
- *
- * @param action The action to be performed for each key-value pair in this collection
- * @param state the object to be passed as the third parameter to each invocation on the specified
- * triconsumer
- * @param <V> type of the value
- * @param <S> type of the third parameter
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state);
-
- /**
- * Returns the value for the specified key, or {@code null} if the specified key does not exist in this collection.
- *
- * @param key the key whose value to return
- * @return the value for the specified key or {@code null}
- */
- <V> V getValue(final String key);
-
- /**
- * Returns {@code true} if this collection is empty (size is zero), {@code false} otherwise.
- * @return {@code true} if this collection is empty (size is zero)
- */
- boolean isEmpty();
-
- /**
- * Returns the number of key-value pairs in this collection.
- *
- * @return the number of key-value pairs in this collection
- */
- int size();
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
index 20b2c3a..872793d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
@@ -25,6 +25,7 @@ import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.spi.ContextData;
/**
* Provides contextual information about a logged message. A LogEvent must be {@link java.io.Serializable} so that it
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
index 80e1d62..661beab 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/AbstractLogEventWrapperEntity.java
@@ -26,7 +26,7 @@ import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.AbstractLogEvent;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.db.jpa.converter.ContextDataAttributeConverter;
import org.apache.logging.log4j.message.Message;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
index a25fca8..9441ec1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverter.java
@@ -19,7 +19,7 @@ package org.apache.logging.log4j.core.appender.db.jpa.converter;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
/**
* A JPA 2.1 attribute converter for {@link ContextData ContextData<Object>}s in
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
index 93fe95a..03ae801 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverter.java
@@ -23,10 +23,10 @@ import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import javax.persistence.PersistenceException;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
-import org.apache.logging.log4j.core.impl.MutableContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
+import org.apache.logging.log4j.spi.MutableContextData;
+import org.apache.logging.log4j.util.BiConsumer;
import org.apache.logging.log4j.util.Strings;
import com.fasterxml.jackson.databind.JsonNode;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index 895def7..c9a0c68 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -30,7 +30,7 @@ import org.apache.logging.log4j.core.config.ReliabilityStrategy;
import org.apache.logging.log4j.core.impl.ContextDataInjector;
import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
import org.apache.logging.log4j.core.util.Constants;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
index 8c873ff..d20be14 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
@@ -23,11 +23,11 @@ import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext.ContextStack;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.util.Constants;
import org.apache.logging.log4j.message.Message;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
index d95a5b7..04d9ce1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
@@ -21,7 +21,7 @@ import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext.ContextStack;
import org.apache.logging.log4j.core.impl.ContextDataInjector;
import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.message.Message;
import com.lmax.disruptor.EventTranslator;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ArrayContextData.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ArrayContextData.java
deleted file mode 100644
index a1580a2..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ArrayContextData.java
+++ /dev/null
@@ -1,438 +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.core.impl;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.Integers;
-import org.apache.logging.log4j.core.util.TriConsumer;
-import org.apache.logging.log4j.spi.ThreadContextMap;
-
-/**
- * Array-based implementation of the {@code ContextData} interface. Keys are held in a sorted array.
- * <p>
- * This is not a generic collection, but makes some trade-offs to optimize for the Log4j ContextData use case:
- * </p>
- * <ul>
- * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
- * <li>Fast copy. If the ThreadContextMap is also an instance of {@code ArrayContextData}, the full thread context
- * data can be transferred with two array copies and two field updates.</li>
- * <li>Acceptable performance for small data sets. The current implementation stores keys in a sorted array, values
- * are stored in a separate array at the same index.
- * Worst-case performance of {@code get} and {@code containsKey} is O(log N),
- * worst-case performance of {@code put} and {@code remove} is O(N log N).
- * The expectation is that for the small values of {@code N} (less than 100) that are the vast majority of
- * ThreadContext use cases, the constants dominate performance more than the asymptotic performance of the
- * algorithms used.
- * </li>
- * <li>Compact representation.</li>
- * </ul>
- *
- * @see ThreadContextDataInjector
- * @since 2.7
- */
-public class ArrayContextData implements MutableContextData, ThreadContextMap {
-
- /**
- * The default initial capacity.
- */
- private static final int DEFAULT_INITIAL_CAPACITY = 4;
- private static final long serialVersionUID = -5748905872274478116L;
- private static final int HASHVAL = 31;
-
- private static final TriConsumer<String, Object, MutableContextData> PUT_ALL = new TriConsumer<String, Object, MutableContextData>() {
- @Override
- public void accept(final String key, final Object value, final MutableContextData contextData) {
- contextData.putValue(key, value);
- }
- };
-
- /**
- * An empty array instance to share when the table is not inflated.
- */
- private static final String[] EMPTY = {};
-
- private transient String[] keys = EMPTY;
- private transient Object[] values = EMPTY;
-
- /**
- * The number of key-value mappings contained in this map.
- */
- private transient int size;
-
- /**
- * The next size value at which to resize (capacity * load factor).
- * @serial
- */
- // If table == EMPTY_TABLE then this is the initial capacity at which the
- // table will be created when inflated.
- private int threshold;
-
- public ArrayContextData() {
- this(DEFAULT_INITIAL_CAPACITY);
- }
-
- public ArrayContextData(final int initialCapacity) {
- if (initialCapacity < 1) {
- throw new IllegalArgumentException("Initial capacity must be at least one but was " + initialCapacity);
- }
- threshold = Integers.ceilingNextPowerOfTwo(initialCapacity);
- }
-
- public ArrayContextData(final ContextData other) {
- if (other instanceof ArrayContextData) {
- initFrom0((ArrayContextData) other);
- } else if (other != null) {
- resize(Integers.ceilingNextPowerOfTwo(other.size()));
- other.forEach(PUT_ALL, this);
- }
- }
-
- @Override
- public void clear() {
- Arrays.fill(keys, 0, size, null);
- Arrays.fill(values, 0, size, null);
- size = 0;
- }
-
- @Override
- public boolean containsKey(final String key) {
- return indexOfKey(key) >= 0;
- }
-
- @Override
- public Map<String, String> asMap() {
- final Map<String, String> result = new HashMap<>(size());
- for (int i = 0; i < size(); i++) {
- final Object value = getValueAt(i);
- result.put(getKeyAt(i), value == null ? null : String.valueOf(value));
- }
- return result;
- }
-
- @Override
- public Map<String, String> getCopy() {
- return asMap();
- }
-
- @Override
- public Map<String, String> getImmutableMapOrNull() {
- return isEmpty() ? null : Collections.unmodifiableMap(asMap());
- }
-
- @Override
- public String get(final String key) {
- final Object result = getValue(key);
- return result == null ? null : String.valueOf(result);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V> V getValue(final String key) {
- final int index = indexOfKey(key);
- if (index < 0) {
- return null;
- }
- return (V) values[index];
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
-
- int indexOfKey(final String key) {
- if (keys == EMPTY) {
- return -1;
- }
- if (key == null) { // null key is located at the start of the array
- return nullKeyIndex(); // insert at index zero
- }
- final int start = size > 0 && keys[0] == null ? 1 : 0;
- return Arrays.binarySearch(keys, start, size, key);
- }
-
- private int nullKeyIndex() {
- return size > 0 && keys[0] == null ? 0 : ~0;
- }
-
- @Override
- public void put(final String key, final String value) {
- putValue(key, value);
- }
-
- @Override
- public void putValue(final String key, final Object value) {
- if (keys == EMPTY) {
- inflateTable(threshold);
- }
- final int index = indexOfKey(key);
- if (index >= 0) {
- keys[index] = key;
- values[index] = value;
- } else { // not found, so insert.
- insertAt(~index, key, value);
- }
- }
-
- private void insertAt(final int index, final String key, final Object value) {
- ensureCapacity();
- System.arraycopy(keys, index, keys, index + 1, size - index);
- System.arraycopy(values, index, values, index + 1, size - index);
- keys[index] = key;
- values[index] = value;
- size++;
- }
-
- @Override
- public void putAll(final ContextData source) {
- if (source instanceof ArrayContextData) {
- initFrom0((ArrayContextData) source);
- } else if (source != null) {
- source.forEach(PUT_ALL, this);
- }
- }
-
- public void initFrom(final ArrayContextData other) {
- initFrom0(other);
- }
-
- private void initFrom0(final ArrayContextData other) {
- if (keys.length < other.size) {
- keys = new String[other.threshold];
- values = new Object[other.threshold];
- }
- System.arraycopy(other.keys, 0, keys, 0, other.size);
- System.arraycopy(other.values, 0, values, 0, other.size);
-
- size = other.size;
- threshold = other.threshold;
- }
-
- private void ensureCapacity() {
- if (size >= threshold) {
- resize(threshold * 2);
- }
- }
-
- private void resize(final int newCapacity) {
- final String[] oldKeys = keys;
- final Object[] oldValues = values;
-
- keys = new String[newCapacity];
- values = new Object[newCapacity];
-
- System.arraycopy(oldKeys, 0, keys, 0, size);
- System.arraycopy(oldValues, 0, values, 0, size);
-
- threshold = newCapacity;
- }
-
- /**
- * Inflates the table.
- */
- private void inflateTable(int toSize) {
- threshold = toSize;
- keys = new String[toSize];
- values = new Object[toSize];
- }
-
- @Override
- public void remove(final String key) {
- if (keys == EMPTY) {
- return;
- }
- final int index = indexOfKey(key);
- if (index >= 0) {
- System.arraycopy(keys, index + 1, keys, index, size - index);
- System.arraycopy(values, index + 1, values, index, size - index);
- size--;
- }
- }
-
- String getKeyAt(final int index) {
- if (index < 0 || index >= size) {
- return null;
- }
- return keys[index];
- }
-
- @SuppressWarnings("unchecked")
- <V> V getValueAt(final int index) {
- if (index < 0 || index >= size) {
- return null;
- }
- return (V) values[index];
- }
-
- @Override
- public int size() {
- return size;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V> void forEach(BiConsumer<String, ? super V> action) {
- for (int i = 0; i < size; i++) {
- action.accept(keys[i], (V) values[i]);
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V, T> void forEach(TriConsumer<String, ? super V, T> action, T state) {
- for (int i = 0; i < size; i++) {
- action.accept(keys[i], (V) values[i], state);
- }
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof ArrayContextData)) {
- return false;
- }
- ArrayContextData other = (ArrayContextData) obj;
- if (this.size() != other.size()) {
- return false;
- }
- for (int i = 0; i < size(); i++) {
- if (!Objects.equals(keys[i], other.keys[i])) {
- return false;
- }
- if (!Objects.equals(values[i], other.values[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = 37;
- result = HASHVAL * result + size;
- result = HASHVAL * result + hashCode(keys, size);
- result = HASHVAL * result + hashCode(values, size);
- return result;
- }
-
- private static int hashCode(Object[] values, int length) {
- int result = 1;
- for (int i = 0; i < length; i++) {
- result = HASHVAL * result + (values[i] == null ? 0 : values[i].hashCode());
- }
- return result;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(256);
- sb.append('{');
- for (int i = 0; i < size; i++) {
- if (i > 0) {
- sb.append(", ");
- }
- sb.append(keys[i]).append('=');
- sb.append(values[i] == this ? "(this map)" : values[i]);
- }
- sb.append('}');
- return sb.toString();
- }
-
- /**
- * Save the state of the {@code ArrayContextData} instance to a stream (i.e.,
- * serialize it).
- *
- * @serialData The <i>capacity</i> of the ArrayContextData (the length of the
- * bucket array) is emitted (int), followed by the
- * <i>size</i> (an int, the number of key-value
- * mappings), followed by the key (Object) and value (Object)
- * for each key-value mapping. The key-value mappings are
- * emitted in no particular order.
- */
- private void writeObject(java.io.ObjectOutputStream s) throws IOException {
- // Write out the threshold, and any hidden stuff
- s.defaultWriteObject();
-
- // Write out number of buckets
- if (keys == EMPTY) {
- s.writeInt(Integers.ceilingNextPowerOfTwo(threshold));
- } else {
- s.writeInt(keys.length);
- }
-
- // Write out size (number of Mappings)
- s.writeInt(size);
-
- // Write out keys and values (alternating)
- if (size > 0) {
- for (int i = 0; i < size; i++) {
- s.writeObject(keys[i]);
- s.writeObject(values[i]);
- }
- }
- }
-
- /**
- * Reconstitute the {@code ArrayContextData} instance from a stream (i.e.,
- * deserialize it).
- */
- private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
- // Read in the threshold (ignored), and any hidden stuff
- s.defaultReadObject();
-
- // set other fields that need values
- keys = EMPTY;
- values = EMPTY;
-
- // Read in number of buckets
- int capacity = s.readInt();
- if (capacity < 0) {
- throw new InvalidObjectException("Illegal capacity: " + capacity);
- }
-
- // Read number of mappings
- int mappings = s.readInt();
- if (mappings < 0) {
- throw new InvalidObjectException("Illegal mappings count: " + mappings);
- }
-
- // allocate the bucket array;
- if (mappings > 0) {
- inflateTable(capacity);
- } else {
- threshold = capacity;
- }
-
- // Read the keys and values, and put the mappings in the arrays
- for (int i = 0; i < mappings; i++) {
- keys[i] = (String) s.readObject();
- values[i] = s.readObject();
- }
- size = mappings;
- }
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
index 6cf5ad1..340298a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
@@ -16,6 +16,9 @@
*/
package org.apache.logging.log4j.core.impl;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
+import org.apache.logging.log4j.spi.OpenHashMapContextData;
import org.apache.logging.log4j.util.PropertiesUtil;
/**
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
index 5b74968..0c77b9c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
@@ -19,6 +19,8 @@ package org.apache.logging.log4j.core.impl;
import java.util.List;
import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
/**
* Responsible for initializing the ContextData of LogEvents. Context data is data that is set by the application to be
@@ -32,6 +34,7 @@ import org.apache.logging.log4j.core.config.Property;
* any arbitrary context.
* </p>
*
+ * @see ContextData
* @see ContextDataInjectorFactory
* @see org.apache.logging.log4j.core.ContextData
* @see org.apache.logging.log4j.ThreadContext
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
index ac5c38c..51b17cd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.impl;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.spi.ThreadContextMap;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.LoaderUtil;
@@ -31,6 +32,7 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* {@code ThreadContextDataInjector}.
*
* @see ContextDataInjector
+ * @see ContextData
* @see ThreadContextDataInjector
* @see org.apache.logging.log4j.core.ContextData
* @see LogEvent#getContextData()
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
index 81af731..e45a405 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
@@ -26,7 +26,8 @@ import java.util.Objects;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.async.RingBufferLogEvent;
import org.apache.logging.log4j.core.config.LoggerConfig;
@@ -40,6 +41,7 @@ import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.message.TimestampMessage;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableContextData.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableContextData.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableContextData.java
deleted file mode 100644
index 739f043..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableContextData.java
+++ /dev/null
@@ -1,68 +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.core.impl;
-
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.TriConsumer;
-
-/**
- * Exposes methods to add and remove key-value pairs to and from {@code ContextData}.
- *
- * @see ContextData
- * @since 2.7
- */
-public interface MutableContextData extends ContextData {
-
- /**
- * Removes all key-value pairs from this collection.
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- void clear();
-
- /**
- * Puts the specified key-value pair into the collection.
- *
- * @param key the key to add or remove. Keys may be {@code null}.
- * @param value the value to add. Values may be {@code null}.
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- void putValue(final String key, final Object value);
-
- /**
- * Copy all key-value pairs from the specified {@code ContextData} into this {@code MutableContextData}.
- * @param source the {@code ContextData} to copy key-value pairs from
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- void putAll(final ContextData source);
-
- /**
- * Removes the key-value pair for the specified key from this context data collection.
- *
- * @param key the key to remove. May be {@code null}.
- * @throws java.util.ConcurrentModificationException some implementations may not support structural modifications
- * to this context data while iterating over the contents with {@link #forEach(BiConsumer)} or
- * {@link #forEach(TriConsumer, Object)}.
- */
- void remove(final String key);
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
index 08423c6..bcdc40d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
@@ -24,13 +24,14 @@ import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.util.Constants;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.util.Strings;
/**
[02/16] logging-log4j2 git commit: LOG4J2-1349 GC-free ThreadContext
initial commit
Posted by rp...@apache.org.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/OpenHashMapContextData.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/OpenHashMapContextData.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/OpenHashMapContextData.java
deleted file mode 100644
index e1c0e13..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/OpenHashMapContextData.java
+++ /dev/null
@@ -1,887 +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.core.impl;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.Integers;
-import org.apache.logging.log4j.core.util.TriConsumer;
-import org.apache.logging.log4j.spi.ThreadContextMap;
-
-/**
- * Open hash map-based implementation of the {@code ContextData} interface.
- * Implementation based on <a href="http://fastutil.di.unimi.it/">fastutil</a>'s
- * <a href="http://fastutil.di.unimi.it/docs/it/unimi/dsi/fastutil/objects/Object2ObjectOpenHashMap.html">Object2ObjectOpenHashMap</a>.
- * <p>
- * A type-specific hash map with a fast, small-footprint implementation.
- *
- * <P>
- * Instances of this class use a hash table to represent a map. The table is
- * filled up to a specified <em>load factor</em>, and then doubled in size to
- * accommodate new entries. If the table is emptied below <em>one fourth</em> of
- * the load factor, it is halved in size. However, halving is not performed when
- * deleting entries from an iterator, as it would interfere with the iteration
- * process.
- *
- * <p>
- * Note that {@link #clear()} does not modify the hash table size. Rather, the
- * {@link #trim(int)} method lets you control the size of
- * the table; this is particularly useful if you reuse instances of this class.
- * <p>
- * <ul>
- * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
- * <li>Fast copy. If the ThreadContextMap is also an instance of {@code OpenHashMapContextData},
- * the full thread context data can be transferred with two array copies and five field updates.</li>
- * </ul>
- *
- * @see ThreadContextDataInjector
- * @since 2.7
- */
-public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadContextMap {
- /** The initial default size of a hash table. */
- public static final int DEFAULT_INITIAL_SIZE = 16;
-
- /** The default load factor of a hash table. */
- public static final float DEFAULT_LOAD_FACTOR = .75f;
-
- private static final long serialVersionUID = -1486744623338827187L;
-
- /** The array of keys. */
- protected transient K[] keys;
- /** The array of values. */
- protected transient V[] values;
- /** The mask for wrapping a position counter. */
- protected transient int mask;
- /** Whether this set contains the key zero. */
- protected transient boolean containsNullKey;
- /** The current table size. */
- protected transient int arraySize;
- /**
- * Threshold after which we rehash. It must be the table size times {@link #loadFactor}.
- */
- protected transient int maxFill;
- /** Number of entries in the set (including the key zero, if present). */
- protected int size;
- /** The acceptable load factor. */
- protected final float loadFactor;
-
- private V defRetValue = null;
-
- /**
- * Creates a new hash map with initial expected
- * {@link #DEFAULT_INITIAL_SIZE} entries and
- * {@link #DEFAULT_LOAD_FACTOR} as load factor.
- */
- public OpenHashMapContextData() {
- this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR);
- }
- /**
- * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load factor.
- *
- * @param expected
- * the expected number of elements in the hash map.
- */
- public OpenHashMapContextData(final int expected) {
- this(expected, DEFAULT_LOAD_FACTOR);
- }
- /**
- * Creates a new hash map.
- *
- * <p>
- * The actual table size will be the least power of two greater than
- * <code>expected</code>/<code>f</code>.
- *
- * @param expected
- * the expected number of elements in the hash set.
- * @param f
- * the load factor.
- */
- @SuppressWarnings("unchecked")
- public OpenHashMapContextData(final int expected, final float f) {
- if (f <= 0 || f > 1) {
- throw new IllegalArgumentException(
- "Load factor must be greater than 0 and smaller than or equal to 1");
- }
- if (expected < 0){
- throw new IllegalArgumentException(
- "The expected number of elements must be nonnegative");
- }
- this.loadFactor = f;
- arraySize = HashCommon.arraySize(expected, f);
- mask = arraySize - 1;
- maxFill = HashCommon.maxFill(arraySize, f);
- keys = (K[]) new Object[arraySize + 1];
- values = (V[]) new Object[arraySize + 1];
- }
- /**
- * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load
- * factor copying a given one.
- *
- * @param map
- * a {@link Map} to be copied into the new hash map.
- */
- public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
- this(map, DEFAULT_LOAD_FACTOR);
- }
- /**
- * Creates a new hash map copying a given one.
- *
- * @param map
- * a {@link Map} to be copied into the new hash map.
- * @param f
- * the load factor.
- */
- public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
- this(map.size(), f);
- putAll(map);
- }
-
- /**
- * Creates a new hash map with {@link #DEFAULT_LOAD_FACTOR} as load
- * factor copying a given type-specific one.
- *
- * @param contextData
- * a type-specific map to be copied into the new hash map.
- */
- public OpenHashMapContextData(final ContextData contextData) {
- this(contextData, DEFAULT_LOAD_FACTOR);
- }
- /**
- * Creates a new hash map copying a given type-specific one.
- *
- * @param contextData
- * a type-specific map to be copied into the new hash map.
- * @param f
- * the load factor.
- */
- public OpenHashMapContextData(final ContextData contextData, final float f) {
- this(contextData.size(), f);
- if (contextData instanceof OpenHashMapContextData) {
- initFrom0((OpenHashMapContextData) contextData);
- } else {
- contextData.forEach(PUT_ALL, this);
- }
- }
- private static final TriConsumer<String, Object, MutableContextData> PUT_ALL =
- new TriConsumer<String, Object, MutableContextData>() {
- @Override
- public void accept(final String key, final Object value, final MutableContextData contextData) {
- contextData.putValue(key, value);
- }
- };
-
- @SuppressWarnings("unchecked")
- private void initFrom0(final OpenHashMapContextData other) {
- // this.loadFactor = other.loadFactor; // final field
- this.arraySize = other.arraySize;
- this.size = other.size;
- this.containsNullKey = other.containsNullKey;
- this.mask = other.mask;
- this.maxFill = other.maxFill;
- keys = (K[]) Arrays.copyOf(other.keys, arraySize + 1);
- values = (V[]) Arrays.copyOf(other.values, arraySize + 1);
- }
-
- private int realSize() {
- return containsNullKey ? size - 1 : size;
- }
-
- private void ensureCapacity(final int capacity) {
- final int needed = HashCommon.arraySize(capacity, loadFactor);
- if (needed > arraySize) {
- rehash(needed);
- }
- }
-
- private void tryCapacity(final long capacity) {
- final int needed = (int) Math.min(
- 1 << 30, Math.max(2, Integers.ceilingNextPowerOfTwo((int) Math.ceil(capacity / loadFactor))));
- if (needed > arraySize) {
- rehash(needed);
- }
- }
-
- /** {@inheritDoc} */
- public void putAll(Map<? extends K, ? extends V> map) {
- if (loadFactor <= .5) {
- // The resulting map will be sized for m.size() elements
- ensureCapacity(map.size());
- } else {
- // The resulting map will be tentatively sized for size() + m.size() elements
- tryCapacity(size() + map.size());
- }
- for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
- putObjectValue(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public Map<String, String> asMap() {
- final Map<String, String> result = new HashMap<>(size);
- forEach(COPY_INTO_MAP, result);
- return result;
- }
-
- private static final TriConsumer<String, Object, Map<String, String>> COPY_INTO_MAP =
- new TriConsumer<String, Object, Map<String, String>>() {
- @Override
- public void accept(final String k, final Object v, final Map<String, String> map) {
- map.put(k, v == null ? null : v.toString());
- }
- };
-
- /*
- * Removes all elements from this map.
- *
- * <P>To increase object reuse, this method does not change the table size.
- * If you want to reduce the table size, you must use {@link #trim()}.
- */
- @Override
- public void clear() {
- if (size == 0) {
- return;
- }
- size = 0;
- containsNullKey = false;
- Arrays.fill(keys, (null));
- Arrays.fill(values, null);
- }
-
- @Override
- public boolean containsKey(final String key) {
- return containsObjectKey((Object) key);
- }
-
- @SuppressWarnings("unchecked")
- private boolean containsObjectKey(final Object k) {
- if (k == null) {
- return containsNullKey;
- }
- K curr;
- final K[] key = this.keys;
- int pos;
- // The starting point.
- if ((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null) {
- return false;
- }
- if (k.equals(curr)) {
- return true;
- }
- // There's always an unused entry.
- while (true) {
- if ((curr = key[pos = (pos + 1) & mask]) == null) {
- return false;
- }
- if (k.equals(curr)) {
- return true;
- }
- }
- }
-
- public boolean equals(final Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof ContextData)) {
- return false;
- }
- final ContextData other = (ContextData) obj;
- if (other.size() != size()) {
- return false;
- }
- int pos = arraySize;
- if (containsNullKey) {
- if (!Objects.equals(getObjectValue(null), other.getValue(null))) {
- return false;
- }
- }
- --pos;
- final K myKeys[] = this.keys;
- for (; pos >= 0; pos--) {
- K k;
- if ((k = myKeys[pos]) != null) {
- if (!Objects.equals(values[pos], other.getValue((String) k))) {
- return false;
- }
- }
- }
- return true;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <VAL> void forEach(final BiConsumer<String, ? super VAL> action) {
- final int startSize = size;
- final K myKeys[] = this.keys;
- int pos = arraySize;
- if (containsNullKey) {
- action.accept((String) myKeys[pos], (VAL) values[pos]);
- if (size != startSize) {
- throw new ConcurrentModificationException();
- }
- }
- --pos;
- for (; pos >= 0; pos--) {
- if (myKeys[pos] != null) {
- action.accept((String) myKeys[pos], (VAL) values[pos]);
- if (size != startSize) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <VAL, STATE> void forEach(final TriConsumer<String, ? super VAL, STATE> action, final STATE state) {
- final int startSize = size;
- final K myKeys[] = this.keys;
- int pos = arraySize;
- if (containsNullKey) {
- action.accept((String) myKeys[pos], (VAL) values[pos], state);
- if (size != startSize) {
- throw new ConcurrentModificationException();
- }
- }
- --pos;
- for (; pos >= 0; pos--) {
- if (myKeys[pos] != null) {
- action.accept((String) myKeys[pos], (VAL) values[pos], state);
- if (size != startSize) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
-
- @Override
- public String get(final String key) {
- return (String) getObjectValue(key);
- }
-
- @SuppressWarnings("unchecked")
- private V getObjectValue(final Object k) {
- if (k == null) {
- return containsNullKey ? values[arraySize] : defRetValue;
- }
- K curr;
- final K[] key = this.keys;
- int pos;
- // The starting point.
- if ((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null) {
- return defRetValue;
- }
- if (k.equals(curr)) {
- return values[pos];
- }
- // There's always an unused entry.
- while (true) {
- if (((curr = key[pos = (pos + 1) & mask]) == null)) {
- return defRetValue;
- }
- if (k.equals(curr)) {
- return values[pos];
- }
- }
- }
-
- @Override
- public Map<String, String> getCopy() {
- return asMap();
- }
-
- @Override
- public Map<String, String> getImmutableMapOrNull() {
- return isEmpty() ? null : Collections.unmodifiableMap(asMap());
- }
-
- @Override
- public <VAL> VAL getValue(final String key) {
- return (VAL) getObjectValue((Object) key);
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void put(final String key, final String value) {
- putObjectValue((K) key, (V) value);
- }
-
- private int insert(final K k, final V v) {
- int pos;
- if (k == null) {
- if (containsNullKey) {
- return arraySize;
- }
- containsNullKey = true;
- pos = arraySize;
- } else {
- K curr;
- final K[] key = this.keys;
- // The starting point.
- if (!((curr = key[pos = HashCommon.mix(k.hashCode()) & mask]) == null)) {
- if (curr.equals(k)) {
- return pos;
- }
- while (!((curr = key[pos = (pos + 1) & mask]) == null)) {
- if (curr.equals(k)) {
- return pos;
- }
- }
- }
- }
- keys[pos] = k;
- values[pos] = v;
- if (size++ >= maxFill) {
- rehash(HashCommon.arraySize(size + 1, loadFactor));
- }
- return -1;
- }
-
- @Override
- public void putAll(final ContextData source) {
- if (size() == 0 && source instanceof OpenHashMapContextData) {
- initFrom0((OpenHashMapContextData) source);
- } else if (source != null) {
- source.forEach(PUT_ALL, this);
- }
- }
-
- private V putObjectValue(final K k, final V v) {
- final int pos = insert(k, v);
- if (pos < 0) {
- return defRetValue;
- }
- final V oldValue = values[pos];
- values[pos] = v;
- return oldValue;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void putValue(final String key, final Object value) {
- putObjectValue((K) key, (V) value);
- }
-
- @Override
- public void remove(final String key) {
- removeObjectKey((Object) key);
- }
-
- @SuppressWarnings("unchecked")
- private V removeObjectKey(final Object k) {
- if (k == null) {
- if (containsNullKey) {
- return removeNullEntry();
- }
- return defRetValue;
- }
- final K[] key = this.keys;
- int pos = HashCommon.mix(k.hashCode()) & mask;
- K curr = key[pos & mask];
- // The starting point.
- if (curr == null) {
- return defRetValue;
- }
- if (k.equals(curr)) {
- return removeEntry(pos);
- }
- while (true) {
- if ((curr = key[pos = (pos + 1) & mask]) == null) {
- return defRetValue;
- }
- if (k.equals(curr)) {
- return removeEntry(pos);
- }
- }
- }
- private V removeEntry(final int pos) {
- final V oldValue = values[pos];
- values[pos] = null;
- size--;
- shiftKeys(pos);
- if (size < maxFill / 4 && arraySize > DEFAULT_INITIAL_SIZE) {
- rehash(arraySize / 2);
- }
- return oldValue;
- }
- private V removeNullEntry() {
- containsNullKey = false;
- keys[arraySize] = null;
- final V oldValue = values[arraySize];
- values[arraySize] = null;
- size--;
- if (size < maxFill / 4 && arraySize > DEFAULT_INITIAL_SIZE) {
- rehash(arraySize / 2);
- }
- return oldValue;
- }
- /**
- * Shifts left entries with the specified hash code, starting at the
- * specified position, and empties the resulting free entry.
- *
- * @param pos
- * a starting position.
- */
- private void shiftKeys(int pos) {
- // Shift entries with the same hash.
- int last, slot;
- K curr;
- final K[] myKeys = this.keys;
- for (;;) {
- pos = ((last = pos) + 1) & mask;
- for (;;) {
- if (((curr = myKeys[pos]) == null)) {
- myKeys[last] = (null);
- values[last] = null;
- return;
- }
- slot = HashCommon.mix(curr.hashCode()) & mask;
- if (last <= pos ? (last >= slot || slot > pos) : (last >= slot && slot > pos)) {
- break;
- }
- pos = (pos + 1) & mask;
- }
- myKeys[last] = curr;
- values[last] = values[pos];
- }
- }
-
- @Override
- public int size() {
- return size;
- }
-
- /**
- * Rehashes this map if the table is too large.
- *
- * <P>
- * Let <var>N</var> be the smallest table size that can hold
- * <code>max(n,{@link #size()})</code> entries, still satisfying the load
- * factor. If the current table size is smaller than or equal to
- * <var>N</var>, this method does nothing. Otherwise, it rehashes this map
- * in a table of size <var>N</var>.
- *
- * <P>
- * This method is useful when reusing maps. {@linkplain #clear() Clearing a
- * map} leaves the table size untouched. If you are reusing a map many times,
- * you can call this method with a typical size to avoid keeping around a
- * very large table just because of a few large transient maps.
- *
- * @param n
- * the threshold for the trimming.
- * @return true if there was enough memory to trim the map.
- */
- public boolean trim(final int n) {
- final int l = HashCommon.nextPowerOfTwo((int) Math.ceil(n / loadFactor));
- if (l >= n || size > HashCommon.maxFill(l, loadFactor))
- return true;
- try {
- rehash(l);
- } catch (OutOfMemoryError cantDoIt) { // unusual to catch OOME but in this case appropriate
- return false;
- }
- return true;
- }
- /**
- * Rehashes the map.
- *
- * <P>
- * This method implements the basic rehashing strategy, and may be overriden
- * by subclasses implementing different rehashing strategies (e.g.,
- * disk-based rehashing). However, you should not override this method
- * unless you understand the internal workings of this class.
- *
- * @param newN
- * the new size
- */
- @SuppressWarnings("unchecked")
- protected void rehash(final int newN) {
- final K myKeys[] = this.keys;
- final V myValues[] = this.values;
- final int mask = newN - 1; // Note that this is used by the hashing
- // macro
- final K newKey[] = (K[]) new Object[newN + 1];
- final V newValue[] = (V[]) new Object[newN + 1];
- int i = arraySize, pos;
- for (int j = realSize(); j-- != 0;) {
- while (myKeys[--i] == null) {
- // advance i until we find an existing key
- }
- if (newKey[pos = HashCommon.mix(myKeys[i].hashCode()) & mask] != null) { // rehash & check slot availability
- while (newKey[pos = (pos + 1) & mask] != null) {
- // find available slot at (or immediately following) pos
- }
- }
- newKey[pos] = myKeys[i];
- newValue[pos] = myValues[i];
- }
- newValue[newN] = myValues[arraySize];
- arraySize = newN;
- this.mask = mask;
- maxFill = HashCommon.maxFill(arraySize, loadFactor);
- this.keys = newKey;
- this.values = newValue;
- }
-
- /**
- * Returns a hash code for this map.
- *
- * @return a hash code for this map.
- */
- public int hashCode() {
- int result = 0;
- for (int j = realSize(), i = 0, t = 0; j-- != 0;) {
- while (keys[i] == null) {
- i++;
- }
- if (this != keys[i]) {
- t = keys[i].hashCode();
- }
- if (this != values[i]) {
- t ^= (values[i] == null ? 0 : values[i].hashCode());
- }
- result += t;
- i++;
- }
- // Zero / null keys have hash zero.
- if (containsNullKey) {
- result += (values[arraySize] == null ? 0 : values[arraySize].hashCode());
- }
- return result;
- }
-
- @SuppressWarnings("unchecked")
- private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
- s.defaultReadObject();
- arraySize = HashCommon.arraySize(size, loadFactor);
- maxFill = HashCommon.maxFill(arraySize, loadFactor);
- mask = arraySize - 1;
- final K key[] = this.keys = (K[]) new Object[arraySize + 1];
- final V value[] = this.values = (V[]) new Object[arraySize + 1];
- K k;
- V v;
- for (int i = size, pos; i-- != 0;) {
- k = (K) s.readObject();
- v = (V) s.readObject();
- if (k == null) {
- pos = arraySize;
- containsNullKey = true;
- } else {
- pos = HashCommon.mix(k.hashCode()) & mask;
- while (key[pos] != null) {
- pos = (pos + 1) & mask;
- }
- }
- key[pos] = k;
- value[pos] = v;
- }
- }
-
- private void writeObject(final ObjectOutputStream s) throws IOException {
- s.defaultWriteObject();
- try {
- forEach(SERIALIZER, s);
- } catch (final RuntimeException runex) {
- if (runex.getCause() instanceof IOException) {
- throw (IOException) runex.getCause();
- }
- throw runex;
- }
- }
-
- private static final TriConsumer<String, Object, ObjectOutputStream> SERIALIZER =
- new TriConsumer<String, Object, ObjectOutputStream>() {
- @Override
- public void accept(final String k, final Object v, final ObjectOutputStream objectOutputStream) {
- try {
- objectOutputStream.writeObject(k);
- objectOutputStream.writeObject(v);
- } catch (final IOException ioex) {
- throw new IllegalStateException(ioex);
- }
- }
- };
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(256);
- sb.append('{');
- final K myKeys[] = this.keys;
- int pos = arraySize;
- boolean first = true;
- if (containsNullKey) {
- sb.append(myKeys[pos] == this ? "(this map)" : myKeys[pos]);
- sb.append('=');
- sb.append(values[pos] == this ? "(this map)" : values[pos]);
- first = false;
- }
- --pos;
- for (; pos >= 0; pos--) {
- if (myKeys[pos] != null) {
- if (first) {
- first = false;
- } else {
- sb.append(", ");
- }
- sb.append(myKeys[pos] == this ? "(this map)" : myKeys[pos]);
- sb.append('=');
- sb.append(values[pos] == this ? "(this map)" : values[pos]);
- }
- }
- sb.append('}');
- return sb.toString();
- }
-
- private static class HashCommon {
- private HashCommon() {}
-
- /** 2<sup>32</sup> · φ, φ = (√5 − 1)/2. */
- private static final int INT_PHI = 0x9E3779B9;
-
- /** The reciprocal of {@link #INT_PHI} modulo 2<sup>32</sup>. */
- private static final int INV_INT_PHI = 0x144cbc89;
-
- /** Avalanches the bits of an integer by applying the finalisation step of MurmurHash3.
- *
- * <p>This method implements the finalisation step of Austin Appleby's
- * <a href="http://code.google.com/p/smhasher/">MurmurHash3</a>.
- * Its purpose is to avalanche the bits of the argument to within 0.25% bias.
- *
- * @param x an integer.
- * @return a hash value with good avalanching properties.
- */
- public static int murmurHash3(int x) {
- x ^= x >>> 16;
- x *= 0x85ebca6b;
- x ^= x >>> 13;
- x *= 0xc2b2ae35;
- x ^= x >>> 16;
- return x;
- }
-
- /**
- * Quickly mixes the bits of an integer.
- *
- * <p>This method mixes the bits of the argument by multiplying by the golden ratio and
- * xorshifting the result. It is borrowed from <a href="https://github.com/OpenHFT/Koloboke">Koloboke</a>, and
- * it has slightly worse behaviour than {@link #murmurHash3(int)} (in open-addressing hash tables the average
- * number of probes is slightly larger), but it's much faster.
- *
- * @param x an integer.
- * @return a hash value obtained by mixing the bits of {@code x}.
- * @see #invMix(int)
- */
- public static int mix(final int x) {
- final int h = x * INT_PHI;
- return h ^ (h >>> 16);
- }
-
- /** The inverse of {@link #mix(int)}. This method is mainly useful to create unit tests.
- *
- * @param x an integer.
- * @return a value that passed through {@link #mix(int)} would give {@code x}.
- */
- public static int invMix(final int x) {
- return (x ^ x >>> 16) * INV_INT_PHI;
- }
-
- /** Return the least power of two greater than or equal to the specified value.
- *
- * <p>Note that this function will return 1 when the argument is 0.
- *
- * @param x an integer smaller than or equal to 2<sup>30</sup>.
- * @return the least power of two greater than or equal to the specified value.
- */
- public static int nextPowerOfTwo(int x) {
- if (x == 0) {
- return 1;
- }
- x--;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- return (x | x >> 16) + 1;
- }
-
- /** Return the least power of two greater than or equal to the specified value.
- *
- * <p>Note that this function will return 1 when the argument is 0.
- *
- * @param x a long integer smaller than or equal to 2<sup>62</sup>.
- * @return the least power of two greater than or equal to the specified value.
- */
- public static long nextPowerOfTwo(long x) {
- if (x == 0) {
- return 1;
- }
- x--;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- return (x | x >> 32) + 1;
- }
-
-
- /** Returns the maximum number of entries that can be filled before rehashing.
- *
- * @param n the size of the backing array.
- * @param f the load factor.
- * @return the maximum number of entries before rehashing.
- */
- public static int maxFill(final int n, final float f) {
- /* We must guarantee that there is always at least
- * one free entry (even with pathological load factors). */
- return Math.min((int) Math.ceil(n * f), n - 1);
- }
-
- /**
- * Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or equal to
- * <code>Math.ceil( expected / f )</code>.
- *
- * @param expected the expected number of elements in a hash table.
- * @param f the load factor.
- * @return the minimum possible size for a backing array.
- * @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>.
- */
- public static int arraySize(final int expected, final float f) {
- final long result = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f)));
- if (result > (1 << 30)) {
- throw new IllegalArgumentException("Too large (" + expected +
- " expected elements with load factor " + f + ")");
- }
- return (int) result;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
index 93a160e..62608a5 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
@@ -28,6 +28,7 @@ import org.apache.logging.log4j.core.util.Clock;
import org.apache.logging.log4j.core.util.ClockFactory;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.TimestampMessage;
+import org.apache.logging.log4j.spi.MutableContextData;
/**
* Garbage-free LogEventFactory that reuses a single mutable log event.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
index af534fb..786d23f 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
@@ -20,8 +20,10 @@ import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.spi.MutableContextData;
+import org.apache.logging.log4j.spi.ThreadContextMap;
/**
* {@code ThreadContextDataInjector} contains a number of strategies for copying key-value pairs from the various
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListDeserializer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListDeserializer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListDeserializer.java
index 4b83fc7..c6d5245 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListDeserializer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListDeserializer.java
@@ -21,7 +21,7 @@ import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListSerializer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListSerializer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListSerializer.java
index 8fbffdf..329c6fa 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListSerializer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataAsEntryListSerializer.java
@@ -19,8 +19,8 @@ package org.apache.logging.log4j.core.jackson;
import java.io.IOException;
import java.util.Map;
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.BiConsumer;
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.util.BiConsumer;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataDeserializer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataDeserializer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataDeserializer.java
index 0988a98..c3dc827 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataDeserializer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataDeserializer.java
@@ -21,7 +21,7 @@ import java.io.IOException;
import java.util.Map;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataSerializer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataSerializer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataSerializer.java
index 8868c0d..67d6c8b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataSerializer.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/ContextDataSerializer.java
@@ -19,8 +19,8 @@ package org.apache.logging.log4j.core.jackson;
import java.io.IOException;
import java.util.Map;
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.util.TriConsumer;
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.util.TriConsumer;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java
index 2338cb0..30b01a8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventJsonMixIn.java
@@ -21,7 +21,7 @@ import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext.ContextStack;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.message.Message;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java
index e40c65a..c195001 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/jackson/LogEventWithContextListMixIn.java
@@ -21,7 +21,7 @@ import java.util.Map;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext.ContextStack;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.message.Message;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
index 9edd36b..2f4853c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
@@ -38,7 +38,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.net.Severity;
import org.apache.logging.log4j.core.util.JsonUtils;
import org.apache.logging.log4j.core.util.KeyValuePair;
-import org.apache.logging.log4j.core.util.TriConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.StringBuilderFormattable;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
index bcbdbd8..1fc0512 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/MdcPatternConverter.java
@@ -16,11 +16,11 @@
*/
package org.apache.logging.log4j.core.pattern;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.util.Constants;
-import org.apache.logging.log4j.core.util.TriConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
import org.apache.logging.log4j.util.StringBuilders;
/**
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BiConsumer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BiConsumer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BiConsumer.java
deleted file mode 100644
index 081b9c2..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/BiConsumer.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apache.logging.log4j.core.util;
-
-/**
- * An operation that accepts two input arguments and returns no result.
- *
- * @param <K> type of the first argument
- * @param <V> type of the second argument
- * @see org.apache.logging.log4j.core.ContextData
- * @since 2.7
- */
-public interface BiConsumer<K, V> {
-
- /**
- * Performs the operation given the specified arguments.
- * @param k the first input argument
- * @param v the second input argument
- */
- void accept(K k, V v);
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TriConsumer.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TriConsumer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TriConsumer.java
deleted file mode 100644
index a7d6213..0000000
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/TriConsumer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.apache.logging.log4j.core.util;
-
-/**
- * An operation that accepts three input arguments and returns no result.
- *
- * @param <K> type of the first argument
- * @param <V> type of the second argument
- * @param <S> type of the third argument
- * @see org.apache.logging.log4j.core.ContextData
- * @since 2.7
- */
-public interface TriConsumer<K, V, S> {
-
- /**
- * Performs the operation given the specified arguments.
- * @param k the first input argument
- * @param v the second input argument
- * @param s the third input argument
- */
- void accept(K k, V v, S s);
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
index f0e94ec..facb29d 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/TestBaseEntity.java
@@ -34,7 +34,7 @@ import javax.persistence.Transient;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.core.ContextData;
+import org.apache.logging.log4j.spi.ContextData;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.db.jpa.converter.LevelAttributeConverter;
import org.apache.logging.log4j.core.appender.db.jpa.converter.MessageAttributeConverter;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
index 9f86d16..00ee55f 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
@@ -16,8 +16,8 @@
*/
package org.apache.logging.log4j.core.appender.db.jpa.converter;
-import org.apache.logging.log4j.core.impl.ArrayContextData;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
index 76ce5d2..6930ed5 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.logging.log4j.core.appender.db.jpa.converter;
-import org.apache.logging.log4j.core.ContextData;
-import org.apache.logging.log4j.core.impl.ArrayContextData;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
index cb1de14..46de4ab 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
@@ -29,7 +29,7 @@ import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext.ContextStack;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.impl.MutableContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.SimpleMessage;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ArrayContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ArrayContextDataTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ArrayContextDataTest.java
deleted file mode 100644
index 4613c04..0000000
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/ArrayContextDataTest.java
+++ /dev/null
@@ -1,568 +0,0 @@
-package org.apache.logging.log4j.core.impl;/*
- * 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.
- */
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.logging.log4j.core.util.BiConsumer;
-import org.apache.logging.log4j.core.util.TriConsumer;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests the ArrayContextData class.
- */
-public class ArrayContextDataTest {
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsNegativeCapacity() throws Exception {
- new ArrayContextData(-1);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsZeroCapacity() throws Exception {
- new ArrayContextData(0);
- }
-
- @Test
- public void testConstructorIgnoresNull() throws Exception {
- assertEquals(0, new ArrayContextData(null).size());
- }
-
- @Test
- public void testToString() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals("{3=3value, B=Bvalue, a=avalue}", original.toString());
- }
-
- @Test
- public void testSerialization() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final byte[] binary = serialize(original);
- final ArrayContextData copy = deserialize(binary);
- assertEquals(original, copy);
- }
-
- private byte[] serialize(final ArrayContextData data) throws IOException {
- final ByteArrayOutputStream arr = new ByteArrayOutputStream();
- final ObjectOutputStream out = new ObjectOutputStream(arr);
- out.writeObject(data);
- return arr.toByteArray();
- }
-
- private ArrayContextData deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
- final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
- final ObjectInputStream in = new ObjectInputStream(inArr);
- final ArrayContextData result = (ArrayContextData) in.readObject();
- return result;
- }
-
- @Test
- public void testPutAll() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final ArrayContextData other = new ArrayContextData();
- other.putAll(original);
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testEquals() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(original, original); // equal to itself
-
- final ArrayContextData other = new ArrayContextData();
- other.putValue("a", "avalue");
- assertNotEquals(original, other);
-
- other.putValue("B", "Bvalue");
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testAsMap() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final Map<String, Object> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
-
- assertEquals(expected, original.asMap());
- }
-
- @Test
- public void testGetCopyDelegatesToAsMap() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(original.getCopy(), original.asMap());
-
- original.putValue("B", "Bvalue");
- assertEquals(original.getCopy(), original.asMap());
-
- original.putValue("3", "3value");
- assertEquals(original.getCopy(), original.asMap());
- }
-
- @Test
- public void testGetImmutableMapOrNull() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- original.putValue("B", "Bvalue");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- original.putValue("3", "3value");
- assertEquals(original.getImmutableMapOrNull(), original.asMap());
-
- try {
- original.getImmutableMapOrNull().put("abc", "xyz");
- fail("Expected map to be immutable");
- } catch (final UnsupportedOperationException ok) {
- //ok
- }
- }
-
- @Test
- public void testPutInsertsInAlphabeticOrder() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.put("a", "avalue");
- original.put("B", "Bvalue");
- original.put("3", "3value");
- original.put("c", "cvalue");
- original.put("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("cvalue", original.getValueAt(3));
-
- assertEquals("dvalue", original.getValue("d"));
- assertEquals("dvalue", original.getValueAt(4));
- }
-
- @Test
- public void testPutValueInsertsInAlphabeticOrder() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("cvalue", original.getValueAt(3));
-
- assertEquals("dvalue", original.getValue("d"));
- assertEquals("dvalue", original.getValueAt(4));
- }
-
- @Test
- public void testNullKeysAllowed() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
- assertEquals("{3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "otherNullvalue");
- assertEquals("{null=otherNullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
- assertEquals(6, original.size());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "abc");
- assertEquals(6, original.size());
- assertEquals("{null=abc, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
- }
-
- @Test
- public void testNullKeysCopiedToAsMap() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
-
- HashMap<String, String> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
- expected.put("c", "cvalue");
- expected.put("d", "dvalue");
- assertEquals("initial", expected, original.asMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key", expected, original.asMap());
-
- original.putValue(null, "otherNullvalue");
- expected.put(null, "otherNullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value2", expected, original.asMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value1 again", expected, original.asMap());
-
- original.putValue(null, "abc");
- expected.put(null, "abc");
- assertEquals(6, original.size());
- assertEquals("with null key value3", expected, original.asMap());
- }
-
- @Test
- public void testRemove() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.remove("a");
- assertEquals(0, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.remove("B");
- assertEquals(0, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testNullValuesArePreserved() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.putValue("a", null);
- assertEquals(1, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.putValue("B", null);
- assertEquals(2, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testGet() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.put("a", "avalue");
- original.put("B", "Bvalue");
- original.put("3", "3value");
-
- assertEquals("avalue", original.get("a"));
- assertEquals("Bvalue", original.get("B"));
- assertEquals("3value", original.get("3"));
-
- original.putValue("0", "0value");
- assertEquals("0value", original.get("0"));
- assertEquals("3value", original.get("3"));
- assertEquals("Bvalue", original.get("B"));
- assertEquals("avalue", original.get("a"));
- }
-
- @Test
- public void testGetValue_GetValueAt() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- original.putValue("0", "0value");
- assertEquals("0value", original.getValue("0"));
- assertEquals("0value", original.getValueAt(0));
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(1));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(2));
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(3));
- }
-
- @Test
- public void testClear() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(3, original.size());
-
- original.clear();
- assertEquals(0, original.size());
-
- // ensure slots in the values array are nulled out
- Field f = ArrayContextData.class.getDeclaredField("values");
- f.setAccessible(true);
- Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
- }
-
- @Test
- public void testIndexOfKey() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(0, original.indexOfKey("a"));
-
- original.putValue("B", "Bvalue");
- assertEquals(1, original.indexOfKey("a"));
- assertEquals(0, original.indexOfKey("B"));
-
- original.putValue("3", "3value");
- assertEquals(2, original.indexOfKey("a"));
- assertEquals(1, original.indexOfKey("B"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("A", "AAA");
- assertEquals(3, original.indexOfKey("a"));
- assertEquals(2, original.indexOfKey("B"));
- assertEquals(1, original.indexOfKey("A"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("C", "CCC");
- assertEquals(4, original.indexOfKey("a"));
- assertEquals(3, original.indexOfKey("C"));
- assertEquals(2, original.indexOfKey("B"));
- assertEquals(1, original.indexOfKey("A"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("2", "222");
- assertEquals(5, original.indexOfKey("a"));
- assertEquals(4, original.indexOfKey("C"));
- assertEquals(3, original.indexOfKey("B"));
- assertEquals(2, original.indexOfKey("A"));
- assertEquals(1, original.indexOfKey("3"));
- assertEquals(0, original.indexOfKey("2"));
- }
-
- @Test
- public void testContainsKey() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- assertFalse("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("a", "avalue");
- assertTrue("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("B", "Bvalue");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("3", "3value");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("A", "AAA");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertTrue("A", original.containsKey("A"));
- }
-
- @Test
- public void testGetValueAt() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals("a", original.getKeyAt(0));
- assertEquals("avalue", original.getValueAt(0));
-
- original.putValue("B", "Bvalue");
- assertEquals("B", original.getKeyAt(0));
- assertEquals("Bvalue", original.getValueAt(0));
- assertEquals("a", original.getKeyAt(1));
- assertEquals("avalue", original.getValueAt(1));
-
- original.putValue("3", "3value");
- assertEquals("3", original.getKeyAt(0));
- assertEquals("3value", original.getValueAt(0));
- assertEquals("B", original.getKeyAt(1));
- assertEquals("Bvalue", original.getValueAt(1));
- assertEquals("a", original.getKeyAt(2));
- assertEquals("avalue", original.getValueAt(2));
- }
-
- @Test
- public void testSizeAndIsEmpty() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- assertEquals(0, original.size());
- assertTrue("initial", original.isEmpty());
-
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("B", "Bvalue");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("3", "3value");
- assertEquals(3, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("B");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("3");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("a");
- assertEquals(0, original.size());
- assertTrue("size=" + original.size(), original.isEmpty());
- }
-
- @Test
- public void testForEachBiConsumer() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- original.forEach(new BiConsumer<String, String>() {
- int count = 0;
- @Override
- public void accept(final String key, final String value) {
- assertEquals("key", key, original.getKeyAt(count));
- assertEquals("val", value, original.getValueAt(count));
- count++;
- assertTrue("count should not exceed size but was " + count, count <= original.size());
- }
- });
- }
-
- static class State {
- ArrayContextData data;
- int count;
- }
- static TriConsumer<String, String, State> COUNTER = new TriConsumer<String, String, State>() {
- @Override
- public void accept(final String key, final String value, final State state) {
- assertEquals("key", key, state.data.getKeyAt(state.count));
- assertEquals("val", value, state.data.getValueAt(state.count));
- state.count++;
- assertTrue("count should not exceed size but was " + state.count,
- state.count <= state.data.size());
- }
- };
-
- @Test
- public void testForEachTriConsumer() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final State state = new State();
- state.data = original;
- original.forEach(COUNTER, state);
- assertEquals(state.count, original.size());
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
index 964a017..931fb15 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
@@ -40,6 +40,8 @@ import org.apache.logging.log4j.core.util.DummyNanoClock;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.util.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1118b27f/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
index e531adc..0b2babf 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
@@ -29,6 +29,8 @@ import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.ArrayContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
import org.apache.logging.log4j.spi.MutableThreadContextStack;
import org.junit.Test;
[12/16] logging-log4j2 git commit: LOG4J2-1349 add Accessor utility
for package-protected method getThreadContextMap
Posted by rp...@apache.org.
LOG4J2-1349 add Accessor utility for package-protected method getThreadContextMap
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/fcb58f14
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/fcb58f14
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/fcb58f14
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: fcb58f14ba6686ab607129c26633a9b72ba92a61
Parents: ffdd2d9
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 10:37:02 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:14 2016 +0900
----------------------------------------------------------------------
.../logging/log4j/ThreadContextAccess.java | 46 ++++++++++++++++++++
1 file changed, 46 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/fcb58f14/log4j-core/src/main/java/org/apache/logging/log4j/ThreadContextAccess.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/ThreadContextAccess.java b/log4j-core/src/main/java/org/apache/logging/log4j/ThreadContextAccess.java
new file mode 100644
index 0000000..2e2a992
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/ThreadContextAccess.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import org.apache.logging.log4j.spi.ThreadContextMap;
+
+/**
+ * <em>This class is intended for internal log4j2 usage and should not be used directly by applications.</em>
+ * <p>
+ * Utility class to access package protected methods in {@code ThreadContext}.
+ * </p>
+ *
+ * @see ThreadContext
+ * @since 2.7
+ */
+public final class ThreadContextAccess {
+ private ThreadContextAccess() { // this class should not be instantiated
+ }
+
+ /**
+ * Returns the internal data structure used to store thread context key-value pairs.
+ * <p><em>
+ * This method is intended for internal log4j2 usage.
+ * The returned data structure is not intended to be used directly by applications.
+ * </em></p>
+ * @return the internal data structure used to store thread context key-value pairs
+ */
+ public static ThreadContextMap getThreadContextMap() {
+ return ThreadContext.getThreadContextMap();
+ }
+}
[09/16] logging-log4j2 git commit: LOG4J2-1349 added concrete
ThreadContextMap implementations
Posted by rp...@apache.org.
LOG4J2-1349 added concrete ThreadContextMap implementations
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/5c6acbf1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/5c6acbf1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/5c6acbf1
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 5c6acbf15529f83f26da6b14051c8dcaff4ddc5d
Parents: 2ab025e
Author: rpopma <rp...@apache.org>
Authored: Sat Aug 20 14:36:22 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:10 2016 +0900
----------------------------------------------------------------------
...AbstractCopyOnWriteMutableThreadContext.java | 13 +++++-
...AbstractGarbageFreeMutableThreadContext.java | 13 +++++-
.../CopyOnWriteOpenHashMapThreadContextMap.java | 39 ++++++++++++++++++
.../CopyOnWriteSortedArrayThreadContextMap.java | 39 ++++++++++++++++++
.../GarbageFreeOpenHashMapThreadContextMap.java | 42 ++++++++++++++++++++
.../GarbageFreeSortedArrayThreadContextMap.java | 42 ++++++++++++++++++++
6 files changed, 186 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/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
index 42529f7..9837ba8 100644
--- 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
@@ -31,6 +31,17 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* @since 2.7
*/
public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadContextMap, ThreadContextMap2 {
+
+ /**
+ * 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.
@@ -62,7 +73,7 @@ public abstract class AbstractCopyOnWriteMutableThreadContext implements ThreadC
protected abstract MutableContextData createMutableContextData();
- protected abstract MutableContextData createMutableContextData(final MutableContextData original);
+ protected abstract MutableContextData createMutableContextData(final ContextData original);
@Override
public void put(final String key, final String value) {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/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
index 73636a9..7a0e6f8 100644
--- 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
@@ -31,6 +31,17 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* @since 2.7
*/
public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadContextMap, ThreadContextMap2 {
+
+ /**
+ * 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.
@@ -62,7 +73,7 @@ public abstract class AbstractGarbageFreeMutableThreadContext implements ThreadC
protected abstract MutableContextData createMutableContextData();
- protected abstract MutableContextData createMutableContextData(final MutableContextData original);
+ protected abstract MutableContextData createMutableContextData(final ContextData original);
private MutableContextData getThreadLocalMap() {
MutableContextData map = localMap.get();
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
new file mode 100644
index 0000000..a1b82bb
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteOpenHashMapThreadContextMap.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * {@code OpenHashMapContextData}-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
+ *
+ * @since 2.7
+ */
+public class CopyOnWriteOpenHashMapThreadContextMap extends AbstractCopyOnWriteMutableThreadContext {
+ @Override
+ protected MutableContextData createMutableContextData() {
+ return new OpenHashMapContextData<>(PropertiesUtil.getProperties().getIntegerProperty(
+ PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
+ }
+
+ @Override
+ protected MutableContextData createMutableContextData(final ContextData original) {
+ return new OpenHashMapContextData<>(original);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/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
new file mode 100644
index 0000000..f1bf509
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -0,0 +1,39 @@
+/*
+ * 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 org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * {@code SortedArrayContextData}-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
+ *
+ * @since 2.7
+ */
+public class CopyOnWriteSortedArrayThreadContextMap extends AbstractCopyOnWriteMutableThreadContext {
+ @Override
+ protected MutableContextData createMutableContextData() {
+ return new ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
+ PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
+ }
+
+ @Override
+ protected MutableContextData createMutableContextData(final ContextData original) {
+ return new ArrayContextData(original);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
new file mode 100644
index 0000000..09ebb15
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeOpenHashMapThreadContextMap.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * {@code OpenHashMapContextData}-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.
+ * </p>
+ *
+ * @since 2.7
+ */
+public class GarbageFreeOpenHashMapThreadContextMap extends AbstractGarbageFreeMutableThreadContext {
+ @Override
+ protected MutableContextData createMutableContextData() {
+ return new OpenHashMapContextData<>(PropertiesUtil.getProperties().getIntegerProperty(
+ PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
+ }
+
+ @Override
+ protected MutableContextData createMutableContextData(final ContextData original) {
+ return new OpenHashMapContextData<>(original);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/5c6acbf1/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
new file mode 100644
index 0000000..241af5c
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * {@code SortedArrayContextData}-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.
+ * </p>
+ *
+ * @since 2.7
+ */
+public class GarbageFreeSortedArrayThreadContextMap extends AbstractGarbageFreeMutableThreadContext {
+ @Override
+ protected MutableContextData createMutableContextData() {
+ return new ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
+ PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
+ }
+
+ @Override
+ protected MutableContextData createMutableContextData(final ContextData original) {
+ return new ArrayContextData(original);
+ }
+}
[06/16] logging-log4j2 git commit: LOG4J2-1349 added
putAll(Map method (same erasure as putAll(K, V)...)
Posted by rp...@apache.org.
LOG4J2-1349 added putAll(Map<String,String> method (same erasure as putAll(K, V)...)
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4a0962b6
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4a0962b6
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4a0962b6
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: 4a0962b67ca7a4e73da859265cb60d3bb16e1070
Parents: efb2293
Author: rpopma <rp...@apache.org>
Authored: Fri Aug 19 23:17:04 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:05 2016 +0900
----------------------------------------------------------------------
.../log4j/spi/OpenHashMapContextData.java | 35 +++++++++++---------
1 file changed, 19 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a0962b6/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
index 9f27a6c..00712cc 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/OpenHashMapContextData.java
@@ -140,7 +140,8 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
* @param map
* a {@link Map} to be copied into the new hash map.
*/
- public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
+ // TODO public OpenHashMapContextData(final Map<? extends K, ? extends V> map) {
+ public OpenHashMapContextData(final Map<String, String> map) {
this(map, DEFAULT_LOAD_FACTOR);
}
/**
@@ -151,7 +152,8 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
* @param f
* the load factor.
*/
- public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
+ // TODO public OpenHashMapContextData(final Map<? extends K, ? extends V> map, final float f) {
+ public OpenHashMapContextData(final Map<String, String> map, final float f) {
this(map.size(), f);
putAll(map);
}
@@ -221,20 +223,6 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
}
}
- /** {@inheritDoc} */
- public void putAll(Map<? extends K, ? extends V> map) {
- if (loadFactor <= .5) {
- // The resulting map will be sized for m.size() elements
- ensureCapacity(map.size());
- } else {
- // The resulting map will be tentatively sized for size() + m.size() elements
- tryCapacity(size() + map.size());
- }
- for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
- putObjectValue(entry.getKey(), entry.getValue());
- }
- }
-
@Override
public Map<String, String> asMap() {
final Map<String, String> result = new HashMap<>(size);
@@ -471,6 +459,21 @@ public class OpenHashMapContextData<K, V> implements MutableContextData, ThreadC
}
}
+ /** {@inheritDoc} */
+ //TODO public void putAll(Map<? extends K, ? extends V> map) {
+ public void putAll(Map<String, String> map) {
+ if (loadFactor <= .5) {
+ // The resulting map will be sized for m.size() elements
+ ensureCapacity(map.size());
+ } else {
+ // The resulting map will be tentatively sized for size() + m.size() elements
+ tryCapacity(size() + map.size());
+ }
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ putValue(entry.getKey(), entry.getValue());
+ }
+ }
+
private V putObjectValue(final K k, final V v) {
final int pos = insert(k, v);
if (pos < 0) {
[14/16] logging-log4j2 git commit: LOG4J2-1349 add default
constructor for reflective construction in benchmark
Posted by rp...@apache.org.
LOG4J2-1349 add default constructor for reflective construction in benchmark
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/bdee3854
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/bdee3854
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/bdee3854
Branch: refs/heads/LOG4J2-1349-gcfree-threadcontext
Commit: bdee385413dfd4aea1d27c06c094de5ff4bc9da0
Parents: 24cbcc6
Author: rpopma <rp...@apache.org>
Authored: Sun Aug 21 20:07:33 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Tue Aug 23 00:31:17 2016 +0900
----------------------------------------------------------------------
.../org/apache/logging/log4j/spi/DefaultThreadContextMap.java | 4 ++++
1 file changed, 4 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bdee3854/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
index a45ece5..4f61e98 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
@@ -39,6 +39,10 @@ public class DefaultThreadContextMap implements ThreadContextMap, ThreadContextM
private final boolean useMap;
private final ThreadLocal<Map<String, String>> localMap;
+ public DefaultThreadContextMap() {
+ this(true);
+ }
+
public DefaultThreadContextMap(final boolean useMap) {
this.useMap = useMap;
this.localMap = createThreadLocalMap(useMap);