### Eclipse Workspace Patch 1.0 #P log4j2 Index: api/src/main/java/org/apache/logging/log4j/ThreadContext.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/ThreadContext.java (revision 1439358) +++ api/src/main/java/org/apache/logging/log4j/ThreadContext.java (working copy) @@ -17,6 +17,15 @@ package org.apache.logging.log4j; +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; + import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.spi.DefaultThreadContextMap; import org.apache.logging.log4j.spi.LoggerContextFactory; @@ -26,15 +35,6 @@ 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. *

@@ -192,8 +192,9 @@ * @return An immutable copy of the ThreadContext Map. */ public static Map getImmutableContext() { - final Map map = contextMap.get(); - return map == null ? new ImmutableMap() : new ImmutableMap(map); + // delegate to ThreadContextMap: the implementing class may have a + // very efficient way to create an immutable copy + return contextMap.getImmutableContext(); } /** @@ -217,7 +218,7 @@ */ public static ContextStack cloneStack() { final ContextStack stack = localStack.get(); - return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList()); + return stack == null ? new ThreadContextStack() : stack.copy(); } /** @@ -226,6 +227,8 @@ */ public static ContextStack getImmutableStack() { final ContextStack stack = localStack.get(); + + // here we use the constructor that makes a shallow copy of the stack return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList()); } @@ -237,6 +240,7 @@ if (stack.size() == 0 || !useStack) { return; } + // use the constructor that makes a deep copy of the selection localStack.set(new ThreadContextStack(stack)); } @@ -264,7 +268,10 @@ if (s == null || s.getDepth() == 0) { return ""; } - return s.pop(); + final ContextStack copy = s.copy(); // copy on write + final String result = copy.pop(); + localStack.set(copy); + return result; } /** @@ -296,13 +303,17 @@ if (!useStack) { return; } - ContextStack stack = localStack.get(); - if (stack == null) { - stack = new ThreadContextStack(); - localStack.set(stack); - } - stack.push(message); + final ContextStack stack = localStack.get(); + ContextStack copy = null; // copy on write + if (stack == null) { + copy = new ThreadContextStack(); + } else { + copy = stack.copy(); + } + copy.push(message); + localStack.set(copy); } + /** * Push new diagnostic context information for the current thread. *

@@ -318,12 +329,7 @@ if (!useStack) { return; } - ContextStack stack = localStack.get(); - if (stack == null) { - stack = new ThreadContextStack(); - localStack.set(stack); - } - stack.push(ParameterizedMessage.format(message, args)); + push(ParameterizedMessage.format(message, args)); } /** @@ -380,7 +386,9 @@ public static void trim(final int depth) { final ContextStack stack = localStack.get(); if (stack != null) { - stack.trim(depth); + final ContextStack copy = stack.copy(); // copy on write + copy.trim(depth); + localStack.set(copy); } } @@ -441,63 +449,85 @@ /** * The ContextStack implementation. */ - private static class ThreadContextStack extends ArrayList implements ContextStack { + private static class ThreadContextStack implements ContextStack { - private static final long serialVersionUID = 5050501L; + private static final long serialVersionUID = 5050501L; - public ThreadContextStack() { - super(); - } + private List list; - public ThreadContextStack(final Collection collection) { - super(collection); - } + public ThreadContextStack() { + list = new ArrayList(); + } - 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"); - } + /** + * This constructor uses the specified list as its internal data + * structure unchanged. It does not make a defensive copy. + */ + public ThreadContextStack(List aList) { + list = aList; // don't copy! + } - public String peek() { - final int index = size() - 1; - if (index >= 0) { - return get(index); - } - return null; - } + /** + * This constructor copies the elements of the specified collection into + * a new list. Changes to the specified collection will not affect this + * {@code ThreadContextStack}. + */ + public ThreadContextStack(Collection collection) { + list = new ArrayList(collection); + } - public void push(final String message) { - add(message); - } + public void clear() { + list.clear(); + } - public int getDepth() { - return size(); - } + public String pop() { + int index = list.size() - 1; + if (index >= 0) { + String result = list.get(index); + list.remove(index); + return result; + } + throw new NoSuchElementException("The ThreadContext stack is empty"); + } - public List asList() { - return this; - } + public String peek() { + int index = list.size() - 1; + if (index >= 0) { + return list.get(index); + } + return null; + } - public void trim(final int depth) { - if (depth < 0) { - throw new IllegalArgumentException("Maximum stack depth cannot be negative"); - } - while (size() > depth) { - remove(size() - 1); - } + public void push(String message) { + list.add(message); + } - } + public int getDepth() { + return list.size(); + } - public ContextStack copy() { - return new ThreadContextStack(this); - } + public List asList() { + return list; + } + + public void trim(int depth) { + if (depth < 0) { + throw new IllegalArgumentException( + "Maximum stack depth cannot be negative"); + } + while (list.size() > depth) { + list.remove(list.size() - 1); + } + } + + /** + * Returns a deep copy of this stack. + */ + public ContextStack copy() { + return new ThreadContextStack(new ArrayList(list)); + } } - + /** * An immutable ContextStack. */ @@ -508,21 +538,21 @@ public ImmutableStack() { } - public ImmutableStack(final Collection collection) { + public ImmutableStack(Collection collection) { super(collection); } - public ImmutableStack(final ThreadContextStack stack) { - super(stack); + public ImmutableStack(ThreadContextStack stack) { + super(stack.list); } @Override - public void push(final String message) { + public void push(String message) { throw new UnsupportedOperationException("Stack cannot be modified"); } @Override - public void trim(final int depth) { + public void trim(int depth) { throw new UnsupportedOperationException("Stack cannot be modified"); } }