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/09/11 06:28:16 UTC
[35/50] logging-log4j2 git commit: LOG4J2-1349 implement support for
freeze() and isFrozen(); add tests
LOG4J2-1349 implement support for freeze() and isFrozen(); add tests
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e59f15f6
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e59f15f6
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e59f15f6
Branch: refs/heads/master
Commit: e59f15f65a7b1b39f0f61b9b7e2b3f28ada387c0
Parents: 950b62f
Author: rpopma <rp...@apache.org>
Authored: Sun Sep 4 15:55:10 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Sun Sep 4 15:55:10 2016 +0900
----------------------------------------------------------------------
.../logging/log4j/util/ArrayContextData.java | 63 +++++-
.../log4j/util/ArrayContextDataTest.java | 208 +++++++++++++++++++
2 files changed, 267 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e59f15f6/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
index bdc9951..3b665b3 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InvalidObjectException;
import java.util.Arrays;
import java.util.Collections;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -70,6 +71,7 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
* An empty array instance to share when the table is not inflated.
*/
private static final String[] EMPTY = {};
+ private static final String FROZEN = "Frozen collection cannot be modified";
private transient String[] keys = EMPTY;
private transient Object[] values = EMPTY;
@@ -86,6 +88,8 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
// If table == EMPTY_TABLE then this is the initial capacity at which the
// table will be created when inflated.
private int threshold;
+ private boolean immutable;
+ private transient boolean iterating;
public ArrayContextData() {
this(DEFAULT_INITIAL_CAPACITY);
@@ -107,8 +111,26 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
}
}
+ private void assertNotFrozen() {
+ if (immutable) {
+ throw new UnsupportedOperationException(FROZEN);
+ }
+ }
+
+ private void assertNoConcurrentModification() {
+ if (iterating) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
@Override
public void clear() {
+ if (keys == EMPTY) {
+ return;
+ }
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
Arrays.fill(keys, 0, size, null);
Arrays.fill(values, 0, size, null);
size = 0;
@@ -130,6 +152,16 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
}
@Override
+ public void freeze() {
+ immutable = true;
+ }
+
+ @Override
+ public boolean isFrozen() {
+ return immutable;
+ }
+
+ @Override
public Map<String, String> getCopy() {
return asMap();
}
@@ -182,6 +214,9 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
@Override
public void putValue(final String key, final Object value) {
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
if (keys == EMPTY) {
inflateTable(threshold);
}
@@ -205,9 +240,15 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
@Override
public void putAll(final ContextData source) {
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
if (source instanceof ArrayContextData) {
initFrom0((ArrayContextData) source);
} else if (source != null) {
+ if (source == this) {
+ return; // this.putAll(this) does not modify this collection
+ }
source.forEach(PUT_ALL, this);
}
}
@@ -261,8 +302,12 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
if (keys == EMPTY) {
return;
}
+
final int index = indexOfKey(key);
if (index >= 0) {
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
System.arraycopy(keys, index + 1, keys, index, size - index);
System.arraycopy(values, index + 1, values, index, size - index);
size--;
@@ -292,16 +337,26 @@ public class ArrayContextData implements MutableContextData, ThreadContextMap {
@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]);
+ iterating = true;
+ try {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i]);
+ }
+ } finally {
+ iterating = false;
}
}
@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);
+ iterating = true;
+ try {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i], state);
+ }
+ } finally {
+ iterating = false;
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e59f15f6/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
index e3ac0a4..ae79888 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
@@ -181,6 +182,213 @@ public class ArrayContextDataTest {
}
@Test
+ public void testPutAll_KeepsExistingValues() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.put("b", "bbb");
+ original.put("c", "ccc");
+ assertEquals("size", 3, original.size());
+
+ // add empty context data
+ original.putAll(new ArrayContextData());
+ assertEquals("size after put empty", 3, original.size());
+ assertEquals("aaa", original.get("a"));
+ assertEquals("bbb", original.get("b"));
+ assertEquals("ccc", original.get("c"));
+
+ final ArrayContextData other = new ArrayContextData();
+ other.put("1", "111");
+ other.put("2", "222");
+ other.put("3", "333");
+ original.putAll(other);
+
+ assertEquals("size after put other", 6, original.size());
+ assertEquals("aaa", original.get("a"));
+ assertEquals("bbb", original.get("b"));
+ assertEquals("ccc", original.get("c"));
+ assertEquals("111", original.get("1"));
+ assertEquals("222", original.get("2"));
+ assertEquals("333", original.get("3"));
+ }
+
+ @Test
+ public void testPutAllSelfDoesNotModify() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.put("b", "bbb");
+ original.put("c", "ccc");
+ assertEquals("size", 3, original.size());
+
+ // putAll with self
+ original.putAll(original);
+ assertEquals("size after put empty", 3, original.size());
+ assertEquals("aaa", original.get("a"));
+ assertEquals("bbb", original.get("b"));
+ assertEquals("ccc", original.get("c"));
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationBiConsumerPut() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new BiConsumer<String, Object>() {
+ @Override
+ public void accept(final String s, final Object o) {
+ original.put("c", "other");
+ }
+ });
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationBiConsumerPutValue() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new BiConsumer<String, Object>() {
+ @Override
+ public void accept(final String s, final Object o) {
+ original.putValue("c", "other");
+ }
+ });
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationBiConsumerRemove() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new BiConsumer<String, Object>() {
+ @Override
+ public void accept(final String s, final Object o) {
+ original.remove("a");
+ }
+ });
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationBiConsumerClear() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new BiConsumer<String, Object>() {
+ @Override
+ public void accept(final String s, final Object o) {
+ original.clear();
+ }
+ });
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationTriConsumerPut() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new TriConsumer<String, Object, Object>() {
+ @Override
+ public void accept(final String s, final Object o, final Object o2) {
+ original.put("c", "other");
+ }
+ }, null);
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationTriConsumerPutValue() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new TriConsumer<String, Object, Object>() {
+ @Override
+ public void accept(final String s, final Object o, final Object o2) {
+ original.putValue("c", "other");
+ }
+ }, null);
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationTriConsumerRemove() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new TriConsumer<String, Object, Object>() {
+ @Override
+ public void accept(final String s, final Object o, final Object o2) {
+ original.remove("a");
+ }
+ }, null);
+ }
+
+ @Test(expected = ConcurrentModificationException.class)
+ public void testConcurrentModificationTriConsumerClear() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.forEach(new TriConsumer<String, Object, Object>() {
+ @Override
+ public void accept(final String s, final Object o, final Object o2) {
+ original.clear();
+ }
+ }, null);
+ }
+
+ @Test
+ public void testInitiallyNotFrozen() {
+ assertFalse(new ArrayContextData().isFrozen());
+ }
+
+ @Test
+ public void testIsFrozenAfterCallingFreeze() {
+ final ArrayContextData original = new ArrayContextData();
+ assertFalse("before freeze", original.isFrozen());
+ original.freeze();
+ assertTrue("after freeze", original.isFrozen());
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testFreezeProhibitsPut() {
+ final ArrayContextData original = new ArrayContextData();
+ original.freeze();
+ original.put("a", "aaa");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testFreezeProhibitsPutValue() {
+ final ArrayContextData original = new ArrayContextData();
+ original.freeze();
+ original.putValue("a", "aaa");
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testFreezeProhibitsRemove() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("b", "bbb");
+ original.freeze();
+ original.remove("b"); // existing key: modifies the collection
+ }
+
+ @Test
+ public void testFreezeAllowsRemoveOfNonExistingKey() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("b", "bbb");
+ original.freeze();
+ original.remove("a"); // no actual modification
+ }
+
+ @Test
+ public void testFreezeAllowsRemoveIfEmpty() {
+ final ArrayContextData original = new ArrayContextData();
+ original.freeze();
+ original.remove("a"); // no exception
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testFreezeProhibitsClear() {
+ final ArrayContextData original = new ArrayContextData();
+ original.put("a", "aaa");
+ original.freeze();
+ original.clear();
+ }
+
+ @Test
+ public void testFreezeAllowsClearIfEmpty() {
+ final ArrayContextData original = new ArrayContextData();
+ original.freeze();
+ original.clear();
+ }
+
+ @Test
public void testPutInsertsInAlphabeticOrder() throws Exception {
final ArrayContextData original = new ArrayContextData();
original.put("a", "avalue");