You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2018/01/11 02:19:57 UTC

[1/2] logging-log4j2 git commit: [LOG4J2-2179] The MongoDB Appender should use a keys and values for a Log4j MapMessage. [LOG4J2-2180] Add a MongoDbProvider builder for and deprecate org.apache.logging.log4j.mongodb.MongoDbProvider.createNoSqlProvider().

Repository: logging-log4j2
Updated Branches:
  refs/heads/master ed600f7e0 -> 26fc9e079


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
index c4df884..f7794e6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
@@ -419,7 +419,7 @@ public class JmsManager extends AbstractManager {
 
     private MapMessage map(final org.apache.logging.log4j.message.MapMessage<?, ?> log4jMapMessage,
             final MapMessage jmsMapMessage) {
-        // Map without calling rg.apache.logging.log4j.message.MapMessage#getData() which makes a copy of the map.
+        // Map without calling org.apache.logging.log4j.message.MapMessage#getData() which makes a copy of the map.
         log4jMapMessage.forEach(new BiConsumer<String, Object>() {
             @Override
             public void accept(final String key, final Object value) {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlAppender.java
index c26ad8f..aae28ff 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlAppender.java
@@ -16,14 +16,17 @@
  */
 package org.apache.logging.log4j.core.appender.nosql;
 
+import java.io.Serializable;
+
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.appender.AbstractAppender;
 import org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.util.Booleans;
 
 /**
@@ -41,39 +44,97 @@ import org.apache.logging.log4j.core.util.Booleans;
  */
 @Plugin(name = "NoSql", category = "Core", elementType = Appender.ELEMENT_TYPE, printObject = true)
 public final class NoSqlAppender extends AbstractDatabaseAppender<NoSqlDatabaseManager<?>> {
-    private final String description;
 
-    private NoSqlAppender(final String name, final Filter filter, final boolean ignoreExceptions,
-                          final NoSqlDatabaseManager<?> manager) {
-        super(name, filter, ignoreExceptions, manager);
-        this.description = this.getName() + "{ manager=" + this.getManager() + " }";
-    }
+    /**
+     * Builds ConsoleAppender instances.
+     * 
+     * @param <B>
+     *            The type to build
+     */
+    public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
+            implements org.apache.logging.log4j.core.util.Builder<NoSqlAppender> {
 
-    @Override
-    public String toString() {
-        return this.description;
+        @PluginBuilderAttribute("bufferSize")
+        private int bufferSize;
+
+        @PluginElement("NoSqlProvider")
+        private NoSqlProvider<?> provider;
+
+        @SuppressWarnings("resource")
+        @Override
+        public NoSqlAppender build() {
+            final String name = getName();
+            if (provider == null) {
+                LOGGER.error("NoSQL provider not specified for appender [{}].", name);
+                return null;
+            }
+
+            final String managerName = "noSqlManager{ description=" + name + ", bufferSize=" + bufferSize
+                    + ", provider=" + provider + " }";
+
+            final NoSqlDatabaseManager<?> manager = NoSqlDatabaseManager.getNoSqlDatabaseManager(managerName,
+                    bufferSize, provider);
+            if (manager == null) {
+                return null;
+            }
+
+            return new NoSqlAppender(name, getFilter(), getLayout(), isIgnoreExceptions(), manager);
+        }
+
+        /**
+         * Sets the buffer size.
+         * 
+         * @param bufferSize
+         *            If an integer greater than 0, this causes the appender to buffer log events and flush whenever the
+         *            buffer reaches this size.
+         * @return this
+         */
+        public B setBufferSize(int bufferSize) {
+            this.bufferSize = bufferSize;
+            return asBuilder();
+        }
+
+        /**
+         * Sets the provider.
+         * 
+         * @param provider
+         *            The NoSQL provider that provides connections to the chosen NoSQL database.
+         * @return this
+         */
+        public B setProvider(NoSqlProvider<?> provider) {
+            this.provider = provider;
+            return asBuilder();
+        }
     }
 
     /**
      * Factory method for creating a NoSQL appender within the plugin manager.
      *
-     * @param name The name of the appender.
-     * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
-     *               they are propagated to the caller.
-     * @param filter The filter, if any, to use.
-     * @param bufferSize If an integer greater than 0, this causes the appender to buffer log events and flush whenever
-     *                   the buffer reaches this size.
-     * @param provider The NoSQL provider that provides connections to the chosen NoSQL database.
+     * @param name
+     *            The name of the appender.
+     * @param ignore
+     *            If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they
+     *            are propagated to the caller.
+     * @param filter
+     *            The filter, if any, to use.
+     * @param bufferSize
+     *            If an integer greater than 0, this causes the appender to buffer log events and flush whenever the
+     *            buffer reaches this size.
+     * @param provider
+     *            The NoSQL provider that provides connections to the chosen NoSQL database.
      * @return a new NoSQL appender.
+     * @deprecated since 2.10.1; use {@link Builder}.
      */
     @SuppressWarnings("resource")
-    @PluginFactory
+    @Deprecated
     public static NoSqlAppender createAppender(
-            @PluginAttribute("name") final String name,
-            @PluginAttribute("ignoreExceptions") final String ignore,
-            @PluginElement("Filter") final Filter filter,
-            @PluginAttribute("bufferSize") final String bufferSize,
-            @PluginElement("NoSqlProvider") final NoSqlProvider<?> provider) {
+    // @formatter:off
+            final String name,
+            final String ignore, 
+            final Filter filter,
+            final String bufferSize,
+            final NoSqlProvider<?> provider) {
+    // @formatter:on
         if (provider == null) {
             LOGGER.error("NoSQL provider not specified for appender [{}].", name);
             return null;
@@ -82,16 +143,33 @@ public final class NoSqlAppender extends AbstractDatabaseAppender<NoSqlDatabaseM
         final int bufferSizeInt = AbstractAppender.parseInt(bufferSize, 0);
         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
 
-        final String managerName = "noSqlManager{ description=" + name + ", bufferSize=" + bufferSizeInt
-                + ", provider=" + provider + " }";
+        final String managerName = "noSqlManager{ description=" + name + ", bufferSize=" + bufferSizeInt + ", provider="
+                + provider + " }";
 
-        final NoSqlDatabaseManager<?> manager = NoSqlDatabaseManager.getNoSqlDatabaseManager(
-                managerName, bufferSizeInt, provider
-        );
+        final NoSqlDatabaseManager<?> manager = NoSqlDatabaseManager.getNoSqlDatabaseManager(managerName, bufferSizeInt,
+                provider);
         if (manager == null) {
             return null;
         }
 
-        return new NoSqlAppender(name, filter, ignoreExceptions, manager);
+        return new NoSqlAppender(name, filter, null, ignoreExceptions, manager);
+    }
+
+    @PluginBuilderFactory
+    public static <B extends Builder<B>> B newBuilder() {
+        return new Builder<B>().asBuilder();
+    }
+
+    private final String description;
+
+    private NoSqlAppender(final String name, final Filter filter, Layout<? extends Serializable> layout,
+            final boolean ignoreExceptions, final NoSqlDatabaseManager<?> manager) {
+        super(name, filter, layout, ignoreExceptions, manager);
+        this.description = this.getName() + "{ manager=" + this.getManager() + " }";
+    }
+
+    @Override
+    public String toString() {
+        return this.description;
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java
index a0a29ce..361ae2b 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java
@@ -16,6 +16,10 @@
  */
 package org.apache.logging.log4j.core.appender.nosql;
 
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LogEvent;
@@ -23,6 +27,7 @@ import org.apache.logging.log4j.core.appender.AppenderLoggingException;
 import org.apache.logging.log4j.core.appender.ManagerFactory;
 import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
 import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.message.MapMessage;
 import org.apache.logging.log4j.util.BiConsumer;
 import org.apache.logging.log4j.util.ReadOnlyStringMap;
 
@@ -64,14 +69,40 @@ public final class NoSqlDatabaseManager<W> extends AbstractDatabaseManager {
         }
     }
 
+    @Deprecated
     @Override
     protected void writeInternal(final LogEvent event) {
+        writeInternal(event, null);
+    }
+    
+    @Override
+    protected void writeInternal(final LogEvent event, final Serializable serializable) {
         if (!this.isRunning() || this.connection == null || this.connection.isClosed()) {
             throw new AppenderLoggingException(
                     "Cannot write logging event; NoSQL manager not connected to the database.");
         }
 
         final NoSqlObject<W> entity = this.connection.createObject();
+        if (serializable instanceof MapMessage) {
+            setFields((MapMessage<?, ?>) serializable, entity);
+        } else {
+            setFields(event, entity);
+        }
+
+        this.connection.insertObject(entity);
+    }
+
+    private void setFields(final MapMessage<?, ?> mapMessage, final NoSqlObject<W> noSqlObject) {
+        // Map without calling org.apache.logging.log4j.message.MapMessage#getData() which makes a copy of the map.
+        mapMessage.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String key, final Object value) {
+                noSqlObject.set(key, value);
+            }
+        });
+    }
+
+    private void setFields(final LogEvent event, final NoSqlObject<W> entity) {
         entity.set("level", event.getLevel());
         entity.set("loggerName", event.getLoggerName());
         entity.set("message", event.getMessage() == null ? null : event.getMessage().getFormattedMessage());
@@ -139,8 +170,6 @@ public final class NoSqlDatabaseManager<W> extends AbstractDatabaseManager {
         } else {
             entity.set("contextStack", contextStack.asList().toArray());
         }
-
-        this.connection.insertObject(entity);
     }
 
     private NoSqlObject<W> buildMarkerEntity(final Marker marker) {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java
index cf33cea..c21311a 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java
@@ -95,14 +95,14 @@ public class AbstractDatabaseAppenderTest {
 
         appender.append(event1);
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event1));
+        then(manager).should().writeInternal(same(event1), null);
         then(manager).should().commitAndClose();
 
         reset(manager);
 
         appender.append(event2);
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event2));
+        then(manager).should().writeInternal(same(event2), null);
         then(manager).should().commitAndClose();
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java
index 41e7dd8..26462c3 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java
@@ -24,6 +24,8 @@ import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.BDDMockito.then;
 import static org.mockito.Mockito.*;
 
+import java.io.Serializable;
+
 public class AbstractDatabaseManagerTest {
     private AbstractDatabaseManager manager;
 
@@ -89,21 +91,21 @@ public class AbstractDatabaseManagerTest {
         then(manager).should().startupInternal();
         reset(manager);
 
-        manager.write(event1);
+        manager.write(event1, null);
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event1));
+        then(manager).should().writeInternal(same(event1), null);
         then(manager).should().commitAndClose();
         reset(manager);
 
-        manager.write(event2);
+        manager.write(event2, null);
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event2));
+        then(manager).should().writeInternal(same(event2), null);
         then(manager).should().commitAndClose();
         reset(manager);
 
-        manager.write(event3);
+        manager.write(event3, null);
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event3));
+        then(manager).should().writeInternal(same(event3), null);
         then(manager).should().commitAndClose();
         then(manager).shouldHaveNoMoreInteractions();
     }
@@ -130,16 +132,16 @@ public class AbstractDatabaseManagerTest {
         manager.startup();
         then(manager).should().startupInternal();
 
-        manager.write(event1);
-        manager.write(event2);
-        manager.write(event3);
-        manager.write(event4);
+        manager.write(event1, null);
+        manager.write(event2, null);
+        manager.write(event3, null);
+        manager.write(event4, null);
 
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event1copy));
-        then(manager).should().writeInternal(same(event2copy));
-        then(manager).should().writeInternal(same(event3copy));
-        then(manager).should().writeInternal(same(event4copy));
+        then(manager).should().writeInternal(same(event1copy), null);
+        then(manager).should().writeInternal(same(event2copy), null);
+        then(manager).should().writeInternal(same(event3copy), null);
+        then(manager).should().writeInternal(same(event4copy), null);
         then(manager).should().commitAndClose();
         then(manager).shouldHaveNoMoreInteractions();
     }
@@ -163,15 +165,15 @@ public class AbstractDatabaseManagerTest {
         manager.startup();
         then(manager).should().startupInternal();
 
-        manager.write(event1);
-        manager.write(event2);
-        manager.write(event3);
+        manager.write(event1, null);
+        manager.write(event2, null);
+        manager.write(event3, null);
         manager.flush();
 
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event1copy));
-        then(manager).should().writeInternal(same(event2copy));
-        then(manager).should().writeInternal(same(event3copy));
+        then(manager).should().writeInternal(same(event1copy), null);
+        then(manager).should().writeInternal(same(event2copy), null);
+        then(manager).should().writeInternal(same(event3copy), null);
         then(manager).should().commitAndClose();
         then(manager).shouldHaveNoMoreInteractions();
     }
@@ -195,15 +197,15 @@ public class AbstractDatabaseManagerTest {
         manager.startup();
         then(manager).should().startupInternal();
 
-        manager.write(event1);
-        manager.write(event2);
-        manager.write(event3);
+        manager.write(event1, null);
+        manager.write(event2, null);
+        manager.write(event3, null);
         manager.shutdown();
 
         then(manager).should().connectAndStart();
-        then(manager).should().writeInternal(same(event1copy));
-        then(manager).should().writeInternal(same(event2copy));
-        then(manager).should().writeInternal(same(event3copy));
+        then(manager).should().writeInternal(same(event1copy), null);
+        then(manager).should().writeInternal(same(event2copy), null);
+        then(manager).should().writeInternal(same(event3copy), null);
         then(manager).should().commitAndClose();
         then(manager).should().shutdownInternal();
         then(manager).shouldHaveNoMoreInteractions();
@@ -218,6 +220,7 @@ public class AbstractDatabaseManagerTest {
 
         @Override
         protected void startupInternal() throws Exception {
+            // noop
         }
 
         @Override
@@ -227,15 +230,23 @@ public class AbstractDatabaseManagerTest {
 
         @Override
         protected void connectAndStart() {
+            // noop
         }
 
+        @Deprecated
         @Override
         protected void writeInternal(final LogEvent event) {
+            // noop
         }
 
         @Override
+        protected void writeInternal(LogEvent event, Serializable serializable) {
+            // noop
+        }
+        @Override
         protected boolean commitAndClose() {
             return true;
         }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManagerTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManagerTest.java
index 7597cb5..772a947 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManagerTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManagerTest.java
@@ -102,7 +102,7 @@ public class NoSqlDatabaseManagerTest {
         try (final NoSqlDatabaseManager<?> manager = NoSqlDatabaseManager.getNoSqlDatabaseManager("name", 0,
             provider)) {
             expectedException.expect(AppenderLoggingException.class);
-            manager.writeInternal(mock(LogEvent.class));
+            manager.writeInternal(mock(LogEvent.class), null);
         }
     }
 
@@ -118,7 +118,7 @@ public class NoSqlDatabaseManagerTest {
             then(provider).should().getConnection();
 
             expectedException.expect(AppenderLoggingException.class);
-            manager.writeInternal(mock(LogEvent.class));
+            manager.writeInternal(mock(LogEvent.class), null);
         }
     }
 
@@ -145,7 +145,7 @@ public class NoSqlDatabaseManagerTest {
                 .setTimeMillis(1234567890123L)
                 .build();
 
-            manager.writeInternal(event);
+            manager.writeInternal(event, null);
             then(connection).should().insertObject(captor.capture());
 
             final NoSqlObject<Map<String, Object>> inserted = captor.getValue();
@@ -217,7 +217,7 @@ public class NoSqlDatabaseManagerTest {
                 .setContextStack(stack)
                 .build();
 
-            manager.writeInternal(event);
+            manager.writeInternal(event, null);
             then(connection).should().insertObject(captor.capture());
 
             final NoSqlObject<Map<String, Object>> inserted = captor.getValue();
@@ -318,7 +318,7 @@ public class NoSqlDatabaseManagerTest {
                 .setContextStack(stack)
                 .build();
 
-            manager.writeInternal(event);
+            manager.writeInternal(event, null);
             then(connection).should().insertObject(captor.capture());
 
             final NoSqlObject<Map<String, Object>> inserted = captor.getValue();

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
deleted file mode 100644
index 0098057..0000000
--- a/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.test;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * A JUnit TestRule to discover an available port and save it in a system property. Useful for setting up tests using
- * Apache Active MQ.
- */
-public class AvailablePortSystemPropertyRule implements TestRule {
-
-    public static AvailablePortSystemPropertyRule create(final String name) {
-        return new AvailablePortSystemPropertyRule(name);
-    }
-
-    protected final String name;
-    protected int port;
-
-    protected AvailablePortSystemPropertyRule(final String name) {
-        this.name = name;
-    }
-
-    @Override
-    public Statement apply(final Statement base, final Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                final String oldValue = System.getProperty(name);
-                try {
-                    port = AvailablePortFinder.getNextAvailable();
-                    System.setProperty(name, Integer.toString(port));
-                    base.evaluate();
-                } finally {
-                    // Restore if previously set
-                    if (oldValue != null) {
-                        System.setProperty(name, oldValue);
-                    }
-                }
-            }
-        };
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder builder = new StringBuilder();
-        builder.append("AvailablePortSystemPropertyRule [name=");
-        builder.append(name);
-        builder.append(", port=");
-        builder.append(port);
-        builder.append("]");
-        return builder.toString();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyTestRule.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyTestRule.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyTestRule.java
new file mode 100644
index 0000000..1bece7b
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyTestRule.java
@@ -0,0 +1,81 @@
+/*
+ * 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.test;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit TestRule to discover an available port and save it in a system property. Useful for setting up tests using
+ * Apache Active MQ.
+ */
+public class AvailablePortSystemPropertyTestRule implements TestRule {
+
+    public static AvailablePortSystemPropertyTestRule create(final String name) {
+        return new AvailablePortSystemPropertyTestRule(name);
+    }
+
+    protected final String name;
+    protected int port;
+
+    protected AvailablePortSystemPropertyTestRule(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final String oldValue = System.getProperty(name);
+                try {
+                    port = AvailablePortFinder.getNextAvailable();
+                    System.setProperty(name, Integer.toString(port));
+                    base.evaluate();
+                } finally {
+                    // Restore if previously set
+                    if (oldValue != null) {
+                        System.setProperty(name, oldValue);
+                    }
+                }
+            }
+        };
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("AvailablePortSystemPropertyRule [name=");
+        builder.append(name);
+        builder.append(", port=");
+        builder.append(port);
+        builder.append("]");
+        return builder.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-mongodb/pom.xml b/log4j-mongodb/pom.xml
index 8e484ee..2440cfc 100644
--- a/log4j-mongodb/pom.xml
+++ b/log4j-mongodb/pom.xml
@@ -70,6 +70,12 @@
       <artifactId>log4j-slf4j-impl</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>de.flapdoodle.embed</groupId>
+      <artifactId>de.flapdoodle.embed.mongo</artifactId>
+      <version>2.0.0</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/Java8Test.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/Java8Test.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/Java8Test.java
new file mode 100644
index 0000000..817141d
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/Java8Test.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mongodb;
+
+import org.apache.commons.lang3.JavaVersion;
+import org.apache.commons.lang3.SystemUtils;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Runs all MongoDB only on Java 8.
+ * <p>
+ * The test framework {@code de.flapdoodle.embed.mongo} requires Java 8.
+ * </p>
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({ MongoDbTestTestRuleTestJava8.class, MongoDbAuthTestJava8.class, MongoDbCappedTestJava8.class,
+        MongoDbMapMessageTestJava8.class, MongoDbTestJava8.class })
+public class Java8Test {
+
+    @BeforeClass
+    public static void beforeClass() {
+        Assume.assumeTrue(SystemUtils.JAVA_VERSION, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_8));
+    }
+
+    @Test
+    public void test() {
+        // noop
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTest.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTest.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTest.java
deleted file mode 100644
index 142a470..0000000
--- a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.mongodb;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Ignore("Requires a running MongoDB server")
-@Category(Appenders.MongoDb.class)
-public class MongoDbAuthTest {
-
-    @ClassRule
-    public static LoggerContextRule context = new LoggerContextRule("log4j2-mongodb-auth.xml");
-
-    @Test
-    public void test() {
-        final Logger logger = LogManager.getLogger();
-        logger.info("Hello log");
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTestJava8.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTestJava8.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTestJava8.java
new file mode 100644
index 0000000..64f51a2
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbAuthTestJava8.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mongodb;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.categories.Appenders;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.AvailablePortSystemPropertyTestRule;
+import org.apache.logging.log4j.test.RuleChainFactory;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.RuleChain;
+
+/**
+ * This class name does NOT end in "Test" in order to only be picked up by {@link Java8Test}.
+ */
+@Category(Appenders.MongoDb.class)
+public class MongoDbAuthTestJava8 {
+
+    static LoggerContextRule loggerContextTestRule = new LoggerContextRule("log4j2-mongodb-auth.xml");
+
+    static final AvailablePortSystemPropertyTestRule mongoDbPortTestRule = AvailablePortSystemPropertyTestRule
+            .create(MongoDbTestJava8.class.getName());
+
+    static final MongoDbTestRule mongoDbTestRule = new MongoDbTestRule(mongoDbPortTestRule.getName());
+
+    @ClassRule
+    public static RuleChain ruleChain = RuleChainFactory.create(mongoDbPortTestRule, mongoDbTestRule,
+            loggerContextTestRule);
+
+    @Test
+    public void test() {
+        final Logger logger = LogManager.getLogger();
+        logger.info("Hello log");
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTest.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTest.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTest.java
deleted file mode 100644
index d5aa5a9..0000000
--- a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.mongodb;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Ignore("Requires a running MongoDB server")
-@Category(Appenders.MongoDb.class)
-public class MongoDbCappedTest {
-
-    @ClassRule
-    public static LoggerContextRule context = new LoggerContextRule("log4j2-mongodb-capped.xml");
-
-    @Test
-    public void test() {
-        final Logger logger = LogManager.getLogger();
-        logger.info("Hello log");
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTestJava8.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTestJava8.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTestJava8.java
new file mode 100644
index 0000000..78668f3
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbCappedTestJava8.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mongodb;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.categories.Appenders;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.AvailablePortSystemPropertyTestRule;
+import org.apache.logging.log4j.test.RuleChainFactory;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.RuleChain;
+
+/**
+ * This class name does NOT end in "Test" in order to only be picked up by {@link Java8Test}.
+ */
+@Category(Appenders.MongoDb.class)
+public class MongoDbCappedTestJava8 {
+
+    static LoggerContextRule loggerContextTestRule = new LoggerContextRule("log4j2-mongodb-capped.xml");
+
+    static final AvailablePortSystemPropertyTestRule mongoDbPortTestRule = AvailablePortSystemPropertyTestRule
+            .create(MongoDbTestJava8.class.getName());
+
+    static final MongoDbTestRule mongoDbTestRule = new MongoDbTestRule(mongoDbPortTestRule.getName());
+
+    @ClassRule
+    public static RuleChain ruleChain = RuleChainFactory.create(mongoDbPortTestRule, mongoDbTestRule,
+            loggerContextTestRule);
+
+    @Test
+    public void test() {
+        final Logger logger = LogManager.getLogger();
+        logger.info("Hello log");
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java
new file mode 100644
index 0000000..76432ee
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java
@@ -0,0 +1,74 @@
+/*
+ * 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.mongodb;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.categories.Appenders;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.message.MapMessage;
+import org.apache.logging.log4j.test.AvailablePortSystemPropertyTestRule;
+import org.apache.logging.log4j.test.RuleChainFactory;
+import org.bson.Document;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.RuleChain;
+
+import com.mongodb.MongoClient;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+
+/**
+ * This class name does NOT end in "Test" in order to only be picked up by {@link Java8Test}.
+ */
+@Category(Appenders.MongoDb.class)
+public class MongoDbMapMessageTestJava8 {
+
+    static LoggerContextRule loggerContextTestRule = new LoggerContextRule("log4j2-mongodb-map-message.xml");
+
+    static final AvailablePortSystemPropertyTestRule mongoDbPortTestRule = AvailablePortSystemPropertyTestRule
+            .create(MongoDbMapMessageTestJava8.class.getName());
+
+    static final MongoDbTestRule mongoDbTestRule = new MongoDbTestRule(mongoDbPortTestRule.getName());
+
+    @ClassRule
+    public static RuleChain ruleChain = RuleChainFactory.create(mongoDbPortTestRule, mongoDbTestRule,
+            loggerContextTestRule);
+
+    @Test
+    public void test() {
+        final Logger logger = LogManager.getLogger();
+        final MapMessage<?, Object> map = new MapMessage<>();
+        map.with("SomeName", "SomeValue");
+        map.with("SomeInt", 1);
+        logger.info(map);
+        //
+        try (final MongoClient mongoClient = mongoDbTestRule.getMongoClient()) {
+            final MongoDatabase database = mongoClient.getDatabase("test");
+            Assert.assertNotNull(database);
+            final MongoCollection<Document> collection = database.getCollection("applog");
+            Assert.assertNotNull(collection);
+            Document first = collection.find().first();
+            Assert.assertNotNull(first);
+            Assert.assertEquals(first.toJson(), "SomeValue", first.getString("SomeName"));
+            Assert.assertEquals(first.toJson(), Integer.valueOf(1), first.getInteger("SomeInt"));
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTest.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTest.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTest.java
deleted file mode 100644
index eab3c1c..0000000
--- a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.mongodb;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Ignore("Requires a running MongoDB server")
-@Category(Appenders.MongoDb.class)
-public class MongoDbTest {
-
-    @ClassRule
-    public static LoggerContextRule context = new LoggerContextRule("log4j2-mongodb.xml");
-
-    @Test
-    public void test() {
-        final Logger logger = LogManager.getLogger();
-        logger.info("Hello log");
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestJava8.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestJava8.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestJava8.java
new file mode 100644
index 0000000..032eb73
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestJava8.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mongodb;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.categories.Appenders;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.AvailablePortSystemPropertyTestRule;
+import org.apache.logging.log4j.test.RuleChainFactory;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.RuleChain;
+
+/**
+ * This class name does NOT end in "Test" in order to only be picked up by {@link Java8Test}.
+ */
+@Category(Appenders.MongoDb.class)
+public class MongoDbTestJava8 {
+
+    static LoggerContextRule loggerContextTestRule = new LoggerContextRule("log4j2-mongodb.xml");
+
+    static final AvailablePortSystemPropertyTestRule mongoDbPortTestRule = AvailablePortSystemPropertyTestRule
+            .create(MongoDbTestJava8.class.getName());
+
+    static final MongoDbTestRule mongoDbTestRule = new MongoDbTestRule(mongoDbPortTestRule.getName());
+
+    @ClassRule
+    public static RuleChain ruleChain = RuleChainFactory.create(mongoDbPortTestRule, mongoDbTestRule,
+            loggerContextTestRule);
+
+    @Test
+    public void test() {
+        final Logger logger = LogManager.getLogger();
+        logger.info("Hello log");
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestRule.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestRule.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestRule.java
new file mode 100644
index 0000000..73fef40
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestRule.java
@@ -0,0 +1,139 @@
+/*
+ * 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.mongodb;
+
+import java.util.Objects;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import com.mongodb.MongoClient;
+
+import de.flapdoodle.embed.mongo.MongodExecutable;
+import de.flapdoodle.embed.mongo.MongodProcess;
+import de.flapdoodle.embed.mongo.MongodStarter;
+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
+import de.flapdoodle.embed.mongo.config.Net;
+import de.flapdoodle.embed.mongo.config.Timeout;
+import de.flapdoodle.embed.mongo.distribution.Version;
+import de.flapdoodle.embed.process.runtime.Network;
+
+/**
+ * A JUnit test rule to manage a MongoDB embedded instance.
+ */
+public class MongoDbTestRule implements TestRule {
+
+    private static final int BUILDER_TIMEOUT_MILLIS = 30000;
+
+    /**
+     * Store {@link MongodStarter} (or RuntimeConfig) in a static final field if you want to use artifact store caching
+     * (or else disable caching).
+     * <p>
+     * The test framework {@code de.flapdoodle.embed.mongo} requires Java 8.
+     * </p>
+     */
+    protected static final MongodStarter starter = MongodStarter.getDefaultInstance();
+
+    public static int getBuilderTimeoutMillis() {
+        return BUILDER_TIMEOUT_MILLIS;
+    }
+
+    public static MongodStarter getStarter() {
+        return starter;
+    }
+
+    protected final String portSystemPropertyName;
+    protected MongoClient mongoClient;
+    protected MongodExecutable mongodExecutable;
+    protected MongodProcess mongodProcess;
+
+    /**
+     * Constructs a new test rule.
+     * 
+     * @param portSystemPropertyName
+     *            The system property name for the MongoDB port.
+     */
+    public MongoDbTestRule(final String portSystemPropertyName) {
+        this.portSystemPropertyName = Objects.requireNonNull(portSystemPropertyName, "portSystemPropertyName");
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final String value = Objects.requireNonNull(System.getProperty(portSystemPropertyName),
+                        "System property '" + portSystemPropertyName + "' is null");
+                final int port = Integer.parseInt(value);
+                mongodExecutable = starter.prepare(
+                // @formatter:off
+                        new MongodConfigBuilder()
+                            .version(Version.Main.PRODUCTION)
+                            .timeout(new Timeout(BUILDER_TIMEOUT_MILLIS))
+                            .net(
+                                    new Net("localhost", port, Network.localhostIsIPv6()))
+                            .build());
+                // @formatter:on
+                mongodProcess = mongodExecutable.start();
+                mongoClient = new MongoClient("localhost", port);
+                try {
+                    base.evaluate();
+                } finally {
+                    if (mongodProcess != null) {
+                        mongodProcess.stop();
+                        mongodProcess = null;
+                    }
+                    if (mongodExecutable != null) {
+                        mongodExecutable.stop();
+                        mongodExecutable = null;
+                    }
+                }
+            }
+        };
+    }
+
+    public MongoClient getMongoClient() {
+        return mongoClient;
+    }
+
+    public MongodExecutable getMongodExecutable() {
+        return mongodExecutable;
+    }
+
+    public MongodProcess getMongodProcess() {
+        return mongodProcess;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("MongoDbTestRule [portSystemPropertyName=");
+        builder.append(portSystemPropertyName);
+        builder.append(", mongoClient=");
+        builder.append(mongoClient);
+        builder.append(", mongodExecutable=");
+        builder.append(mongodExecutable);
+        builder.append(", mongodProcess=");
+        builder.append(mongodProcess);
+        builder.append("]");
+        return builder.toString();
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestTestRuleTestJava8.java
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestTestRuleTestJava8.java b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestTestRuleTestJava8.java
new file mode 100644
index 0000000..545c959
--- /dev/null
+++ b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbTestTestRuleTestJava8.java
@@ -0,0 +1,69 @@
+/*
+ * 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.mongodb;
+
+import org.apache.commons.lang3.JavaVersion;
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.logging.log4j.test.AvailablePortSystemPropertyTestRule;
+import org.apache.logging.log4j.test.RuleChainFactory;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+
+import com.mongodb.client.MongoIterable;
+
+/**
+ * Tests MongoDbRule. This class name does NOT end in "Test" in order to only be picked up by {@link Java8Test}.
+ * <p>
+ * The test framework {@code de.flapdoodle.embed.mongo} requires Java 8.
+ * </p>
+ */
+public class MongoDbTestTestRuleTestJava8 {
+
+    @BeforeClass
+    public static void beforeClass() {
+        Assume.assumeTrue(SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_8));
+    }
+
+    public static final AvailablePortSystemPropertyTestRule mongoDbPortTestRule = AvailablePortSystemPropertyTestRule
+            .create(MongoDbTestTestRuleTestJava8.class.getName());
+
+    public static final MongoDbTestRule mongoDbTestRule = new MongoDbTestRule(mongoDbPortTestRule.getName());
+
+    @ClassRule
+    public static RuleChain mongoDbChain = RuleChainFactory.create(mongoDbPortTestRule, mongoDbTestRule);
+
+    @Test
+    public void testAccess() {
+        final MongoIterable<String> databaseNames = mongoDbTestRule.getMongoClient().listDatabaseNames();
+        Assert.assertNotNull(databaseNames);
+        Assert.assertNotNull(databaseNames.first());
+    }
+
+    @Test
+    public void testMongoDbTestRule() {
+        Assert.assertNotNull(MongoDbTestRule.getStarter());
+        Assert.assertNotNull(mongoDbTestRule);
+        Assert.assertNotNull(mongoDbTestRule.getMongoClient());
+        Assert.assertNotNull(mongoDbTestRule.getMongodExecutable());
+        Assert.assertNotNull(mongoDbTestRule.getMongodProcess());
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-mongodb/src/test/resources/log4j2-mongodb-map-message.xml
----------------------------------------------------------------------
diff --git a/log4j-mongodb/src/test/resources/log4j2-mongodb-map-message.xml b/log4j-mongodb/src/test/resources/log4j2-mongodb-map-message.xml
new file mode 100644
index 0000000..84e49e5
--- /dev/null
+++ b/log4j-mongodb/src/test/resources/log4j2-mongodb-map-message.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<Configuration status="debug">
+  <Appenders>
+    <NoSql name="MongoDbAppender">
+      <MongoDb databaseName="test" collectionName="applog" server="localhost"
+        port="${sys:org.apache.logging.log4j.mongodb.MongoDbMapMessageTestJava8:-27017}">
+      </MongoDb>
+      <MessageLayout />
+    </NoSql>
+  </Appenders>
+  <Loggers>
+    <Root level="ALL">
+      <AppenderRef ref="MongoDbAppender" />
+    </Root>
+  </Loggers>
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 52ce994..55e7051 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -74,6 +74,12 @@
       <action issue="LOG4J2-2165" dev="ggregory" type="update">
         Update Jackson from 2.9.2 to 2.9.3.
       </action>
+      <action issue="LOG4J2-2179" dev="ggregory" type="add">
+        The MongoDB Appender should use a keys and values for a Log4j MapMessage.
+      </action>
+      <action issue="LOG4J2-2180" dev="ggregory" type="add">
+        Add a MongoDbProvider builder for and deprecate org.apache.logging.log4j.mongodb.MongoDbProvider.createNoSqlProvider().
+      </action>
     </release>
     <release version="2.10.0" date="2017-11-18" description="GA Release 2.10.0">
       <action issue="LOG4J2-2120" dev="mikes" type="add" due-to="Carter Douglas Kozak ">


[2/2] logging-log4j2 git commit: [LOG4J2-2179] The MongoDB Appender should use a keys and values for a Log4j MapMessage. [LOG4J2-2180] Add a MongoDbProvider builder for and deprecate org.apache.logging.log4j.mongodb.MongoDbProvider.createNoSqlProvider().

Posted by gg...@apache.org.
[LOG4J2-2179] The MongoDB Appender should use a keys and values for a
Log4j MapMessage.
[LOG4J2-2180] Add a MongoDbProvider builder for and deprecate
org.apache.logging.log4j.mongodb.MongoDbProvider.createNoSqlProvider().

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

Branch: refs/heads/master
Commit: 26fc9e0794c0e3a4f5c57ba5009ef31ebea9c960
Parents: ed600f7
Author: Gary Gregory <gg...@apache.org>
Authored: Wed Jan 10 19:19:51 2018 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Wed Jan 10 19:19:51 2018 -0700

----------------------------------------------------------------------
 .../log4j/core/appender/AbstractAppender.java   |  11 +
 .../core/appender/OutputStreamManager.java      |   3 +-
 .../appender/db/AbstractDatabaseAppender.java   | 301 ++++-----
 .../appender/db/AbstractDatabaseManager.java    | 487 +++++++-------
 .../appender/db/jdbc/JdbcDatabaseManager.java   | 633 ++++++++++---------
 .../appender/db/jpa/JpaDatabaseManager.java     | 393 ++++++------
 .../log4j/core/appender/mom/JmsAppender.java    |   2 +-
 .../log4j/core/appender/mom/JmsManager.java     |   2 +-
 .../core/appender/nosql/NoSqlAppender.java      | 138 +++-
 .../appender/nosql/NoSqlDatabaseManager.java    |  33 +-
 .../db/AbstractDatabaseAppenderTest.java        |   4 +-
 .../db/AbstractDatabaseManagerTest.java         |  63 +-
 .../nosql/NoSqlDatabaseManagerTest.java         |  10 +-
 .../test/AvailablePortSystemPropertyRule.java   |  81 ---
 .../AvailablePortSystemPropertyTestRule.java    |  81 +++
 log4j-mongodb/pom.xml                           |   6 +
 .../apache/logging/log4j/mongodb/Java8Test.java |  48 ++
 .../logging/log4j/mongodb/MongoDbAuthTest.java  |  40 --
 .../log4j/mongodb/MongoDbAuthTestJava8.java     |  52 ++
 .../log4j/mongodb/MongoDbCappedTest.java        |  40 --
 .../log4j/mongodb/MongoDbCappedTestJava8.java   |  52 ++
 .../mongodb/MongoDbMapMessageTestJava8.java     |  74 +++
 .../logging/log4j/mongodb/MongoDbTest.java      |  40 --
 .../logging/log4j/mongodb/MongoDbTestJava8.java |  52 ++
 .../logging/log4j/mongodb/MongoDbTestRule.java  | 139 ++++
 .../mongodb/MongoDbTestTestRuleTestJava8.java   |  69 ++
 .../resources/log4j2-mongodb-map-message.xml    |  23 +
 src/changes/changes.xml                         |   6 +
 28 files changed, 1734 insertions(+), 1149 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java
index 98bb240..57974bd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractAppender.java
@@ -252,6 +252,17 @@ public abstract class AbstractAppender extends AbstractFilterable implements App
         this.handler = handler;
     }
 
+    /**
+     * Serializes the given event using the appender's layout if present.
+     * 
+     * @param event
+     *            the event to serialize.
+     * @return the serialized event or null if no layout is present.
+     */
+    protected Serializable toSerializable(final LogEvent event) {
+        return layout != null ? layout.toSerializable(event) : null;
+    }
+
     @Override
     public String toString() {
         return name;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
index 51ef174..0873822 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.appender;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Serializable;
+import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
@@ -289,7 +290,7 @@ public class OutputStreamManager extends AbstractManager implements ByteBufferDe
      * @since 2.6
      */
     protected synchronized void flushBuffer(final ByteBuffer buf) {
-        buf.flip();
+        ((Buffer) buf).flip();
         if (buf.remaining() > 0) {
             writeToDestination(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
         }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppender.java
index 38f412c..09798fd 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppender.java
@@ -1,142 +1,159 @@
-/*
- * 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.core.appender.db;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.apache.logging.log4j.LoggingException;
-import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.appender.AppenderLoggingException;
-
-/**
- * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
- * should inherit from this base appender. Three implementations are currently provided:
- * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
- * JPA}, and {@link org.apache.logging.log4j.core.appender.nosql NoSQL}.
- *
- * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
- */
-public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
-
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
-    private final Lock readLock = lock.readLock();
-    private final Lock writeLock = lock.writeLock();
-
-    private T manager;
-
-    /**
-     * Instantiates the base appender.
-     *
-     * @param name The appender name.
-     * @param filter The filter, if any, to use.
-     * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
-     *                         they are propagated to the caller.
-     * @param manager The matching {@link AbstractDatabaseManager} implementation.
-     */
-    protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
-                                       final T manager) {
-        super(name, filter, null, ignoreExceptions);
-        this.manager = manager;
-    }
-
-    /**
-     * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
-     * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
-     *
-     * @return {@code null}.
-     */
-    @Override
-    public final Layout<LogEvent> getLayout() {
-        return null;
-    }
-
-    /**
-     * Returns the underlying manager in use within this appender.
-     *
-     * @return the manager.
-     */
-    public final T getManager() {
-        return this.manager;
-    }
-
-    @Override
-    public final void start() {
-        if (this.getManager() == null) {
-            LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
-        }
-        super.start();
-        if (this.getManager() != null) {
-            this.getManager().startup();
-        }
-    }
-
-    @Override
-    public boolean stop(final long timeout, final TimeUnit timeUnit) {
-        setStopping();
-        boolean stopped = super.stop(timeout, timeUnit, false);
-        if (this.getManager() != null) {
-            stopped &= this.getManager().stop(timeout, timeUnit);
-        }
-        setStopped();
-        return stopped;
-    }
-
-    @Override
-    public final void append(final LogEvent event) {
-        this.readLock.lock();
-        try {
-            this.getManager().write(event);
-        } catch (final LoggingException e) {
-            LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
-                    this.getName(), e);
-            throw e;
-        } catch (final Exception e) {
-            LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
-                    this.getName(), e);
-            throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
-        } finally {
-            this.readLock.unlock();
-        }
-    }
-
-    /**
-     * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
-     * events are written to the database without losing buffered or in-progress events. The existing manager is
-     * released only after the new manager has been installed. This method is thread-safe.
-     *
-     * @param manager The new manager to install.
-     */
-    protected final void replaceManager(final T manager) {
-        this.writeLock.lock();
-        try {
-            final T old = this.getManager();
-            if (!manager.isRunning()) {
-                manager.startup();
-            }
-            this.manager = manager;
-            old.close();
-        } finally {
-            this.writeLock.unlock();
-        }
-    }
-}
+/*
+ * 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.core.appender.db;
+
+import java.io.Serializable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.logging.log4j.LoggingException;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+
+/**
+ * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
+ * should inherit from this base appender. Three implementations are currently provided:
+ * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
+ * JPA}, and {@link org.apache.logging.log4j.core.appender.nosql NoSQL}.
+ *
+ * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
+ */
+public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender {
+
+    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    private final Lock readLock = lock.readLock();
+    private final Lock writeLock = lock.writeLock();
+
+    private T manager;
+
+    /**
+     * Instantiates the base appender.
+     *
+     * @param name The appender name.
+     * @param filter The filter, if any, to use.
+     * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
+     *                         they are propagated to the caller.
+     * @param manager The matching {@link AbstractDatabaseManager} implementation.
+     */
+    protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean ignoreExceptions,
+                                       final T manager) {
+        super(name, filter, null, ignoreExceptions);
+        this.manager = manager;
+    }
+
+    /**
+     * Instantiates the base appender.
+     *
+     * @param name The appender name.
+     * @param filter The filter, if any, to use.
+     * @param layout The layout to use to format the event.
+     * @param ignoreExceptions If {@code true} exceptions encountered when appending events are logged; otherwise
+     *                         they are propagated to the caller.
+     * @param manager The matching {@link AbstractDatabaseManager} implementation.
+     */
+    protected AbstractDatabaseAppender(final String name, final Filter filter,
+            final Layout<? extends Serializable> layout, final boolean ignoreExceptions, final T manager) {
+        super(name, filter, layout, ignoreExceptions);
+        this.manager = manager;
+    }
+
+    /**
+     * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
+     * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
+     *
+     * @return {@code null}.
+     */
+    @Override
+    public final Layout<LogEvent> getLayout() {
+        return null;
+    }
+
+    /**
+     * Returns the underlying manager in use within this appender.
+     *
+     * @return the manager.
+     */
+    public final T getManager() {
+        return this.manager;
+    }
+
+    @Override
+    public final void start() {
+        if (this.getManager() == null) {
+            LOGGER.error("No AbstractDatabaseManager set for the appender named [{}].", this.getName());
+        }
+        super.start();
+        if (this.getManager() != null) {
+            this.getManager().startup();
+        }
+    }
+
+    @Override
+    public boolean stop(final long timeout, final TimeUnit timeUnit) {
+        setStopping();
+        boolean stopped = super.stop(timeout, timeUnit, false);
+        if (this.getManager() != null) {
+            stopped &= this.getManager().stop(timeout, timeUnit);
+        }
+        setStopped();
+        return stopped;
+    }
+
+    @Override
+    public final void append(final LogEvent event) {
+        this.readLock.lock();
+        try {
+            this.getManager().write(event, toSerializable(event));
+        } catch (final LoggingException e) {
+            LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
+                    this.getName(), e);
+            throw e;
+        } catch (final Exception e) {
+            LOGGER.error("Unable to write to database [{}] for appender [{}].", this.getManager().getName(),
+                    this.getName(), e);
+            throw new AppenderLoggingException("Unable to write to database in appender: " + e.getMessage(), e);
+        } finally {
+            this.readLock.unlock();
+        }
+    }
+
+    /**
+     * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
+     * events are written to the database without losing buffered or in-progress events. The existing manager is
+     * released only after the new manager has been installed. This method is thread-safe.
+     *
+     * @param manager The new manager to install.
+     */
+    protected final void replaceManager(final T manager) {
+        this.writeLock.lock();
+        try {
+            final T old = this.getManager();
+            if (!manager.isRunning()) {
+                manager.startup();
+            }
+            this.manager = manager;
+            old.close();
+        } finally {
+            this.writeLock.unlock();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java
index b8b9899..54c33d1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java
@@ -1,232 +1,255 @@
-/*
- * 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.core.appender.db;
-
-import java.io.Flushable;
-import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractManager;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-
-/**
- * Manager that allows database appenders to have their configuration reloaded without losing events.
- */
-public abstract class AbstractDatabaseManager extends AbstractManager implements Flushable {
-    private final ArrayList<LogEvent> buffer;
-    private final int bufferSize;
-
-    private boolean running = false;
-
-    /**
-     * Instantiates the base manager.
-     *
-     * @param name The manager name, which should include any configuration details that one might want to be able to
-     *             reconfigure at runtime, such as database name, username, (hashed) password, etc.
-     * @param bufferSize The size of the log event buffer.
-     */
-    protected AbstractDatabaseManager(final String name, final int bufferSize) {
-        super(null, name);
-        this.bufferSize = bufferSize;
-        this.buffer = new ArrayList<>(bufferSize + 1);
-    }
-
-    /**
-     * Implementations should implement this method to perform any proprietary startup operations. This method will
-     * never be called twice on the same instance. It is safe to throw any exceptions from this method. This method
-     * does not necessarily connect to the database, as it is generally unreliable to connect once and use the same
-     * connection for hours.
-     */
-    protected abstract void startupInternal() throws Exception;
-
-    /**
-     * This method is called within the appender when the appender is started. If it has not already been called, it
-     * calls {@link #startupInternal()} and catches any exceptions it might throw.
-     */
-    public final synchronized void startup() {
-        if (!this.isRunning()) {
-            try {
-                this.startupInternal();
-                this.running = true;
-            } catch (final Exception e) {
-                logError("Could not perform database startup operations", e);
-            }
-        }
-    }
-
-    /**
-     * Implementations should implement this method to perform any proprietary disconnection / shutdown operations. This
-     * method will never be called twice on the same instance, and it will only be called <em>after</em>
-     * {@link #startupInternal()}. It is safe to throw any exceptions from this method. This method does not
-     * necessarily disconnect from the database for the same reasons outlined in {@link #startupInternal()}.
-     * @return true if all resources were closed normally, false otherwise.
-     */
-    protected abstract boolean shutdownInternal() throws Exception;
-
-    /**
-     * This method is called from the {@link #close()} method when the appender is stopped or the appender's manager
-     * is replaced. If it has not already been called, it calls {@link #shutdownInternal()} and catches any exceptions
-     * it might throw.
-     * @return true if all resources were closed normally, false otherwise.
-     */
-    public final synchronized boolean shutdown() {
-        boolean closed = true;
-        this.flush();
-        if (this.isRunning()) {
-            try {
-                closed &= this.shutdownInternal();
-            } catch (final Exception e) {
-                logWarn("Caught exception while performing database shutdown operations", e);
-                closed = false;
-            } finally {
-                this.running = false;
-            }
-        }
-        return closed;
-    }
-
-    /**
-     * Indicates whether the manager is currently connected {@link #startup()} has been called and {@link #shutdown()}
-     * has not been called).
-     *
-     * @return {@code true} if the manager is connected.
-     */
-    public final boolean isRunning() {
-        return this.running;
-    }
-
-    /**
-     * Connects to the database and starts a transaction (if applicable). With buffering enabled, this is called when
-     * flushing the buffer begins, before the first call to {@link #writeInternal}. With buffering disabled, this is
-     * called immediately before every invocation of {@link #writeInternal}.
-     */
-    protected abstract void connectAndStart();
-
-    /**
-     * Performs the actual writing of the event in an implementation-specific way. This method is called immediately
-     * from {@link #write(LogEvent)} if buffering is off, or from {@link #flush()} if the buffer has reached its limit.
-     *
-     * @param event The event to write to the database.
-     */
-    protected abstract void writeInternal(LogEvent event);
-
-    /**
-     * Commits any active transaction (if applicable) and disconnects from the database (returns the connection to the
-     * connection pool). With buffering enabled, this is called when flushing the buffer completes, after the last call
-     * to {@link #writeInternal}. With buffering disabled, this is called immediately after every invocation of
-     * {@link #writeInternal}.
-     * @return true if all resources were closed normally, false otherwise.
-     */
-    protected abstract boolean commitAndClose();
-
-    /**
-     * This method is called automatically when the buffer size reaches its maximum or at the beginning of a call to
-     * {@link #shutdown()}. It can also be called manually to flush events to the database.
-     */
-    @Override
-    public final synchronized void flush() {
-        if (this.isRunning() && this.buffer.size() > 0) {
-            this.connectAndStart();
-            try {
-                for (final LogEvent event : this.buffer) {
-                    this.writeInternal(event);
-                }
-            } finally {
-                this.commitAndClose();
-                // not sure if this should be done when writing the events failed
-                this.buffer.clear();
-            }
-        }
-    }
-
-    /**
-     * This method manages buffering and writing of events.
-     *
-     * @param event The event to write to the database.
-     */
-    public final synchronized void write(final LogEvent event) {
-        if (this.bufferSize > 0) {
-            this.buffer.add(event.toImmutable());
-            if (this.buffer.size() >= this.bufferSize || event.isEndOfBatch()) {
-                this.flush();
-            }
-        } else {
-            this.connectAndStart();
-            try {
-                this.writeInternal(event);
-            } finally {
-                this.commitAndClose();
-            }
-        }
-    }
-
-    @Override
-    public final boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
-        return this.shutdown();
-    }
-
-    @Override
-    public final String toString() {
-        return this.getName();
-    }
-
-    /**
-     * Implementations should define their own getManager method and call this method from that to create or get
-     * existing managers.
-     *
-     * @param name The manager name, which should include any configuration details that one might want to be able to
-     *             reconfigure at runtime, such as database name, username, (hashed) password, etc.
-     * @param data The concrete instance of {@link AbstractFactoryData} appropriate for the given manager.
-     * @param factory A factory instance for creating the appropriate manager.
-     * @param <M> The concrete manager type.
-     * @param <T> The concrete {@link AbstractFactoryData} type.
-     * @return a new or existing manager of the specified type and name.
-     */
-    protected static <M extends AbstractDatabaseManager, T extends AbstractFactoryData> M getManager(
-            final String name, final T data, final ManagerFactory<M, T> factory
-    ) {
-        return AbstractManager.getManager(name, factory, data);
-    }
-
-    /**
-     * Implementations should extend this class for passing data between the getManager method and the manager factory
-     * class.
-     */
-    protected abstract static class AbstractFactoryData {
-        private final int bufferSize;
-
-        /**
-         * Constructs the base factory data.
-         *
-         * @param bufferSize The size of the buffer.
-         */
-        protected AbstractFactoryData(final int bufferSize) {
-            this.bufferSize = bufferSize;
-        }
-
-        /**
-         * Gets the buffer size.
-         *
-         * @return the buffer size.
-         */
-        public int getBufferSize() {
-            return bufferSize;
-        }
-    }
-}
+/*
+ * 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.core.appender.db;
+
+import java.io.Flushable;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager that allows database appenders to have their configuration reloaded without losing events.
+ */
+public abstract class AbstractDatabaseManager extends AbstractManager implements Flushable {
+    private final ArrayList<LogEvent> buffer;
+    private final int bufferSize;
+
+    private boolean running = false;
+
+    /**
+     * Instantiates the base manager.
+     *
+     * @param name The manager name, which should include any configuration details that one might want to be able to
+     *             reconfigure at runtime, such as database name, username, (hashed) password, etc.
+     * @param bufferSize The size of the log event buffer.
+     */
+    protected AbstractDatabaseManager(final String name, final int bufferSize) {
+        super(null, name);
+        this.bufferSize = bufferSize;
+        this.buffer = new ArrayList<>(bufferSize + 1);
+    }
+
+    /**
+     * Implementations should implement this method to perform any proprietary startup operations. This method will
+     * never be called twice on the same instance. It is safe to throw any exceptions from this method. This method
+     * does not necessarily connect to the database, as it is generally unreliable to connect once and use the same
+     * connection for hours.
+     */
+    protected abstract void startupInternal() throws Exception;
+
+    /**
+     * This method is called within the appender when the appender is started. If it has not already been called, it
+     * calls {@link #startupInternal()} and catches any exceptions it might throw.
+     */
+    public final synchronized void startup() {
+        if (!this.isRunning()) {
+            try {
+                this.startupInternal();
+                this.running = true;
+            } catch (final Exception e) {
+                logError("Could not perform database startup operations", e);
+            }
+        }
+    }
+
+    /**
+     * Implementations should implement this method to perform any proprietary disconnection / shutdown operations. This
+     * method will never be called twice on the same instance, and it will only be called <em>after</em>
+     * {@link #startupInternal()}. It is safe to throw any exceptions from this method. This method does not
+     * necessarily disconnect from the database for the same reasons outlined in {@link #startupInternal()}.
+     * @return true if all resources were closed normally, false otherwise.
+     */
+    protected abstract boolean shutdownInternal() throws Exception;
+
+    /**
+     * This method is called from the {@link #close()} method when the appender is stopped or the appender's manager
+     * is replaced. If it has not already been called, it calls {@link #shutdownInternal()} and catches any exceptions
+     * it might throw.
+     * @return true if all resources were closed normally, false otherwise.
+     */
+    public final synchronized boolean shutdown() {
+        boolean closed = true;
+        this.flush();
+        if (this.isRunning()) {
+            try {
+                closed &= this.shutdownInternal();
+            } catch (final Exception e) {
+                logWarn("Caught exception while performing database shutdown operations", e);
+                closed = false;
+            } finally {
+                this.running = false;
+            }
+        }
+        return closed;
+    }
+
+    /**
+     * Indicates whether the manager is currently connected {@link #startup()} has been called and {@link #shutdown()}
+     * has not been called).
+     *
+     * @return {@code true} if the manager is connected.
+     */
+    public final boolean isRunning() {
+        return this.running;
+    }
+
+    /**
+     * Connects to the database and starts a transaction (if applicable). With buffering enabled, this is called when
+     * flushing the buffer begins, before the first call to {@link #writeInternal}. With buffering disabled, this is
+     * called immediately before every invocation of {@link #writeInternal}.
+     */
+    protected abstract void connectAndStart();
+
+    /**
+     * Performs the actual writing of the event in an implementation-specific way. This method is called immediately
+     * from {@link #write(LogEvent, Serializable)} if buffering is off, or from {@link #flush()} if the buffer has reached its limit.
+     *
+     * @param event The event to write to the database.
+     * @deprecated Use {@link #writeInternal(LogEvent, Serializable)}.
+     */
+    @Deprecated
+    protected abstract void writeInternal(LogEvent event);
+
+    /**
+     * Performs the actual writing of the event in an implementation-specific way. This method is called immediately
+     * from {@link #write(LogEvent, Serializable)} if buffering is off, or from {@link #flush()} if the buffer has reached its limit.
+     *
+     * @param event The event to write to the database.
+     */
+    protected abstract void writeInternal(LogEvent event, Serializable serializable);
+
+    /**
+     * Commits any active transaction (if applicable) and disconnects from the database (returns the connection to the
+     * connection pool). With buffering enabled, this is called when flushing the buffer completes, after the last call
+     * to {@link #writeInternal}. With buffering disabled, this is called immediately after every invocation of
+     * {@link #writeInternal}.
+     * @return true if all resources were closed normally, false otherwise.
+     */
+    protected abstract boolean commitAndClose();
+
+    /**
+     * This method is called automatically when the buffer size reaches its maximum or at the beginning of a call to
+     * {@link #shutdown()}. It can also be called manually to flush events to the database.
+     */
+    @Override
+    public final synchronized void flush() {
+        if (this.isRunning() && this.buffer.size() > 0) {
+            this.connectAndStart();
+            try {
+                for (final LogEvent event : this.buffer) {
+                    this.writeInternal(event);
+                }
+            } finally {
+                this.commitAndClose();
+                // not sure if this should be done when writing the events failed
+                this.buffer.clear();
+            }
+        }
+    }
+
+    /**
+     * This method manages buffering and writing of events.
+     *
+     * @param event The event to write to the database.
+     * @deprecated since 2.10.1 Use {@link #write(LogEvent, Serializable)}.
+     */
+    @Deprecated
+    public final synchronized void write(final LogEvent event) {
+        write(event, null);
+    }
+
+    /**
+     * This method manages buffering and writing of events.
+     *
+     * @param event The event to write to the database.
+     * @param serializable Serializable event
+     */
+    public final synchronized void write(final LogEvent event, final Serializable serializable) {
+        if (this.bufferSize > 0) {
+            this.buffer.add(event.toImmutable());
+            if (this.buffer.size() >= this.bufferSize || event.isEndOfBatch()) {
+                this.flush();
+            }
+        } else {
+            this.connectAndStart();
+            try {
+                this.writeInternal(event, serializable);
+            } finally {
+                this.commitAndClose();
+            }
+        }
+    }
+
+    @Override
+    public final boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+        return this.shutdown();
+    }
+
+    @Override
+    public final String toString() {
+        return this.getName();
+    }
+
+    /**
+     * Implementations should define their own getManager method and call this method from that to create or get
+     * existing managers.
+     *
+     * @param name The manager name, which should include any configuration details that one might want to be able to
+     *             reconfigure at runtime, such as database name, username, (hashed) password, etc.
+     * @param data The concrete instance of {@link AbstractFactoryData} appropriate for the given manager.
+     * @param factory A factory instance for creating the appropriate manager.
+     * @param <M> The concrete manager type.
+     * @param <T> The concrete {@link AbstractFactoryData} type.
+     * @return a new or existing manager of the specified type and name.
+     */
+    protected static <M extends AbstractDatabaseManager, T extends AbstractFactoryData> M getManager(
+            final String name, final T data, final ManagerFactory<M, T> factory
+    ) {
+        return AbstractManager.getManager(name, factory, data);
+    }
+
+    /**
+     * Implementations should extend this class for passing data between the getManager method and the manager factory
+     * class.
+     */
+    protected abstract static class AbstractFactoryData {
+        private final int bufferSize;
+
+        /**
+         * Constructs the base factory data.
+         *
+         * @param bufferSize The size of the buffer.
+         */
+        protected AbstractFactoryData(final int bufferSize) {
+            this.bufferSize = bufferSize;
+        }
+
+        /**
+         * Gets the buffer size.
+         *
+         * @return the buffer size.
+         */
+        public int getBufferSize() {
+            return bufferSize;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
index 2d80f70..a652c2c 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java
@@ -1,313 +1,320 @@
-/*
- * 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.core.appender.db.jdbc;
-
-import java.io.StringReader;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.NClob;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AppenderLoggingException;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
-import org.apache.logging.log4j.core.appender.db.ColumnMapping;
-import org.apache.logging.log4j.core.config.plugins.convert.DateTypeConverter;
-import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
-import org.apache.logging.log4j.core.util.Closer;
-import org.apache.logging.log4j.spi.ThreadContextMap;
-import org.apache.logging.log4j.spi.ThreadContextStack;
-import org.apache.logging.log4j.util.ReadOnlyStringMap;
-import org.apache.logging.log4j.util.Strings;
-
-/**
- * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JDBC.
- */
-public final class JdbcDatabaseManager extends AbstractDatabaseManager {
-
-    private static final JdbcDatabaseManagerFactory INSTANCE = new JdbcDatabaseManagerFactory();
-
-    // NOTE: prepared statements are prepared in this order: column mappings, then column configs
-    private final List<ColumnMapping> columnMappings;
-    private final List<ColumnConfig> columnConfigs;
-    private final ConnectionSource connectionSource;
-    private final String sqlStatement;
-
-    private Connection connection;
-    private PreparedStatement statement;
-    private boolean isBatchSupported;
-
-    private JdbcDatabaseManager(final String name, final int bufferSize, final ConnectionSource connectionSource,
-                                final String sqlStatement, final List<ColumnConfig> columnConfigs,
-                                final List<ColumnMapping> columnMappings) {
-        super(name, bufferSize);
-        this.connectionSource = connectionSource;
-        this.sqlStatement = sqlStatement;
-        this.columnConfigs = columnConfigs;
-        this.columnMappings = columnMappings;
-    }
-
-    @Override
-    protected void startupInternal() throws Exception {
-        this.connection = this.connectionSource.getConnection();
-        final DatabaseMetaData metaData = this.connection.getMetaData();
-        this.isBatchSupported = metaData.supportsBatchUpdates();
-        Closer.closeSilently(this.connection);
-    }
-
-    @Override
-    protected boolean shutdownInternal() {
-        if (this.connection != null || this.statement != null) {
-            return this.commitAndClose();
-        }
-        return true;
-    }
-
-    @Override
-    protected void connectAndStart() {
-        try {
-            this.connection = this.connectionSource.getConnection();
-            this.connection.setAutoCommit(false);
-            this.statement = this.connection.prepareStatement(this.sqlStatement);
-        } catch (final SQLException e) {
-            throw new AppenderLoggingException(
-                    "Cannot write logging event or flush buffer; JDBC manager cannot connect to the database.", e
-            );
-        }
-    }
-
-    @Override
-    protected void writeInternal(final LogEvent event) {
-        StringReader reader = null;
-        try {
-            if (!this.isRunning() || this.connection == null || this.connection.isClosed() || this.statement == null
-                    || this.statement.isClosed()) {
-                throw new AppenderLoggingException(
-                        "Cannot write logging event; JDBC manager not connected to the database.");
-            }
-
-            int i = 1;
-            for (final ColumnMapping mapping : this.columnMappings) {
-                if (ThreadContextMap.class.isAssignableFrom(mapping.getType())
-                    || ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(i++, event.getContextData().toMap());
-                } else if (ThreadContextStack.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(i++, event.getContextStack().asList());
-                } else if (Date.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setObject(i++,
-                        DateTypeConverter.fromMillis(event.getTimeMillis(), mapping.getType().asSubclass(Date.class)));
-                } else if (Clob.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setClob(i++, new StringReader(mapping.getLayout().toSerializable(event)));
-                } else if (NClob.class.isAssignableFrom(mapping.getType())) {
-                    this.statement.setNClob(i++, new StringReader(mapping.getLayout().toSerializable(event)));
-                } else {
-                    final Object value = TypeConverters.convert(mapping.getLayout().toSerializable(event),
-                        mapping.getType(), null);
-                    if (value == null) {
-                        this.statement.setNull(i++, Types.NULL);
-                    } else {
-                        this.statement.setObject(i++, value);
-                    }
-                }
-            }
-            for (final ColumnConfig column : this.columnConfigs) {
-                if (column.isEventTimestamp()) {
-                    this.statement.setTimestamp(i++, new Timestamp(event.getTimeMillis()));
-                } else if (column.isClob()) {
-                    reader = new StringReader(column.getLayout().toSerializable(event));
-                    if (column.isUnicode()) {
-                        this.statement.setNClob(i++, reader);
-                    } else {
-                        this.statement.setClob(i++, reader);
-                    }
-                } else if (column.isUnicode()) {
-                    this.statement.setNString(i++, column.getLayout().toSerializable(event));
-                } else {
-                    this.statement.setString(i++, column.getLayout().toSerializable(event));
-                }
-            }
-
-            if (this.isBatchSupported) {
-                this.statement.addBatch();
-            } else if (this.statement.executeUpdate() == 0) {
-                throw new AppenderLoggingException(
-                        "No records inserted in database table for log event in JDBC manager.");
-            }
-        } catch (final SQLException e) {
-            throw new AppenderLoggingException("Failed to insert record for log event in JDBC manager: " +
-                    e.getMessage(), e);
-        } finally {
-            Closer.closeSilently(reader);
-        }
-    }
-
-    @Override
-    protected boolean commitAndClose() {
-        boolean closed = true;
-        try {
-            if (this.connection != null && !this.connection.isClosed()) {
-                if (this.isBatchSupported) {
-                    this.statement.executeBatch();
-                }
-                this.connection.commit();
-            }
-        } catch (final SQLException e) {
-            throw new AppenderLoggingException("Failed to commit transaction logging event or flushing buffer.", e);
-        } finally {
-            try {
-                Closer.close(this.statement);
-            } catch (final Exception e) {
-                logWarn("Failed to close SQL statement logging event or flushing buffer", e);
-                closed = false;
-            } finally {
-                this.statement = null;
-            }
-
-            try {
-                Closer.close(this.connection);
-            } catch (final Exception e) {
-                logWarn("Failed to close database connection logging event or flushing buffer", e);
-                closed = false;
-            } finally {
-                this.connection = null;
-            }
-        }
-        return closed;
-    }
-
-    /**
-     * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists.
-     *
-     * @param name The name of the manager, which should include connection details and hashed passwords where possible.
-     * @param bufferSize The size of the log event buffer.
-     * @param connectionSource The source for connections to the database.
-     * @param tableName The name of the database table to insert log events into.
-     * @param columnConfigs Configuration information about the log table columns.
-     * @return a new or existing JDBC manager as applicable.
-     * @deprecated use {@link #getManager(String, int, ConnectionSource, String, ColumnConfig[], ColumnMapping[])}
-     */
-    @Deprecated
-    public static JdbcDatabaseManager getJDBCDatabaseManager(final String name, final int bufferSize,
-                                                             final ConnectionSource connectionSource,
-                                                             final String tableName,
-                                                             final ColumnConfig[] columnConfigs) {
-
-        return getManager(name,
-            new FactoryData(bufferSize, connectionSource, tableName, columnConfigs, new ColumnMapping[0]),
-            getFactory());
-    }
-
-    /**
-     * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists.
-     *
-     * @param name The name of the manager, which should include connection details and hashed passwords where possible.
-     * @param bufferSize The size of the log event buffer.
-     * @param connectionSource The source for connections to the database.
-     * @param tableName The name of the database table to insert log events into.
-     * @param columnConfigs Configuration information about the log table columns.
-     * @param columnMappings column mapping configuration (including type conversion).
-     * @return a new or existing JDBC manager as applicable.
-     */
-    public static JdbcDatabaseManager getManager(final String name,
-                                                 final int bufferSize,
-                                                 final ConnectionSource connectionSource,
-                                                 final String tableName,
-                                                 final ColumnConfig[] columnConfigs,
-                                                 final ColumnMapping[] columnMappings) {
-        return getManager(name, new FactoryData(bufferSize, connectionSource, tableName, columnConfigs, columnMappings),
-            getFactory());
-    }
-
-    private static JdbcDatabaseManagerFactory getFactory() {
-        return INSTANCE;
-    }
-
-    /**
-     * Encapsulates data that {@link JdbcDatabaseManagerFactory} uses to create managers.
-     */
-    private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
-        private final ConnectionSource connectionSource;
-        private final String tableName;
-        private final ColumnConfig[] columnConfigs;
-        private final ColumnMapping[] columnMappings;
-
-        protected FactoryData(final int bufferSize, final ConnectionSource connectionSource, final String tableName,
-                              final ColumnConfig[] columnConfigs, final ColumnMapping[] columnMappings) {
-            super(bufferSize);
-            this.connectionSource = connectionSource;
-            this.tableName = tableName;
-            this.columnConfigs = columnConfigs;
-            this.columnMappings = columnMappings;
-        }
-    }
-
-    /**
-     * Creates managers.
-     */
-    private static final class JdbcDatabaseManagerFactory implements ManagerFactory<JdbcDatabaseManager, FactoryData> {
-        @Override
-        public JdbcDatabaseManager createManager(final String name, final FactoryData data) {
-            final StringBuilder sb = new StringBuilder("INSERT INTO ").append(data.tableName).append(" (");
-            // so this gets a little more complicated now that there are two ways to configure column mappings, but
-            // both mappings follow the same exact pattern for the prepared statement
-            for (final ColumnMapping mapping : data.columnMappings) {
-                sb.append(mapping.getName()).append(',');
-            }
-            for (final ColumnConfig config : data.columnConfigs) {
-                sb.append(config.getColumnName()).append(',');
-            }
-            // at least one of those arrays is guaranteed to be non-empty
-            sb.setCharAt(sb.length() - 1, ')');
-            sb.append(" VALUES (");
-            final List<ColumnMapping> columnMappings = new ArrayList<>(data.columnMappings.length);
-            for (final ColumnMapping mapping : data.columnMappings) {
-                if (Strings.isNotEmpty(mapping.getLiteralValue())) {
-                    sb.append(mapping.getLiteralValue());
-                } else {
-                    sb.append('?');
-                    columnMappings.add(mapping);
-                }
-                sb.append(',');
-            }
-            final List<ColumnConfig> columnConfigs = new ArrayList<>(data.columnConfigs.length);
-            for (final ColumnConfig config : data.columnConfigs) {
-                if (Strings.isNotEmpty(config.getLiteralValue())) {
-                    sb.append(config.getLiteralValue());
-                } else {
-                    sb.append('?');
-                    columnConfigs.add(config);
-                }
-                sb.append(',');
-            }
-            // at least one of those arrays is guaranteed to be non-empty
-            sb.setCharAt(sb.length() - 1, ')');
-            final String sqlStatement = sb.toString();
-
-            return new JdbcDatabaseManager(name, data.getBufferSize(), data.connectionSource, sqlStatement,
-                columnConfigs, columnMappings);
-        }
-    }
-
-}
+/*
+ * 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.core.appender.db.jdbc;
+
+import java.io.Serializable;
+import java.io.StringReader;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
+import org.apache.logging.log4j.core.appender.db.ColumnMapping;
+import org.apache.logging.log4j.core.config.plugins.convert.DateTypeConverter;
+import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters;
+import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.apache.logging.log4j.spi.ThreadContextStack;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JDBC.
+ */
+public final class JdbcDatabaseManager extends AbstractDatabaseManager {
+
+    private static final JdbcDatabaseManagerFactory INSTANCE = new JdbcDatabaseManagerFactory();
+
+    // NOTE: prepared statements are prepared in this order: column mappings, then column configs
+    private final List<ColumnMapping> columnMappings;
+    private final List<ColumnConfig> columnConfigs;
+    private final ConnectionSource connectionSource;
+    private final String sqlStatement;
+
+    private Connection connection;
+    private PreparedStatement statement;
+    private boolean isBatchSupported;
+
+    private JdbcDatabaseManager(final String name, final int bufferSize, final ConnectionSource connectionSource,
+                                final String sqlStatement, final List<ColumnConfig> columnConfigs,
+                                final List<ColumnMapping> columnMappings) {
+        super(name, bufferSize);
+        this.connectionSource = connectionSource;
+        this.sqlStatement = sqlStatement;
+        this.columnConfigs = columnConfigs;
+        this.columnMappings = columnMappings;
+    }
+
+    @Override
+    protected void startupInternal() throws Exception {
+        this.connection = this.connectionSource.getConnection();
+        final DatabaseMetaData metaData = this.connection.getMetaData();
+        this.isBatchSupported = metaData.supportsBatchUpdates();
+        Closer.closeSilently(this.connection);
+    }
+
+    @Override
+    protected boolean shutdownInternal() {
+        if (this.connection != null || this.statement != null) {
+            return this.commitAndClose();
+        }
+        return true;
+    }
+
+    @Override
+    protected void connectAndStart() {
+        try {
+            this.connection = this.connectionSource.getConnection();
+            this.connection.setAutoCommit(false);
+            this.statement = this.connection.prepareStatement(this.sqlStatement);
+        } catch (final SQLException e) {
+            throw new AppenderLoggingException(
+                    "Cannot write logging event or flush buffer; JDBC manager cannot connect to the database.", e
+            );
+        }
+    }
+
+    @Deprecated
+    @Override
+    protected void writeInternal(final LogEvent event) {
+        writeInternal(event, null);
+    }
+    
+    @Override
+    protected void writeInternal(final LogEvent event, final Serializable serializable) {
+        StringReader reader = null;
+        try {
+            if (!this.isRunning() || this.connection == null || this.connection.isClosed() || this.statement == null
+                    || this.statement.isClosed()) {
+                throw new AppenderLoggingException(
+                        "Cannot write logging event; JDBC manager not connected to the database.");
+            }
+
+            int i = 1;
+            for (final ColumnMapping mapping : this.columnMappings) {
+                if (ThreadContextMap.class.isAssignableFrom(mapping.getType())
+                    || ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) {
+                    this.statement.setObject(i++, event.getContextData().toMap());
+                } else if (ThreadContextStack.class.isAssignableFrom(mapping.getType())) {
+                    this.statement.setObject(i++, event.getContextStack().asList());
+                } else if (Date.class.isAssignableFrom(mapping.getType())) {
+                    this.statement.setObject(i++,
+                        DateTypeConverter.fromMillis(event.getTimeMillis(), mapping.getType().asSubclass(Date.class)));
+                } else if (Clob.class.isAssignableFrom(mapping.getType())) {
+                    this.statement.setClob(i++, new StringReader(mapping.getLayout().toSerializable(event)));
+                } else if (NClob.class.isAssignableFrom(mapping.getType())) {
+                    this.statement.setNClob(i++, new StringReader(mapping.getLayout().toSerializable(event)));
+                } else {
+                    final Object value = TypeConverters.convert(mapping.getLayout().toSerializable(event),
+                        mapping.getType(), null);
+                    if (value == null) {
+                        this.statement.setNull(i++, Types.NULL);
+                    } else {
+                        this.statement.setObject(i++, value);
+                    }
+                }
+            }
+            for (final ColumnConfig column : this.columnConfigs) {
+                if (column.isEventTimestamp()) {
+                    this.statement.setTimestamp(i++, new Timestamp(event.getTimeMillis()));
+                } else if (column.isClob()) {
+                    reader = new StringReader(column.getLayout().toSerializable(event));
+                    if (column.isUnicode()) {
+                        this.statement.setNClob(i++, reader);
+                    } else {
+                        this.statement.setClob(i++, reader);
+                    }
+                } else if (column.isUnicode()) {
+                    this.statement.setNString(i++, column.getLayout().toSerializable(event));
+                } else {
+                    this.statement.setString(i++, column.getLayout().toSerializable(event));
+                }
+            }
+
+            if (this.isBatchSupported) {
+                this.statement.addBatch();
+            } else if (this.statement.executeUpdate() == 0) {
+                throw new AppenderLoggingException(
+                        "No records inserted in database table for log event in JDBC manager.");
+            }
+        } catch (final SQLException e) {
+            throw new AppenderLoggingException("Failed to insert record for log event in JDBC manager: " +
+                    e.getMessage(), e);
+        } finally {
+            Closer.closeSilently(reader);
+        }
+    }
+
+    @Override
+    protected boolean commitAndClose() {
+        boolean closed = true;
+        try {
+            if (this.connection != null && !this.connection.isClosed()) {
+                if (this.isBatchSupported) {
+                    this.statement.executeBatch();
+                }
+                this.connection.commit();
+            }
+        } catch (final SQLException e) {
+            throw new AppenderLoggingException("Failed to commit transaction logging event or flushing buffer.", e);
+        } finally {
+            try {
+                Closer.close(this.statement);
+            } catch (final Exception e) {
+                logWarn("Failed to close SQL statement logging event or flushing buffer", e);
+                closed = false;
+            } finally {
+                this.statement = null;
+            }
+
+            try {
+                Closer.close(this.connection);
+            } catch (final Exception e) {
+                logWarn("Failed to close database connection logging event or flushing buffer", e);
+                closed = false;
+            } finally {
+                this.connection = null;
+            }
+        }
+        return closed;
+    }
+
+    /**
+     * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists.
+     *
+     * @param name The name of the manager, which should include connection details and hashed passwords where possible.
+     * @param bufferSize The size of the log event buffer.
+     * @param connectionSource The source for connections to the database.
+     * @param tableName The name of the database table to insert log events into.
+     * @param columnConfigs Configuration information about the log table columns.
+     * @return a new or existing JDBC manager as applicable.
+     * @deprecated use {@link #getManager(String, int, ConnectionSource, String, ColumnConfig[], ColumnMapping[])}
+     */
+    @Deprecated
+    public static JdbcDatabaseManager getJDBCDatabaseManager(final String name, final int bufferSize,
+                                                             final ConnectionSource connectionSource,
+                                                             final String tableName,
+                                                             final ColumnConfig[] columnConfigs) {
+
+        return getManager(name,
+            new FactoryData(bufferSize, connectionSource, tableName, columnConfigs, new ColumnMapping[0]),
+            getFactory());
+    }
+
+    /**
+     * Creates a JDBC manager for use within the {@link JdbcAppender}, or returns a suitable one if it already exists.
+     *
+     * @param name The name of the manager, which should include connection details and hashed passwords where possible.
+     * @param bufferSize The size of the log event buffer.
+     * @param connectionSource The source for connections to the database.
+     * @param tableName The name of the database table to insert log events into.
+     * @param columnConfigs Configuration information about the log table columns.
+     * @param columnMappings column mapping configuration (including type conversion).
+     * @return a new or existing JDBC manager as applicable.
+     */
+    public static JdbcDatabaseManager getManager(final String name,
+                                                 final int bufferSize,
+                                                 final ConnectionSource connectionSource,
+                                                 final String tableName,
+                                                 final ColumnConfig[] columnConfigs,
+                                                 final ColumnMapping[] columnMappings) {
+        return getManager(name, new FactoryData(bufferSize, connectionSource, tableName, columnConfigs, columnMappings),
+            getFactory());
+    }
+
+    private static JdbcDatabaseManagerFactory getFactory() {
+        return INSTANCE;
+    }
+
+    /**
+     * Encapsulates data that {@link JdbcDatabaseManagerFactory} uses to create managers.
+     */
+    private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
+        private final ConnectionSource connectionSource;
+        private final String tableName;
+        private final ColumnConfig[] columnConfigs;
+        private final ColumnMapping[] columnMappings;
+
+        protected FactoryData(final int bufferSize, final ConnectionSource connectionSource, final String tableName,
+                              final ColumnConfig[] columnConfigs, final ColumnMapping[] columnMappings) {
+            super(bufferSize);
+            this.connectionSource = connectionSource;
+            this.tableName = tableName;
+            this.columnConfigs = columnConfigs;
+            this.columnMappings = columnMappings;
+        }
+    }
+
+    /**
+     * Creates managers.
+     */
+    private static final class JdbcDatabaseManagerFactory implements ManagerFactory<JdbcDatabaseManager, FactoryData> {
+        @Override
+        public JdbcDatabaseManager createManager(final String name, final FactoryData data) {
+            final StringBuilder sb = new StringBuilder("INSERT INTO ").append(data.tableName).append(" (");
+            // so this gets a little more complicated now that there are two ways to configure column mappings, but
+            // both mappings follow the same exact pattern for the prepared statement
+            for (final ColumnMapping mapping : data.columnMappings) {
+                sb.append(mapping.getName()).append(',');
+            }
+            for (final ColumnConfig config : data.columnConfigs) {
+                sb.append(config.getColumnName()).append(',');
+            }
+            // at least one of those arrays is guaranteed to be non-empty
+            sb.setCharAt(sb.length() - 1, ')');
+            sb.append(" VALUES (");
+            final List<ColumnMapping> columnMappings = new ArrayList<>(data.columnMappings.length);
+            for (final ColumnMapping mapping : data.columnMappings) {
+                if (Strings.isNotEmpty(mapping.getLiteralValue())) {
+                    sb.append(mapping.getLiteralValue());
+                } else {
+                    sb.append('?');
+                    columnMappings.add(mapping);
+                }
+                sb.append(',');
+            }
+            final List<ColumnConfig> columnConfigs = new ArrayList<>(data.columnConfigs.length);
+            for (final ColumnConfig config : data.columnConfigs) {
+                if (Strings.isNotEmpty(config.getLiteralValue())) {
+                    sb.append(config.getLiteralValue());
+                } else {
+                    sb.append('?');
+                    columnConfigs.add(config);
+                }
+                sb.append(',');
+            }
+            // at least one of those arrays is guaranteed to be non-empty
+            sb.setCharAt(sb.length() - 1, ')');
+            final String sqlStatement = sb.toString();
+
+            return new JdbcDatabaseManager(name, data.getBufferSize(), data.connectionSource, sqlStatement,
+                columnConfigs, columnMappings);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java
index d5fc922..ddfa18e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java
@@ -1,193 +1,200 @@
-/*
- * 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.core.appender.db.jpa;
-
-import java.lang.reflect.Constructor;
-
-import javax.persistence.EntityManager;
-import javax.persistence.EntityManagerFactory;
-import javax.persistence.EntityTransaction;
-import javax.persistence.Persistence;
-
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AppenderLoggingException;
-import org.apache.logging.log4j.core.appender.ManagerFactory;
-import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
-
-/**
- * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA.
- */
-public final class JpaDatabaseManager extends AbstractDatabaseManager {
-    private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory();
-
-    private final String entityClassName;
-    private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
-    private final String persistenceUnitName;
-
-    private EntityManagerFactory entityManagerFactory;
-
-    private EntityManager entityManager;
-    private EntityTransaction transaction;
-
-    private JpaDatabaseManager(final String name, final int bufferSize,
-                               final Class<? extends AbstractLogEventWrapperEntity> entityClass,
-                               final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
-                               final String persistenceUnitName) {
-        super(name, bufferSize);
-        this.entityClassName = entityClass.getName();
-        this.entityConstructor = entityConstructor;
-        this.persistenceUnitName = persistenceUnitName;
-    }
-
-    @Override
-    protected void startupInternal() {
-        this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName);
-    }
-
-    @Override
-    protected boolean shutdownInternal() {
-        boolean closed = true;
-        if (this.entityManager != null || this.transaction != null) {
-            closed &= this.commitAndClose();
-        }
-        if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) {
-            this.entityManagerFactory.close();
-        }
-        return closed;
-    }
-
-    @Override
-    protected void connectAndStart() {
-        try {
-            this.entityManager = this.entityManagerFactory.createEntityManager();
-            this.transaction = this.entityManager.getTransaction();
-            this.transaction.begin();
-        } catch (final Exception e) {
-            throw new AppenderLoggingException(
-                    "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e
-            );
-        }
-    }
-
-    @Override
-    protected void writeInternal(final LogEvent event) {
-        if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null
-                || this.transaction == null) {
-            throw new AppenderLoggingException(
-                    "Cannot write logging event; JPA manager not connected to the database.");
-        }
-
-        AbstractLogEventWrapperEntity entity;
-        try {
-            entity = this.entityConstructor.newInstance(event);
-        } catch (final Exception e) {
-            throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e);
-        }
-
-        try {
-            this.entityManager.persist(entity);
-        } catch (final Exception e) {
-            if (this.transaction != null && this.transaction.isActive()) {
-                this.transaction.rollback();
-                this.transaction = null;
-            }
-            throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " +
-                    e.getMessage(), e);
-        }
-    }
-
-    @Override
-    protected boolean commitAndClose() {
-        boolean closed = true;
-        try {
-            if (this.transaction != null && this.transaction.isActive()) {
-                this.transaction.commit();
-            }
-        } catch (final Exception e) {
-            if (this.transaction != null && this.transaction.isActive()) {
-                this.transaction.rollback();
-            }
-        } finally {
-            this.transaction = null;
-            try {
-                if (this.entityManager != null && this.entityManager.isOpen()) {
-                    this.entityManager.close();
-                }
-            } catch (final Exception e) {
-                logWarn("Failed to close entity manager while logging event or flushing buffer", e);
-                closed = false;
-            } finally {
-                this.entityManager = null;
-            }
-        }
-        return closed;
-    }
-
-    /**
-     * Creates a JPA manager for use within the {@link JpaAppender}, or returns a suitable one if it already exists.
-     *
-     * @param name The name of the manager, which should include connection details, entity class name, etc.
-     * @param bufferSize The size of the log event buffer.
-     * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete
-     *                    implementation.
-     * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class.
-     * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events.
-     * @return a new or existing JPA manager as applicable.
-     */
-    public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize,
-                                                           final Class<? extends AbstractLogEventWrapperEntity>
-                                                                   entityClass,
-                                                           final Constructor<? extends AbstractLogEventWrapperEntity>
-                                                                   entityConstructor,
-                                                           final String persistenceUnitName) {
-
-        return AbstractDatabaseManager.getManager(
-                name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY
-        );
-    }
-
-    /**
-     * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers.
-     */
-    private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
-        private final Class<? extends AbstractLogEventWrapperEntity> entityClass;
-        private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
-        private final String persistenceUnitName;
-
-        protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass,
-                              final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
-                              final String persistenceUnitName) {
-            super(bufferSize);
-
-            this.entityClass = entityClass;
-            this.entityConstructor = entityConstructor;
-            this.persistenceUnitName = persistenceUnitName;
-        }
-    }
-
-    /**
-     * Creates managers.
-     */
-    private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> {
-        @Override
-        public JpaDatabaseManager createManager(final String name, final FactoryData data) {
-            return new JpaDatabaseManager(
-                    name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName
-            );
-        }
-    }
-}
+/*
+ * 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.core.appender.db.jpa;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.Persistence;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
+
+/**
+ * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JPA.
+ */
+public final class JpaDatabaseManager extends AbstractDatabaseManager {
+    private static final JPADatabaseManagerFactory FACTORY = new JPADatabaseManagerFactory();
+
+    private final String entityClassName;
+    private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
+    private final String persistenceUnitName;
+
+    private EntityManagerFactory entityManagerFactory;
+
+    private EntityManager entityManager;
+    private EntityTransaction transaction;
+
+    private JpaDatabaseManager(final String name, final int bufferSize,
+                               final Class<? extends AbstractLogEventWrapperEntity> entityClass,
+                               final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
+                               final String persistenceUnitName) {
+        super(name, bufferSize);
+        this.entityClassName = entityClass.getName();
+        this.entityConstructor = entityConstructor;
+        this.persistenceUnitName = persistenceUnitName;
+    }
+
+    @Override
+    protected void startupInternal() {
+        this.entityManagerFactory = Persistence.createEntityManagerFactory(this.persistenceUnitName);
+    }
+
+    @Override
+    protected boolean shutdownInternal() {
+        boolean closed = true;
+        if (this.entityManager != null || this.transaction != null) {
+            closed &= this.commitAndClose();
+        }
+        if (this.entityManagerFactory != null && this.entityManagerFactory.isOpen()) {
+            this.entityManagerFactory.close();
+        }
+        return closed;
+    }
+
+    @Override
+    protected void connectAndStart() {
+        try {
+            this.entityManager = this.entityManagerFactory.createEntityManager();
+            this.transaction = this.entityManager.getTransaction();
+            this.transaction.begin();
+        } catch (final Exception e) {
+            throw new AppenderLoggingException(
+                    "Cannot write logging event or flush buffer; manager cannot create EntityManager or transaction.", e
+            );
+        }
+    }
+
+    @Deprecated
+    @Override
+    protected void writeInternal(final LogEvent event) {
+        writeInternal(event, null);
+    }
+    
+    @Override
+    protected void writeInternal(final LogEvent event, final Serializable serializable) {
+        if (!this.isRunning() || this.entityManagerFactory == null || this.entityManager == null
+                || this.transaction == null) {
+            throw new AppenderLoggingException(
+                    "Cannot write logging event; JPA manager not connected to the database.");
+        }
+
+        AbstractLogEventWrapperEntity entity;
+        try {
+            entity = this.entityConstructor.newInstance(event);
+        } catch (final Exception e) {
+            throw new AppenderLoggingException("Failed to instantiate entity class [" + this.entityClassName + "].", e);
+        }
+
+        try {
+            this.entityManager.persist(entity);
+        } catch (final Exception e) {
+            if (this.transaction != null && this.transaction.isActive()) {
+                this.transaction.rollback();
+                this.transaction = null;
+            }
+            throw new AppenderLoggingException("Failed to insert record for log event in JPA manager: " +
+                    e.getMessage(), e);
+        }
+    }
+
+    @Override
+    protected boolean commitAndClose() {
+        boolean closed = true;
+        try {
+            if (this.transaction != null && this.transaction.isActive()) {
+                this.transaction.commit();
+            }
+        } catch (final Exception e) {
+            if (this.transaction != null && this.transaction.isActive()) {
+                this.transaction.rollback();
+            }
+        } finally {
+            this.transaction = null;
+            try {
+                if (this.entityManager != null && this.entityManager.isOpen()) {
+                    this.entityManager.close();
+                }
+            } catch (final Exception e) {
+                logWarn("Failed to close entity manager while logging event or flushing buffer", e);
+                closed = false;
+            } finally {
+                this.entityManager = null;
+            }
+        }
+        return closed;
+    }
+
+    /**
+     * Creates a JPA manager for use within the {@link JpaAppender}, or returns a suitable one if it already exists.
+     *
+     * @param name The name of the manager, which should include connection details, entity class name, etc.
+     * @param bufferSize The size of the log event buffer.
+     * @param entityClass The fully-qualified class name of the {@link AbstractLogEventWrapperEntity} concrete
+     *                    implementation.
+     * @param entityConstructor The one-arg {@link LogEvent} constructor for the concrete entity class.
+     * @param persistenceUnitName The name of the JPA persistence unit that should be used for persisting log events.
+     * @return a new or existing JPA manager as applicable.
+     */
+    public static JpaDatabaseManager getJPADatabaseManager(final String name, final int bufferSize,
+                                                           final Class<? extends AbstractLogEventWrapperEntity>
+                                                                   entityClass,
+                                                           final Constructor<? extends AbstractLogEventWrapperEntity>
+                                                                   entityConstructor,
+                                                           final String persistenceUnitName) {
+
+        return AbstractDatabaseManager.getManager(
+                name, new FactoryData(bufferSize, entityClass, entityConstructor, persistenceUnitName), FACTORY
+        );
+    }
+
+    /**
+     * Encapsulates data that {@link JPADatabaseManagerFactory} uses to create managers.
+     */
+    private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
+        private final Class<? extends AbstractLogEventWrapperEntity> entityClass;
+        private final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor;
+        private final String persistenceUnitName;
+
+        protected FactoryData(final int bufferSize, final Class<? extends AbstractLogEventWrapperEntity> entityClass,
+                              final Constructor<? extends AbstractLogEventWrapperEntity> entityConstructor,
+                              final String persistenceUnitName) {
+            super(bufferSize);
+
+            this.entityClass = entityClass;
+            this.entityConstructor = entityConstructor;
+            this.persistenceUnitName = persistenceUnitName;
+        }
+    }
+
+    /**
+     * Creates managers.
+     */
+    private static final class JPADatabaseManagerFactory implements ManagerFactory<JpaDatabaseManager, FactoryData> {
+        @Override
+        public JpaDatabaseManager createManager(final String name, final FactoryData data) {
+            return new JpaDatabaseManager(
+                    name, data.getBufferSize(), data.entityClass, data.entityConstructor, data.persistenceUnitName
+            );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/26fc9e07/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
index 57e8c75..1704018 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
@@ -266,7 +266,7 @@ public class JmsAppender extends AbstractAppender {
 
     @Override
     public void append(final LogEvent event) {
-        this.manager.send(event, getLayout().toSerializable(event));
+        this.manager.send(event, toSerializable(event));
     }
 
     public JmsManager getManager() {