logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rpo...@apache.org
Subject svn commit: r1486482 - in /logging/log4j/log4j2/trunk: api/src/main/java/org/apache/logging/log4j/ api/src/main/java/org/apache/logging/log4j/spi/ api/src/test/java/org/apache/logging/log4j/ api/src/test/java/org/apache/logging/log4j/spi/ core/src/main...
Date Sun, 26 May 2013 22:11:29 GMT
Author: rpopma
Date: Sun May 26 22:11:29 2013
New Revision: 1486482

URL: http://svn.apache.org/r1486482
Log:
LOG4J2-154 improve ThreadContext performance with copy-on-write map and stack

Added:
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java   (with props)
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java   (with props)
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java   (with props)
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java   (with props)
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java   (with props)
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java   (with props)
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java   (with props)
Modified:
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/ThreadContext.java
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
    logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java
    logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java
    logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java
    logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java
    logging/log4j/log4j2/trunk/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java

Modified: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/ThreadContext.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/ThreadContext.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/ThreadContext.java (original)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/ThreadContext.java Sun May 26 22:11:29 2013
@@ -17,26 +17,28 @@
 
 package org.apache.logging.log4j;
 
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.logging.log4j.message.ParameterizedMessage;
 import org.apache.logging.log4j.spi.DefaultThreadContextMap;
+import org.apache.logging.log4j.spi.DefaultThreadContextStack;
 import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
 import org.apache.logging.log4j.spi.Provider;
 import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.apache.logging.log4j.spi.ThreadContextStack;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.ProviderUtil;
 
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
 /**
- * The ThreadContext allows applications to store information either in a Map.
+ * The ThreadContext allows applications to store information either in a Map or a Stack.
  * <p>
  * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of
  * the mapped diagnostic context of its parent.
@@ -47,36 +49,32 @@ public final class ThreadContext  {
     /**
      * Empty, immutable Map.
      */
-    public static final Map<String, String> EMPTY_MAP = new ImmutableMap();
+    public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
 
     /**
      * Empty, immutable ContextStack.
      */
-    public static final ContextStack EMPTY_STACK = new ImmutableStack();
+    public static final ThreadContextStack EMPTY_STACK = new MutableThreadContextStack(new ArrayList<String>());
 
     private static final String DISABLE_MAP = "disableThreadContextMap";
-
     private static final String DISABLE_STACK = "disableThreadContextStack";
-
     private static final String DISABLE_ALL = "disableThreadContext";
-
     private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
 
     private static boolean all;
-
     private static boolean useMap;
-
     private static boolean useStack;
-
     private static ThreadContextMap contextMap;
-
+    private static ThreadContextStack contextStack;
     private static final Logger LOGGER = StatusLogger.getLogger();
 
     static {
         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
         all = managerProps.getBooleanProperty(DISABLE_ALL);
-        useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || all);
         useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || all);
+        contextStack = new DefaultThreadContextStack(useStack);
+
+        useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || all);
         String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
         final ClassLoader cl = ProviderUtil.findClassLoader();
         if (threadContextMapName != null) {
@@ -114,23 +112,18 @@ public final class ThreadContext  {
                     }
                 }
             }
-            if (contextMap == null) {
-                contextMap = new DefaultThreadContextMap(useMap);
-            }
-
-        } else {
+        }
+        if (contextMap == null) {
             contextMap = new DefaultThreadContextMap(useMap);
         }
     }
 
-    private static ThreadLocal<ContextStack> localStack = new ThreadLocal<ContextStack>();
-
     private ThreadContext() {
 
     }
 
     /**
-     * Put a context value (the <code>o</code> parameter) as identified
+     * Put a context value (the <code>value</code> parameter) as identified
      * with the <code>key</code> parameter into the current thread's
      * context map.
      * <p/>
@@ -144,7 +137,7 @@ public final class ThreadContext  {
     }
 
     /**
-     * Get the context identified by the <code>key</code> parameter.
+     * Get the context value identified by the <code>key</code> parameter.
      * <p/>
      * <p>This method has no side effects.
      * @param key The key to locate.
@@ -155,8 +148,7 @@ public final class ThreadContext  {
     }
 
     /**
-     * Remove the the context identified by the <code>key</code>
-     * parameter.
+     * Remove the context value identified by the <code>key</code> parameter.
      * @param key The key to remove.
      */
     public static void remove(final String key) {
@@ -180,20 +172,20 @@ public final class ThreadContext  {
     }
 
     /**
-     * Get a copy of current thread's context Map.
-     * @return a copy of the context.
+     * Returns a mutable copy of current thread's context Map.
+     * @return a mutable copy of the context.
      */
     public static Map<String, String> getContext() {
-        return contextMap.getContext();
+        return contextMap.getCopy();
     }
 
     /**
-     * Get an immutable copy of the current thread's context Map.
-     * @return An immutable copy of the ThreadContext Map.
+     * Returns an immutable view of the current thread's context Map.
+     * @return An immutable view of the ThreadContext Map.
      */
     public static Map<String, String> getImmutableContext() {
-        final Map<String, String> map = contextMap.get();
-        return map == null ? new ImmutableMap() : new ImmutableMap(map);
+        final Map<String, String> map = contextMap.getImmutableMapOrNull();
+        return map == null ? EMPTY_MAP : map;
     }
 
     /**
@@ -208,7 +200,7 @@ public final class ThreadContext  {
      * Clear the stack for this thread.
      */
     public static void clearStack() {
-        localStack.remove();
+        contextStack.clear();
     }
 
     /**
@@ -216,8 +208,7 @@ public final class ThreadContext  {
      * @return A copy of this thread's stack.
      */
     public static ContextStack cloneStack() {
-        final ContextStack stack = localStack.get();
-        return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList());
+        return contextStack.copy();
     }
 
     /**
@@ -225,8 +216,7 @@ public final class ThreadContext  {
      * @return an immutable copy of the ThreadContext stack.
      */
     public static ContextStack getImmutableStack() {
-        final ContextStack stack = localStack.get();
-        return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList());
+        return contextStack;
     }
 
     /**
@@ -237,7 +227,8 @@ public final class ThreadContext  {
         if (stack.size() == 0 || !useStack) {
             return;
         }
-        localStack.set(new ThreadContextStack(stack));
+        contextStack.clear();
+        contextStack.addAll(stack);
     }
 
     /**
@@ -247,8 +238,7 @@ public final class ThreadContext  {
      * @see #trim
      */
     public static int getDepth() {
-        final ContextStack stack = localStack.get();
-        return stack == null ? 0 : stack.getDepth();
+        return contextStack.getDepth();
     }
 
     /**
@@ -260,11 +250,7 @@ public final class ThreadContext  {
      * @return String The innermost diagnostic context.
      */
     public static String pop() {
-        final ContextStack s = localStack.get();
-        if (s == null || s.getDepth() == 0) {
-            return "";
-        }
-        return s.pop();
+        return contextStack.pop();
     }
 
     /**
@@ -277,11 +263,7 @@ public final class ThreadContext  {
      * @return String The innermost diagnostic context.
      */
     public static String peek() {
-        final ContextStack s = localStack.get();
-        if (s == null || s.getDepth() == 0) {
-            return "";
-        }
-        return s.peek();
+        return contextStack.peek();
     }
 
     /**
@@ -293,15 +275,7 @@ public final class ThreadContext  {
      * @param message The new diagnostic context information.
      */
     public static void push(final String message) {
-        if (!useStack) {
-            return;
-        }
-        ContextStack stack = localStack.get();
-        if (stack == null) {
-            stack = new ThreadContextStack();
-            localStack.set(stack);
-        }
-        stack.push(message);
+        contextStack.push(message);
     }
     /**
      * Push new diagnostic context information for the current thread.
@@ -315,15 +289,7 @@ public final class ThreadContext  {
      * @param args Parameters for the message.
      */
     public static void push(final String message, final Object... args) {
-        if (!useStack) {
-            return;
-        }
-        ContextStack stack = localStack.get();
-        if (stack == null) {
-            stack = new ThreadContextStack();
-            localStack.set(stack);
-        }
-        stack.push(ParameterizedMessage.format(message, args));
+        contextStack.push(ParameterizedMessage.format(message, args));
     }
 
     /**
@@ -345,7 +311,7 @@ public final class ThreadContext  {
      * memory.
      */
     public static void removeStack() {
-        localStack.remove();
+        contextStack.clear();
     }
 
     /**
@@ -378,10 +344,7 @@ public final class ThreadContext  {
      * @param depth The number of elements to keep.
      */
     public static void trim(final int depth) {
-        final ContextStack stack = localStack.get();
-        if (stack != null) {
-            stack.trim(depth);
-        }
+        contextStack.trim(depth);
     }
 
     /**
@@ -433,139 +396,8 @@ public final class ThreadContext  {
 
         /**
          * Returns a copy of the ContextStack.
-         * @return a copy of the ContextStack.
+         * @return a copy of the ContextStack.s
          */
         ContextStack copy();
     }
-
-    /**
-     * The ContextStack implementation.
-     */
-    private static class ThreadContextStack extends ArrayList<String> implements ContextStack {
-
-        private static final long serialVersionUID = 5050501L;
-
-        public ThreadContextStack() {
-            super();
-        }
-
-        public ThreadContextStack(final Collection<String> collection) {
-            super(collection);
-        }
-
-        @Override
-        public String pop() {
-            final int index = size() - 1;
-            if (index >= 0) {
-                final String result = get(index);
-                remove(index);
-                return result;
-            }
-            throw new NoSuchElementException("The ThreadContext stack is empty");
-        }
-
-        @Override
-        public String peek() {
-            final int index = size() - 1;
-            if (index >= 0) {
-                return get(index);
-            }
-            return null;
-        }
-
-        @Override
-        public void push(final String message) {
-            add(message);
-        }
-
-        @Override
-        public int getDepth() {
-            return size();
-        }
-
-        @Override
-        public List<String> asList() {
-            return this;
-        }
-
-        @Override
-        public void trim(final int depth) {
-            if (depth < 0) {
-                throw new IllegalArgumentException("Maximum stack depth cannot be negative");
-            }
-            while (size() > depth) {
-                remove(size() - 1);
-            }
-
-        }
-
-        @Override
-        public ContextStack copy() {
-            return new ThreadContextStack(this);
-        }
-    }
-
-    /**
-     * An immutable ContextStack.
-     */
-    public static class ImmutableStack extends ThreadContextStack {
-
-        private static final long serialVersionUID = 5050502L;
-
-        public ImmutableStack() {
-        }
-
-        public ImmutableStack(final Collection<String> collection) {
-            super(collection);
-        }
-
-        public ImmutableStack(final ThreadContextStack stack) {
-            super(stack);
-        }
-
-        @Override
-        public void push(final String message) {
-            throw new UnsupportedOperationException("Stack cannot be modified");
-        }
-
-        @Override
-        public void trim(final int depth) {
-            throw new UnsupportedOperationException("Stack cannot be modified");
-        }
-    }
-
-    /**
-     * An immutable Context Map.
-     */
-    public static class ImmutableMap extends HashMap<String, String> {
-        private static final long serialVersionUID = 5050503L;
-
-        public ImmutableMap() {
-            super();
-        }
-
-        public ImmutableMap(final Map<String, String> map) {
-            super(map);
-        }
-
-        @Override
-        public String put(final String s, final String s1) {
-            throw new UnsupportedOperationException("Map cannot be modified");
-        }
-
-        @Override
-        public void putAll(final Map<? extends String, ? extends String> map) {
-            throw new UnsupportedOperationException("Map cannot be modified");
-        }
-
-        @Override
-        public String remove(final Object o) {
-            throw new UnsupportedOperationException("Map cannot be modified");
-        }
-
-        @Override
-        public void clear() {
-            throw new UnsupportedOperationException("Map cannot be modified");
-        }
-    }
 }

Modified: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java (original)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java Sun May 26 22:11:29 2013
@@ -16,11 +16,15 @@
  */
 package org.apache.logging.log4j.spi;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- *
+ * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored
+ * is always immutable. This means the Map can be passed to other threads without concern that it will be updated.
+ * Since it is expected that the Map will be passed to many more log events than the number of keys it contains
+ * the performance should be much better than if the Map was copied for each event.
  */
 public class DefaultThreadContextMap implements ThreadContextMap {
 
@@ -30,7 +34,8 @@ public class DefaultThreadContextMap imp
         new InheritableThreadLocal<Map<String, String>>() {
             @Override
             protected Map<String, String> childValue(final Map<String, String> parentValue) {
-                return parentValue == null || !useMap ? null : new HashMap<String, String>(parentValue);
+                return parentValue == null || !useMap ? null :
+                    Collections.unmodifiableMap(new HashMap<String, String>(parentValue));
             }
         };
 
@@ -54,11 +59,9 @@ public class DefaultThreadContextMap imp
             return;
         }
         Map<String, String> map = localMap.get();
-        if (map == null) {
-            map = new HashMap<String, String>();
-            localMap.set(map);
-        }
+        map = map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
         map.put(key, value);
+        localMap.set(Collections.unmodifiableMap(map));
     }
 
     /**
@@ -83,7 +86,9 @@ public class DefaultThreadContextMap imp
     public void remove(final String key) {
         final Map<String, String> map = localMap.get();
         if (map != null) {
-            map.remove(key);
+            final Map<String, String> copy = new HashMap<String, String>(map);
+            copy.remove(key);
+            localMap.set(Collections.unmodifiableMap(copy));
         }
     }
 
@@ -103,25 +108,25 @@ public class DefaultThreadContextMap imp
     @Override
     public boolean containsKey(final String key) {
         final Map<String, String> map = localMap.get();
-        return map == null ? false : map.containsKey(key);
+        return map != null && map.containsKey(key);
     }
 
     /**
-     * Get a copy of current thread's context Map.
-     * @return a copy of the context.
+     * Returns a non-{@code null} mutable copy of the ThreadContext Map.
+     * @return a non-{@code null} mutable copy of the context.
      */
     @Override
-    public Map<String, String> getContext() {
+    public Map<String, String> getCopy() {
         final Map<String, String> map = localMap.get();
         return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
     }
 
     /**
-     * Return the context Map.
+     * Returns either {@code null} or an immutable view of the context Map.
      * @return the Context Map.
      */
     @Override
-    public Map<String, String> get() {
+    public Map<String, String> getImmutableMapOrNull() {
         return localMap.get();
     }
 

Added: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java (added)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java Sun May 26 22:11:29 2013
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * A copy-on-write thread-safe variant of
+ * {@code org.apache.logging.log4j.spi.ThreadContextStack} in which all mutative
+ * operations (add, pop, and so on) are implemented by making a fresh copy of
+ * the underlying list.
+ */
+public class DefaultThreadContextStack implements ThreadContextStack {
+
+    private static final long serialVersionUID = 5050501L;
+
+    private final boolean useStack;
+    private static ThreadLocal<List<String>> stack = new ThreadLocal<List<String>>();
+
+    public DefaultThreadContextStack(boolean useStack) {
+        this.useStack = useStack;
+    }
+
+    public String pop() {
+        if (!useStack) {
+            return "";
+        }
+        final List<String> list = stack.get();
+        if (list == null || list.size() == 0) {
+            throw new NoSuchElementException("The ThreadContext stack is empty");
+        }
+        final List<String> copy = new ArrayList<String>(list);
+        final int last = copy.size() - 1;
+        final String result = copy.remove(last);
+        stack.set(Collections.unmodifiableList(copy));
+        return result;
+    }
+
+    public String peek() {
+        final List<String> list = stack.get();
+        if (list == null || list.size() == 0) {
+            return null;
+        }
+        final int last = list.size() - 1;
+        return list.get(last);
+    }
+
+    public void push(final String message) {
+        if (!useStack) {
+            return;
+        }
+        add(message);
+    }
+
+    public int getDepth() {
+        final List<String> list = stack.get();
+        return list == null ? 0 : list.size();
+    }
+
+    public List<String> asList() {
+        final List<String> list = stack.get();
+        if (list == null) {
+            return Collections.emptyList();
+        }
+        return list;
+    }
+
+    public void trim(final int depth) {
+        if (depth < 0) {
+            throw new IllegalArgumentException(
+                    "Maximum stack depth cannot be negative");
+        }
+        final List<String> list = stack.get();
+        if (list == null) {
+            return;
+        }
+        final List<String> copy = new ArrayList<String>();
+        int count = Math.min(depth, list.size());
+        for(int i = 0; i < count; i++) {
+            copy.add(list.get(i));
+        }
+        stack.set(copy);
+    }
+
+    public ThreadContextStack copy() {
+        List<String> result = null;
+        if (!useStack || (result = stack.get()) == null) {
+            return new MutableThreadContextStack(new ArrayList<String>());
+        }
+        return new MutableThreadContextStack(result);
+    }
+
+    public void clear() {
+        stack.remove();
+    }
+
+    public int size() {
+        final List<String> result = stack.get();
+        return result == null ? 0 : result.size();
+    }
+
+    public boolean isEmpty() {
+        final List<String> result = stack.get();
+        return result == null || result.isEmpty();
+    }
+
+    public boolean contains(Object o) {
+        final List<String> result = stack.get();
+        return result != null && result.contains(o);
+    }
+
+    public Iterator<String> iterator() {
+        final List<String> immutable = stack.get();
+        if (immutable == null) {
+            final List<String> empty = Collections.emptyList();
+            return empty.iterator();
+        }
+        return immutable.iterator();
+    }
+
+    public Object[] toArray() {
+        final List<String> result = stack.get();
+        if (result == null) {
+            return new String[0];
+        }
+        return result.toArray(new Object[result.size()]);
+    }
+
+    public <T> T[] toArray(T[] ts) {
+        List<String> result = stack.get();
+        if (result == null) {
+            if (ts.length > 0) { // as per the contract of j.u.List#toArray(T[])
+                ts[0] = null;
+            }
+            return ts;
+        }
+        return result.toArray(ts);
+    }
+
+    public boolean add(String s) {
+        if (!useStack) {
+            return false;
+        }
+        final List<String> list = stack.get();
+        final List<String> copy = list == null ? new ArrayList<String>()
+                : new ArrayList<String>(list);
+        copy.add(s);
+        stack.set(Collections.unmodifiableList(copy));
+        return true;
+    }
+
+    public boolean remove(Object o) {
+        if (!useStack) {
+            return false;
+        }
+        final List<String> list = stack.get();
+        if (list == null || list.size() == 0) {
+            return false;
+        }
+        final List<String> copy = new ArrayList<String>(list);
+        final boolean result = copy.remove(o);
+        stack.set(Collections.unmodifiableList(copy));
+        return result;
+    }
+
+    public boolean containsAll(Collection<?> objects) {
+        if (objects.isEmpty()) { // quick check before accessing the ThreadLocal
+            return true; // looks counter-intuitive, but see
+                         // j.u.AbstractCollection
+        }
+        final List<String> list = stack.get();
+        return list != null && list.containsAll(objects);
+    }
+
+    public boolean addAll(Collection<? extends String> strings) {
+        if (!useStack || strings.isEmpty()) {
+            return false;
+        }
+        final List<String> list = stack.get();
+        final List<String> copy = list == null ? new ArrayList<String>()
+                : new ArrayList<String>(list);
+        copy.addAll(strings);
+        stack.set(Collections.unmodifiableList(copy));
+        return true;
+    }
+
+    public boolean removeAll(Collection<?> objects) {
+        if (!useStack || objects.isEmpty()) {
+            return false;
+        }
+        final List<String> list = stack.get();
+        if (list == null || list.isEmpty()) {
+            return false;
+        }
+        final List<String> copy = new ArrayList<String>(list);
+        boolean result = copy.removeAll(objects);
+        stack.set(Collections.unmodifiableList(copy));
+        return result;
+    }
+
+    public boolean retainAll(Collection<?> objects) {
+        if (!useStack || objects.isEmpty()) {
+            return false;
+        }
+        final List<String> list = stack.get();
+        if (list == null || list.isEmpty()) {
+            return false;
+        }
+        final List<String> copy = new ArrayList<String>(list);
+        final boolean result = copy.retainAll(objects);
+        stack.set(Collections.unmodifiableList(copy));
+        return result;
+    }
+}

Propchange: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java (added)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java Sun May 26 22:11:29 2013
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ */
+public class MutableThreadContextStack implements ThreadContextStack {
+
+    private static final long serialVersionUID = 50505011L;
+
+    /**
+     * The underlying list (never null).
+     */
+    private final List<String> list;
+
+    public MutableThreadContextStack(List<String> list) {
+        this.list = new ArrayList<String>(list);
+    }
+
+    private MutableThreadContextStack(MutableThreadContextStack stack) {
+        this.list = new ArrayList<String>(stack.list);
+    }
+
+    public String pop() {
+        if (list.isEmpty()) {
+            return null;
+        }
+        final int last = list.size() - 1;
+        final String result = list.remove(last);
+        return result;
+    }
+
+    public String peek() {
+        if (list.isEmpty()) {
+            return null;
+        }
+        final int last = list.size() - 1;
+        return list.get(last);
+    }
+
+    public void push(final String message) {
+        list.add(message);
+    }
+
+    public int getDepth() {
+        return list.size();
+    }
+
+    public List<String> asList() {
+        return list;
+    }
+
+    public void trim(final int depth) {
+        if (depth < 0) {
+            throw new IllegalArgumentException("Maximum stack depth cannot be negative");
+        }
+        if (list == null) {
+            return;
+        }
+        final List<String> copy = new ArrayList<String>(list.size());
+        int count = Math.min(depth, list.size());
+        for(int i = 0; i < count; i++) {
+            copy.add(list.get(i));
+        }
+        list.clear();
+        list.addAll(copy);
+    }
+
+    public ThreadContextStack copy() {
+        return new MutableThreadContextStack(this);
+    }
+
+    public void clear() {
+        list.clear();
+    }
+
+    public int size() {
+        return list.size();
+    }
+
+    public boolean isEmpty() {
+        return list.isEmpty();
+    }
+
+    public boolean contains(Object o) {
+        return list.contains(o);
+    }
+
+    public Iterator<String> iterator() {
+        return list.iterator();
+    }
+
+    public Object[] toArray() {
+        return list.toArray();
+    }
+
+    public <T> T[] toArray(T[] ts) {
+        return list.toArray(ts);
+    }
+
+    public boolean add(String s) {
+        return list.add(s);
+    }
+
+    public boolean remove(Object o) {
+        return list.remove(o);
+    }
+
+    public boolean containsAll(Collection<?> objects) {
+        return list.containsAll(objects);
+    }
+
+    public boolean addAll(Collection<? extends String> strings) {
+        return list.addAll(strings);
+    }
+
+    public boolean removeAll(Collection<?> objects) {
+        return list.removeAll(objects);
+    }
+
+    public boolean retainAll(Collection<?> objects) {
+        return list.retainAll(objects);
+    }
+}

Propchange: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java (original)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap.java Sun May 26 22:11:29 2013
@@ -63,16 +63,16 @@ public interface ThreadContextMap {
     boolean containsKey(final String key);
 
     /**
-     * Get a copy of current thread's context Map.
-     * @return a copy of the context.
+     * Get a non-{@code null} mutable copy of current thread's context Map.
+     * @return a mutable copy of the context.
      */
-    Map<String, String> getContext();
+    Map<String, String> getCopy();
 
     /**
-     * Return the actual context Map.
-     * @return the actual context Map.
+     * Return an immutable view on the context Map or {@code null} if the context map is empty.
+     * @return an immutable context Map or {@code null}.
      */
-    Map<String, String> get();
+    Map<String, String> getImmutableMapOrNull();
 
     /**
      * Returns true if the Map is empty.

Added: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java (added)
+++ logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java Sun May 26 22:11:29 2013
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import org.apache.logging.log4j.ThreadContext;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ *
+ */
+public interface ThreadContextStack extends ThreadContext.ContextStack,
+        Collection<String>, Serializable {
+}

Propchange: logging/log4j/log4j2/trunk/api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java (original)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java Sun May 26 22:11:29 2013
@@ -18,8 +18,9 @@ package org.apache.logging.log4j;
 
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import java.util.Map;
+
+import static org.junit.Assert.*;
 
 /**
  *
@@ -29,9 +30,12 @@ public class ThreadContextTest {
     @Test
     public void testPush() {
         ThreadContext.push("Hello");
-        ThreadContext.push("{} is {}", ThreadContextTest.class.getSimpleName(), "running");
-        assertEquals("Incorrect parameterized stack value", ThreadContext.pop(), "ThreadContextTest is running");
-        assertEquals("Incorrect simple stack value", ThreadContext.pop(), "Hello");
+        ThreadContext.push("{} is {}", ThreadContextTest.class.getSimpleName(),
+                "running");
+        assertEquals("Incorrect parameterized stack value",
+                ThreadContext.pop(), "ThreadContextTest is running");
+        assertEquals("Incorrect simple stack value", ThreadContext.pop(),
+                "Hello");
     }
 
     @Test
@@ -43,15 +47,131 @@ public class ThreadContextTest {
         thread.start();
         thread.join();
         String str = sb.toString();
-        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual " + str, "Hello".equals(str));
+        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
+                + str, "Hello".equals(str));
         sb = new StringBuilder();
         thread = new TestThread(sb);
         thread.start();
         thread.join();
         str = sb.toString();
-        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual " + str, "Hello".equals(str));
+        assertTrue("Unexpected ThreadContext value. Expected Hello. Actual "
+                + str, "Hello".equals(str));
+    }
+
+    @Test
+    public void perfTest() throws Exception {
+        ThreadContext.clear();
+        Timer complete = new Timer("ThreadContextTest");
+        complete.start();
+        ThreadContext.put("Var1", "value 1");
+        ThreadContext.put("Var2", "value 2");
+        ThreadContext.put("Var3", "value 3");
+        ThreadContext.put("Var4", "value 4");
+        ThreadContext.put("Var5", "value 5");
+        ThreadContext.put("Var6", "value 6");
+        ThreadContext.put("Var7", "value 7");
+        ThreadContext.put("Var8", "value 8");
+        ThreadContext.put("Var9", "value 9");
+        ThreadContext.put("Var10", "value 10");
+        final int loopCount = 1000000;
+        Timer timer = new Timer("ThreadContextCopy", loopCount);
+        timer.start();
+        for (int i = 0; i < loopCount; ++i) {
+            Map<String, String> map = ThreadContext.getImmutableContext();
+            assertNotNull(map);
+        }
+        timer.stop();
+        complete.stop();
+        System.out.println(timer.toString());
+        System.out.println(complete.toString());
+    }
+
+    @Test
+    public void testGetContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clear();
+        assertTrue(ThreadContext.getContext().isEmpty());
+    }
+
+    @Test
+    public void testGetContextReturnsMutableCopy() {
+        ThreadContext.clear();
+        Map<String, String> map1 = ThreadContext.getContext();
+        assertTrue(map1.isEmpty());
+        map1.put("K", "val"); // no error
+        assertEquals("val", map1.get("K"));
+
+        // adding to copy does not affect thread context map
+        assertTrue(ThreadContext.getContext().isEmpty());
+
+        ThreadContext.put("key", "val2");
+        Map<String, String> map2 = ThreadContext.getContext();
+        assertEquals(1, map2.size());
+        assertEquals("val2", map2.get("key"));
+        map2.put("K", "val"); // no error
+        assertEquals("val", map2.get("K"));
+
+        // first copy is not affected
+        assertNotSame(map1, map2);
+        assertEquals(1, map1.size());
+    }
+
+    @Test
+    public void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+        ThreadContext.clear();
+        assertTrue(ThreadContext.getImmutableContext().isEmpty());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+        ThreadContext.clear();
+        ThreadContext.put("key", "val");
+        Map<String, String> immutable = ThreadContext.getImmutableContext();
+        immutable.put("otherkey", "otherval");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+        ThreadContext.clear();
+        Map<String, String> immutable = ThreadContext.getImmutableContext();
+        immutable.put("otherkey", "otherval");
     }
 
+    @Test
+    public void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+        ThreadContext.clearStack();
+        assertTrue(ThreadContext.getImmutableStack().asList().isEmpty());
+    }
+
+    @Test
+    public void testPut() {
+        ThreadContext.clear();
+        assertNull(ThreadContext.get("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertEquals("testValue", ThreadContext.get("testKey"));
+    }
+
+    @Test
+    public void testRemove() {
+        ThreadContext.clear();
+        assertNull(ThreadContext.get("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertEquals("testValue", ThreadContext.get("testKey"));
+        
+        ThreadContext.remove("testKey");
+        assertNull(ThreadContext.get("testKey"));
+        assertTrue(ThreadContext.isEmpty());
+    }
+
+    @Test
+    public void testContainsKey() {
+        ThreadContext.clear();
+        assertFalse(ThreadContext.containsKey("testKey"));
+        ThreadContext.put("testKey", "testValue");
+        assertTrue(ThreadContext.containsKey("testKey"));
+        
+        ThreadContext.remove("testKey");
+        assertFalse(ThreadContext.containsKey("testKey"));
+    }
 
     private class TestThread extends Thread {
 

Added: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java (added)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java Sun May 26 22:11:29 2013
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import static org.junit.Assert.*;
+
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Tests the {@code DefaultThreadContextMap} class.
+ */
+public class DefaultThreadContextMapTest {
+
+    @Test
+    public void testDoesNothingIfConstructedWithUseMapIsFalse() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(false);
+        assertTrue(map.isEmpty());
+        assertFalse(map.containsKey("key"));
+        map.put("key", "value");
+
+        assertTrue(map.isEmpty());
+        assertFalse(map.containsKey("key"));
+        assertNull(map.get("key"));
+    }
+
+    @Test
+    public void testPut() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        assertTrue(map.isEmpty());
+        assertFalse(map.containsKey("key"));
+        map.put("key", "value");
+
+        assertFalse(map.isEmpty());
+        assertTrue(map.containsKey("key"));
+        assertEquals("value", map.get("key"));
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.logging.log4j.spi.DefaultThreadContextMap#remove(java.lang.String)}
+     * .
+     */
+    @Test
+    public void testRemove() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        assertTrue(map.isEmpty());
+        map.put("key", "value");
+        map.put("key2", "value2");
+        assertEquals("value", map.get("key"));
+        assertEquals("value2", map.get("key2"));
+
+        map.remove("key");
+        assertFalse(map.containsKey("key"));
+        assertEquals("value2", map.get("key2"));
+    }
+
+    @Test
+    public void testClear() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        assertTrue(map.isEmpty());
+        map.put("key", "value");
+        map.put("key2", "value2");
+        assertEquals("value", map.get("key"));
+        assertEquals("value2", map.get("key2"));
+
+        map.clear();
+        assertTrue(map.isEmpty());
+        assertFalse(map.containsKey("key"));
+        assertFalse(map.containsKey("key2"));
+    }
+
+    @Test
+    public void testGetCopyReturnsMutableMap() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        assertTrue(map.isEmpty());
+        Map<String, String> copy = map.getCopy();
+        assertTrue(copy.isEmpty());
+
+        copy.put("key", "value"); // mutable
+        assertEquals("value", copy.get("key"));
+
+        // thread context map not affected
+        assertTrue(map.isEmpty());
+    }
+
+    @Test
+    public void testGetCopyReturnsMutableCopy() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        map.put("key1", "value1");
+        assertFalse(map.isEmpty());
+        Map<String, String> copy = map.getCopy();
+        assertEquals("value1", copy.get("key1")); // copy has values too
+
+        copy.put("key", "value"); // copy is mutable
+        assertEquals("value", copy.get("key"));
+
+        // thread context map not affected
+        assertFalse(map.containsKey("key"));
+
+        // clearing context map does not affect copy
+        map.clear();
+        assertTrue(map.isEmpty());
+
+        assertFalse(copy.isEmpty());
+    }
+
+    @Test
+    public void testGetImmutableMapReturnsNullIfEmpty() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        assertTrue(map.isEmpty());
+        assertNull(map.getImmutableMapOrNull());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetImmutableMapReturnsImmutableMapIfNonEmpty() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        map.put("key1", "value1");
+        assertFalse(map.isEmpty());
+
+        Map<String, String> immutable = map.getImmutableMapOrNull();
+        assertEquals("value1", immutable.get("key1")); // copy has values too
+
+        // immutable
+        immutable.put("key", "value"); // error
+    }
+
+    @Test
+    public void testGetImmutableMapCopyNotAffectdByContextMapChanges() {
+        DefaultThreadContextMap map = new DefaultThreadContextMap(true);
+        map.put("key1", "value1");
+        assertFalse(map.isEmpty());
+
+        Map<String, String> immutable = map.getImmutableMapOrNull();
+        assertEquals("value1", immutable.get("key1")); // copy has values too
+
+        // clearing context map does not affect copy
+        map.clear();
+        assertTrue(map.isEmpty());
+
+        assertFalse(immutable.isEmpty());
+    }
+}

Propchange: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java (added)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java Sun May 26 22:11:29 2013
@@ -0,0 +1,304 @@
+package org.apache.logging.log4j.spi;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.junit.Test;
+
+public class DefaultThreadContextStackTest {
+
+    @Test
+    public void testDoesNothingIfConstructedWithUseStackIsFalse() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(false);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg");
+
+        // nothing was added
+        assertTrue(stack.isEmpty());
+        assertEquals(0, stack.size());
+    }
+
+    @Test
+    public void testPushAndAddIncreaseStack() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+
+        assertEquals(2, stack.size());
+    }
+
+    @Test
+    public void testPeekReturnsLastAddedItem() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+
+        assertEquals(2, stack.size());
+        assertEquals("msg2", stack.peek());
+
+        stack.push("msg3");
+        assertEquals("msg3", stack.peek());
+    }
+
+    @Test
+    public void testPopRemovesLastAddedItem() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        assertEquals(3, stack.getDepth());
+
+        assertEquals("msg3", stack.pop());
+        assertEquals(2, stack.size());
+        assertEquals(2, stack.getDepth());
+
+        assertEquals("msg2", stack.pop());
+        assertEquals(1, stack.size());
+        assertEquals(1, stack.getDepth());
+
+        assertEquals("msg1", stack.pop());
+        assertEquals(0, stack.size());
+        assertEquals(0, stack.getDepth());
+    }
+
+    @Test
+    public void testAsList() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+
+        assertEquals(Arrays.asList("msg1", "msg2", "msg3"), stack.asList());
+    }
+
+    @Test
+    public void testTrim() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.trim(1);
+        assertEquals(1, stack.size());
+        assertEquals("msg1", stack.peek());
+    }
+
+    @Test
+    public void testCopy() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        ThreadContextStack copy = stack.copy();
+        assertEquals(3, copy.size());
+        assertTrue(copy.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+
+        // clearing stack does not affect copy
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        assertEquals(3, copy.size()); // not affected
+        assertTrue(copy.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+
+        // adding to copy does not affect stack
+        copy.add("other");
+        assertEquals(4, copy.size()); // not affected
+        assertTrue(stack.isEmpty());
+        
+        // adding to stack does not affect copy
+        stack.push("newStackMsg");
+        assertEquals(1, stack.size());
+        assertEquals(4, copy.size()); // not affected
+        
+        // clearing copy does not affect stack
+        copy.clear();
+        assertTrue(copy.isEmpty());
+        assertEquals(1, stack.size());
+    }
+
+    @Test
+    public void testClear() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.clear();
+        assertTrue(stack.isEmpty());
+    }
+
+    @Test
+    public void testContains() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        assertTrue(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+    }
+
+    @Test
+    public void testIteratorReturnsInListOrderNotStackOrder() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        Iterator<String> iter = stack.iterator();
+        assertTrue(iter.hasNext());
+        assertEquals("msg1", iter.next());
+        assertTrue(iter.hasNext());
+        assertEquals("msg2", iter.next());
+        assertTrue(iter.hasNext());
+        assertEquals("msg3", iter.next());
+        assertFalse(iter.hasNext());
+    }
+
+    @Test
+    public void testToArray() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        String[] expecteds = {"msg1", "msg2", "msg3"};
+        assertArrayEquals(expecteds, stack.toArray());
+    }
+
+    @Test
+    public void testToArrayTArray() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        String[] expecteds = {"msg1", "msg2", "msg3"};
+        String[] result = new String[3] ;
+        assertArrayEquals(expecteds, stack.toArray(result));
+        assertSame(result, stack.toArray(result));
+    }
+
+    @Test
+    public void testRemove() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+        
+        stack.remove("msg1");
+        assertEquals(2, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg2", "msg3")));
+        assertEquals("msg3", stack.peek());
+        
+        stack.remove("msg3");
+        assertEquals(1, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg2")));
+        assertEquals("msg2", stack.peek());
+    }
+
+    @Test
+    public void testContainsAll() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+
+        assertTrue(stack.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+    }
+
+    @Test
+    public void testAddAll() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.addAll(Arrays.asList("msg4", "msg5"));
+        assertEquals(5, stack.size());
+        assertTrue(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+        assertTrue(stack.contains("msg4"));
+        assertTrue(stack.contains("msg5"));
+    }
+
+    @Test
+    public void testRemoveAll() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.removeAll(Arrays.asList("msg1", "msg3"));
+        assertEquals(1, stack.size());
+        assertFalse(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertFalse(stack.contains("msg3"));
+    }
+
+    @Test
+    public void testRetainAll() {
+        DefaultThreadContextStack stack = new DefaultThreadContextStack(true);
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.retainAll(Arrays.asList("msg1", "msg3"));
+        assertEquals(2, stack.size());
+        assertTrue(stack.contains("msg1"));
+        assertFalse(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+    }
+
+}

Propchange: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/DefaultThreadContextStackTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java (added)
+++ logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java Sun May 26 22:11:29 2013
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spi;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+public class MutableThreadContextStackTest {
+
+    @Test
+    public void testEmptyIfConstructedWithEmptyList() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        assertTrue(stack.isEmpty());
+    }
+
+    @Test
+    public void testConstructorCopiesListContents() {
+        List<String> initial = Arrays.asList("a", "b", "c");
+        MutableThreadContextStack stack = new MutableThreadContextStack(initial);
+        assertFalse(stack.isEmpty());
+        assertTrue(stack.containsAll(initial));
+    }
+
+    @Test
+    public void testPushAndAddIncreaseStack() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+
+        assertEquals(2, stack.size());
+    }
+
+    @Test
+    public void testPeekReturnsLastAddedItem() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+
+        assertEquals(2, stack.size());
+        assertEquals("msg2", stack.peek());
+
+        stack.push("msg3");
+        assertEquals("msg3", stack.peek());
+    }
+
+    @Test
+    public void testPopRemovesLastAddedItem() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        assertEquals(3, stack.getDepth());
+
+        assertEquals("msg3", stack.pop());
+        assertEquals(2, stack.size());
+        assertEquals(2, stack.getDepth());
+
+        assertEquals("msg2", stack.pop());
+        assertEquals(1, stack.size());
+        assertEquals(1, stack.getDepth());
+
+        assertEquals("msg1", stack.pop());
+        assertEquals(0, stack.size());
+        assertEquals(0, stack.getDepth());
+    }
+
+    @Test
+    public void testAsList() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+
+        assertEquals(Arrays.asList("msg1", "msg2", "msg3"), stack.asList());
+    }
+
+    @Test
+    public void testTrim() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.trim(1);
+        assertEquals(1, stack.size());
+        assertEquals("msg1", stack.peek());
+    }
+
+    @Test
+    public void testCopy() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        ThreadContextStack copy = stack.copy();
+        assertEquals(3, copy.size());
+        assertTrue(copy.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+
+        // clearing stack does not affect copy
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        assertEquals(3, copy.size()); // not affected
+        assertTrue(copy.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+
+        // adding to copy does not affect stack
+        copy.add("other");
+        assertEquals(4, copy.size()); // not affected
+        assertTrue(stack.isEmpty());
+        
+        // adding to stack does not affect copy
+        stack.push("newStackMsg");
+        assertEquals(1, stack.size());
+        assertEquals(4, copy.size()); // not affected
+        
+        // clearing copy does not affect stack
+        copy.clear();
+        assertTrue(copy.isEmpty());
+        assertEquals(1, stack.size());
+    }
+
+    @Test
+    public void testClear() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.clear();
+        assertTrue(stack.isEmpty());
+    }
+
+    @Test
+    public void testContains() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        assertTrue(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+    }
+
+    @Test
+    public void testIteratorReturnsInListOrderNotStackOrder() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        Iterator<String> iter = stack.iterator();
+        assertTrue(iter.hasNext());
+        assertEquals("msg1", iter.next());
+        assertTrue(iter.hasNext());
+        assertEquals("msg2", iter.next());
+        assertTrue(iter.hasNext());
+        assertEquals("msg3", iter.next());
+        assertFalse(iter.hasNext());
+    }
+
+    @Test
+    public void testToArray() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        String[] expecteds = {"msg1", "msg2", "msg3"};
+        assertArrayEquals(expecteds, stack.toArray());
+    }
+
+    @Test
+    public void testToArrayTArray() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        String[] expecteds = {"msg1", "msg2", "msg3"};
+        String[] result = new String[3] ;
+        assertArrayEquals(expecteds, stack.toArray(result));
+        assertSame(result, stack.toArray(result));
+    }
+
+    @Test
+    public void testRemove() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+        
+        stack.remove("msg1");
+        assertEquals(2, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg2", "msg3")));
+        assertEquals("msg3", stack.peek());
+        
+        stack.remove("msg3");
+        assertEquals(1, stack.size());
+        assertTrue(stack.containsAll(Arrays.asList("msg2")));
+        assertEquals("msg2", stack.peek());
+    }
+
+    @Test
+    public void testContainsAll() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+
+        assertTrue(stack.containsAll(Arrays.asList("msg1", "msg2", "msg3")));
+    }
+
+    @Test
+    public void testAddAll() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.addAll(Arrays.asList("msg4", "msg5"));
+        assertEquals(5, stack.size());
+        assertTrue(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+        assertTrue(stack.contains("msg4"));
+        assertTrue(stack.contains("msg5"));
+    }
+
+    @Test
+    public void testRemoveAll() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.removeAll(Arrays.asList("msg1", "msg3"));
+        assertEquals(1, stack.size());
+        assertFalse(stack.contains("msg1"));
+        assertTrue(stack.contains("msg2"));
+        assertFalse(stack.contains("msg3"));
+    }
+
+    @Test
+    public void testRetainAll() {
+        MutableThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+        stack.push("msg1");
+        stack.add("msg2");
+        stack.push("msg3");
+        assertEquals(3, stack.size());
+        
+        stack.retainAll(Arrays.asList("msg1", "msg3"));
+        assertEquals(2, stack.size());
+        assertTrue(stack.contains("msg1"));
+        assertFalse(stack.contains("msg2"));
+        assertTrue(stack.contains("msg3"));
+    }
+}

Propchange: logging/log4j/log4j2/trunk/api/src/test/java/org/apache/logging/log4j/spi/MutableThreadContextStackTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverter.java Sun May 26 22:11:29 2013
@@ -18,10 +18,12 @@ package org.apache.logging.log4j.core.ap
 
 import java.io.IOException;
 import java.util.List;
+
 import javax.persistence.AttributeConverter;
 import javax.persistence.PersistenceException;
 
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.spi.DefaultThreadContextStack;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 
@@ -56,6 +58,8 @@ public class ContextStackJsonAttributeCo
             throw new PersistenceException("Failed to convert JSON string to list for stack.", e);
         }
 
-        return new ThreadContext.ImmutableStack(list);
+        DefaultThreadContextStack result = new DefaultThreadContextStack(true);
+        result.addAll(list);
+        return result;
     }
 }

Modified: logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (original)
+++ logging/log4j/log4j2/trunk/core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java Sun May 26 22:11:29 2013
@@ -28,6 +28,8 @@ import org.apache.logging.log4j.message.
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -119,20 +121,21 @@ public class Log4jLogEvent implements Lo
     }
 
     private static Map<String, String> createMap(final List<Property> properties) {
-        if (ThreadContext.isEmpty() && (properties == null || properties.size() == 0)) {
+        final Map<String, String> contextMap = ThreadContext.getImmutableContext();
+        if (contextMap == null && (properties == null || properties.size() == 0)) {
             return null;
         }
         if (properties == null || properties.size() == 0) {
-            return ThreadContext.getImmutableContext();
+            return contextMap; // contextMap is not null
         }
-        final Map<String, String> map = ThreadContext.getContext();
+        final Map<String, String> map = new HashMap<String, String>(contextMap);
 
         for (final Property prop : properties) {
             if (!map.containsKey(prop.getName())) {
                 map.put(prop.getName(), prop.getValue());
             }
         }
-        return new ThreadContext.ImmutableMap(map);
+        return Collections.unmodifiableMap(map);
     }
 
     /**

Added: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java?rev=1486482&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java (added)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java Sun May 26 22:11:29 2013
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Timer;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class LocationPerfCheck {
+
+    private static final int LOOP_COUNT = 100000;
+
+
+    @Test
+    public void testLocation() {
+        Timer timer = new Timer("LogEvent", LOOP_COUNT);
+        timer.start();
+        for (int i = 0; i < LOOP_COUNT; ++i) {
+            final LogEvent event1 = new Log4jLogEvent(this.getClass().getName(), null,
+                "org.apache.logging.log4j.core.impl.Log4jLogEvent",
+                Level.INFO, new SimpleMessage("Hello, world!"), null);
+            StackTraceElement element = event1.getSource();
+        }
+        timer.stop();
+        System.out.println(timer.toString());
+    }
+}

Propchange: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackAttributeConverterTest.java Sun May 26 22:11:29 2013
@@ -16,15 +16,16 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa.converter;
 
+import static org.junit.Assert.*;
+
 import java.util.Arrays;
 
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-
 public class ContextStackAttributeConverterTest {
     private ContextStackAttributeConverter converter;
 
@@ -40,7 +41,8 @@ public class ContextStackAttributeConver
 
     @Test
     public void testConvertToDatabaseColumn01() {
-        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("value1", "another2"));
+        ThreadContext.ContextStack stack = new MutableThreadContextStack(
+                Arrays.asList("value1", "another2"));
 
         assertEquals("The converted value is not correct.", "value1\nanother2",
                 this.converter.convertToDatabaseColumn(stack));
@@ -48,9 +50,11 @@ public class ContextStackAttributeConver
 
     @Test
     public void testConvertToDatabaseColumn02() {
-        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("key1", "value2", "my3"));
+        ThreadContext.ContextStack stack = new MutableThreadContextStack(
+                Arrays.asList("key1", "value2", "my3"));
 
-        assertEquals("The converted value is not correct.", "key1\nvalue2\nmy3",
+        assertEquals("The converted value is not correct.",
+                "key1\nvalue2\nmy3",
                 this.converter.convertToDatabaseColumn(stack));
     }
 

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextStackJsonAttributeConverterTest.java Sun May 26 22:11:29 2013
@@ -16,15 +16,16 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa.converter;
 
+import static org.junit.Assert.*;
+
 import java.util.Arrays;
 
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
-
 public class ContextStackJsonAttributeConverterTest {
     private ContextStackJsonAttributeConverter converter;
 
@@ -40,29 +41,37 @@ public class ContextStackJsonAttributeCo
 
     @Test
     public void testConvert01() {
-        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("value1", "another2"));
+        ThreadContext.clearStack();
+        ThreadContext.ContextStack stack = new MutableThreadContextStack(
+                Arrays.asList("value1", "another2"));
 
         String converted = this.converter.convertToDatabaseColumn(stack);
 
         assertNotNull("The converted value should not be null.", converted);
 
-        ThreadContext.ContextStack reversed = this.converter.convertToEntityAttribute(converted);
+        ThreadContext.ContextStack reversed = this.converter
+                .convertToEntityAttribute(converted);
 
         assertNotNull("The reversed value should not be null.", reversed);
-        assertEquals("The reversed value is not correct.", stack.asList(), reversed.asList());
+        assertEquals("The reversed value is not correct.", stack.asList(),
+                reversed.asList());
     }
 
     @Test
     public void testConvert02() {
-        ThreadContext.ContextStack stack = new ThreadContext.ImmutableStack(Arrays.asList("key1", "value2", "my3"));
+        ThreadContext.clearStack();
+        ThreadContext.ContextStack stack = new MutableThreadContextStack(
+                Arrays.asList("key1", "value2", "my3"));
 
         String converted = this.converter.convertToDatabaseColumn(stack);
 
         assertNotNull("The converted value should not be null.", converted);
 
-        ThreadContext.ContextStack reversed = this.converter.convertToEntityAttribute(converted);
+        ThreadContext.ContextStack reversed = this.converter
+                .convertToEntityAttribute(converted);
 
         assertNotNull("The reversed value should not be null.", reversed);
-        assertEquals("The reversed value is not correct.", stack.asList(), reversed.asList());
+        assertEquals("The reversed value is not correct.", stack.asList(),
+                reversed.asList());
     }
 }

Modified: logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java (original)
+++ logging/log4j/log4j2/trunk/core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java Sun May 26 22:11:29 2013
@@ -16,12 +16,12 @@
 */
 package org.apache.logging.log4j.core.appender.rewrite;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.MarkerManager;
-import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.helpers.KeyValuePair;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
@@ -29,6 +29,8 @@ import org.apache.logging.log4j.core.imp
 import org.apache.logging.log4j.message.MapMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
+import org.apache.logging.log4j.spi.ThreadContextStack;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -49,12 +51,13 @@ public class MapRewritePolicyTest {
 		logEvent1 = new Log4jLogEvent("test", null, "MapRewritePolicyTest.setupClass()", Level.ERROR,
         new MapMessage(map), null, map, null, "none",
         new StackTraceElement("MapRewritePolicyTest", "setupClass", "MapRewritePolicyTest", 29), 2);
+    ThreadContextStack stack = new MutableThreadContextStack(new ArrayList<String>(map.values()));
 		logEvent2 = new Log4jLogEvent("test", MarkerManager.getMarker("test"), "MapRewritePolicyTest.setupClass()",
         Level.TRACE, new StructuredDataMessage("test", "Nothing", "test", map), new RuntimeException("test"), null,
-        new ThreadContext.ImmutableStack(map.values()), "none", new StackTraceElement("MapRewritePolicyTest",
+        stack, "none", new StackTraceElement("MapRewritePolicyTest",
         "setupClass", "MapRewritePolicyTest", 30), 20000000);
 		logEvent3 = new Log4jLogEvent("test", null, "MapRewritePolicyTest.setupClass()", Level.ALL, new MapMessage(map),
-        null, map, new ThreadContext.ImmutableStack(map.values()), null, new StackTraceElement("MapRewritePolicyTest",
+        null, map, stack, null, new StackTraceElement("MapRewritePolicyTest",
         "setupClass", "MapRewritePolicyTest", 31), Long.MAX_VALUE);
 		rewrite = new KeyValuePair[] {new KeyValuePair("test2", "2"), new KeyValuePair("test3", "three")};
 	}

Modified: logging/log4j/log4j2/trunk/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java?rev=1486482&r1=1486481&r2=1486482&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java (original)
+++ logging/log4j/log4j2/trunk/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java Sun May 26 22:11:29 2013
@@ -52,13 +52,13 @@ public class MDCContextMap implements Th
 
     @Override
     @SuppressWarnings("unchecked") // nothing we can do about this, restricted by SLF4J API
-    public Map<String, String> getContext() {
+    public Map<String, String> getCopy() {
         return MDC.getCopyOfContextMap();
     }
 
     @Override
     @SuppressWarnings("unchecked") // nothing we can do about this, restricted by SLF4J API
-    public Map<String, String> get() {
+    public Map<String, String> getImmutableMapOrNull() {
         return MDC.getCopyOfContextMap();
     }
 



Mime
View raw message