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; /**
