You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2014/09/06 06:58:35 UTC

[02/29] Split out JMS appender and receiver into new log4j module.

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>log4j</artifactId>
+    <groupId>org.apache.logging.log4j</groupId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>log4j-mom</artifactId>
+  <name>Log4j 2 MOM Plugins</name>
+  <description>Message Oriented Middleware plugins for Log4j 2</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.spec.javax.jms</groupId>
+      <artifactId>jboss-jms-api_1.1_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockejb</groupId>
+      <artifactId>mockejb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <version>1.9.5</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
@@ -0,0 +1,111 @@
+/*
+ * 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.mom.jms.appender;
+
+import java.io.Serializable;
+
+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;
+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.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
+@Plugin(name = "JMSQueue", category = "Core", elementType = "appender", printObject = true)
+public final class JmsQueueAppender extends AbstractAppender {
+
+    private final JmsQueueManager manager;
+
+    private JmsQueueAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+                             final JmsQueueManager manager, final boolean ignoreExceptions) {
+        super(name, filter, layout, ignoreExceptions);
+        this.manager = manager;
+    }
+
+    /**
+     * Actual writing occurs here.
+     * 
+     * @param event The LogEvent.
+     */
+    @Override
+    public void append(final LogEvent event) {
+        try {
+            manager.send(getLayout().toSerializable(event));
+        } catch (final Exception ex) {
+            throw new AppenderLoggingException(ex);
+        }
+    }
+
+    /**
+     * Create a JmsQueueAppender.
+     * @param name The name of the Appender.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory.
+     * @param queueBindingName The name to use to locate the Queue.
+     * @param userName The user ID to use to create the Queue Connection.
+     * @param password The password to use to create the Queue Connection.
+     * @param layout The layout to use (defaults to SerializedLayout).
+     * @param filter The Filter or null.
+     * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
+     *               they are propagated to the caller.
+     * @return The JmsQueueAppender.
+     */
+    @PluginFactory
+    public static JmsQueueAppender createAppender(
+            @PluginAttribute("name") final String name,
+            @PluginAttribute("factoryName") final String factoryName,
+            @PluginAttribute("providerURL") final String providerURL,
+            @PluginAttribute("urlPkgPrefixes") final String urlPkgPrefixes,
+            @PluginAttribute("securityPrincipalName") final String securityPrincipalName,
+            @PluginAttribute("securityCredentials") final String securityCredentials,
+            @PluginAttribute("factoryBindingName") final String factoryBindingName,
+            @PluginAttribute("queueBindingName") final String queueBindingName,
+            @PluginAttribute("userName") final String userName,
+            @PluginAttribute("password") final String password,
+            @PluginElement("Layout") Layout<? extends Serializable> layout,
+            @PluginElement("Filter") final Filter filter,
+            @PluginAttribute("ignoreExceptions") final String ignore) {
+        if (name == null) {
+            LOGGER.error("No name provided for JmsQueueAppender");
+            return null;
+        }
+        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+        final JmsQueueManager manager = JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
+            securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password);
+        if (manager == null) {
+            return null;
+        }
+        if (layout == null) {
+            layout = SerializedLayout.createLayout();
+        }
+        return new JmsQueueAppender(name, filter, layout, manager, ignoreExceptions);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
new file mode 100644
index 0000000..9985746
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
@@ -0,0 +1,112 @@
+/*
+ * 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.mom.jms.appender;
+
+import java.io.Serializable;
+
+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;
+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.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
+@Plugin(name = "JMSTopic", category = "Core", elementType = "appender", printObject = true)
+public final class JmsTopicAppender extends AbstractAppender {
+
+    private final JmsTopicManager manager;
+
+    private JmsTopicAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+                             final JmsTopicManager manager, final boolean ignoreExceptions) {
+        super(name, filter, layout, ignoreExceptions);
+        this.manager = manager;
+    }
+
+    /**
+     * Actual writing occurs here.
+     * <p/>
+     * @param event The LogEvent.
+     */
+    @Override
+    public void append(final LogEvent event) {
+        try {
+            manager.send(getLayout().toSerializable(event));
+        } catch (final Exception ex) {
+            throw new AppenderLoggingException(ex);
+        }
+    }
+
+    /**
+     * Create a JmsTopicAppender.
+     * @param name The name of the Appender.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
+     * @param topicBindingName The name to use to locate the Topic.
+     * @param userName The userid to use to create the Topic Connection.
+     * @param password The password to use to create the Topic Connection.
+     * @param layout The layout to use (defaults to SerializedLayout).
+     * @param filter The Filter or null.
+     * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
+     *               they are propagated to the caller.
+     * @return The JmsTopicAppender.
+     */
+    @PluginFactory
+    public static JmsTopicAppender createAppender(
+            @PluginAttribute("name") final String name,
+            @PluginAttribute("factoryName") final String factoryName,
+            @PluginAttribute("providerURL") final String providerURL,
+            @PluginAttribute("urlPkgPrefixes") final String urlPkgPrefixes,
+            @PluginAttribute("securityPrincipalName") final String securityPrincipalName,
+            @PluginAttribute("securityCredentials") final String securityCredentials,
+            @PluginAttribute("factoryBindingName") final String factoryBindingName,
+            @PluginAttribute("topicBindingName") final String topicBindingName,
+            @PluginAttribute("userName") final String userName,
+            @PluginAttribute("password") final String password,
+            @PluginElement("Layout") Layout<? extends Serializable> layout,
+            @PluginElement("Filters") final Filter filter,
+            @PluginAttribute("ignoreExceptions") final String ignore) {
+
+        if (name == null) {
+            LOGGER.error("No name provided for JmsQueueAppender");
+            return null;
+        }
+        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+        final JmsTopicManager manager = JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
+            securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password);
+        if (manager == null) {
+            return null;
+        }
+        if (layout == null) {
+            layout = SerializedLayout.createLayout();
+        }
+        return new JmsTopicAppender(name, filter, layout, manager, ignoreExceptions);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
@@ -0,0 +1,155 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+    /**
+     * The Constructor.
+     * @param name The name of the Appender.
+     */
+    public AbstractJmsManager(final String name) {
+        super(name);
+    }
+
+    /**
+     * Create the InitialContext.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @return the InitialContext.
+     * @throws NamingException if a naming error occurs.
+     */
+    protected static Context createContext(final String factoryName, final String providerURL,
+                                           final String urlPkgPrefixes, final String securityPrincipalName,
+                                           final String securityCredentials)
+        throws NamingException {
+
+        final Properties props = getEnvironment(factoryName, providerURL, urlPkgPrefixes, securityPrincipalName,
+                                          securityCredentials);
+        return new InitialContext(props);
+    }
+
+    /**
+     * Looks up the name in the context.
+     * @param ctx The Context.
+     * @param name The name to locate.
+     * @return The object to be located.
+     * @throws NamingException If an error occurs locating the name.
+     */
+    protected static Object lookup(final Context ctx, final String name) throws NamingException {
+        try {
+            return ctx.lookup(name);
+        } catch (final NameNotFoundException e) {
+            LOGGER.warn("Could not find name [{}].", name);
+            throw e;
+        }
+    }
+
+    /**
+     * Sets up the properties to pass to the InitialContext.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @return The Properties.
+     */
+    protected static Properties getEnvironment(final String factoryName, final String providerURL,
+                                               final String urlPkgPrefixes, final String securityPrincipalName,
+                                               final String securityCredentials) {
+        final Properties props = new Properties();
+        if (factoryName != null) {
+            props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+            if (providerURL != null) {
+                props.put(Context.PROVIDER_URL, providerURL);
+            } else {
+                LOGGER.warn("The InitialContext factory name has been provided without a ProviderURL. " +
+                    "This is likely to cause problems");
+            }
+            if (urlPkgPrefixes != null) {
+                props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+            }
+            if (securityPrincipalName != null) {
+                props.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+                if (securityCredentials != null) {
+                    props.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+                } else {
+                    LOGGER.warn("SecurityPrincipalName has been set without SecurityCredentials. " +
+                        "This is likely to cause problems.");
+                }
+            }
+            return props;
+        }
+        return null;
+    }
+
+    /**
+     * Send the message.
+     * @param object The Object to sent.
+     * @throws Exception if an error occurs.
+     */
+    public abstract void send(Serializable object) throws Exception;
+
+    /**
+     * Send the Object.
+     * @param object The Object to send.
+     * @param session The Session.
+     * @param producer The MessageProducer.
+     * @throws Exception if an error occurs.
+     */
+    public synchronized void send(final Serializable object, final Session session, final MessageProducer producer)
+        throws Exception {
+        try {
+            Message msg;
+            if (object instanceof String) {
+                msg = session.createTextMessage();
+                ((TextMessage) msg).setText((String) object);
+            } else {
+                msg = session.createObjectMessage();
+                ((ObjectMessage) msg).setObject(object);
+            }
+            producer.send(msg);
+        } catch (final JMSException ex) {
+            LOGGER.error("Could not publish message via JMS {}", getName());
+            throw ex;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener implements javax.jms.MessageListener {
+
+    /**
+     * Logger to capture diagnostics.
+     */
+    protected Logger logger = LogManager.getLogger(this.getClass().getName());
+
+    /**
+     * Listener that receives the event.
+     * @param message The received message.
+     */
+    @Override
+    public void onMessage(final javax.jms.Message message) {
+        try {
+            if (message instanceof ObjectMessage) {
+                final ObjectMessage objectMessage = (ObjectMessage) message;
+                final Serializable object = objectMessage.getObject();
+                if (object instanceof LogEvent) {
+                    log((LogEvent) object);
+                } else {
+                    logger.warn("Received message is of type " + object.getClass().getName() + ", was expecting LogEvent.");
+                }
+            } else {
+                logger.warn("Received message is of type " + message.getJMSType()
+                    + ", was expecting ObjectMessage.");
+            }
+        } catch (final JMSException jmse) {
+            logger.error("Exception thrown while processing incoming message.",
+                jmse);
+        }
+    }
+
+    /**
+     * Looks up an object from the Context.
+     * @param ctx The Context.
+     * @param name The name of the object to locate.
+     * @return The object.
+     * @throws NamingException if an error occurs.
+     */
+    protected Object lookup(final Context ctx, final String name) throws NamingException {
+        try {
+            return ctx.lookup(name);
+        } catch (final NameNotFoundException e) {
+            logger.error("Could not find name [" + name + "].");
+            throw e;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
new file mode 100644
index 0000000..6825282
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+    private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
+
+    private QueueInfo info;
+    private final String factoryBindingName;
+    private final String queueBindingName;
+    private final String userName;
+    private final String password;
+    private final Context context;
+
+    /**
+     * The Constructor.
+     * @param name The unique name of the connection.
+     * @param context The context.
+     * @param factoryBindingName The factory binding name.
+     * @param queueBindingName The queue binding name.
+     * @param userName The user name.
+     * @param password The credentials for the user.
+     * @param info The Queue connection info.
+     */
+    protected JmsQueueManager(final String name, final Context context, final String factoryBindingName,
+                              final String queueBindingName, final String userName, final String password,
+                              final QueueInfo info) {
+        super(name);
+        this.context = context;
+        this.factoryBindingName = factoryBindingName;
+        this.queueBindingName = queueBindingName;
+        this.userName = userName;
+        this.password = password;
+        this.info = info;
+    }
+
+    /**
+     * Obtain a JmsQueueManager.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory.
+     * @param queueBindingName The name to use to locate the Queue.
+     * @param userName The userid to use to create the Queue Connection.
+     * @param password The password to use to create the Queue Connection.
+     * @return The JmsQueueManager.
+     */
+    public static JmsQueueManager getJmsQueueManager(final String factoryName, final String providerURL,
+                                                     final String urlPkgPrefixes, final String securityPrincipalName,
+                                                     final String securityCredentials, final String factoryBindingName,
+                                                     final String queueBindingName, final String userName,
+                                                     final String password) {
+
+        if (factoryBindingName == null) {
+            LOGGER.error("No factory name provided for JmsQueueManager");
+            return null;
+        }
+        if (queueBindingName == null) {
+            LOGGER.error("No topic name provided for JmsQueueManager");
+            return null;
+        }
+
+        final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
+        return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+            securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
+    }
+
+    @Override
+    public synchronized void send(final Serializable object) throws Exception {
+        if (info == null) {
+            info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
+        }
+        try {
+            super.send(object, info.session, info.sender);
+        } catch (final Exception ex) {
+            cleanup(true);
+            throw ex;
+        }
+    }
+
+    @Override
+    public void releaseSub() {
+        if (info != null) {
+            cleanup(false);
+        }
+    }
+
+    private void cleanup(final boolean quiet) {
+        try {
+            info.session.close();
+        } catch (final Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing session for " + getName(), e);
+            }
+        }
+        try {
+            info.conn.close();
+        } catch (final Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing connection for " + getName(), e);
+            }
+        }
+        info = null;
+    }
+
+    /**
+     * Data for the factory.
+     */
+    private static class FactoryData {
+        private final String factoryName;
+        private final String providerURL;
+        private final String urlPkgPrefixes;
+        private final String securityPrincipalName;
+        private final String securityCredentials;
+        private final String factoryBindingName;
+        private final String queueBindingName;
+        private final String userName;
+        private final String password;
+
+        public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+                           final String securityPrincipalName, final String securityCredentials,
+                           final String factoryBindingName, final String queueBindingName, final String userName,
+                           final String password) {
+            this.factoryName = factoryName;
+            this.providerURL = providerURL;
+            this.urlPkgPrefixes = urlPkgPrefixes;
+            this.securityPrincipalName = securityPrincipalName;
+            this.securityCredentials = securityCredentials;
+            this.factoryBindingName = factoryBindingName;
+            this.queueBindingName = queueBindingName;
+            this.userName = userName;
+            this.password = password;
+        }
+    }
+
+    private static QueueInfo connect(final Context context, final String factoryBindingName,
+                                     final String queueBindingName, final String userName, final String password,
+                                     final boolean suppress) throws Exception {
+        try {
+            final QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
+            QueueConnection conn;
+            if (userName != null) {
+                conn = factory.createQueueConnection(userName, password);
+            } else {
+                conn = factory.createQueueConnection();
+            }
+            final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+            final Queue queue = (Queue) lookup(context, queueBindingName);
+            final QueueSender sender = sess.createSender(queue);
+            conn.start();
+            return new QueueInfo(conn, sess, sender);
+        } catch (final NamingException ex) {
+            LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
+            if (!suppress) {
+                throw ex;
+            }
+        } catch (final JMSException ex) {
+            LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
+            if (!suppress) {
+                throw ex;
+            }
+        }
+        return null;
+    }
+
+    /** Queue connection information */
+    private static class QueueInfo {
+        private final QueueConnection conn;
+        private final QueueSession session;
+        private final QueueSender sender;
+
+        public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
+            this.conn = conn;
+            this.session = session;
+            this.sender = sender;
+        }
+    }
+
+    /**
+     * Factory to create the JmsQueueManager.
+     */
+    private static class JMSQueueManagerFactory implements ManagerFactory<JmsQueueManager, FactoryData> {
+
+        @Override
+        public JmsQueueManager createManager(final String name, final FactoryData data) {
+            try {
+                final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+                                            data.securityPrincipalName, data.securityCredentials);
+                final QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
+                    data.password, true);
+                return new JmsQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
+                    data.userName, data.password, info);
+            } catch (final NamingException ex) {
+                LOGGER.error("Unable to locate resource", ex);
+            } catch (final Exception ex) {
+                LOGGER.error("Unable to connect", ex);
+            }
+
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
new file mode 100644
index 0000000..b231489
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+    /**
+     * Constructor.
+     * @param qcfBindingName The QueueConnectionFactory binding name.
+     * @param queueBindingName The Queue binding name.
+     * @param username The userid to connect to the queue.
+     * @param password The password to connect to the queue.
+     */
+    public JmsQueueReceiver(final String qcfBindingName, final String queueBindingName, final String username,
+                            final String password) {
+
+        try {
+            final Context ctx = new InitialContext();
+            QueueConnectionFactory queueConnectionFactory;
+            queueConnectionFactory = (QueueConnectionFactory) lookup(ctx, qcfBindingName);
+            final QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(username, password);
+            queueConnection.start();
+            final QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+            final Queue queue = (Queue) ctx.lookup(queueBindingName);
+            final QueueReceiver queueReceiver = queueSession.createReceiver(queue);
+            queueReceiver.setMessageListener(this);
+        } catch (final JMSException e) {
+            logger.error("Could not read JMS message.", e);
+        } catch (final NamingException e) {
+            logger.error("Could not read JMS message.", e);
+        } catch (final RuntimeException e) {
+            logger.error("Could not read JMS message.", e);
+        }
+    }
+
+    /**
+     * Main startup for the receiver.
+     * @param args The command line arguments.
+     * @throws Exception if an error occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        if (args.length != 4) {
+            usage("Wrong number of arguments.");
+        }
+
+        final String qcfBindingName = args[0];
+        final String queueBindingName = args[1];
+        final String username = args[2];
+        final String password = args[3];
+
+        new JmsQueueReceiver(qcfBindingName, queueBindingName, username, password);
+
+        final Charset enc = Charset.defaultCharset();
+        final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, enc));
+        // Loop until the word "exit" is typed
+        System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+        while (true) {
+            final String line = stdin.readLine();
+            if (line == null || line.equalsIgnoreCase("exit")) {
+                System.out.println("Exiting. Kill the application if it does not exit "
+                    + "due to daemon threads.");
+                return;
+            }
+        }
+    }
+
+
+    private static void usage(final String msg) {
+        System.err.println(msg);
+        System.err.println("Usage: java " + JmsQueueReceiver.class.getName()
+            + " QueueConnectionFactoryBindingName QueueBindingName username password");
+        System.exit(1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
new file mode 100644
index 0000000..08d7128
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for JMS Topic connections.
+ */
+public class JmsTopicManager extends AbstractJmsManager {
+
+    private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
+
+    private TopicInfo info;
+    private final String factoryBindingName;
+    private final String topicBindingName;
+    private final String userName;
+    private final String password;
+    private final Context context;
+    /**
+     * Constructor.
+     * @param name The unique name of the connection.
+     * @param context The context.
+     * @param factoryBindingName The factory binding name.
+     * @param topicBindingName The queue binding name.
+     * @param userName The user name.
+     * @param password The credentials for the user.
+     * @param info The Queue connection info.
+     */
+    protected JmsTopicManager(final String name, final Context context, final String factoryBindingName,
+                              final String topicBindingName, final String userName, final String password,
+                              final TopicInfo info) {
+        super(name);
+        this.context = context;
+        this.factoryBindingName = factoryBindingName;
+        this.topicBindingName = topicBindingName;
+        this.userName = userName;
+        this.password = password;
+        this.info = info;
+    }
+
+    /**
+     * Obtain a JSMTopicManager.
+     * @param factoryName The fully qualified class name of the InitialContextFactory.
+     * @param providerURL The URL of the provider to use.
+     * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+     * will create a URL context factory
+     * @param securityPrincipalName The name of the identity of the Principal.
+     * @param securityCredentials The security credentials of the Principal.
+     * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
+     * @param topicBindingName The name to use to locate the Topic.
+     * @param userName The userid to use to create the Topic Connection.
+     * @param password The password to use to create the Topic Connection.
+     * @return A JmsTopicManager.
+     */
+    public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL,
+                                                     final String urlPkgPrefixes, final String securityPrincipalName,
+                                                     final String securityCredentials, final String factoryBindingName,
+                                                     final String topicBindingName, final String userName,
+                                                     final String password) {
+
+        if (factoryBindingName == null) {
+            LOGGER.error("No factory name provided for JmsTopicManager");
+            return null;
+        }
+        if (topicBindingName == null) {
+            LOGGER.error("No topic name provided for JmsTopicManager");
+            return null;
+        }
+
+        final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
+        return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+            securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
+    }
+
+
+    @Override
+    public void send(final Serializable object) throws Exception {
+        if (info == null) {
+            info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
+        }
+        try {
+            super.send(object, info.session, info.publisher);
+        } catch (final Exception ex) {
+            cleanup(true);
+            throw ex;
+        }
+    }
+
+    @Override
+    public void releaseSub() {
+        if (info != null) {
+            cleanup(false);
+        }
+    }
+
+    private void cleanup(final boolean quiet) {
+        try {
+            info.session.close();
+        } catch (final Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing session for " + getName(), e);
+            }
+        }
+        try {
+            info.conn.close();
+        } catch (final Exception e) {
+            if (!quiet) {
+                LOGGER.error("Error closing connection for " + getName(), e);
+            }
+        }
+        info = null;
+    }
+
+    /**
+     * Data for the factory.
+     */
+    private static class FactoryData {
+        private final String factoryName;
+        private final String providerURL;
+        private final String urlPkgPrefixes;
+        private final String securityPrincipalName;
+        private final String securityCredentials;
+        private final String factoryBindingName;
+        private final String topicBindingName;
+        private final String userName;
+        private final String password;
+
+        public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+                           final String securityPrincipalName, final String securityCredentials,
+                           final String factoryBindingName, final String topicBindingName,
+                           final String userName, final String password) {
+            this.factoryName = factoryName;
+            this.providerURL = providerURL;
+            this.urlPkgPrefixes = urlPkgPrefixes;
+            this.securityPrincipalName = securityPrincipalName;
+            this.securityCredentials = securityCredentials;
+            this.factoryBindingName = factoryBindingName;
+            this.topicBindingName = topicBindingName;
+            this.userName = userName;
+            this.password = password;
+        }
+    }
+
+    private static TopicInfo connect(final Context context, final String factoryBindingName,
+                                     final String queueBindingName, final String userName, final String password,
+                                     final boolean suppress) throws Exception {
+        try {
+            final TopicConnectionFactory factory = (TopicConnectionFactory) lookup(context, factoryBindingName);
+            TopicConnection conn;
+            if (userName != null) {
+                conn = factory.createTopicConnection(userName, password);
+            } else {
+                conn = factory.createTopicConnection();
+            }
+            final TopicSession sess = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+            final Topic topic = (Topic) lookup(context, queueBindingName);
+            final TopicPublisher publisher = sess.createPublisher(topic);
+            conn.start();
+            return new TopicInfo(conn, sess, publisher);
+        } catch (final NamingException ex) {
+            LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
+            if (!suppress) {
+                throw ex;
+            }
+        } catch (final JMSException ex) {
+            LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
+            if (!suppress) {
+                throw ex;
+            }
+        }
+        return null;
+    }
+
+    /** Topic connection information */
+    private static class TopicInfo {
+        private final TopicConnection conn;
+        private final TopicSession session;
+        private final TopicPublisher publisher;
+
+        public TopicInfo(final TopicConnection conn, final TopicSession session, final TopicPublisher publisher) {
+            this.conn = conn;
+            this.session = session;
+            this.publisher = publisher;
+        }
+    }
+
+    /**
+     * Factory to create the JmsQueueManager.
+     */
+    private static class JMSTopicManagerFactory implements ManagerFactory<JmsTopicManager, FactoryData> {
+
+        @Override
+        public JmsTopicManager createManager(final String name, final FactoryData data) {
+            try {
+                final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+                    data.securityPrincipalName, data.securityCredentials);
+                final TopicInfo info = connect(ctx, data.factoryBindingName, data.topicBindingName, data.userName,
+                    data.password, true);
+                return new JmsTopicManager(name, ctx, data.factoryBindingName, data.topicBindingName,
+                    data.userName, data.password, info);
+            } catch (final NamingException ex) {
+                LOGGER.error("Unable to locate resource", ex);
+            } catch (final Exception ex) {
+                LOGGER.error("Unable to connect", ex);
+            }
+
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicReceiver.java
new file mode 100644
index 0000000..0149ee5
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicReceiver.java
@@ -0,0 +1,105 @@
+/*
+ * 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.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Topic messages that contain LogEvents. This implementation expects that all messages
+ * are serialized log events.
+ */
+public class JmsTopicReceiver extends AbstractJmsReceiver {
+
+    /**
+     * Constructor.
+     * @param tcfBindingName The TopicConnectionFactory binding name.
+     * @param topicBindingName The Topic binding name.
+     * @param username The userid to connect to the topic.
+     * @param password The password to connect to the topic.
+     */
+    public JmsTopicReceiver(final String tcfBindingName, final String topicBindingName, final String username,
+                            final String password) {
+        try {
+            final Context ctx = new InitialContext();
+            TopicConnectionFactory topicConnectionFactory;
+            topicConnectionFactory = (TopicConnectionFactory) lookup(ctx, tcfBindingName);
+            final TopicConnection topicConnection = topicConnectionFactory.createTopicConnection(username, password);
+            topicConnection.start();
+            final TopicSession topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+            final Topic topic = (Topic) ctx.lookup(topicBindingName);
+            final TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
+            topicSubscriber.setMessageListener(this);
+        } catch (final JMSException e) {
+            logger.error("Could not read JMS message.", e);
+        } catch (final NamingException e) {
+            logger.error("Could not read JMS message.", e);
+        } catch (final RuntimeException e) {
+            logger.error("Could not read JMS message.", e);
+        }
+    }
+
+    /**
+     * Main startup for the receiver.
+     * @param args The command line arguments.
+     * @throws Exception if an error occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        if (args.length != 4) {
+            usage("Wrong number of arguments.");
+        }
+
+        final String tcfBindingName = args[0];
+        final String topicBindingName = args[1];
+        final String username = args[2];
+        final String password = args[3];
+
+        new JmsTopicReceiver(tcfBindingName, topicBindingName, username, password);
+
+        final Charset enc = Charset.defaultCharset();
+        final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, enc));
+        // Loop until the word "exit" is typed
+        System.out.println("Type \"exit\" to quit JmsTopicReceiver.");
+        while (true) {
+            final String line = stdin.readLine();
+            if (line == null || line.equalsIgnoreCase("exit")) {
+                System.out.println("Exiting. Kill the application if it does not exit "
+                    + "due to daemon threads.");
+                return;
+            }
+        }
+    }
+
+    private static void usage(final String msg) {
+        System.err.println(msg);
+        System.err.println("Usage: java " + JmsTopicReceiver.class.getName()
+            + " TopicConnectionFactoryBindingName TopicBindingName username password");
+        System.exit(1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/package-info.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/package-info.java
new file mode 100644
index 0000000..1f233e8
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+/**
+ * Supporting network code for JMS appenders.
+ * <p/>
+ * Note that you can use JmsQueueReceiver or JmsTopicReceiver as executable main classes to receive log events over
+ * JMS (sent via the appropriate JMS appender) that can be subsequently logged according to the configuration given to
+ * the running process. Of course, use of these classes as standalone executables are entirely optional and can
+ * be used directly in your application (e.g., through your Spring {@code beans.xml} configuration).
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppenderTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppenderTest.java
new file mode 100644
index 0000000..f05a9fc
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppenderTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.mom.jms.appender;
+
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.mom.jms.receiver.AbstractJmsReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueReceiver;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockejb.jms.MockQueue;
+import org.mockejb.jms.QueueConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class JmsQueueAppenderTest {
+
+    private static final String FACTORY_NAME = "TestQueueConnectionFactory";
+    private static final String QUEUE_NAME = "TestQueue";
+
+    private static Context context;
+    private static AbstractJmsReceiver receiver;
+
+    private static final String CONFIG = "log4j-jmsqueue.xml";
+
+    LoggerContext ctx = (LoggerContext) LogManager.getContext();
+    Logger root = ctx.getLogger("JmsQueueTest");
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        // MockContextFactory becomes the primary JNDI provider
+        final StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
+        StatusLogger.getLogger().registerListener(listener);
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(FACTORY_NAME, new QueueConnectionFactoryImpl());
+        context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        receiver = new JmsQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        StatusLogger.getLogger().reset();
+    }
+
+    @Test
+    public void testConfiguration() throws Exception {
+        final LoggerContext ctx = (LoggerContext) LogManager.getContext();
+        final Configuration config = ctx.getConfiguration();
+        final Map<String, Appender> appenders = config.getAppenders();
+        assertTrue(appenders.containsKey("JMSQueue"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueFailoverTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueFailoverTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueFailoverTest.java
new file mode 100644
index 0000000..25c3068
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueFailoverTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.mom.jms.appender;
+
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.mom.jms.receiver.AbstractJmsReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueReceiver;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockejb.jms.MockQueue;
+import org.mockejb.jms.QueueConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class JmsQueueFailoverTest {
+
+    private static final String FACTORY_NAME = "QueueConnectionFactory";
+    private static final String QUEUE_NAME = "Log4j2Queue";
+
+    private static Context context;
+    private static AbstractJmsReceiver receiver;
+
+    private static final String CONFIG = "log4j-jmsqueue-failover.xml";
+
+    private static Configuration config;
+    private static ListAppender listAppender;
+    private static LoggerContext ctx;
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        setupQueue();
+        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        ctx = (LoggerContext) LogManager.getContext(false);
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
+        ctx.reconfigure();
+        StatusLogger.getLogger().reset();
+    }
+
+    @Before
+    public void before() {
+        config = ctx.getConfiguration();
+        listAppender = (ListAppender) config.getAppender("List");
+        assertNotNull("No Appender", listAppender);
+        listAppender.clear();
+        ThreadContext.clearMap();
+    }
+
+    private static void setupQueue() throws Exception {
+        // MockContextFactory becomes the primary JNDI provider
+        final StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
+        StatusLogger.getLogger().registerListener(listener);
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(FACTORY_NAME, new QueueConnectionFactoryImpl());
+        //context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+        //System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        //receiver = new JmsQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+    }
+
+    @Test
+    public void testFailover() throws Exception {
+        ThreadContext.put("appender", "Failover");
+        final Logger logger = LogManager.getLogger(JmsQueueFailoverTest.class);
+        logger.debug("Test Message");
+        final List<LogEvent> events = listAppender.getEvents();
+        assertNotNull("No events returned", events);
+        assertTrue("No events returned", events.size() > 0);
+        assertTrue("Incorrect event", "Test Message".equals(events.get(0).getMessage().getFormattedMessage()));
+    }
+
+    @Test
+    public void testReconnect() throws Exception {
+        context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+        receiver = new JmsQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+        ThreadContext.put("appender", "Failover");
+        final Logger logger = LogManager.getLogger(JmsQueueFailoverTest.class);
+        logger.debug("Test Message");
+        final List<LogEvent> events = listAppender.getEvents();
+        assertNotNull("No events returned", events);
+        assertTrue("No events returned", events.size() > 0);
+        assertTrue("Incorrect event", "Test Message".equals(events.get(0).getMessage().getFormattedMessage()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueTest.java
new file mode 100644
index 0000000..7a61efa
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.mom.jms.receiver.AbstractJmsReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicReceiver;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockejb.jms.MockQueue;
+import org.mockejb.jms.QueueConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class JmsQueueTest {
+
+    private static final String FACTORY_NAME = "TestQueueConnectionFactory";
+    private static final String QUEUE_NAME = "TestQueue";
+
+    private static Context context;
+    private static AbstractJmsReceiver receiver;
+
+    LoggerContext ctx = (LoggerContext) LogManager.getContext();
+    Logger root = ctx.getLogger("JmsQueueTest");
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        // MockContextFactory becomes the primary JNDI provider
+        final StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
+        StatusLogger.getLogger().registerListener(listener);
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(FACTORY_NAME, new QueueConnectionFactoryImpl());
+        context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+        ((LoggerContext) LogManager.getContext()).reconfigure();
+        receiver = new JmsQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        StatusLogger.getLogger().reset();
+    }
+
+    @After
+    public void teardown() {
+        final Map<String,Appender> map = root.getAppenders();
+        for (final Map.Entry<String, Appender> entry : map.entrySet()) {
+            final Appender app = entry.getValue();
+            root.removeAppender(app);
+            app.stop();
+        }
+    }
+
+    @Test
+    public void testServer() throws Exception {
+        final Filter clientFilter = new MessageFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
+        final Filter serverFilter = new MessageFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
+        final CompositeFilter clientFilters = CompositeFilter.createFilters(new Filter[]{clientFilter});
+        final JmsQueueAppender appender = JmsQueueAppender.createAppender("Test", null, null, null, null, null, FACTORY_NAME,
+                QUEUE_NAME, null, null, null, clientFilters, "true");
+        appender.start();
+        final CompositeFilter serverFilters = CompositeFilter.createFilters(new Filter[]{serverFilter});
+        final ListAppender listApp = new ListAppender("Events", serverFilters, null, false, false);
+        listApp.start();
+        final PatternLayout layout = PatternLayout.newBuilder().withPattern("%m %ex%n").build();
+        final ConsoleAppender console = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false", "true");
+        console.start();
+        final Logger serverLogger = ctx.getLogger(JmsTopicReceiver.class.getName());
+        serverLogger.addAppender(console);
+        serverLogger.setAdditive(false);
+
+
+        // set appender on root and set level to debug
+        root.addAppender(listApp);
+        root.addAppender(appender);
+        root.setAdditive(false);
+        root.setLevel(Level.DEBUG);
+        root.debug("This is a test message");
+        Thread.sleep(100);
+        final List<LogEvent> events = listApp.getEvents();
+        assertNotNull("No event retrieved", events);
+        assertTrue("No events retrieved", events.size() > 0);
+        assertTrue("Incorrect event", events.get(0).getMessage().getFormattedMessage().equals("This is a test message"));
+    }
+
+    private class MessageFilter extends AbstractFilter {
+        public MessageFilter(final Result onMatch, final Result onMismatch) {
+            super(onMatch, onMismatch);
+        }
+
+        @Override
+        public Result filter(final LogEvent event) {
+            final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+            for (final StackTraceElement element : stackTrace) {
+                if (element.getMethodName().equals("onMessage")) {
+                    return onMatch;
+                } else if (element.getMethodName().equals("testServer")) {
+                    return onMismatch;
+                }
+            }
+            return onMismatch;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicFailoverTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicFailoverTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicFailoverTest.java
new file mode 100644
index 0000000..a8de6de
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicFailoverTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.mom.jms.appender;
+
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.mom.jms.receiver.AbstractJmsReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicReceiver;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockejb.jms.MockTopic;
+import org.mockejb.jms.TopicConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class JmsTopicFailoverTest {
+
+    private static final String FACTORY_NAME = "TopicConnectionFactory";
+    private static final String TOPIC_NAME = "Log4j2Topic";
+
+    private static Context context;
+
+    private static final String CONFIG = "log4j-jmstopic-failover.xml";
+
+    private static Configuration config;
+    private static ListAppender listAppender;
+    private static LoggerContext ctx;
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        setupQueue();
+        System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        ctx = (LoggerContext) LogManager.getContext(false);
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        System.clearProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
+        ctx.reconfigure();
+        StatusLogger.getLogger().reset();
+    }
+
+    @Before
+    public void before() {
+        config = ctx.getConfiguration();
+        listAppender = (ListAppender) config.getAppender("List");
+        assertNotNull("No Appender", listAppender);
+        listAppender.clear();
+        ThreadContext.clearMap();
+    }
+
+    private static void setupQueue() throws Exception {
+        // MockContextFactory becomes the primary JNDI provider
+        final StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
+        StatusLogger.getLogger().registerListener(listener);
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(FACTORY_NAME, new TopicConnectionFactoryImpl());
+        //context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+        //System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG);
+        //receiver = new JmsQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+    }
+
+    @Test
+    public void testFailover() throws Exception {
+        ThreadContext.put("appender", "Failover");
+        final Logger logger = LogManager.getLogger(JmsTopicFailoverTest.class);
+        logger.debug("Test Message");
+        final List<LogEvent> events = listAppender.getEvents();
+        assertNotNull("No events returned", events);
+        assertTrue("No events returned", events.size() > 0);
+        assertTrue("Incorrect event", "Test Message".equals(events.get(0).getMessage().getFormattedMessage()));
+    }
+
+    @Test
+    public void testReconnect() throws Exception {
+        context.rebind(TOPIC_NAME, new MockTopic(TOPIC_NAME));
+        final AbstractJmsReceiver receiver = new JmsTopicReceiver(FACTORY_NAME, TOPIC_NAME, null, null);
+        ThreadContext.put("appender", "Failover");
+        final Logger logger = LogManager.getLogger(JmsTopicFailoverTest.class);
+        logger.debug("Test Message");
+        final List<LogEvent> events = listAppender.getEvents();
+        assertNotNull("No events returned", events);
+        assertTrue("No events returned", events.size() > 0);
+        assertTrue("Incorrect event", "Test Message".equals(events.get(0).getMessage().getFormattedMessage()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicTest.java
new file mode 100644
index 0000000..6781b75
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.mom.jms.receiver.AbstractJmsReceiver;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicReceiver;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockejb.jms.MockTopic;
+import org.mockejb.jms.TopicConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class JmsTopicTest {
+
+    private static final String FACTORY_NAME = "TestTopicConnectionFactory";
+    private static final String TOPIC_NAME = "TestTopic";
+
+    private static Context context;
+    private static AbstractJmsReceiver receiver;
+
+    LoggerContext ctx = (LoggerContext) LogManager.getContext();
+    Logger root = ctx.getLogger("JmsTopicTest");
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        // MockContextFactory becomes the primary JNDI provider
+        final StatusConsoleListener listener = new StatusConsoleListener(Level.ERROR);
+        StatusLogger.getLogger().registerListener(listener);
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(FACTORY_NAME, new TopicConnectionFactoryImpl());
+        context.rebind(TOPIC_NAME, new MockTopic(TOPIC_NAME));
+        ((LoggerContext) LogManager.getContext()).reconfigure();
+        receiver = new JmsTopicReceiver(FACTORY_NAME, TOPIC_NAME, null, null);
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        StatusLogger.getLogger().reset();
+    }
+
+    @After
+    public void teardown() {
+        final Map<String,Appender> map = root.getAppenders();
+        for (final Map.Entry<String, Appender> entry : map.entrySet()) {
+            final Appender app = entry.getValue();
+            root.removeAppender(app);
+            app.stop();
+        }
+    }
+
+    @Test
+    public void testServer() throws Exception {
+        final Filter clientFilter = new MessageFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
+        final Filter serverFilter = new MessageFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
+        final CompositeFilter clientFilters = CompositeFilter.createFilters(new Filter[]{clientFilter});
+        final JmsTopicAppender appender = JmsTopicAppender.createAppender("Test", null, null, null, null, null, FACTORY_NAME,
+                TOPIC_NAME, null, null, null, clientFilters, "true");
+        appender.start();
+        final CompositeFilter serverFilters = CompositeFilter.createFilters(new Filter[]{serverFilter});
+        final ListAppender listApp = new ListAppender("Events", serverFilters, null, false, false);
+        listApp.start();
+        final PatternLayout layout = PatternLayout.newBuilder().withPattern("%m %ex%n").build();
+        final ConsoleAppender console =
+                ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false", "true");
+        console.start();
+        final Logger serverLogger = ctx.getLogger(JmsTopicReceiver.class.getName());
+        serverLogger.addAppender(console);
+        serverLogger.setAdditive(false);
+
+
+        // set appender on root and set level to debug
+        root.addAppender(listApp);
+        root.addAppender(appender);
+        root.setAdditive(false);
+        root.setLevel(Level.DEBUG);
+        root.debug("This is a test message");
+        Thread.sleep(100);
+        final List<LogEvent> events = listApp.getEvents();
+        assertNotNull("No event retrieved", events);
+        assertTrue("No events retrieved", events.size() > 0);
+        assertTrue("Incorrect event", events.get(0).getMessage().getFormattedMessage().equals("This is a test message"));
+    }
+
+    private class MessageFilter extends AbstractFilter {
+        public MessageFilter(final Result onMatch, final Result onMismatch) {
+            super(onMatch, onMismatch);
+        }
+
+        @Override
+        public Result filter(final LogEvent event) {
+            final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+            for (final StackTraceElement element : stackTrace) {
+                if (element.getMethodName().equals("onMessage")) {
+                    return onMatch;
+                } else if (element.getMethodName().equals("testServer")) {
+                    return onMismatch;
+                }
+            }
+            return onMismatch;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 96ca302..6c54144 100644
--- a/pom.xml
+++ b/pom.xml
@@ -989,6 +989,7 @@
     <module>log4j-nosql</module>
     <module>log4j-web</module>
     <module>log4j-perf</module>
+    <module>log4j-mom</module>
   </modules>
   <profiles>
     <profile>