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");