Repository: logging-log4j2
Updated Branches:
  refs/heads/master c13e31913 -> de7487ed2


[LOG4J2-1934] JMS Appender does not know how to recover from a broken
connection.
[LOG4J2-1955]JMS Appender should be able connect to a broker (later)
even it is not present at configuration time.


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

Branch: refs/heads/master
Commit: de7487ed22b03f5c1a73ddb8f8d0f8bc89b8f4d8
Parents: c13e319
Author: Gary Gregory <[email protected]>
Authored: Wed Jul 5 17:55:48 2017 -0700
Committer: Gary Gregory <[email protected]>
Committed: Wed Jul 5 17:55:48 2017 -0700

----------------------------------------------------------------------
 log4j-core-its/pom.xml                          |   6 +-
 .../AbstractJmsAppenderReconnectIT.java         |  93 +++++
 .../activemq/ActiveMqBrokerServiceHelper.java   |  37 +-
 .../mom/activemq/JmsAppenderConnectLaterIT.java | 109 ------
 .../JmsAppenderConnectPostStartupIT.java        |  91 +++--
 .../activemq/JmsAppenderConnectReConnectIT.java |  93 ++---
 .../mom/activemq/JmsClientTestConfig.java       | 113 +++---
 .../src/test/resources/logback-test.xml         |  29 ++
 .../log4j/core/appender/mom/JmsAppender.java    | 129 ++-----
 .../log4j/core/appender/mom/JmsManager.java     | 380 +++++++++++++++----
 .../logging/log4j/core/net/JndiManager.java     |  72 +++-
 .../log4j/core/appender/HttpAppenderTest.java   |   2 +
 pom.xml                                         |   6 -
 src/changes/changes.xml                         |   3 +
 src/site/xdoc/manual/appenders.xml              |  14 +
 15 files changed, 693 insertions(+), 484 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-core-its/pom.xml b/log4j-core-its/pom.xml
index 3b720c2..a9985dd 100644
--- a/log4j-core-its/pom.xml
+++ b/log4j-core-its/pom.xml
@@ -201,6 +201,9 @@
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-failsafe-plugin</artifactId>
         <configuration>
+          <additionalClasspathElements>
+            
<additionalClasspathElement>${project.basedir}/src/test/resources</additionalClasspathElement>
+          </additionalClasspathElements>
           <includes>
             <include>**/*.java</include>
           </includes>
@@ -208,7 +211,8 @@
             <exclude>**/ForceNoDefClassFoundError.*</exclude>
           </excludes>
           <groups>
-            org.apache.logging.log4j.categories.PerformanceTests
+            org.apache.logging.log4j.categories.PerformanceTests,
+            org.apache.logging.log4j.categories.Appenders$Jms
           </groups>
         </configuration>
       </plugin>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderReconnectIT.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderReconnectIT.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderReconnectIT.java
new file mode 100644
index 0000000..d457162
--- /dev/null
+++ 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderReconnectIT.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender.mom.activemq;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.activemq.broker.BrokerService;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.mom.JmsAppender;
+import org.apache.logging.log4j.core.appender.mom.JmsManager;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.StringMapMessage;
+import org.junit.After;
+import org.junit.Assert;
+
+/**
+ * Subclass for tests that reconnect to Apache Active MQ. The class makes sure 
resources are properly shutdown after
+ * each @Test method. A subclass normally only has one @Test method.
+ * <p>
+ * LOG4J2-1934 JMS Appender does not know how to recover from a broken 
connection. See
+ * https://issues.apache.org/jira/browse/LOG4J2-1934
+ * </p>
+ */
+public class AbstractJmsAppenderReconnectIT {
+
+    protected JmsClientTestConfig jmsClientTestConfig;
+    protected JmsAppender appender;
+    protected BrokerService brokerService;
+
+    @After
+    public void after() {
+        try {
+            ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
+        } catch (final Exception e) {
+            // Just log to the console for now.
+            e.printStackTrace();
+        }
+        if (appender != null) {
+            appender.stop();
+        }
+        if (jmsClientTestConfig != null) {
+            jmsClientTestConfig.stop();
+        }
+        // Make sure the manager is gone as to not have bad side effect on 
other tests.
+        @SuppressWarnings("resource")
+        final JmsManager appenderManager = appender.getManager();
+        if (appenderManager != null) {
+            
Assert.assertFalse(AbstractManager.hasManager(appenderManager.getName()));
+        }
+        // Make sure the manager is gone as to not have bad side effect on 
other tests.
+        @SuppressWarnings("resource")
+        final JmsManager testManager = jmsClientTestConfig.getJmsManager();
+        if (testManager != null) {
+            
Assert.assertFalse(AbstractManager.hasManager(testManager.getName()));
+        }
+    }
+
+    protected void appendEvent(final JmsAppender appender) {
+        final Map<String, String> map = new HashMap<>();
+        final String messageText = "Hello, World!";
+        final String loggerName = this.getClass().getName();
+        map.put("messageText", messageText);
+        map.put("threadName", Thread.currentThread().getName());
+        // @formatter:off
+               final LogEvent event = Log4jLogEvent.newBuilder()
+                               .setLoggerName(loggerName)
+                               .setLoggerFqcn(loggerName)
+                               .setLevel(Level.INFO)
+                               .setMessage(new StringMapMessage(map))
+                               .setTimeMillis(System.currentTimeMillis())
+                               .build();
+               // @formatter:on
+        appender.append(event);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
index 6f6949d..67d2b3e 100644
--- 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
+++ 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
@@ -26,23 +26,26 @@ import org.apache.activemq.broker.BrokerService;
  */
 public class ActiveMqBrokerServiceHelper {
 
-       static BrokerService startBrokerService(final String brokerName, String 
brokerUrlString, final int port) throws Exception {
-               // TODO Abstract out scheme
-               brokerUrlString = "tcp://localhost:" + port;
-               final BrokerService broker = new BrokerService();
-               // configure the Broker
-               broker.setBrokerName(brokerName);
-               broker.addConnector(brokerUrlString);
-               broker.setPersistent(false);
-               broker.start();
-               broker.waitUntilStarted();
-               return broker;
-       }
+    static BrokerService startBrokerService(final String brokerName, String 
brokerUrlString, final int port)
+            throws Exception {
+        // TODO Abstract out scheme
+        brokerUrlString = "tcp://localhost:" + port;
+        final BrokerService broker = new BrokerService();
+        // configure the Broker
+        broker.setBrokerName(brokerName);
+        broker.addConnector(brokerUrlString);
+        broker.setPersistent(false);
+        broker.start();
+        broker.waitUntilStarted();
+        return broker;
+    }
 
-       static void stopBrokerService(final BrokerService broker) throws 
IOException, Exception {
-               broker.deleteAllMessages();
-               broker.stop();
-               broker.waitUntilStopped();
-       }
+    static void stopBrokerService(final BrokerService brokerService) throws 
IOException, Exception {
+        if (brokerService != null) {
+            brokerService.deleteAllMessages();
+            brokerService.stop();
+            brokerService.waitUntilStopped();
+        }
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectLaterIT.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectLaterIT.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectLaterIT.java
deleted file mode 100644
index 6fcb9d2..0000000
--- 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectLaterIT.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.core.appender.mom.activemq;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.activemq.broker.BrokerService;
-import org.apache.activemq.jndi.ActiveMQInitialContextFactory;
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.mom.JmsAppender;
-import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.core.layout.MessageLayout;
-import org.apache.logging.log4j.message.StringMapMessage;
-import org.apache.logging.log4j.test.AvailablePortFinder;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * Tests that a JMS Appender can reconnect to a JMS broker after it has been
- * recycled.
- * <p>
- * LOG4J2-1934 JMS Appender does not know how to recover from a broken
- * connection. See https://issues.apache.org/jira/browse/LOG4J2-1934
- * </p>
- */
-@Ignore
-@Category(Appenders.Jms.class)
-public class JmsAppenderConnectLaterIT {
-
-    private void appendEvent(final JmsAppender appender) {
-        final Map<String, String> map = new HashMap<>();
-        final String messageText = "Hello, World!";
-        final String loggerName = this.getClass().getName();
-        map.put("messageText", messageText);
-        map.put("threadName", Thread.currentThread().getName());
-        // @formatter:off
-               final LogEvent event = Log4jLogEvent.newBuilder()
-                               .setLoggerName(loggerName)
-                               .setLoggerFqcn(loggerName)
-                               .setLevel(Level.INFO)
-                               .setMessage(new StringMapMessage(map))
-                               .setTimeMillis(System.currentTimeMillis())
-                               .build();
-               // @formatter:on
-        appender.append(event);
-    }
-
-    @Test
-    public void testConnectReConnect() throws Exception {
-        // Start broker
-        final int port = AvailablePortFinder.getNextAvailable();
-        final String brokerUrlString = "tcp://localhost:" + port;
-        // Start appender
-        // final JmsClientTestConfig jmsClientTestConfig = new 
JmsClientTestConfig(
-        // ActiveMQInitialContextFactory.class.getName(), brokerUrlString, 
"admin",
-        // "admin".toCharArray());
-        // jmsClientTestConfig.start();
-        // final JmsAppender appender =
-        // jmsClientTestConfig.createAppender(MessageLayout.createLayout());
-        
-        // @formatter:off
-               final JmsAppender appender = JmsAppender.newBuilder()
-                       .setName("JmsAppender")
-                       .setLayout(MessageLayout.createLayout())
-                       .setIgnoreExceptions(true)
-                       .setFactoryBindingName("ConnectionFactory")
-                       .setProviderUrl(brokerUrlString)
-                       .setUserName("admin")
-                       .setPassword("admin".toCharArray())
-                       .build();
-           // @formatter:on
-               appender.start();
-
-        // Log message
-        appendEvent(appender);
-        // Start broker
-        BrokerService brokerService = ActiveMqBrokerServiceHelper
-                .startBrokerService(JmsAppenderConnectLaterIT.class.getName(), 
brokerUrlString, port);
-        // Stop broker
-        ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
-        // Restart broker
-        brokerService = 
ActiveMqBrokerServiceHelper.startBrokerService(JmsAppenderConnectLaterIT.class.getName(),
-                brokerUrlString, port);
-        // Logging again should cause the appender to reconnect
-        appendEvent(appender);
-        // Stop broker
-        ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.java
index bf3d50c..87425b2 100644
--- 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.java
+++ 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.java
@@ -14,66 +14,61 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
-
 package org.apache.logging.log4j.core.appender.mom.activemq;
 
 import org.apache.activemq.jndi.ActiveMQInitialContextFactory;
 import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.test.AvailablePortSystemPropertyRule;
-import org.apache.logging.log4j.test.RuleChainFactory;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Rule;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.layout.MessageLayout;
+import org.apache.logging.log4j.test.AvailablePortFinder;
+import org.junit.Assert;
+import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.rules.RuleChain;
 
 /**
- * Integration test for JmsAppender using an embedded ActiveMQ broker with in
- * socket communications between clients and broker. This test manages a client
- * connection to JMS like a Appender would. This test appender is managed at 
the
- * class level by a JmsTestConfigRule.
+ * Tests that a JMS Appender start when there is no broker and connect the 
broker when it is started later..
  * <p>
- * Tests that a JMS appender can connect to a broker AFTER Log4j startup.
+ * LOG4J2-1934 JMS Appender does not know how to recover from a broken 
connection. See
+ * https://issues.apache.org/jira/browse/LOG4J2-1934
  * </p>
  * <p>
- * LOG4J2-1934 JMS Appender does not know how to recover from a broken
- * connection. See https://issues.apache.org/jira/browse/LOG4J2-1934
+ * This test class' single test method performs the following:
  * </p>
+ * <ol>
+ * <li>Starts a JMS Appender</li>
+ * <li>Logs an event (fails and starts the reconnect thread)</li>
+ * <li>Starts Apache ActiveMQ</li>
+ * <li>Logs an event successfully</li>
+ * </ol>
  */
-@Ignore
 @Category(Appenders.Jms.class)
-public class JmsAppenderConnectPostStartupIT extends AbstractJmsAppenderIT {
-
-       public static final AvailablePortSystemPropertyRule portRule = 
AvailablePortSystemPropertyRule
-                       .create(ActiveMqBrokerServiceRule.PORT_PROPERTY_NAME);
-
-       @Rule
-       public final ActiveMqBrokerServiceRule activeMqBrokerServiceRule = new 
ActiveMqBrokerServiceRule(
-                       JmsAppenderConnectPostStartupIT.class.getName(), 
portRule.getName());
-
-       // "admin"/"admin" are the default Apache Active MQ creds.
-       private static final JmsClientTestConfigRule jmsClientTestConfigRule = 
new JmsClientTestConfigRule(
-                       ActiveMQInitialContextFactory.class.getName(), 
"tcp://localhost:" + portRule.getPort(), "admin", "admin".toCharArray());
-
-       /**
-        * Assign the port and client ONCE for the whole test suite.
-        */
-       @ClassRule
-       public static final RuleChain ruleChain = 
RuleChainFactory.create(portRule, jmsClientTestConfigRule);
-
-       @AfterClass
-       public static void afterClass() {
-               jmsClientTestConfigRule.getJmsClientTestConfig().stop();
-       }
-
-       @BeforeClass
-       public static void beforeClass() {
-               jmsClientTestConfigRule.getJmsClientTestConfig().start();
-       }
+public class JmsAppenderConnectPostStartupIT extends 
AbstractJmsAppenderReconnectIT {
 
-       public JmsAppenderConnectPostStartupIT() {
-               super(jmsClientTestConfigRule);
-       }
+    @Test
+    public void testConnectPostStartup() throws Exception {
+        //
+        // Start appender
+        final int port = AvailablePortFinder.getNextAvailable();
+        final String brokerUrlString = "tcp://localhost:" + port;
+        jmsClientTestConfig = new 
JmsClientTestConfig(ActiveMQInitialContextFactory.class.getName(), 
brokerUrlString,
+                "admin", "admin".toCharArray());
+        jmsClientTestConfig.start();
+        appender = 
jmsClientTestConfig.createAppender(MessageLayout.createLayout());
+        //
+        // Logging will fail but the JMS manager is now running a reconnect 
thread.
+        try {
+            appendEvent(appender);
+            Assert.fail("Expected to catch a " + 
AppenderLoggingException.class.getName());
+        } catch (final AppenderLoggingException e) {
+            // Expected.
+        }
+        //
+        // Start broker
+        brokerService = 
ActiveMqBrokerServiceHelper.startBrokerService(JmsAppenderConnectPostStartupIT.class.getName(),
+                brokerUrlString, port);
+        //
+        // Logging now should just work
+        
Thread.sleep(appender.getManager().getJmsManagerConfiguration().getReconnectIntervalMillis());
+        appendEvent(appender);
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
index 8944d5b..d77e32a 100644
--- 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
+++ 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
@@ -16,74 +16,55 @@
  */
 package org.apache.logging.log4j.core.appender.mom.activemq;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.activemq.broker.BrokerService;
 import org.apache.activemq.jndi.ActiveMQInitialContextFactory;
-import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.categories.Appenders;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.mom.JmsAppender;
-import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.layout.MessageLayout;
-import org.apache.logging.log4j.message.StringMapMessage;
 import org.apache.logging.log4j.test.AvailablePortFinder;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 /**
- * Tests that a JMS Appender can reconnect to a JMS broker after it has been
- * recycled.
+ * Tests that a JMS Appender can reconnect to a JMS broker after it has been 
recycled.
+ * <p>
+ * LOG4J2-1934 JMS Appender does not know how to recover from a broken 
connection. See
+ * https://issues.apache.org/jira/browse/LOG4J2-1934
+ * </p>
  * <p>
- * LOG4J2-1934 JMS Appender does not know how to recover from a broken
- * connection. See https://issues.apache.org/jira/browse/LOG4J2-1934
+ * This test class' single test method performs the following:
  * </p>
+ * <ol>
+ * <li>Starts Apache ActiveMQ</li>
+ * <li>Starts a JMS Appender</li>
+ * <li>Logs an event</li>
+ * <li>Stops Apache ActiveMQ</li>
+ * <li>Starts Apache ActiveMQ</li>
+ * <li>Logs an event</li>
+ * </ol>
  */
 @Category(Appenders.Jms.class)
-public class JmsAppenderConnectReConnectIT {
-
-       private void appendEvent(final JmsAppender appender) {
-               final Map<String, String> map = new HashMap<>();
-               final String messageText = "Hello, World!";
-               final String loggerName = this.getClass().getName();
-               map.put("messageText", messageText);
-               map.put("threadName", Thread.currentThread().getName());
-               // @formatter:off
-               final LogEvent event = Log4jLogEvent.newBuilder()
-                               .setLoggerName(loggerName)
-                               .setLoggerFqcn(loggerName)
-                               .setLevel(Level.INFO)
-                               .setMessage(new StringMapMessage(map))
-                               .setTimeMillis(System.currentTimeMillis())
-                               .build();
-               // @formatter:on
-               appender.append(event);
-       }
+public class JmsAppenderConnectReConnectIT extends 
AbstractJmsAppenderReconnectIT {
 
-       @Test
-       public void testConnectReConnect() throws Exception {
-               // Start broker
-               final int port = AvailablePortFinder.getNextAvailable();
-               final String brokerUrlString = "tcp://localhost:" + port;
-               BrokerService brokerService = ActiveMqBrokerServiceHelper
-                               
.startBrokerService(JmsAppenderConnectReConnectIT.class.getName(), 
brokerUrlString, port);
-               // Start appender
-               final JmsClientTestConfig jmsClientTestConfig = new 
JmsClientTestConfig(ActiveMQInitialContextFactory.class.getName(),
-                               brokerUrlString, "admin", 
"admin".toCharArray());
-               jmsClientTestConfig.start();
-               final JmsAppender appender = 
jmsClientTestConfig.createAppender(MessageLayout.createLayout());
-               // Log message
-               appendEvent(appender);
-               // Stop broker
-               ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
-               // Restart broker
-               brokerService = 
ActiveMqBrokerServiceHelper.startBrokerService(JmsAppenderConnectReConnectIT.class.getName(),
-                               brokerUrlString, port);
-               // Logging again should cause the appender to reconnect
-               appendEvent(appender);
-               // Stop broker
-               ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
-       }
+    @Test
+    public void testConnectReConnect() throws Exception {
+        // Start broker
+        final int port = AvailablePortFinder.getNextAvailable();
+        final String brokerUrlString = "tcp://localhost:" + port;
+        brokerService = 
ActiveMqBrokerServiceHelper.startBrokerService(JmsAppenderConnectReConnectIT.class.getName(),
+                brokerUrlString, port);
+        // Start appender
+        jmsClientTestConfig = new 
JmsClientTestConfig(ActiveMQInitialContextFactory.class.getName(), 
brokerUrlString,
+                "admin", "admin".toCharArray());
+        jmsClientTestConfig.start();
+        appender = 
jmsClientTestConfig.createAppender(MessageLayout.createLayout());
+        // Log message
+        appendEvent(appender);
+        // Stop broker
+        ActiveMqBrokerServiceHelper.stopBrokerService(brokerService);
+        // Restart broker
+        brokerService = 
ActiveMqBrokerServiceHelper.startBrokerService(JmsAppenderConnectReConnectIT.class.getName(),
+                brokerUrlString, port);
+        // Logging again should cause the appender to reconnect
+        appendEvent(appender);
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
----------------------------------------------------------------------
diff --git 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
index f88a804..160d65e 100644
--- 
a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
+++ 
b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
@@ -34,62 +34,63 @@ class JmsClientTestConfig {
                                .setLayout(layout)
                                .setIgnoreExceptions(true)
                                .setJmsManager(jmsManager)
+                               .setReconnectIntervalMillis(2000)
                                .build();
                // @formatter:on
-               jmsAppender.start();
-               return jmsAppender;
-       }
-
-       JmsAppender getJmsAppender() {
-               return jmsAppender;
-       }
-
-       String getJmsInitialContextFactoryClassName() {
-               return jmsInitialContextFactoryClassName;
-       }
-
-       JmsManager getJmsManager() {
-               return jmsManager;
-       }
-
-       char[] getJmsPassword() {
-               return jmsPassword;
-       }
-
-       String getJmsProviderUrlStr() {
-               return jmsProviderUrlStr;
-       }
-
-       String getJmsUserName() {
-               return jmsUserName;
-       }
-
-       void setJmsAppender(final JmsAppender jmsAppender) {
-               this.jmsAppender = jmsAppender;
-       }
-
-       void setJmsManager(final JmsManager jmsManager) {
-               this.jmsManager = jmsManager;
-       }
-
-       void start() {
-               
System.setProperty(AbstractJmsAppenderIT.KEY_SERIALIZABLE_PACKAGES,
-                               
"org.apache.logging.log4j.core.impl,org.apache.logging.log4j.util,org.apache.logging.log4j,java.rmi");
-               final Properties additional = new Properties();
-               additional.setProperty("queue.TestQueue", "TestQueue");
-               // jndiManager is closed in stop() through the jmsManager
-               @SuppressWarnings("resource")
-               final JndiManager jndiManager = 
JndiManager.getJndiManager(jmsInitialContextFactoryClassName,
-                               jmsProviderUrlStr, null, null, null, 
additional);
-               jmsManager = JmsManager.getJmsManager("JmsManager", 
jndiManager, "ConnectionFactory", "TestQueue",
-                               jmsUserName, jmsPassword);
-       }
-
-       void stop() {
-               if (jmsManager != null) {
-                       jmsManager.close();
-                       jmsManager = null;
-               }
-               
System.getProperties().remove(AbstractJmsAppenderIT.KEY_SERIALIZABLE_PACKAGES);
-       }
+        jmsAppender.start();
+        return jmsAppender;
+    }
+
+    JmsAppender getJmsAppender() {
+        return jmsAppender;
+    }
+
+    String getJmsInitialContextFactoryClassName() {
+        return jmsInitialContextFactoryClassName;
+    }
+
+    JmsManager getJmsManager() {
+        return jmsManager;
+    }
+
+    char[] getJmsPassword() {
+        return jmsPassword;
+    }
+
+    String getJmsProviderUrlStr() {
+        return jmsProviderUrlStr;
+    }
+
+    String getJmsUserName() {
+        return jmsUserName;
+    }
+
+    void setJmsAppender(final JmsAppender jmsAppender) {
+        this.jmsAppender = jmsAppender;
+    }
+
+    void setJmsManager(final JmsManager jmsManager) {
+        this.jmsManager = jmsManager;
+    }
+
+    void start() {
+        System.setProperty(AbstractJmsAppenderIT.KEY_SERIALIZABLE_PACKAGES,
+                
"org.apache.logging.log4j.core.impl,org.apache.logging.log4j.util,org.apache.logging.log4j,java.rmi");
+        final Properties additional = new Properties();
+        additional.setProperty("queue.TestQueue", "TestQueue");
+        // jndiManager is closed in stop() through the jmsManager
+        final Properties jndiProperties = 
JndiManager.createProperties(jmsInitialContextFactoryClassName,
+                jmsProviderUrlStr, null, null, null, additional);
+        final String name = JmsManager.class.getName() + "-" + 
getClass().getSimpleName() + "@" + hashCode();
+        jmsManager = JmsManager.getJmsManager(name, jndiProperties, 
"ConnectionFactory", "TestQueue", jmsUserName,
+                jmsPassword, false, 
JmsAppender.Builder.DEFAULT_RECONNECT_INTERVAL_MILLIS);
+    }
+
+    void stop() {
+        if (jmsManager != null) {
+            jmsManager.close();
+            jmsManager = null;
+        }
+        
System.getProperties().remove(AbstractJmsAppenderIT.KEY_SERIALIZABLE_PACKAGES);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core-its/src/test/resources/logback-test.xml
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/resources/logback-test.xml 
b/log4j-core-its/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..989bea6
--- /dev/null
+++ b/log4j-core-its/src/test/resources/logback-test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="info">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
index 906b36f..57e8c75 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java
@@ -18,11 +18,10 @@
 package org.apache.logging.log4j.core.appender.mom;
 
 import java.io.Serializable;
-import java.util.Objects;
+import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.JMSException;
-import javax.jms.Message;
 
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Filter;
@@ -30,7 +29,6 @@ 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.AbstractManager;
-import org.apache.logging.log4j.core.appender.AppenderLoggingException;
 import 
org.apache.logging.log4j.core.appender.mom.JmsManager.JmsManagerConfiguration;
 import org.apache.logging.log4j.core.config.Node;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
@@ -40,7 +38,6 @@ import 
org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
 import 
org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
 import org.apache.logging.log4j.core.net.JndiManager;
-import org.apache.logging.log4j.status.StatusLogger;
 
 /**
  * Generic JMS Appender plugin for both queues and topics. This Appender 
replaces the previous split ones. However,
@@ -52,6 +49,8 @@ public class JmsAppender extends AbstractAppender {
 
     public static class Builder implements 
org.apache.logging.log4j.core.util.Builder<JmsAppender> {
 
+        public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000;
+
         @PluginBuilderAttribute
         @Required(message = "A name for the JmsAppender must be specified")
         private String name;
@@ -92,18 +91,14 @@ public class JmsAppender extends AbstractAppender {
         @PluginElement("Filter")
         private Filter filter;
 
-        @PluginElement("ReconnectOnExceptionMessage")
-        private String[] reconnectOnExceptionMessages = new String[] { 
"closed" };
-
-        @PluginBuilderAttribute("reconnectAttempts")
-        private final int reconnectAttempts = 3;
-
-        @PluginBuilderAttribute("reconnectIntervalMillis")
-        private final long reconnectIntervalMillis = 1000;
+        private long reconnectIntervalMillis = 
DEFAULT_RECONNECT_INTERVAL_MILLIS;
 
         @PluginBuilderAttribute
         private boolean ignoreExceptions = true;
 
+        @PluginBuilderAttribute
+        private boolean immediateFail;
+
         // Programmatic access only for now.
         private JmsManager jmsManager;
 
@@ -114,13 +109,12 @@ public class JmsAppender extends AbstractAppender {
         @Override
         public JmsAppender build() {
             JmsManager actualJmsManager = jmsManager;
-            JndiManager jndiManager = null;
             JmsManagerConfiguration configuration = null;
             if (actualJmsManager == null) {
-                jndiManager = JndiManager.getJndiManager(factoryName, 
providerUrl, urlPkgPrefixes,
+                final Properties jndiProperties = 
JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes,
                         securityPrincipalName, securityCredentials, null);
-                configuration = new JmsManagerConfiguration(jndiManager, 
factoryBindingName, destinationBindingName,
-                        userName, password);
+                configuration = new JmsManagerConfiguration(jndiProperties, 
factoryBindingName, destinationBindingName,
+                        userName, password, false, reconnectIntervalMillis);
                 actualJmsManager = AbstractManager.getManager(name, 
JmsManager.FACTORY, configuration);
             }
             if (actualJmsManager == null) {
@@ -131,8 +125,12 @@ public class JmsAppender extends AbstractAppender {
                 LOGGER.error("No layout provided for JmsAppender");
                 return null;
             }
-            return new JmsAppender(name, filter, layout, ignoreExceptions, 
reconnectOnExceptionMessages,
-                    reconnectAttempts, reconnectIntervalMillis, 
actualJmsManager);
+            try {
+                return new JmsAppender(name, filter, layout, ignoreExceptions, 
actualJmsManager);
+            } catch (final JMSException e) {
+                //  Never happens since the ctor no longer actually throws a 
JMSException.
+                throw new IllegalStateException(e);
+            }
         }
 
         public Builder setDestinationBindingName(final String 
destinationBindingName) {
@@ -160,6 +158,11 @@ public class JmsAppender extends AbstractAppender {
             return this;
         }
 
+        public Builder setImmediateFail(final boolean immediateFail) {
+            this.immediateFail = immediateFail;
+            return this;
+        }
+
         public Builder setJmsManager(final JmsManager jmsManager) {
             this.jmsManager = jmsManager;
             return this;
@@ -194,6 +197,11 @@ public class JmsAppender extends AbstractAppender {
             return this;
         }
 
+        public Builder setReconnectIntervalMillis(final long 
reconnectIntervalMillis) {
+            this.reconnectIntervalMillis = reconnectIntervalMillis;
+            return this;
+        }
+
         public Builder setSecurityCredentials(final String 
securityCredentials) {
             this.securityCredentials = securityCredentials;
             return this;
@@ -236,10 +244,6 @@ public class JmsAppender extends AbstractAppender {
                     + jmsManager + "]";
         }
 
-        public void setReconnectOnExceptionMessage(final String[] 
reconnectOnExceptionMessage) {
-            this.reconnectOnExceptionMessages = reconnectOnExceptionMessage;
-        }
-
     }
 
     @PluginBuilderFactory
@@ -248,94 +252,25 @@ public class JmsAppender extends AbstractAppender {
     }
 
     private volatile JmsManager manager;
-    private final String[] reconnectOnExceptionMessages;
-    private final int reconnectAttempts;
-    private final long reconnectIntervalMillis;
 
     /**
-     * 
-     * @throws JMSException not thrown as of 2.9 but retained in the signature 
for compatibility
-     * @deprecated Use the other constructor
+     *
+     * @throws JMSException
+     *             not thrown as of 2.9 but retained in the signature for 
compatibility, will be removed in 3.0
      */
-    @Deprecated
     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.reconnectOnExceptionMessages = null;
-        this.reconnectAttempts = 0;
-        this.reconnectIntervalMillis = 0;
-    }
-
-    protected JmsAppender(final String name, final Filter filter, final 
Layout<? extends Serializable> layout,
-            final boolean ignoreExceptions, final String[] 
reconnectOnExceptionMessage, final int reconnectAttempts,
-            final long reconnectIntervalMillis, final JmsManager manager) {
-        super(name, filter, layout, ignoreExceptions);
-        this.manager = manager;
-        this.reconnectOnExceptionMessages = reconnectOnExceptionMessage;
-        this.reconnectAttempts = reconnectAttempts;
-        this.reconnectIntervalMillis = reconnectIntervalMillis;
     }
 
     @Override
     public void append(final LogEvent event) {
-        Serializable serializable = null;
-        try {
-            serializable = getLayout().toSerializable(event);
-            send(event, serializable);
-        } catch (final JMSException e) {
-            // Try to reconnect once under specific conditions
-            // reconnectOnExceptionMessages MUST be set to demonstrate intent
-            // This is designed to handle the use case where an application is 
running and the JMS broker is recycled.
-            if (reconnectOnExceptionMessages == null) {
-                throw new AppenderLoggingException(e);
-            }
-            boolean reconnect = false;
-            for (final String message : reconnectOnExceptionMessages) {
-                reconnect = Objects.toString(e.getMessage()).contains(message);
-                if (reconnect) {
-                    break;
-                }
-            }
-            if (reconnect) {
-                int count = 0;
-                while (count < reconnectAttempts) {
-                    // TODO How to best synchronize this?
-                    final JmsManagerConfiguration config = 
this.manager.getJmsManagerConfiguration();
-                    this.manager = AbstractManager.getManager(getName(), 
JmsManager.FACTORY, config);
-                    try {
-                        if (serializable != null) {
-                            count++;
-                            StatusLogger.getLogger().debug(
-                                    "Reconnect attempt {} of {} for JMS 
appender {} and configuration {} due to {}",
-                                    count, reconnectAttempts, getName(), 
config, e.toString(), e);
-                            send(event, serializable);
-                            return;
-                        }
-                    } catch (final JMSException e1) {
-                        if (count == reconnectAttempts) {
-                            throw new AppenderLoggingException(e);
-                        }
-                        StatusLogger.getLogger().debug(
-                                "Reconnect attempt {} of {} FAILED for JMS 
appender {} and configuration {} due to {}; slepping {} milliseconds...",
-                                count, reconnectAttempts, getName(), config, 
e.toString(), reconnectIntervalMillis, e);
-                        if (reconnectIntervalMillis > 0) {
-                            try {
-                                Thread.sleep(reconnectIntervalMillis);
-                            } catch (final InterruptedException e2) {
-                                throw new AppenderLoggingException(e2);
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        this.manager.send(event, getLayout().toSerializable(event));
     }
 
-    private void send(final LogEvent event, final Serializable serializable) 
throws JMSException {
-        final Message message = this.manager.createMessage(serializable);
-        message.setJMSTimestamp(event.getTimeMillis());
-        this.manager.send(message);
+    public JmsManager getManager() {
+        return manager;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
index a985980..4cc8e68 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
@@ -18,6 +18,8 @@
 package org.apache.logging.log4j.core.appender.mom;
 
 import java.io.Serializable;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import javax.jms.Connection;
@@ -32,42 +34,84 @@ import javax.jms.Session;
 import javax.naming.NamingException;
 
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.appender.AbstractManager;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
 import org.apache.logging.log4j.core.appender.ManagerFactory;
 import org.apache.logging.log4j.core.net.JndiManager;
+import org.apache.logging.log4j.core.util.Log4jThread;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.BiConsumer;
 
 /**
+ * Consider this class <b>private</b>; it is only <b>public</b> for access by 
integration tests.
+ *
+ * <p>
  * JMS connection and session manager. Can be used to access MessageProducer, 
MessageConsumer, and Message objects
  * involving a configured ConnectionFactory and Destination.
+ * </p>
  */
 public class JmsManager extends AbstractManager {
 
-    static class JmsManagerConfiguration {
-        private final JndiManager jndiManager;
+    public static class JmsManagerConfiguration {
+        private final Properties jndiProperties;
         private final String connectionFactoryName;
         private final String destinationName;
         private final String userName;
         private final char[] password;
+        private final boolean immediateFail;
+        private final boolean retry;
+        private final long reconnectIntervalMillis;
 
-        JmsManagerConfiguration(final JndiManager jndiManager, final String 
connectionFactoryName,
-                final String destinationName, final String userName, final 
char[] password) {
-            this.jndiManager = jndiManager;
+        JmsManagerConfiguration(final Properties jndiProperties, final String 
connectionFactoryName,
+                final String destinationName, final String userName, final 
char[] password, final boolean immediateFail,
+                final long reconnectIntervalMillis) {
+            this.jndiProperties = jndiProperties;
             this.connectionFactoryName = connectionFactoryName;
             this.destinationName = destinationName;
             this.userName = userName;
             this.password = password;
+            this.immediateFail = immediateFail;
+            this.reconnectIntervalMillis = reconnectIntervalMillis;
+            this.retry = reconnectIntervalMillis > 0;
         }
 
-        /**
-         * Does not include the password.
-         */
-        @Override
-        public String toString() {
-            return "JmsConfiguration [jndiManager=" + jndiManager + ", 
connectionFactoryName=" + connectionFactoryName
-                    + ", destinationName=" + destinationName + ", userName=" + 
userName + "]";
+        public String getConnectionFactoryName() {
+            return connectionFactoryName;
+        }
+
+        public String getDestinationName() {
+            return destinationName;
+        }
+
+        public JndiManager getJndiManager() {
+            return JndiManager.getJndiManager(getJndiProperties());
+        }
+
+        public Properties getJndiProperties() {
+            return jndiProperties;
         }
+
+        public char[] getPassword() {
+            return password;
+        }
+
+        public long getReconnectIntervalMillis() {
+            return reconnectIntervalMillis;
+        }
+
+        public String getUserName() {
+            return userName;
+        }
+
+        public boolean isImmediateFail() {
+            return immediateFail;
+        }
+
+        public boolean isRetry() {
+            return retry;
+        }
+
     }
 
     private static class JmsManagerFactory implements 
ManagerFactory<JmsManager, JmsManagerConfiguration> {
@@ -83,7 +127,72 @@ public class JmsManager extends AbstractManager {
         }
     }
 
+    /**
+     * Handles reconnecting to a Socket on a Thread.
+     */
+    private class Reconnector extends Log4jThread {
+
+        private final CountDownLatch latch = new CountDownLatch(1);
+
+        private volatile boolean shutdown = false;
+
+        private final Object owner;
+
+        public Reconnector(final Object owner) {
+            super("JmsManager-Reconnector");
+            this.owner = owner;
+        }
+
+        public void latch() {
+            try {
+                latch.await();
+            } catch (final InterruptedException ex) {
+                // Ignore the exception.
+            }
+        }
+
+        void reconnect() throws NamingException, JMSException {
+            final JndiManager jndiManager2 = getJndiManager();
+            final Connection connection2 = createConnection(jndiManager2);
+            final Session session2 = createSession(connection2);
+            final Destination destination2 = createDestination(jndiManager2);
+            final MessageProducer messageProducer2 = 
createMessageProducer(session2, destination2);
+            connection2.start();
+            synchronized (owner) {
+                jndiManager = jndiManager2;
+                connection = connection2;
+                session = session2;
+                destination = destination2;
+                messageProducer = messageProducer2;
+                reconnector = null;
+                shutdown = true;
+            }
+            LOGGER.debug("Connection reestablished to {}", configuration);
+        }
+
+        @Override
+        public void run() {
+            while (!shutdown) {
+                try {
+                    sleep(configuration.getReconnectIntervalMillis());
+                    reconnect();
+                } catch (final InterruptedException | JMSException | 
NamingException e) {
+                    LOGGER.debug("Cannot reestablish JMS connection to {}: 
{}", configuration, e.getLocalizedMessage(),
+                            e);
+                } finally {
+                    latch.countDown();
+                }
+            }
+        }
+
+        public void shutdown() {
+            shutdown = true;
+        }
+
+    }
+
     private static final Logger LOGGER = StatusLogger.getLogger();
+
     static final JmsManagerFactory FACTORY = new JmsManagerFactory();
 
     /**
@@ -91,8 +200,6 @@ public class JmsManager extends AbstractManager {
      *
      * @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
@@ -101,69 +208,121 @@ public class JmsManager extends AbstractManager {
      *            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.
-     * @deprecated Use the other getJmsManager() method
-     */
-    @Deprecated
-    public static JmsManager getJmsManager(final String name, final 
JndiManager jndiManager,
-            final String connectionFactoryName, final String destinationName, 
final String userName,
-            final String password) {
-        final JmsManagerConfiguration configuration = new 
JmsManagerConfiguration(jndiManager, connectionFactoryName,
-                destinationName, userName, password == null ? null : 
password.toCharArray());
-        return getManager(name, FACTORY, configuration);
-    }
-
-    /**
-     * Gets a JmsManager using the specified configuration parameters.
-     *
-     * @param name
-     *            The name to use for this JmsManager.
+     * @param immediateFail
+     *            Whether or not to fail immediately with a {@link 
AppenderLoggingException} when connecting to JMS
+     *            fails.
+     * @param reconnectIntervalMillis
+     *            How to log sleep in milliseconds before trying to reconnect 
to JMS.
      * @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,
+    public static JmsManager getJmsManager(final String name, final Properties 
jndiProperties,
             final String connectionFactoryName, final String destinationName, 
final String userName,
-            final char[] password) {
-        final JmsManagerConfiguration configuration = new 
JmsManagerConfiguration(jndiManager, connectionFactoryName,
-                destinationName, userName, password);
+            final char[] password, final boolean immediateFail, final long 
reconnectIntervalMillis) {
+        final JmsManagerConfiguration configuration = new 
JmsManagerConfiguration(jndiProperties, connectionFactoryName,
+                destinationName, userName, password, immediateFail, 
reconnectIntervalMillis);
         return getManager(name, FACTORY, configuration);
     }
 
-    private final JndiManager jndiManager;
-    private final Connection connection;
-    private final Session session;
-
-    private final Destination destination;
-
     private final JmsManagerConfiguration configuration;
 
-    private final MessageProducer producer;
+    private volatile Reconnector reconnector;
+    private volatile JndiManager jndiManager;
+    private volatile Connection connection;
+    private volatile Session session;
+    private volatile Destination destination;
+    private volatile MessageProducer messageProducer;
 
-    private JmsManager(final String name, final JmsManagerConfiguration 
configuration)
-            throws NamingException, JMSException {
+    private JmsManager(final String name, final JmsManagerConfiguration 
configuration) {
         super(null, name);
         this.configuration = configuration;
-        this.jndiManager = configuration.jndiManager;
-        final ConnectionFactory connectionFactory = 
this.jndiManager.lookup(configuration.connectionFactoryName);
-        if (configuration.userName != null && configuration.password != null) {
-            this.connection = 
connectionFactory.createConnection(configuration.userName,
-                    configuration.password == null ? null : 
String.valueOf(configuration.password));
-        } else {
-            this.connection = connectionFactory.createConnection();
+        this.jndiManager = configuration.getJndiManager();
+        try {
+            this.connection = createConnection(this.jndiManager);
+            this.session = createSession(this.connection);
+            this.destination = createDestination(this.jndiManager);
+            this.messageProducer = createMessageProducer(this.session, 
this.destination);
+            this.connection.start();
+        } catch (NamingException | JMSException e) {
+            this.reconnector = createReconnector();
+            this.reconnector.start();
+        }
+    }
+
+    private boolean closeConnection() {
+        if (connection == null) {
+            return true;
+        }
+        final Connection temp = connection;
+        connection = null;
+        try {
+            temp.close();
+            return true;
+        } catch (final JMSException e) {
+            StatusLogger.getLogger().debug(
+                    "Caught exception closing JMS Connection: {} ({}); 
contiuing JMS manager shutdown",
+                    e.getLocalizedMessage(), temp, e);
+            return false;
+        }
+    }
+
+    private boolean closeJndiManager() {
+        if (jndiManager == null) {
+            return true;
+        }
+        final JndiManager tmp = jndiManager;
+        jndiManager = null;
+        tmp.close();
+        return true;
+    }
+
+    private boolean closeMessageProducer() {
+        if (messageProducer == null) {
+            return true;
+        }
+        final MessageProducer temp = messageProducer;
+        messageProducer = null;
+        try {
+            temp.close();
+            return true;
+        } catch (final JMSException e) {
+            StatusLogger.getLogger().debug(
+                    "Caught exception closing JMS MessageProducer: {} ({}); 
contiuing JMS manager shutdown",
+                    e.getLocalizedMessage(), temp, e);
+            return false;
+        }
+    }
+
+    private boolean closeSession() {
+        if (session == null) {
+            return true;
         }
-        this.session = this.connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
-        this.destination = 
this.jndiManager.lookup(configuration.destinationName);
-        this.producer = createMessageProducer();
-        this.connection.start();
+        final Session temp = session;
+        session = null;
+        try {
+            temp.close();
+            return true;
+        } catch (final JMSException e) {
+            StatusLogger.getLogger().debug(
+                    "Caught exception closing JMS Session: {} ({}); contiuing 
JMS manager shutdown",
+                    e.getLocalizedMessage(), temp, e);
+            return false;
+        }
+    }
+
+    private Connection createConnection(final JndiManager jndiManager) throws 
NamingException, JMSException {
+        final ConnectionFactory connectionFactory = 
jndiManager.lookup(configuration.getConnectionFactoryName());
+        if (configuration.getUserName() != null && configuration.getPassword() 
!= null) {
+            return 
connectionFactory.createConnection(configuration.getUserName(),
+                    configuration.getPassword() == null ? null : 
String.valueOf(configuration.getPassword()));
+        }
+        return connectionFactory.createConnection();
+
+    }
+
+    private Destination createDestination(final JndiManager jndiManager) 
throws NamingException {
+        return jndiManager.lookup(configuration.getDestinationName());
     }
 
     /**
@@ -196,6 +355,12 @@ public class JmsManager extends AbstractManager {
         return this.session.createObjectMessage(object);
     }
 
+    private void createMessageAndSend(final LogEvent event, final Serializable 
serializable) throws JMSException {
+        final Message message = createMessage(serializable);
+        message.setJMSTimestamp(event.getTimeMillis());
+        messageProducer.send(message);
+    }
+
     /**
      * Creates a MessageConsumer on this Destination using the current Session.
      *
@@ -209,17 +374,40 @@ public class JmsManager extends AbstractManager {
     /**
      * Creates a MessageProducer on this Destination using the current Session.
      *
+     * @param session
+     *            The JMS Session to use to create the MessageProducer
+     * @param destination
+     *            The JMS Destination for the MessageProducer
      * @return A MessageProducer on this Destination.
      * @throws JMSException
      */
-    public MessageProducer createMessageProducer() throws JMSException {
-        return this.session.createProducer(this.destination);
+    public MessageProducer createMessageProducer(final Session session, final 
Destination destination) throws JMSException {
+        return session.createProducer(destination);
+    }
+
+    private Reconnector createReconnector() {
+        final Reconnector recon = new Reconnector(this);
+        recon.setDaemon(true);
+        recon.setPriority(Thread.MIN_PRIORITY);
+        return recon;
+    }
+
+    private Session createSession(final Connection connection) throws 
JMSException {
+        return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
     }
 
-    JmsManagerConfiguration getJmsManagerConfiguration() {
+    public JmsManagerConfiguration getJmsManagerConfiguration() {
         return configuration;
     }
 
+    JndiManager getJndiManager() {
+        return configuration.getJndiManager();
+    }
+
+    <T> T lookup(final String destinationName) throws NamingException {
+        return this.jndiManager.lookup(destinationName);
+    }
+
     private MapMessage map(final 
org.apache.logging.log4j.message.MapMessage<?, ?> log4jMapMessage,
             final MapMessage jmsMapMessage) {
         // Map without calling 
rg.apache.logging.log4j.message.MapMessage#getData() which makes a copy of the 
map.
@@ -239,24 +427,56 @@ public class JmsManager extends AbstractManager {
 
     @Override
     protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
-        boolean closed = true;
-        try {
-            this.session.close();
-        } catch (final JMSException ignored) {
-            // ignore
-            closed = false;
-        }
-        try {
-            this.connection.close();
-        } catch (final JMSException ignored) {
-            // ignore
-            closed = false;
+        if (reconnector != null) {
+            reconnector.shutdown();
+            reconnector.interrupt();
+            reconnector = null;
         }
+        boolean closed = false;
+        closed &= closeJndiManager();
+        closed &= closeMessageProducer();
+        closed &= closeSession();
+        closed &= closeConnection();
         return closed && this.jndiManager.stop(timeout, timeUnit);
     }
 
-    void send(final Message message) throws JMSException {
-        producer.send(message);
+    void send(final LogEvent event, final Serializable serializable) {
+        if (messageProducer == null) {
+            if (reconnector != null && !configuration.isImmediateFail()) {
+                reconnector.latch();
+            }
+            if (messageProducer == null) {
+                throw new AppenderLoggingException(
+                        "Error sending to JMS Manager '" + getName() + "': JMS 
message producer not available");
+            }
+        }
+        synchronized (this) {
+            try {
+                createMessageAndSend(event, serializable);
+            } catch (final JMSException causeEx) {
+                if (configuration.isRetry() && reconnector == null) {
+                    reconnector = createReconnector();
+                    try {
+                        closeJndiManager();
+                        reconnector.reconnect();
+                    } catch (NamingException | JMSException reconnEx) {
+                        LOGGER.debug("Cannot reestablish JMS connection to {}: 
{}; starting reconnector thread {}",
+                                configuration, reconnEx.getLocalizedMessage(), 
reconnector.getName(), reconnEx);
+                        reconnector.start();
+                        throw new AppenderLoggingException(
+                                String.format("Error sending to %s for %s", 
getName(), configuration), causeEx);
+                    }
+                    try {
+                        createMessageAndSend(event, serializable);
+                    } catch (final JMSException e) {
+                        throw new AppenderLoggingException(
+                                String.format("Error sending to %s after 
reestablishing connection for %s", getName(),
+                                        configuration),
+                                causeEx);
+                    }
+                }
+            }
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
index 7923190..2384680 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
@@ -29,7 +29,7 @@ import org.apache.logging.log4j.core.appender.ManagerFactory;
 import org.apache.logging.log4j.core.util.JndiCloser;
 
 /**
- * JNDI {@link javax.naming.Context} manager.
+ * Manages a JNDI {@link javax.naming.Context}.
  *
  * @since 2.1
  */
@@ -55,6 +55,7 @@ public class JndiManager extends AbstractManager {
 
     /**
      * Gets a named JndiManager using the default {@link 
javax.naming.InitialContext}.
+     * 
      * @param name the name of the JndiManager instance to create or use if 
available
      * @return a default JndiManager
      */
@@ -76,22 +77,64 @@ public class JndiManager extends AbstractManager {
      * @return the JndiManager for the provided parameters.
      */
     public static JndiManager getJndiManager(final String 
initialContextFactoryName,
-                                             final String providerURL,
-                                             final String urlPkgPrefixes,
-                                             final String securityPrincipal,
-                                             final String securityCredentials,
-                                             final Properties 
additionalProperties) {
-        final String name = JndiManager.class.getName() + '@' + 
JndiManager.class.hashCode();
+            final String providerURL,
+            final String urlPkgPrefixes,
+            final String securityPrincipal,
+            final String securityCredentials,
+            final Properties additionalProperties) {
+        Properties properties = createProperties(initialContextFactoryName, 
providerURL, urlPkgPrefixes,
+                securityPrincipal, securityCredentials, additionalProperties);
+        return getManager(createManagerName(), FACTORY, properties);
+    }
+
+    /**
+     * Gets a JndiManager with the provided configuration information.
+     * 
+     * @param properties JNDI properties, usually created by calling {@link 
#createProperties(String, String, String, String, String, Properties)}.
+     * @return the JndiManager for the provided parameters.
+     * @see #createProperties(String, String, String, String, String, 
Properties)
+     * @since 2.9
+     */
+    public static JndiManager getJndiManager(final Properties properties) {
+        return getManager(createManagerName(), FACTORY, properties);
+    }
+
+    private static String createManagerName() {
+        return JndiManager.class.getName() + '@' + 
JndiManager.class.hashCode();
+    }
+
+    /**
+     * Creates JNDI Properties with the provided configuration information.
+     *
+     * @param initialContextFactoryName
+     *            Fully qualified class name of an implementation of {@link 
javax.naming.spi.InitialContextFactory}.
+     * @param providerURL
+     *            The provider URL to use for the JNDI connection (specific to 
the above factory).
+     * @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 securityPrincipal
+     *            The name of the identity of the Principal.
+     * @param securityCredentials
+     *            The security credentials of the Principal.
+     * @param additionalProperties
+     *            Any additional JNDI environment properties to set or {@code 
null} for none.
+     * @return the Properties for the provided parameters.
+     * @since 2.9
+     */
+    public static Properties createProperties(final String 
initialContextFactoryName, final String providerURL,
+            final String urlPkgPrefixes, final String securityPrincipal, final 
String securityCredentials,
+            final Properties additionalProperties) {
         if (initialContextFactoryName == null) {
-            return getManager(name, FACTORY, null);
+            return null;
         }
         final Properties properties = new Properties();
         properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, 
initialContextFactoryName);
         if (providerURL != null) {
             properties.setProperty(Context.PROVIDER_URL, providerURL);
         } else {
-            LOGGER.warn("The JNDI InitialContextFactory class name [{}] was 
provided, but there was no associated " +
-                "provider URL. This is likely to cause problems.", 
initialContextFactoryName);
+            LOGGER.warn("The JNDI InitialContextFactory class name [{}] was 
provided, but there was no associated "
+                    + "provider URL. This is likely to cause problems.", 
initialContextFactoryName);
         }
         if (urlPkgPrefixes != null) {
             properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
@@ -102,13 +145,13 @@ public class JndiManager extends AbstractManager {
                 properties.setProperty(Context.SECURITY_CREDENTIALS, 
securityCredentials);
             } else {
                 LOGGER.warn("A security principal [{}] was provided, but with 
no corresponding security credentials.",
-                    securityPrincipal);
+                        securityPrincipal);
             }
         }
         if (additionalProperties != null) {
             properties.putAll(additionalProperties);
         }
-        return getManager(name, FACTORY, properties);
+        return properties;
     }
 
     @Override
@@ -122,7 +165,7 @@ public class JndiManager extends AbstractManager {
      * @param name name of the object to look up.
      * @param <T>  the type of the object.
      * @return the named object if it could be located.
-     * @throws NamingException
+     * @throws  NamingException if a naming exception is encountered
      */
     @SuppressWarnings("unchecked")
     public <T> T lookup(final String name) throws NamingException {
@@ -144,6 +187,7 @@ public class JndiManager extends AbstractManager {
 
     @Override
     public String toString() {
-        return "JndiManager [context=" + context + "]";
+        return "JndiManager [context=" + context + ", count=" + count + "]";
     }
+
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderTest.java
index 70c28cf..ac544ea 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/HttpAppenderTest.java
@@ -30,12 +30,14 @@ import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.status.StatusData;
 import org.apache.logging.log4j.status.StatusListener;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
 import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 
+@Ignore
 public class HttpAppenderTest {
 
     private static final String LOG_MESSAGE = "Hello, world!";

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index db3cba2..682265e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1017,14 +1017,8 @@
         <version>${failsafe.plugin.version}</version>
         <executions>
           <execution>
-            <id>integration-tests</id>
             <goals>
               <goal>integration-test</goal>
-            </goals>
-          </execution>
-          <execution>
-            <id>verify</id>
-            <goals>
               <goal>verify</goal>
             </goals>
           </execution>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c99d604..973fb98 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -76,6 +76,9 @@
       <action issue="LOG4J2-1934" dev="ggregory" type="add">
         JMS Appender does not know how to recover from a broken connection.
       </action>
+      <action issue="LOG4J2-1955" dev="ggregory" type="add">
+        JMS Appender should be able connect to a broker (later) even it is not 
present at configuration time.
+      </action>
       <action issue="LOG4J2-1956" dev="ggregory" type="update">
         JMS Appender broker password should be a char[], not a String.
       </action>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/de7487ed/src/site/xdoc/manual/appenders.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/manual/appenders.xml 
b/src/site/xdoc/manual/appenders.xml
index 528dabe..84834f0 100644
--- a/src/site/xdoc/manual/appenders.xml
+++ b/src/site/xdoc/manual/appenders.xml
@@ -1351,6 +1351,20 @@ public class ConnectionFactory {
                 <a href="#FailoverAppender">FailoverAppender</a>.</td>
             </tr>
             <tr>
+              <td>immediateFail</td>
+              <td>boolean</td>
+              <td>When set to true, log events will not wait to try to 
reconnect and will fail immediately if the
+              JMS resources are not available. New in 2.9.</td>
+            </tr>
+            <tr>
+              <td>reconnectIntervalMillis</td>
+              <td>long</td>
+              <td>If set to a value greater than 0, after an error, the 
JMSManager will attempt to reconnect to
+                the broker after waiting the specified number of milliseconds. 
If the reconnect fails then
+                an exception will be thrown (which can be caught by the 
application if <code>ignoreExceptions</code> is
+                set to <code>false</code>). New in 2.9.</td>
+            </tr>
+            <tr>
               <td>urlPkgPrefixes</td>
               <td>String</td>
               <td>A colon-separated list of package prefixes for the class 
name of the factory class that will create

Reply via email to