Repository: logging-log4j2
Updated Branches:
  
refs/heads/LOG4J2-1010&LOG4J2-1447-injectable-contextdata&better-datastructure 
a23b40310 -> a994f15fb


LOG4J2-1010, LOG4J2-1447, LOG4J2-1349 ContextDataInjector::injectContextData 
now returns a MutableContextData object to allow returning copy-on-write 
ThreadContext internal data structures. This allows us to avoid unnecessary 
data copies in non-garbage free configurations.


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

Branch: 
refs/heads/LOG4J2-1010&LOG4J2-1447-injectable-contextdata&better-datastructure
Commit: a994f15fbdfb509e8f4a2ee816693cab6c8a9ce9
Parents: a23b403
Author: rpopma <[email protected]>
Authored: Sat Aug 20 21:23:44 2016 +0900
Committer: rpopma <[email protected]>
Committed: Sat Aug 20 21:23:44 2016 +0900

----------------------------------------------------------------------
 .../logging/log4j/core/async/AsyncLogger.java   |   6 +
 .../log4j/core/async/RingBufferLogEvent.java    |   9 +-
 .../async/RingBufferLogEventTranslator.java     |  11 +-
 .../log4j/core/impl/ContextDataInjector.java    |  21 ++-
 .../core/impl/ContextDataInjectorFactory.java   |  34 ++++-
 .../logging/log4j/core/impl/Log4jLogEvent.java  |   8 +-
 .../core/impl/ReusableLogEventFactory.java      |   2 +-
 .../core/impl/ThreadContextDataInjector.java    | 144 +++++++++++++++----
 .../core/async/RingBufferLogEventTest.java      |  10 +-
 9 files changed, 188 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index 0098305..895def7 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -27,6 +27,8 @@ import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.Property;
 import org.apache.logging.log4j.core.config.ReliabilityStrategy;
+import org.apache.logging.log4j.core.impl.ContextDataInjector;
+import org.apache.logging.log4j.core.impl.ContextDataInjectorFactory;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.impl.MutableContextData;
 import org.apache.logging.log4j.core.util.Clock;
@@ -68,6 +70,7 @@ public class AsyncLogger extends Logger implements 
EventTranslatorVararg<RingBuf
 
     private static final StatusLogger LOGGER = StatusLogger.getLogger();
     private static final Clock CLOCK = ClockFactory.getClock(); // not 
reconfigurable
+    private static final ContextDataInjector CONTEXT_DATA_INJECTOR = 
ContextDataInjectorFactory.createInjector();
 
     private static final ThreadNameCachingStrategy 
THREAD_NAME_CACHING_STRATEGY = ThreadNameCachingStrategy.create();
 
@@ -269,6 +272,9 @@ public class AsyncLogger extends Logger implements 
EventTranslatorVararg<RingBuf
         final Thread currentThread = Thread.currentThread();
         final String threadName = THREAD_NAME_CACHING_STRATEGY.getThreadName();
         event.setValues(asyncLogger, asyncLogger.getName(), marker, fqcn, 
level, message, thrown,
+                // config properties are taken care of in the EventHandler 
thread
+                // in the AsyncLogger#actualAsyncLog method
+                CONTEXT_DATA_INJECTOR.injectContextData(null, 
(MutableContextData) event.getContextData()),
                 contextStack, currentThread.getId(), threadName, 
currentThread.getPriority(), location,
                 CLOCK.currentTimeMillis(), nanoClock.nanoTime());
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/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 1ebb114..8c873ff 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
@@ -82,7 +82,7 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
     private Object[] parameters;
     private transient Throwable thrown;
     private ThrowableProxy thrownProxy;
-    private final MutableContextData contextData = 
ContextDataFactory.createContextData();
+    private MutableContextData contextData = 
ContextDataFactory.createContextData();
     private Marker marker;
     private String fqcn;
     private StackTraceElement location;
@@ -92,8 +92,9 @@ 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 ContextStack aContextStack, final long threadId, final 
String threadName, final int threadPriority,
-            final StackTraceElement aLocation, final long aCurrentTimeMillis, 
final long aNanoTime) {
+            final MutableContextData mutableContextData, 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;
@@ -107,8 +108,8 @@ public class RingBufferLogEvent implements LogEvent, 
ReusableMessage, CharSequen
         this.marker = aMarker;
         this.fqcn = theFqcn;
         this.location = aLocation;
+        this.contextData = mutableContextData;
         this.contextStack = aContextStack;
-        // contextData is never replaced, only its contents are modified
         this.asyncLogger = anAsyncLogger;
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
index 9ae27c7..d95a5b7 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
@@ -54,13 +54,14 @@ public class RingBufferLogEventTranslator implements
     // @Override
     @Override
     public void translateTo(final RingBufferLogEvent event, final long 
sequence) {
-        event.setValues(asyncLogger, loggerName, marker, fqcn, level, message, 
thrown, contextStack,
+
+        event.setValues(asyncLogger, loggerName, marker, fqcn, level, message, 
thrown,
+                // config properties are taken care of in the EventHandler 
thread
+                // in the AsyncLogger#actualAsyncLog method
+                injector.injectContextData(null, (MutableContextData) 
event.getContextData()), contextStack,
                 threadId, threadName, threadPriority, location, 
currentTimeMillis, nanoTime);
 
-        // config properties are taken care of in the EventHandler thread
-        // in the AsyncLogger#actualAsyncLog method
-        injector.injectContextData(null, (MutableContextData) 
event.getContextData());
-        clear();
+        clear(); // clear the translator
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
index 91e5617..5b74968 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjector.java
@@ -23,17 +23,30 @@ import org.apache.logging.log4j.core.config.Property;
 /**
  * Responsible for initializing the ContextData of LogEvents. Context data is 
data that is set by the application to be
  * included in all subsequent log events.
+ * <p>
+ * The source of the context data is implementation-specific. The default 
source for context data is the ThreadContext.
+ * </p><p>
+ * In some asynchronous models, work may be delegated to several threads, 
while conceptually this work shares the same
+ * context. In such models, storing context data in {@code ThreadLocal} 
variables is not convenient or desirable.
+ * By specifying a custom {@code ContextDataInjectorFactory}, users can 
initialize log events with context data from
+ * any arbitrary context.
+ * </p>
  *
+ * @see ContextDataInjectorFactory
  * @see org.apache.logging.log4j.core.ContextData
  * @see org.apache.logging.log4j.ThreadContext
+ * @see ThreadContextDataInjector
  * @since 2.7
  */
 public interface ContextDataInjector {
     /**
-     * Updates the specified ContextData with context key-value pairs.
+     * Returns a {@code MutableContextData} object initialized with the 
specified properties and the appropriate
+     * context data. The returned value may be the specified parameter or a 
different object.
      *
-     * @param properties Properties from the configuration to be added to the 
ContextData
-     * @param contextData the ContextData to initialize
+     * @param properties Properties from the log4j configuration to be added 
to the resulting ContextData
+     * @param reusable a {@code MutableContextData} instance that may be 
reused to avoid creating temporary objects
+     * @return a {@code MutableContextData} instance initialized with the 
specified properties and the appropriate
+     *          context data. The returned value may be the specified 
parameter or a different object.
      */
-    void injectContextData(final List<Property> properties, final 
MutableContextData contextData);
+    MutableContextData injectContextData(final List<Property> properties, 
final MutableContextData reusable);
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
index 0a255cb..ac5c38c 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataInjectorFactory.java
@@ -16,15 +16,22 @@
  */
 package org.apache.logging.log4j.core.impl;
 
+import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.spi.ThreadContextMap;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
- * Factory for ContextDataInjectors.
+ * Factory for ContextDataInjectors. Returns a new {@code ContextDataInjector} 
instance based on the value of system
+ * property {@code log4j2.ContextDataInjector}. Users may use this system 
property to specify the fully qualified class
+ * name of a class that implements the {@code ContextDataInjector} interface.
+ * If no value was specified this factory method returns one of the injectors 
defined in
+ * {@code ThreadContextDataInjector}.
  *
  * @see ContextDataInjector
+ * @see ThreadContextDataInjector
  * @see org.apache.logging.log4j.core.ContextData
  * @see LogEvent#getContextData()
  * @since 2.7
@@ -33,8 +40,9 @@ public class ContextDataInjectorFactory {
 
     /**
      * Returns a new {@code ContextDataInjector} instance based on the value 
of system property
-     * {@code log4j2.ContextDataInjector}. If not value was specified this 
method returns a new
-     * {@link ThreadContextDataInjector}.
+     * {@code log4j2.ContextDataInjector}. If no value was specified this 
factory method returns one of the
+     * {@code ContextDataInjector} classes defined in {@link 
ThreadContextDataInjector} which is most appropriate for
+     * the ThreadContext implementation.
      * <p>
      * Users may use this system property to specify the fully qualified class 
name of a class that implements the
      * {@code ContextDataInjector} interface.
@@ -44,17 +52,29 @@ public class ContextDataInjectorFactory {
     public static ContextDataInjector createInjector() {
         final String className = 
PropertiesUtil.getProperties().getStringProperty("log4j2.ContextDataInjector");
         if (className == null) {
-            return new ThreadContextDataInjector();
+            return createDefaultInjector();
         }
         try {
             final Class<? extends ContextDataInjector> cls = 
LoaderUtil.loadClass(className).asSubclass(
                     ContextDataInjector.class);
             return cls.newInstance();
         } catch (final Exception dynamicFailed) {
+            final ContextDataInjector result = createDefaultInjector();
             StatusLogger.getLogger().warn(
-                    "Could not create ContextDataInjector for '{}', using 
default ThreadContextDataInjector: {}",
-                    className, dynamicFailed);
-            return new ThreadContextDataInjector();
+                    "Could not create ContextDataInjector for '{}', using 
default {}: {}",
+                    className, result.getClass().getName(), dynamicFailed);
+            return result;
         }
     }
+
+    private static ContextDataInjector createDefaultInjector() {
+//        final ThreadContextMap threadContextMap = null; // 
ThreadContext.getThreadContextMap(); TODO LOG4J2-1349
+//        if (threadContextMap instanceof 
AbstractCopyOnWriteMutableThreadContext) {
+//            return new 
ThreadContextDataInjector.ForCopyOnWriteMutableThreadContextMap();
+//        }
+//        if (threadContextMap instanceof 
AbstractGarbageFreeMutableThreadContext) {
+//            return new 
ThreadContextDataInjector.ForGarbageFreeMutableThreadContextMap();
+//        }
+        return new ThreadContextDataInjector.ForDefaultThreadContextMap();
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/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 d97890b..81af731 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
@@ -51,6 +51,8 @@ public class Log4jLogEvent implements LogEvent {
     private static final long serialVersionUID = -8393305700508709443L;
     private static final Clock CLOCK = ClockFactory.getClock();
     private static volatile NanoClock nanoClock = new DummyNanoClock();
+    private static final ContextDataInjector CONTEXT_DATA_INJECTOR = 
ContextDataInjectorFactory.createInjector();
+
     private final String loggerFqcn;
     private final Marker marker;
     private final Level level;
@@ -427,10 +429,8 @@ public class Log4jLogEvent implements LogEvent {
     }
 
     private static MutableContextData createContextData(final List<Property> 
properties) {
-        final MutableContextData result = 
ContextDataFactory.createContextData();
-        final ContextDataInjector injector = 
ContextDataInjectorFactory.createInjector();
-        injector.injectContextData(properties, result);
-        return result;
+        final MutableContextData reusable = 
ContextDataFactory.createContextData();
+        return CONTEXT_DATA_INJECTOR.injectContextData(properties, reusable);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
index cd0db99..93a160e 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ReusableLogEventFactory.java
@@ -74,7 +74,7 @@ public class ReusableLogEventFactory implements 
LogEventFactory {
         result.setLevel(level == null ? Level.OFF : level);
         result.setMessage(message);
         result.setThrown(t);
-        injector.injectContextData(properties, (MutableContextData) 
result.getContextData());
+        result.setContextData(injector.injectContextData(properties, 
(MutableContextData) result.getContextData()));
         result.setContextStack(ThreadContext.getDepth() == 0 ? null : 
ThreadContext.cloneStack());// mutable copy
         result.setTimeMillis(message instanceof TimestampMessage
                 ? ((TimestampMessage) message).getTimestamp()

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
index ddb26a0..af534fb 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
@@ -22,13 +22,13 @@ import java.util.Map;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.ContextData;
 import org.apache.logging.log4j.core.config.Property;
-import org.apache.logging.log4j.spi.ThreadContextMap;
 
 /**
- * {@code ThreadContextDataInjector} copies key-value pairs from the {@code 
ThreadContext} map and from the
- * configuration {@code Properties} into the {@code LogEvent}s' {@code 
ContextData}.
+ * {@code ThreadContextDataInjector} contains a number of strategies for 
copying key-value pairs from the various
+ * {@code ThreadContext} map implementations into a {@code 
MutableContextData}. In the case of duplicate keys,
+ * thread context values overwrite configuration {@code Property} values.
  * <p>
- * This is the default {@code ContextDataInjector} returned by the {@link 
ContextDataInjectorFactory}.
+ * These are the default {@code ContextDataInjector} objects returned by the 
{@link ContextDataInjectorFactory}.
  * </p>
  *
  * @see org.apache.logging.log4j.ThreadContext
@@ -38,37 +38,127 @@ import org.apache.logging.log4j.spi.ThreadContextMap;
  * @see ContextDataInjectorFactory
  * @since 2.7
  */
-public class ThreadContextDataInjector implements ContextDataInjector {
-    @Override
-    public void injectContextData(final List<Property> properties, final 
MutableContextData contextData) {
-        copyProperties(properties, contextData);
-        //copyThreadContextMap(ThreadContext.getThreadContextMap(), 
contextData); //TODO LOG4J2-1349
-        copyThreadContextMap(ThreadContext.getImmutableContext(), contextData);
-    }
+public class ThreadContextDataInjector  {
 
-    private void copyProperties(final List<Property> properties, final 
MutableContextData contextData) {
-        if (properties != null) {
-            for (int i = 0; i < properties.size(); i++) {
-                final Property prop = properties.get(i);
-                contextData.putValue(prop.getName(), prop.getValue());
+    /**
+     * Default {@code ContextDataInjector} for the legacy {@code Map<String, 
String>}-based ThreadContext (which is
+     * also the ThreadContext implementation used for web applications).
+     * <p>
+     * This injector always puts key-value pairs into the specified reusable 
MutableContextData.
+     */
+    public static class ForDefaultThreadContextMap implements 
ContextDataInjector {
+        /**
+         * Puts key-value pairs from both the specified list of properties as 
well as the thread context into the
+         * specified reusable MutableContextData.
+         *
+         * @param props list of configuration properties, may be {@code null}
+         * @param reusable a {@code MutableContextData} instance that may be 
reused to avoid creating temporary objects
+         * @return a {@code MutableContextData} combining configuration 
properties with thread context data
+         */
+        @Override
+        public MutableContextData injectContextData(final List<Property> 
props, final MutableContextData reusable) {
+            // implementation note:
+            // The DefaultThreadContextMap stores context data in a 
Map<String, String>, not in a ContextData object.
+            // Therefore we can populate the specified reusable 
MutableContextData, but
+            // we need to copy the ThreadContext key-value pairs one by one.
+            copyProperties(props, reusable);
+            copyThreadContextMap(ThreadContext.getImmutableContext(), 
reusable);
+            return reusable;
+        }
+
+        /**
+         * Copies key-value pairs from the specified map into the specified 
{@code MutableContextData}.
+         *
+         * @param map map with key-value pairs, may be {@code null}
+         * @param result the {@code MutableContextData} object to add the 
key-values to. Must be non-{@code null}.
+         */
+        private static void copyThreadContextMap(final Map<String, String> 
map, final MutableContextData result) {
+            if (map != null) {
+                for (Map.Entry<String, String> entry : map.entrySet()) {
+                    result.putValue(entry.getKey(), entry.getValue());
+                }
             }
         }
     }
 
-    private void copyThreadContextMap(final Map<String, String> map, final 
MutableContextData contextData) {
-        if (map != null) {
-            for (Map.Entry<String, String> entry : map.entrySet()) {
-                contextData.putValue(entry.getKey(), entry.getValue());
-            }
+    /**
+     * The {@code ContextDataInjector} used when the ThreadContextMap 
implementation is a garbage-free
+     * MutableContextData-based data structure.
+     * <p>
+     * This injector always puts key-value pairs into the specified reusable 
MutableContextData.
+     */
+    public static class ForGarbageFreeMutableThreadContextMap implements 
ContextDataInjector {
+        /**
+         * Puts key-value pairs from both the specified list of properties as 
well as the thread context into the
+         * specified reusable MutableContextData.
+         *
+         * @param props list of configuration properties, may be {@code null}
+         * @param reusable a {@code MutableContextData} instance that may be 
reused to avoid creating temporary objects
+         * @return a {@code MutableContextData} combining configuration 
properties with thread context data
+         */
+        @Override
+        public MutableContextData injectContextData(final List<Property> 
props, final MutableContextData reusable) {
+            // When the ThreadContext is garbage-free, we must copy its 
key-value pairs into the specified reusable
+            // MutableContextData. We cannot return the ThreadContext's 
internal data structure because it will be
+            // modified.
+            copyProperties(props, reusable);
+
+            // TODO LOG4J2-1349
+//            final MutableContextData immutableCopy = 
((AbstractGarbageFreeMutableThreadContext)
+//                    ThreadContext.getThreadContextMap()).getContextData();
+//            reusable.putAll(immutableCopy);
+            return reusable;
         }
     }
 
-    private void copyThreadContextMap(final ThreadContextMap contextMap, final 
MutableContextData contextData) {
-        if (contextMap instanceof ContextData) {
-            contextData.putAll((ContextData) contextMap);
-        } else {
-            if (contextMap != null) {
-                copyThreadContextMap(contextMap.getImmutableMapOrNull(), 
contextData);
+    /**
+     * The {@code ContextDataInjector} used when the ThreadContextMap 
implementation is a copy-on-write
+     * MutableContextData-based data structure.
+     * <p>
+     * If there are no configuration properties, this injector will return the 
thread context's internal data
+     * structure. Otherwise the configuration properties are combined with the 
thread context key-value pairs into the
+     * specified reusable MutableContextData.
+     */
+    public static class ForCopyOnWriteMutableThreadContextMap implements 
ContextDataInjector {
+        /**
+         * If there are no configuration properties, this injector will return 
the thread context's internal data
+         * structure. Otherwise the configuration properties are combined with 
the thread context key-value pairs into the
+         * specified reusable MutableContextData.
+         *
+         * @param props list of configuration properties, may be {@code null}
+         * @param reusable a {@code MutableContextData} instance that may be 
reused to avoid creating temporary objects
+         * @return a {@code MutableContextData} combining configuration 
properties with thread context data
+         */
+        @Override
+        public MutableContextData injectContextData(final List<Property> 
props, final MutableContextData reusable) {
+            // TODO LOG4J2-1349
+
+//            // If there are no configuration properties we want to just 
return the ThreadContext's MutableContextData:
+//            // it is a copy-on-write data structure so we are sure 
ThreadContext changes will not affect our copy.
+//            final MutableContextData immutableCopy = 
((AbstractCopyOnWriteMutableThreadContext)
+//                    ThreadContext.getThreadContextMap()).getContextData();
+//            if (props == null || props.isEmpty()) {
+//                return immutableCopy;
+//            }
+//            // However, if the list of Properties is non-empty we need to 
combine the properties and the ThreadContext
+//            // data. In that case we will copy the key-value pairs into the 
specified reusable MutableContextData.
+//            copyProperties(props, reusable);
+//            reusable.putAll(immutableCopy);
+            return reusable;
+        }
+    }
+
+    /**
+     * Copies key-value pairs from the specified property list into the 
specified {@code MutableContextData}.
+     *
+     * @param properties list of configuration properties, may be {@code null}
+     * @param result the {@code MutableContextData} object to add the 
key-values to. Must be non-{@code null}.
+     */
+    public static void copyProperties(final List<Property> properties, final 
MutableContextData result) {
+        if (properties != null) {
+            for (int i = 0; i < properties.size(); i++) {
+                final Property prop = properties.get(i);
+                result.putValue(prop.getName(), prop.getValue());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a994f15f/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 38370d1..cb1de14 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
@@ -57,7 +57,7 @@ public class RingBufferLogEventTest {
         final StackTraceElement location = null;
         final long currentTimeMillis = 0;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(MutableContextData) evt.getContextData(),
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertEquals(Level.OFF, evt.getLevel());
     }
@@ -76,7 +76,7 @@ public class RingBufferLogEventTest {
         final StackTraceElement location = null;
         final long currentTimeMillis = 0;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(MutableContextData) evt.getContextData(),
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertNotNull(evt.getMessage());
     }
@@ -95,7 +95,7 @@ public class RingBufferLogEventTest {
         final StackTraceElement location = null;
         final long currentTimeMillis = 123;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(MutableContextData) evt.getContextData(),
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         assertEquals(123, evt.getTimeMillis());
     }
@@ -114,7 +114,7 @@ public class RingBufferLogEventTest {
         final StackTraceElement location = null;
         final long currentTimeMillis = 12345;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(MutableContextData) evt.getContextData(),
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         ((MutableContextData) evt.getContextData()).putValue("key", "value");
 
@@ -152,7 +152,7 @@ public class RingBufferLogEventTest {
         final StackTraceElement location = null;
         final long currentTimeMillis = 12345;
         final long nanoTime = 1;
-        evt.setValues(null, loggerName, marker, fqcn, level, data, t,
+        evt.setValues(null, loggerName, marker, fqcn, level, data, t, 
(MutableContextData) evt.getContextData(),
                 contextStack, -1, threadName, -1, location, currentTimeMillis, 
nanoTime);
         ((MutableContextData) evt.getContextData()).putValue("key", "value");
 

Reply via email to