LOG4J2-1447 replace context Map<String,String> with ContextData in LogEvent 
implementation classes; update tests


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/6caf1e83
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/6caf1e83
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/6caf1e83

Branch: 
refs/heads/LOG4J2-1010&LOG4J2-1447-injectable-contextdata&better-datastructure
Commit: 6caf1e8375bf6c23f701c9fe7f88e7b7699dc58e
Parents: e8902af
Author: rpopma <[email protected]>
Authored: Wed Jul 27 01:17:34 2016 +0900
Committer: rpopma <[email protected]>
Committed: Wed Jul 27 01:17:34 2016 +0900

----------------------------------------------------------------------
 .../log4j/core/async/RingBufferLogEvent.java    |  55 +++-------
 .../logging/log4j/core/impl/Log4jLogEvent.java  | 110 ++++++++++++-------
 .../log4j/core/impl/MutableLogEvent.java        |  28 +++--
 .../core/async/RingBufferLogEventTest.java      |  31 +++---
 .../log4j/core/impl/Log4jLogEventTest.java      |  66 +++++++++--
 .../log4j/core/impl/MutableLogEventTest.java    |  27 +++--
 6 files changed, 193 insertions(+), 124 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
index 463536c..20a57ce 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java
@@ -18,17 +18,17 @@ package org.apache.logging.log4j.core.async;
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
+import org.apache.logging.log4j.core.ContextData;
 import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.impl.ContextDataFactory;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.impl.MutableContextData;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
-import org.apache.logging.log4j.core.lookup.StrSubstitutor;
 import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ParameterizedMessage;
@@ -82,7 +82,7 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
     private Object[] parameters;
     private transient Throwable thrown;
     private ThrowableProxy thrownProxy;
-    private Map<String, String> contextMap;
+    private final MutableContextData contextData = 
ContextDataFactory.getContextData();
     private Marker marker;
     private String fqcn;
     private StackTraceElement location;
@@ -92,8 +92,8 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
 
     public void setValues(final AsyncLogger anAsyncLogger, final String 
aLoggerName, final Marker aMarker,
             final String theFqcn, final Level aLevel, final Message msg, final 
Throwable aThrowable,
-            final Map<String, String> aMap, final ContextStack aContextStack, 
final long threadId,
-            final String threadName, final int threadPriority, final 
StackTraceElement aLocation, final long aCurrentTimeMillis, final long 
aNanoTime) {
+            final ContextStack aContextStack, final long threadId, final 
String threadName, final int threadPriority,
+            final StackTraceElement aLocation, final long aCurrentTimeMillis, 
final long aNanoTime) {
         this.threadPriority = threadPriority;
         this.threadId = threadId;
         this.currentTimeMillis = aCurrentTimeMillis;
@@ -104,11 +104,11 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         setMessage(msg);
         this.thrown = aThrowable;
         this.thrownProxy = null;
-        this.contextMap = aMap;
         this.marker = aMarker;
         this.fqcn = theFqcn;
         this.location = aLocation;
         this.contextStack = aContextStack;
+        // contextData is never replaced, only its contents are modified
         this.asyncLogger = anAsyncLogger;
     }
 
@@ -319,9 +319,16 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         return this.thrownProxy;
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
+    public ContextData getContextData() {
+        return contextData;
+    }
+
+    @SuppressWarnings("unchecked")
     @Override
     public Map<String, String> getContextMap() {
-        return contextMap;
+        return contextData.asMap();
     }
 
     @Override
@@ -360,34 +367,6 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
     }
 
     /**
-     * Merges the contents of the specified map into the contextMap, after 
replacing any variables in the property
-     * values with the StrSubstitutor-supplied actual values.
-     *
-     * @param properties configured properties
-     * @param strSubstitutor used to lookup values of variables in properties
-     */
-    public void mergePropertiesIntoContextMap(final Map<Property, Boolean> 
properties,
-            final StrSubstitutor strSubstitutor) {
-        if (properties == null) {
-            return; // nothing to do
-        }
-
-        final Map<String, String> map = contextMap == null ? new 
HashMap<String, String>()
-                : new HashMap<>(contextMap);
-
-        for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) 
{
-            final Property prop = entry.getKey();
-            if (map.containsKey(prop.getName())) {
-                continue; // contextMap overrides config properties
-            }
-            final String value = entry.getValue().booleanValue() ? 
strSubstitutor.replace(prop.getValue()) : prop
-                    .getValue();
-            map.put(prop.getName(), value);
-        }
-        contextMap = map;
-    }
-
-    /**
      * Release references held by ring buffer to allow objects to be 
garbage-collected.
      */
     public void clear() {
@@ -399,7 +378,7 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         this.message = null;
         this.thrown = null;
         this.thrownProxy = null;
-        this.contextMap = null;
+        this.contextData.clear();
         this.contextStack = null;
         this.location = null;
 
@@ -440,7 +419,7 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
      * @param builder the builder whose fields to populate
      */
     public void initializeBuilder(final Log4jLogEvent.Builder builder) {
-        builder.setContextMap(contextMap) //
+        builder.setContextData(contextData) //
                 .setContextStack(contextStack) //
                 .setEndOfBatch(endOfBatch) //
                 .setIncludeLocation(includeLocation) //

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
index 7efc8dd..e283bea 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java
@@ -19,8 +19,6 @@ package org.apache.logging.log4j.core.impl;
 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;
 import java.util.Objects;
@@ -28,6 +26,7 @@ import java.util.Objects;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.ContextData;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.async.RingBufferLogEvent;
 import org.apache.logging.log4j.core.config.LoggerConfig;
@@ -60,7 +59,7 @@ public class Log4jLogEvent implements LogEvent {
     private final long timeMillis;
     private final transient Throwable thrown;
     private ThrowableProxy thrownProxy;
-    private final Map<String, String> contextMap;
+    private final MutableContextData contextData;
     private final ThreadContext.ContextStack contextStack;
     private long threadId;
     private String threadName;
@@ -82,7 +81,7 @@ public class Log4jLogEvent implements LogEvent {
         private Throwable thrown;
         private long timeMillis = CLOCK.currentTimeMillis();
         private ThrowableProxy thrownProxy;
-        private Map<String, String> contextMap = 
ThreadContext.getImmutableContext();
+        private MutableContextData contextData = 
createContextData((List<Property>) null);
         private ThreadContext.ContextStack contextStack = 
ThreadContext.getImmutableStack();
         private long threadId;
         private String threadName;
@@ -112,7 +111,6 @@ public class Log4jLogEvent implements LogEvent {
             this.message = other.getMessage();
             this.timeMillis = other.getTimeMillis();
             this.thrown = other.getThrown();
-            this.contextMap = other.getContextMap();
             this.contextStack = other.getContextStack();
             this.includeLocation = other.isIncludeLocation();
             this.endOfBatch = other.isEndOfBatch();
@@ -121,12 +119,19 @@ public class Log4jLogEvent implements LogEvent {
             // Avoid unnecessarily initializing thrownProxy, threadName and 
source if possible
             if (other instanceof Log4jLogEvent) {
                 final Log4jLogEvent evt = (Log4jLogEvent) other;
+                this.contextData = evt.contextData;
                 this.thrownProxy = evt.thrownProxy;
                 this.source = evt.source;
                 this.threadId = evt.threadId;
                 this.threadName = evt.threadName;
                 this.threadPriority = evt.threadPriority;
             } else {
+                if (other.getContextData() instanceof MutableContextData) {
+                    this.contextData = (MutableContextData) 
other.getContextData();
+                } else {
+                    this.contextData.clear();
+                    this.contextData.putAll(other.getContextData());
+                }
                 this.thrownProxy = other.getThrownProxy();
                 this.source = other.getSource();
                 this.threadId = other.getThreadId();
@@ -175,8 +180,20 @@ public class Log4jLogEvent implements LogEvent {
             return this;
         }
 
+        @SuppressWarnings("unchecked")
+        @Deprecated
         public Builder setContextMap(final Map<String, String> contextMap) {
-            this.contextMap = contextMap;
+            contextData = ContextDataFactory.getContextData(); // replace with 
new instance
+            if (contextMap != null) {
+                for (Map.Entry<String, String> entry : contextMap.entrySet()) {
+                    contextData.putValue(entry.getKey(), entry.getValue());
+                }
+            }
+            return this;
+        }
+
+        public Builder setContextData(final MutableContextData contextData) {
+            this.contextData = contextData;
             return this;
         }
 
@@ -229,7 +246,8 @@ public class Log4jLogEvent implements LogEvent {
         @Override
         public Log4jLogEvent build() {
             final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, 
loggerFqcn, level, message, thrown,
-                    thrownProxy, contextMap, contextStack, threadId, 
threadName, threadPriority, source, timeMillis, nanoTime);
+                    thrownProxy, contextData, contextStack, threadId, 
threadName, threadPriority, source, timeMillis,
+                    nanoTime);
             result.setIncludeLocation(includeLocation);
             result.setEndOfBatch(endOfBatch);
             return result;
@@ -282,14 +300,13 @@ public class Log4jLogEvent implements LogEvent {
     * @param loggerFQCN The fully qualified class name of the caller.
     * @param level The logging Level.
     * @param message The Message.
-    * @param properties properties to add to the event.
+    * @param properties the properties to be merged with ThreadContext 
key-value pairs into the event's ContextData.
     * @param t A Throwable or null.
     */
    // This constructor is called from LogEventFactories.
    public Log4jLogEvent(final String loggerName, final Marker marker, final 
String loggerFQCN, final Level level,
                         final Message message, final List<Property> 
properties, final Throwable t) {
-       this(loggerName, marker, loggerFQCN, level, message, t, null,
-           createMap(properties),
+       this(loggerName, marker, loggerFQCN, level, message, t, null, 
createContextData(properties),
            ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), 
// mutable copy
            0, // thread name
            null, // stack trace element
@@ -316,11 +333,11 @@ public class Log4jLogEvent implements LogEvent {
     * @deprecated use {@link Log4jLogEvent.Builder} instead. This constructor 
will be removed in an upcoming release.
     */
    @Deprecated
-public Log4jLogEvent(final String loggerName, final Marker marker, final 
String loggerFQCN, final Level level,
+   public Log4jLogEvent(final String loggerName, final Marker marker, final 
String loggerFQCN, final Level level,
                         final Message message, final Throwable t, final 
Map<String, String> mdc,
                         final ThreadContext.ContextStack ndc, final String 
threadName,
                         final StackTraceElement location, final long 
timestampMillis) {
-       this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, 
0,
+       this(loggerName, marker, loggerFQCN, level, message, t, null, 
createContextData(mdc), ndc, 0,
                threadName, 0, location, timestampMillis, nanoClock.nanoTime());
    }
 
@@ -349,7 +366,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
                                             final String threadName, final 
StackTraceElement location,
                                             final long timestamp) {
         final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, 
loggerFQCN, level, message, thrown,
-                thrownProxy, mdc, ndc, 0, threadName, 0, location, timestamp, 
nanoClock.nanoTime());
+                thrownProxy, createContextData(mdc), ndc, 0, threadName, 0, 
location, timestamp, nanoClock.nanoTime());
         return result;
     }
 
@@ -362,7 +379,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
      * @param message The Message.
      * @param thrown A Throwable or null.
      * @param thrownProxy A ThrowableProxy or null.
-     * @param contextMap The mapped diagnostic context.
+     * @param contextData The key-value pairs from the context.
      * @param contextStack the nested diagnostic context.
      * @param threadId the thread ID
      * @param threadName The name of the thread.
@@ -374,9 +391,9 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
      */
     private Log4jLogEvent(final String loggerName, final Marker marker, final 
String loggerFQCN, final Level level,
             final Message message, final Throwable thrown, final 
ThrowableProxy thrownProxy,
-            final Map<String, String> contextMap, final 
ThreadContext.ContextStack contextStack, final long threadId,
-            final String threadName, final int threadPriority, final 
StackTraceElement source, final long timestampMillis,
-            final long nanoTime) {
+            final MutableContextData contextData, final 
ThreadContext.ContextStack contextStack, final long threadId,
+            final String threadName, final int threadPriority, final 
StackTraceElement source,
+            final long timestampMillis, final long nanoTime) {
         this.loggerName = loggerName;
         this.marker = marker;
         this.loggerFqcn = loggerFQCN;
@@ -384,7 +401,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
         this.message = message;
         this.thrown = thrown;
         this.thrownProxy = thrownProxy;
-        this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : 
contextMap;
+        this.contextData = contextData == null ? 
ContextDataFactory.getContextData() : contextData;
         this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : 
contextStack;
         this.timeMillis = message instanceof TimestampMessage
                 ? ((TimestampMessage) message).getTimestamp()
@@ -399,19 +416,21 @@ public Log4jLogEvent(final String loggerName, final 
Marker marker, final String
         this.nanoTime = nanoTime;
     }
 
-    static Map<String, String> createMap(final List<Property> properties) {
-        final Map<String, String> contextMap = 
ThreadContext.getImmutableContext();
-        if (properties == null || properties.isEmpty()) {
-            return contextMap; // may be ThreadContext.EMPTY_MAP but not null
-        }
-        final Map<String, String> map = new HashMap<>(contextMap);
-
-        for (final Property prop : properties) {
-            if (!map.containsKey(prop.getName())) {
-                map.put(prop.getName(), prop.getValue());
+    private static MutableContextData createContextData(final Map<String, 
String> contextMap) {
+        final MutableContextData result = ContextDataFactory.getContextData();
+        if (contextMap != null) {
+            for (Map.Entry<String, String> entry : contextMap.entrySet()) {
+                result.putValue(entry.getKey(), entry.getValue());
             }
         }
-        return Collections.unmodifiableMap(map);
+        return result;
+    }
+
+    private static MutableContextData createContextData(final List<Property> 
properties) {
+        final MutableContextData result = ContextDataFactory.getContextData();
+        final ContextDataInjector injector = 
ContextDataInjectorFactory.getInjector();
+        injector.injectContextData(properties, result);
+        return result;
     }
 
     /**
@@ -552,12 +571,21 @@ public Log4jLogEvent(final String loggerName, final 
Marker marker, final String
     }
 
     /**
+     * Returns the {@code ContextData} containing context data key-value pairs.
+     * @return the {@code ContextData} containing context data key-value pairs
+     * @since 2.7
+     */
+    @Override
+    public ContextData getContextData() {
+        return contextData;
+    }
+    /**
      * Returns the immutable copy of the ThreadContext Map.
      * @return The context Map.
      */
     @Override
     public Map<String, String> getContextMap() {
-        return contextMap;
+        return contextData.asMap();
     }
 
     /**
@@ -678,7 +706,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
             final LogEventProxy proxy = (LogEventProxy) event;
             final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, 
proxy.marker,
                     proxy.loggerFQCN, proxy.level, proxy.message,
-                    proxy.thrown, proxy.thrownProxy, proxy.contextMap, 
proxy.contextStack, proxy.threadId,
+                    proxy.thrown, proxy.thrownProxy, proxy.contextData, 
proxy.contextStack, proxy.threadId,
                     proxy.threadName, proxy.threadPriority, proxy.source, 
proxy.timeMillis, proxy.nanoTime);
             result.setEndOfBatch(proxy.isEndOfBatch);
             result.setIncludeLocation(proxy.isLocationRequired);
@@ -746,7 +774,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
         if (marker != null ? !marker.equals(that.marker) : that.marker != 
null) {
             return false;
         }
-        if (contextMap != null ? !contextMap.equals(that.contextMap) : 
that.contextMap != null) {
+        if (contextData != null ? !contextData.equals(that.contextData) : 
that.contextData != null) {
             return false;
         }
         if (!message.equals(that.message)) {
@@ -789,7 +817,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
         result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
         result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
         result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 
0);
-        result = 31 * result + (contextMap != null ? contextMap.hashCode() : 
0);
+        result = 31 * result + (contextData != null ? contextData.hashCode() : 
0);
         result = 31 * result + (contextStack != null ? contextStack.hashCode() 
: 0);
         result = 31 * result + (int) (threadId ^ (threadId >>> 32));
         result = 31 * result + (threadName != null ? threadName.hashCode() : 
0);
@@ -815,7 +843,8 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
         private final long timeMillis;
         private final transient Throwable thrown;
         private final ThrowableProxy thrownProxy;
-        private final Map<String, String> contextMap;
+        /** @since 2.7 */
+        private final MutableContextData contextData;
         private final ThreadContext.ContextStack contextStack;
         /** @since 2.6 */
         private final long threadId;
@@ -839,7 +868,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
             this.timeMillis = event.timeMillis;
             this.thrown = event.thrown;
             this.thrownProxy = event.thrownProxy;
-            this.contextMap = event.contextMap;
+            this.contextData = event.contextData;
             this.contextStack = event.contextStack;
             this.source = includeLocation ? event.getSource() : null;
             this.threadId = event.getThreadId();
@@ -863,7 +892,7 @@ public Log4jLogEvent(final String loggerName, final Marker 
marker, final String
             this.timeMillis = event.getTimeMillis();
             this.thrown = event.getThrown();
             this.thrownProxy = event.getThrownProxy();
-            this.contextMap = event.getContextMap();
+            this.contextData = memento(event.getContextData());
             this.contextStack = event.getContextStack();
             this.source = includeLocation ? event.getSource() : null;
             this.threadId = event.getThreadId();
@@ -874,17 +903,22 @@ public Log4jLogEvent(final String loggerName, final 
Marker marker, final String
             this.nanoTime = event.getNanoTime();
         }
 
-        private Message memento(final ReusableMessage message) {
+        private static Message memento(final ReusableMessage message) {
             return message.memento();
         }
 
+        private static MutableContextData memento(final ContextData data) {
+            return new ArrayContextData(data); // TODO necessary to construct 
new instance?
+        }
+
         /**
          * Returns a Log4jLogEvent using the data in the proxy.
          * @return Log4jLogEvent.
          */
         protected Object readResolve() {
             final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, 
loggerFQCN, level, message, thrown,
-                    thrownProxy, contextMap, contextStack, threadId, 
threadName, threadPriority, source, timeMillis, nanoTime);
+                    thrownProxy, contextData, contextStack, threadId, 
threadName, threadPriority, source, timeMillis,
+                    nanoTime);
             result.setEndOfBatch(isEndOfBatch);
             result.setIncludeLocation(isLocationRequired);
             return result;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
index fc44acc..a5a69d4 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.ContextData;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.util.Constants;
 import org.apache.logging.log4j.message.Message;
@@ -54,7 +55,7 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
     private Object[] parameters;
     private Throwable thrown;
     private ThrowableProxy thrownProxy;
-    private Map<String, String> contextMap;
+    private MutableContextData contextData = 
ContextDataFactory.getContextData();
     private Marker marker;
     private String loggerFqcn;
     private StackTraceElement source;
@@ -87,7 +88,11 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
         this.timeMillis = event.getTimeMillis();
         this.thrown = event.getThrown();
         this.thrownProxy = event.getThrownProxy();
-        this.contextMap = event.getContextMap();
+        if (event.getContextData() instanceof MutableContextData) {
+            this.contextData = (MutableContextData) event.getContextData();
+        } else {
+            this.contextData.putAll(event.getContextData());
+        }
         this.contextStack = event.getContextStack();
         this.source = event.isIncludeLocation() ? event.getSource() : null;
         this.threadId = event.getThreadId();
@@ -101,6 +106,7 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
 
     /**
      * Clears all references this event has to other objects.
+     *
      */
     public void clear() {
         loggerFqcn = null;
@@ -111,7 +117,9 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
         thrown = null;
         thrownProxy = null;
         source = null;
-        contextMap = null;
+        if (contextData != null) {
+            contextData.clear();
+        }
         contextStack = null;
 
         // ThreadName should not be cleared: this field is set in the 
ReusableLogEventFactory
@@ -333,13 +341,19 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
         return source;
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
+    public ContextData getContextData() {
+        return contextData;
+    }
+
     @Override
     public Map<String, String> getContextMap() {
-        return contextMap;
+        return contextData.asMap();
     }
 
-    public void setContextMap(final Map<String, String> contextMap) {
-        this.contextMap = contextMap;
+    public void setContextData(final MutableContextData mutableContextData) {
+        this.contextData = mutableContextData;
     }
 
     @Override
@@ -434,7 +448,7 @@ public class MutableLogEvent implements LogEvent, 
ReusableMessage {
      * @param builder the builder whose fields to populate
      */
     public void initializeBuilder(final Log4jLogEvent.Builder builder) {
-        builder.setContextMap(contextMap) //
+        builder.setContextData(contextData) //
                 .setContextStack(contextStack) //
                 .setEndOfBatch(endOfBatch) //
                 .setIncludeLocation(includeLocation) //

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
index ec3a874..12235d1 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/RingBufferLogEventTest.java
@@ -23,14 +23,13 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext.ContextStack;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.MutableContextData;
 import org.apache.logging.log4j.core.impl.ThrowableProxy;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.SimpleMessage;
@@ -53,13 +52,12 @@ public class RingBufferLogEventTest {
         final Level level = null;
         final Message data = null;
         final Throwable t = null;
-        final Map<String, String> map = null;
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
         final long currentTimeMillis = 0;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertEquals(Level.OFF, evt.getLevel());
     }
@@ -73,13 +71,12 @@ public class RingBufferLogEventTest {
         final Level level = null;
         final Message data = null;
         final Throwable t = null;
-        final Map<String, String> map = null;
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
         final long currentTimeMillis = 0;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertNotNull(evt.getMessage());
     }
@@ -93,13 +90,12 @@ public class RingBufferLogEventTest {
         final Level level = null;
         final Message data = null;
         final Throwable t = null;
-        final Map<String, String> map = null;
         final ContextStack contextStack = null;
         final String threadName = null;
         final StackTraceElement location = null;
         final long currentTimeMillis = 123;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertEquals(123, evt.getTimeMillis());
     }
@@ -113,19 +109,19 @@ public class RingBufferLogEventTest {
         final Level level = Level.TRACE;
         final Message data = new SimpleMessage("message");
         final Throwable t = new InternalError("not a real error");
-        final Map<String, String> map = null;
         final ContextStack contextStack = null;
         final String threadName = "main";
         final StackTraceElement location = null;
         final long currentTimeMillis = 12345;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
-        
+        ((MutableContextData) evt.getContextData()).putValue("key", "value");
+
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
         final ObjectOutputStream out = new ObjectOutputStream(baos);
         out.writeObject(evt);
-        
+
         final ObjectInputStream in = new ObjectInputStream(new 
ByteArrayInputStream(baos.toByteArray()));
         final RingBufferLogEvent other = (RingBufferLogEvent) in.readObject();
         assertEquals(loggerName, other.getLoggerName());
@@ -135,13 +131,13 @@ public class RingBufferLogEventTest {
         assertEquals(data, other.getMessage());
         assertNull("null after serialization", other.getThrown());
         assertEquals(new ThrowableProxy(t), other.getThrownProxy());
-        assertEquals(map, other.getContextMap());
+        assertEquals(evt.getContextData(), other.getContextData());
         assertEquals(contextStack, other.getContextStack());
         assertEquals(threadName, other.getThreadName());
         assertEquals(location, other.getSource());
         assertEquals(currentTimeMillis, other.getTimeMillis());
     }
-    
+
     @Test
     public void testCreateMementoReturnsCopy() {
         final RingBufferLogEvent evt = new RingBufferLogEvent();
@@ -151,16 +147,15 @@ public class RingBufferLogEventTest {
         final Level level = Level.TRACE;
         final Message data = new SimpleMessage("message");
         final Throwable t = new InternalError("not a real error");
-        final Map<String, String> map = new HashMap<>();
-        map.put("key", "value");
         final ContextStack contextStack = new 
MutableThreadContextStack(Arrays.asList("a", "b"));
         final String threadName = "main";
         final StackTraceElement location = null;
         final long currentTimeMillis = 12345;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t, map,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
-        
+        ((MutableContextData) evt.getContextData()).putValue("key", "value");
+
         final LogEvent actual = evt.createMemento();
         assertEquals(evt.getLoggerName(), actual.getLoggerName());
         assertEquals(evt.getMarker(), actual.getMarker());

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
index d57a058..964a017 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
@@ -25,7 +25,6 @@ import java.lang.reflect.Field;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-
 import javax.xml.bind.DatatypeConverter;
 
 import org.apache.logging.log4j.Level;
@@ -264,7 +263,56 @@ public class Log4jLogEventTest {
                 .setTimeMillis(987654321L)
                 .build();
 
-        assertSame(contextMap, event.getContextMap());
+        assertEquals(contextMap, event.getContextMap());
+        assertSame(contextStack, event.getContextStack());
+        assertEquals(true, event.isEndOfBatch());
+        assertEquals(true, event.isIncludeLocation());
+        assertSame(Level.FATAL, event.getLevel());
+        assertSame(fqcn, event.getLoggerFqcn());
+        assertSame(name, event.getLoggerName());
+        assertSame(marker, event.getMarker());
+        assertSame(message, event.getMessage());
+        assertEquals(1234567890L, event.getNanoTime());
+        assertSame(stackTraceElement, event.getSource());
+        assertSame(threadName, event.getThreadName());
+        assertSame(exception, event.getThrown());
+        assertEquals(987654321L, event.getTimeMillis());
+
+        final LogEvent event2 = new Log4jLogEvent.Builder(event).build();
+        assertEquals("copy constructor builder", event2, event);
+        assertEquals("same hashCode", event2.hashCode(), event.hashCode());
+    }
+
+    @Test
+    public void testBuilderCorrectlyCopiesAllEventAttributesInclContextData() {
+        final MutableContextData contextData = new ArrayContextData();
+        contextData.putValue("A", "B");
+        final ContextStack contextStack = ThreadContext.getImmutableStack();
+        final Exception exception = new Exception("test");
+        final Marker marker = MarkerManager.getMarker("EVENTTEST");
+        final Message message = new SimpleMessage("foo");
+        final StackTraceElement stackTraceElement = new StackTraceElement("A", 
"B", "file", 123);
+        final String fqcn = "qualified";
+        final String name = "Ceci n'est pas une pipe";
+        final String threadName = "threadName";
+        final Log4jLogEvent event = Log4jLogEvent.newBuilder() //
+                .setContextData(contextData) //
+                .setContextStack(contextStack) //
+                .setEndOfBatch(true) //
+                .setIncludeLocation(true) //
+                .setLevel(Level.FATAL) //
+                .setLoggerFqcn(fqcn) //
+                .setLoggerName(name) //
+                .setMarker(marker) //
+                .setMessage(message) //
+                .setNanoTime(1234567890L) //
+                .setSource(stackTraceElement) //
+                .setThreadName(threadName) //
+                .setThrown(exception) //
+                .setTimeMillis(987654321L)
+                .build();
+
+        assertSame(contextData, event.getContextData());
         assertSame(contextStack, event.getContextStack());
         assertEquals(true, event.isEndOfBatch());
         assertEquals(true, event.isIncludeLocation());
@@ -286,8 +334,8 @@ public class Log4jLogEventTest {
 
     @Test
     public void testBuilderCorrectlyCopiesMutableLogEvent() throws Exception {
-        final Map<String, String> contextMap = new HashMap<>();
-        contextMap.put("A", "B");
+        final MutableContextData contextData = new ArrayContextData();
+        contextData.putValue("A", "B");
         final ContextStack contextStack = ThreadContext.getImmutableStack();
         final Exception exception = new Exception("test");
         final Marker marker = MarkerManager.getMarker("EVENTTEST");
@@ -297,7 +345,7 @@ public class Log4jLogEventTest {
         final String name = "Ceci n'est pas une pipe";
         final String threadName = "threadName";
         final MutableLogEvent event = new MutableLogEvent();
-        event.setContextMap(contextMap);
+        event.setContextData(contextData);
         event.setContextStack(contextStack);
         event.setEndOfBatch(true);
         event.setIncludeLocation(true);
@@ -312,7 +360,7 @@ public class Log4jLogEventTest {
         event.setThrown(exception);
         event.setTimeMillis(987654321L);
 
-        assertSame(contextMap, event.getContextMap());
+        assertSame(contextData, event.getContextData());
         assertSame(contextStack, event.getContextStack());
         assertEquals(true, event.isEndOfBatch());
         assertEquals(true, event.isIncludeLocation());
@@ -328,7 +376,7 @@ public class Log4jLogEventTest {
         assertEquals(987654321L, event.getTimeMillis());
 
         final LogEvent e2 = new Log4jLogEvent.Builder(event).build();
-        assertSame(contextMap, e2.getContextMap());
+        assertEquals(contextData, e2.getContextData());
         assertSame(contextStack, e2.getContextStack());
         assertEquals(true, e2.isEndOfBatch());
         assertEquals(true, e2.isIncludeLocation());
@@ -381,7 +429,7 @@ public class Log4jLogEventTest {
                 .setTimeMillis(987654321L)
                 .build();
 
-        assertSame(contextMap, event.getContextMap());
+        assertEquals(contextMap, event.getContextMap());
         assertSame(contextStack, event.getContextStack());
         assertEquals(true, event.isEndOfBatch());
         assertEquals(true, event.isIncludeLocation());
@@ -400,7 +448,7 @@ public class Log4jLogEventTest {
         assertEquals("copy constructor builder", event2, event);
         assertEquals("same hashCode", event2.hashCode(), event.hashCode());
 
-        assertSame(contextMap, event2.getContextMap());
+        assertEquals(contextMap, event2.getContextMap());
         assertSame(contextStack, event2.getContextStack());
         assertEquals(true, event2.isEndOfBatch());
         assertEquals(true, event2.isIncludeLocation());

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/6caf1e83/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
index 928e801..e531adc 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
@@ -23,8 +23,6 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.MarkerManager;
@@ -40,13 +38,13 @@ import static org.junit.Assert.*;
  * Tests the MutableLogEvent class.
  */
 public class MutableLogEventTest {
-    private static final Map<String, String> CONTEXTMAP = createContextMap();
+    private static final MutableContextData CONTEXT_DATA = createContextData();
     private static final ThreadContext.ContextStack STACK = new 
MutableThreadContextStack(Arrays.asList("abc", "xyz"));
 
-    private static Map<String,String> createContextMap() {
-        final Map<String,String> result = new HashMap<>();
-        result.put("a", "1");
-        result.put("b", "2");
+    private static MutableContextData createContextData() {
+        final MutableContextData result = new ArrayContextData();
+        result.putValue("a", "1");
+        result.putValue("b", "2");
         return result;
     }
 
@@ -54,7 +52,7 @@ public class MutableLogEventTest {
     public void testInitFromCopiesAllFields() {
 //        private ThrowableProxy thrownProxy;
         final Log4jLogEvent source = Log4jLogEvent.newBuilder() //
-                .setContextMap(CONTEXTMAP) //
+                .setContextData(CONTEXT_DATA) //
                 .setContextStack(STACK) //
                 .setEndOfBatch(true) //
                 .setIncludeLocation(true) //
@@ -71,7 +69,7 @@ public class MutableLogEventTest {
                 .build();
         final MutableLogEvent mutable = new MutableLogEvent();
         mutable.initFrom(source);
-        assertEquals("contextMap", CONTEXTMAP, mutable.getContextMap());
+        assertEquals("contextMap", CONTEXT_DATA, mutable.getContextData());
         assertEquals("stack", STACK, mutable.getContextStack());
         assertEquals("endOfBatch", true, mutable.isEndOfBatch());
         assertEquals("IncludeLocation()", true, mutable.isIncludeLocation());
@@ -93,7 +91,7 @@ public class MutableLogEventTest {
     @Test
     public void testClear() {
         final MutableLogEvent mutable = new MutableLogEvent();
-        assertNull("context map", mutable.getContextMap());
+        assertEquals("context data", 0, mutable.getContextData().size());
         assertNull("context stack", mutable.getContextStack());
         assertFalse("end of batch", mutable.isEndOfBatch());
         assertFalse("incl loc", mutable.isIncludeLocation());
@@ -112,7 +110,7 @@ public class MutableLogEventTest {
         assertNull("source", mutable.getSource());
         assertNull("thrownProxy", mutable.getThrownProxy());
 
-        mutable.setContextMap(CONTEXTMAP);
+        mutable.setContextData(CONTEXT_DATA);
         mutable.setContextStack(STACK);
         mutable.setEndOfBatch(true);
         mutable.setIncludeLocation(true);
@@ -148,7 +146,7 @@ public class MutableLogEventTest {
         assertNotNull("thrownProxy", mutable.getThrownProxy());
 
         mutable.clear();
-        assertNull("context map", mutable.getContextMap());
+        assertEquals("context map", 0, mutable.getContextData().size());
         assertNull("context stack", mutable.getContextStack());
         assertSame("level", Level.OFF, mutable.getLevel());
         assertNull("fqcn", mutable.getLoggerFqcn());
@@ -175,7 +173,7 @@ public class MutableLogEventTest {
     @Test
     public void testJavaIoSerializable() throws Exception {
         final MutableLogEvent evt = new MutableLogEvent();
-        evt.setContextMap(CONTEXTMAP);
+        evt.setContextData(CONTEXT_DATA);
         evt.setContextStack(STACK);
         evt.setEndOfBatch(true);
         evt.setIncludeLocation(true);
@@ -199,6 +197,7 @@ public class MutableLogEventTest {
         assertEquals(evt.getLevel(), evt2.getLevel());
         assertEquals(evt.getLoggerName(), evt2.getLoggerName());
         assertEquals(evt.getMarker(), evt2.getMarker());
+        assertEquals(evt.getContextData(), evt2.getContextData());
         assertEquals(evt.getContextMap(), evt2.getContextMap());
         assertEquals(evt.getContextStack(), evt2.getContextStack());
         assertEquals(evt.getMessage(), evt2.getMessage());
@@ -218,7 +217,7 @@ public class MutableLogEventTest {
     public void testJavaIoSerializableWithThrown() throws Exception {
         final Error thrown = new InternalError("test error");
         final MutableLogEvent evt = new MutableLogEvent();
-        evt.setContextMap(CONTEXTMAP);
+        evt.setContextData(CONTEXT_DATA);
         evt.setContextStack(STACK);
         evt.setEndOfBatch(true);
         evt.setIncludeLocation(true);

Reply via email to