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:46 UTC

[13/29] git commit: Add more generic implementation of JMS plugins.

Add more generic implementation of JMS plugins.

git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/log4j2/branches/messaging-module@1608338 13f79535-47bb-0310-9956-ffa450edef68


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

Branch: refs/heads/master
Commit: 35428a952f39c570de8cd5077ef949664aeb5710
Parents: 08e3f12
Author: mattsicker <ma...@unknown>
Authored: Mon Jul 7 04:53:12 2014 +0000
Committer: mattsicker <ma...@unknown>
Committed: Mon Jul 7 04:53:12 2014 +0000

----------------------------------------------------------------------
 .../log4j/mom/jms/appender/JmsAppender.java     | 195 +++++++++++++++++++
 .../log4j/mom/jms/manager/JmsManager.java       | 169 ++++++++++++++++
 .../log4j/mom/jms/appender/JmsAppenderIT.java   | 126 ++++++++++++
 .../log4j/mom/jms/appender/JmsAppenderTest.java |  79 ++++++++
 .../src/test/resources/JmsAppenderTest.xml      |  14 ++
 5 files changed, 583 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/35428a95/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsAppender.java
new file mode 100644
index 0000000..c98e2a1
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsAppender.java
@@ -0,0 +1,195 @@
+/*
+ * 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 javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+
+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.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.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.manager.JmsManager;
+import org.apache.logging.log4j.mom.jms.manager.JndiManager;
+
+/**
+ * Generic JMS appender plugin for both queues and topics.
+ */
+@Plugin(name = "JMS", category = "Core", elementType = "appender", printObject = true)
+// TODO: use @PluginAliases to make the separated plugins work through this one
+public class JmsAppender extends AbstractAppender {
+
+    private final JmsManager manager;
+    private final MessageProducer producer;
+
+    protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+                        final boolean ignoreExceptions, final JmsManager manager)
+        throws JMSException {
+        super(name, filter, layout, ignoreExceptions);
+        this.manager = manager;
+        this.producer = this.manager.createMessageProducer();
+    }
+
+    @Override
+    public void append(LogEvent event) {
+        try {
+            final Message message = this.manager.createMessage(getLayout().toSerializable(event));
+            message.setJMSTimestamp(event.getTimeMillis());
+            this.producer.send(message);
+        } catch (JMSException e) {
+            throw new AppenderLoggingException(e);
+        }
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
+
+        @PluginBuilderAttribute
+        private String name;
+
+        @PluginBuilderAttribute
+        private String factoryName;
+
+        @PluginBuilderAttribute
+        private String providerUrl;
+
+        @PluginBuilderAttribute
+        private String urlPkgPrefixes;
+
+        @PluginBuilderAttribute
+        private String securityPrincipalName;
+
+        @PluginBuilderAttribute(sensitive = true)
+        private String securityCredentials;
+
+        @PluginBuilderAttribute
+        private String factoryBindingName;
+
+        @PluginBuilderAttribute
+        private String destinationBindingName;
+
+        @PluginBuilderAttribute
+        private String username;
+
+        @PluginBuilderAttribute(sensitive = true)
+        private String password;
+
+        @PluginElement("Layout")
+        private Layout<? extends Serializable> layout = SerializedLayout.createLayout();
+
+        @PluginElement("Filter")
+        private Filter filter;
+
+        @PluginBuilderAttribute
+        private boolean ignoreExceptions = true;
+
+        private Builder() {
+        }
+
+        public Builder setName(final String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder setFactoryName(final String factoryName) {
+            this.factoryName = factoryName;
+            return this;
+        }
+
+        public Builder setProviderUrl(final String providerUrl) {
+            this.providerUrl = providerUrl;
+            return this;
+        }
+
+        public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) {
+            this.urlPkgPrefixes = urlPkgPrefixes;
+            return this;
+        }
+
+        public Builder setSecurityPrincipalName(final String securityPrincipalName) {
+            this.securityPrincipalName = securityPrincipalName;
+            return this;
+        }
+
+        public Builder setSecurityCredentials(final String securityCredentials) {
+            this.securityCredentials = securityCredentials;
+            return this;
+        }
+
+        public Builder setFactoryBindingName(final String factoryBindingName) {
+            this.factoryBindingName = factoryBindingName;
+            return this;
+        }
+
+        public Builder setDestinationBindingName(final String destinationBindingName) {
+            this.destinationBindingName = destinationBindingName;
+            return this;
+        }
+
+        public Builder setUsername(final String username) {
+            this.username = username;
+            return this;
+        }
+
+        public Builder setPassword(final String password) {
+            this.password = password;
+            return this;
+        }
+
+        public Builder setLayout(final Layout<? extends Serializable> layout) {
+            this.layout = layout;
+            return this;
+        }
+
+        public Builder setFilter(final Filter filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
+            this.ignoreExceptions = ignoreExceptions;
+            return this;
+        }
+
+        @Override
+        public JmsAppender build() {
+            final JndiManager jndiManager = JndiManager.getJndiManager(factoryName, providerUrl, urlPkgPrefixes,
+                securityPrincipalName, securityCredentials, null);
+            final JmsManager jmsManager = JmsManager.getJmsManager(name, jndiManager, factoryBindingName,
+                destinationBindingName, username, password);
+            try {
+                return new JmsAppender(name, filter, layout, ignoreExceptions, jmsManager);
+            } catch (final JMSException e) {
+                LOGGER.error("Error creating JmsAppender [{}].", name, e);
+                return null;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/35428a95/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/manager/JmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/manager/JmsManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/manager/JmsManager.java
new file mode 100644
index 0000000..d3ce550
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/manager/JmsManager.java
@@ -0,0 +1,169 @@
+/*
+ * 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.manager;
+
+import java.io.Serializable;
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.Destination;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Session;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects
+ * involving a configured ConnectionFactory and Destination.
+ */
+public class JmsManager extends AbstractManager {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private static final JmsManagerFactory FACTORY = new JmsManagerFactory();
+
+    private final JndiManager jndiManager;
+    private final Connection connection;
+    private final Session session;
+    private final Destination destination;
+
+    private JmsManager(final String name, final JndiManager jndiManager, final String connectionFactoryName,
+                       final String destinationName, final String username, final String password)
+        throws NamingException, JMSException {
+        super(name);
+        this.jndiManager = jndiManager;
+        final ConnectionFactory connectionFactory = this.jndiManager.lookup(connectionFactoryName);
+        if (username != null && password != null) {
+            this.connection = connectionFactory.createConnection(username, password);
+        } else {
+            this.connection = connectionFactory.createConnection();
+        }
+        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+        this.destination = this.jndiManager.lookup(destinationName);
+        this.connection.start();
+    }
+
+    /**
+     * Gets a JmsManager using the specified configuration parameters.
+     *
+     * @param name                  The name to use for this JmsManager.
+     * @param jndiManager           The JndiManager to look up JMS information through.
+     * @param connectionFactoryName The binding name for the {@link javax.jms.ConnectionFactory}.
+     * @param destinationName       The binding name for the {@link javax.jms.Destination}.
+     * @param username              The username to connect with or {@code null} for no authentication.
+     * @param password              The password to use with the given username or {@code null} for no authentication.
+     * @return The JmsManager as configured.
+     */
+    public static JmsManager getJmsManager(final String name, final JndiManager jndiManager,
+                                           final String connectionFactoryName, final String destinationName,
+                                           final String username, final String password) {
+        final JmsConfiguration configuration = new JmsConfiguration(jndiManager, connectionFactoryName, destinationName,
+            username, password);
+        return FACTORY.createManager(name, configuration);
+    }
+
+    /**
+     * Creates a MessageConsumer on this Destination using the current Session.
+     *
+     * @return A MessageConsumer on this Destination.
+     * @throws JMSException
+     */
+    public MessageConsumer createMessageConsumer() throws JMSException {
+        return this.session.createConsumer(this.destination);
+    }
+
+    /**
+     * Creates a MessageProducer on this Destination using the current Session.
+     *
+     * @return A MessageProducer on this Destination.
+     * @throws JMSException
+     */
+    public MessageProducer createMessageProducer() throws JMSException {
+        return this.session.createProducer(this.destination);
+    }
+
+    /**
+     * Creates a TextMessage or ObjectMessage from a Serializable object. For instance, when using a text-based
+     * {@link org.apache.logging.log4j.core.Layout} such as {@link org.apache.logging.log4j.core.layout.PatternLayout},
+     * the {@link org.apache.logging.log4j.core.LogEvent} message will be serialized to a String. When using a
+     * layout such as {@link org.apache.logging.log4j.core.layout.SerializedLayout}, the LogEvent message will be
+     * serialized as a Java object.
+     *
+     * @param object The LogEvent or String message to wrap.
+     * @return A new JMS message containing the provided object.
+     * @throws JMSException
+     */
+    public Message createMessage(final Serializable object) throws JMSException {
+        if (object instanceof String) {
+            return this.session.createTextMessage((String) object);
+        } else {
+            return this.session.createObjectMessage(object);
+        }
+    }
+
+    @Override
+    protected void releaseSub() {
+        try {
+            this.session.close();
+        } catch (final JMSException ignored) {
+        }
+        try {
+            this.connection.close();
+        } catch (final JMSException ignored) {
+        }
+        this.jndiManager.release();
+    }
+
+    private static class JmsConfiguration {
+        private final JndiManager jndiManager;
+        private final String connectionFactoryName;
+        private final String destinationName;
+        private final String username;
+        private final String password;
+
+        private JmsConfiguration(JndiManager jndiManager, String connectionFactoryName, String destinationName,
+                                 String username, String password) {
+            this.jndiManager = jndiManager;
+            this.connectionFactoryName = connectionFactoryName;
+            this.destinationName = destinationName;
+            this.username = username;
+            this.password = password;
+        }
+    }
+
+    private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsConfiguration> {
+
+        @Override
+        public JmsManager createManager(String name, JmsConfiguration data) {
+            try {
+                return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName,
+                    data.username, data.password);
+            } catch (final Exception e) {
+                LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].",
+                    data.connectionFactoryName, data.destinationName, e);
+                return null;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/35428a95/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderIT.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderIT.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderIT.java
new file mode 100644
index 0000000..9ed98fa
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderIT.java
@@ -0,0 +1,126 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+
+import org.apache.activemq.jndi.ActiveMQInitialContextFactory;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.mom.jms.manager.JmsManager;
+import org.apache.logging.log4j.mom.jms.manager.JndiManager;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration test for JmsAppender using an embedded ActiveMQ broker.
+ */
+public class JmsAppenderIT {
+
+    private static JmsManager jmsManager;
+
+    private JmsAppender appender;
+
+    @BeforeClass
+    public static void setUpClass() {
+        final Properties additional = new Properties();
+        additional.setProperty("queue.TestQueue", "TestQueue");
+        JndiManager jndiManager = JndiManager.getJndiManager(ActiveMQInitialContextFactory.class.getName(),
+            "vm://localhost?broker.persistent=false", null, null, null, additional);
+        jmsManager = JmsManager.getJmsManager("JmsManager", jndiManager, "ConnectionFactory", "TestQueue", null, null);
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        jmsManager.release();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        appender = new JmsAppender("JmsAppender", null, SerializedLayout.createLayout(), true, jmsManager);
+        appender.start();
+    }
+
+    @Test
+    public void testLogToQueue() throws Exception {
+        final int messageCount = 100;
+        final MessageConsumer messageConsumer = jmsManager.createMessageConsumer();
+        final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount);
+        messageConsumer.setMessageListener(consumer);
+        final String messageText = "Hello, World!";
+        final String loggerName = this.getClass().getName();
+        for (int i = 0; i < messageCount; i++) {
+            final LogEvent event = Log4jLogEvent.createEvent(loggerName, null, loggerName, Level.ERROR,
+                new SimpleMessage(messageText), null, null, null, null, Thread.currentThread().getName(), null,
+                System.currentTimeMillis());
+            appender.append(event);
+        }
+        consumer.awaitAndAssertAllMessagesConsumed();
+    }
+
+    private static class JmsQueueConsumer implements MessageListener {
+
+        private final int messageCount;
+        private final CountDownLatch countDownLatch;
+        private final Collection<LogEvent> events;
+
+        private JmsQueueConsumer(final int messageCount) {
+            this.messageCount = messageCount;
+            this.countDownLatch = new CountDownLatch(messageCount);
+            this.events = new ArrayList<LogEvent>(messageCount);
+        }
+
+        @Override
+        public void onMessage(Message message) {
+            try {
+                consume((ObjectMessage) message);
+            } catch (JMSException e) {
+                e.printStackTrace();
+            }
+        }
+
+        private void consume(ObjectMessage message) throws JMSException {
+            try {
+                final LogEvent event = (LogEvent) message.getObject();
+                events.add(event);
+            } finally {
+                countDownLatch.countDown();
+            }
+        }
+
+        public void awaitAndAssertAllMessagesConsumed() throws InterruptedException {
+            countDownLatch.await(5, TimeUnit.SECONDS);
+            assertEquals(messageCount, events.size());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/35428a95/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderTest.java b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderTest.java
new file mode 100644
index 0000000..8e54681
--- /dev/null
+++ b/log4j-mom/src/test/java/org/apache/logging/log4j/mom/jms/appender/JmsAppenderTest.java
@@ -0,0 +1,79 @@
+package org.apache.logging.log4j.mom.jms.appender;
+
+import javax.jms.Message;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.util.Closer;
+import org.apache.logging.log4j.junit.InitialLoggerContext;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockejb.jms.MockQueue;
+import org.mockejb.jms.QueueConnectionFactoryImpl;
+import org.mockejb.jndi.MockContextFactory;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.*;
+
+public class JmsAppenderTest {
+
+    private static final String CONNECTION_FACTORY_NAME = "jms/activemq";
+    private static final String DESTINATION_NAME = "jms/destination";
+    private static final String LOG_MESSAGE = "Hello, world!";
+
+    private static Context context;
+
+    private JmsAppender appender;
+    private static MockQueue queue;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        MockContextFactory.setAsInitial();
+        context = new InitialContext();
+        context.rebind(CONNECTION_FACTORY_NAME, new QueueConnectionFactoryImpl());
+        queue = new MockQueue(DESTINATION_NAME);
+        context.rebind(DESTINATION_NAME, queue);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        Closer.closeSilently(context);
+    }
+
+    @Rule
+    public InitialLoggerContext ctx = new InitialLoggerContext("JmsAppenderTest.xml");
+
+    @Before
+    public void setUp() throws Exception {
+        appender = (JmsAppender) ctx.getAppender("JmsQueueAppender");
+        assertEquals(0, queue.size());
+    }
+
+    @Test
+    public void testAppendToQueue() throws Exception {
+        final String loggerName = this.getClass().getName();
+        final long now = System.currentTimeMillis();
+        final LogEvent event = createLogEvent(loggerName, now);
+        appender.append(event);
+        assertEquals(1, queue.size());
+        final Message message = queue.getMessageAt(0);
+        assertNotNull(message);
+        assertThat(message, instanceOf(TextMessage.class));
+        final TextMessage textMessage = (TextMessage) message;
+        assertEquals(LOG_MESSAGE, textMessage.getText());
+    }
+
+    private static Log4jLogEvent createLogEvent(String loggerName, long now) {
+        return Log4jLogEvent.createEvent(loggerName, null, loggerName, Level.INFO,
+            new SimpleMessage(LOG_MESSAGE), null, null, null, null, Thread.currentThread().getName(), null, now);
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/35428a95/log4j-mom/src/test/resources/JmsAppenderTest.xml
----------------------------------------------------------------------
diff --git a/log4j-mom/src/test/resources/JmsAppenderTest.xml b/log4j-mom/src/test/resources/JmsAppenderTest.xml
new file mode 100644
index 0000000..4997747
--- /dev/null
+++ b/log4j-mom/src/test/resources/JmsAppenderTest.xml
@@ -0,0 +1,14 @@
+<Configuration name="JmsAppenderTest" status="OFF">
+  <Appenders>
+    <JMS name="JmsQueueAppender"
+         factoryBindingName="jms/activemq"
+         destinationBindingName="jms/destination">
+      <PatternLayout pattern="%m"/>
+    </JMS>
+  </Appenders>
+  <Loggers>
+    <Root level="info">
+      <AppenderRef ref="JmsQueueAppender"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file