You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2017/06/24 21:28:16 UTC

logging-log4j2 git commit: [LOG4J2-1934] JMS Appender does not know how to recover from a broken connection. Refactor JMS Appender tests for Apache ActiveMQ and add new tests that demonstrate the issue. Failing tests are annotated with @Ignore.

Repository: logging-log4j2
Updated Branches:
  refs/heads/master 1bf75bd5a -> c9ae6a85f


[LOG4J2-1934]
JMS Appender does not know how to recover from a broken connection.
Refactor JMS Appender tests for Apache ActiveMQ and add new tests that
demonstrate the issue. Failing tests are annotated with @Ignore.

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

Branch: refs/heads/master
Commit: c9ae6a85f46c5083f5b63c3442afd4e778291429
Parents: 1bf75bd
Author: Gary Gregory <gg...@apache.org>
Authored: Sat Jun 24 14:28:13 2017 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Sat Jun 24 14:28:13 2017 -0700

----------------------------------------------------------------------
 .../org/apache/logging/log4j/TestMarkers.java   |  31 +++
 .../mom/activemq/AbstractJmsAppenderIT.java     | 180 ++++++++++++++++
 .../activemq/ActiveMqBrokerServiceHelper.java   |  48 +++++
 .../mom/activemq/ActiveMqBrokerServiceRule.java |  96 +++++++++
 .../activemq/JmsAppenderConnectReConnectIT.java |  92 +++++++++
 .../appender/mom/activemq/JmsAppenderIT.java    | 183 -----------------
 .../activemq/JmsAppenderITcpConnectionIT.java   |  76 +++++++
 .../activemq/JmsAppenderIVmConnectionIT.java    |  59 ++++++
 .../mom/activemq/JmsAppenderReConnectionIT.java |  79 +++++++
 .../mom/activemq/JmsClientTestConfig.java       |  95 +++++++++
 .../mom/activemq/JmsClientTestConfigRule.java   |  88 ++++++++
 .../core/appender/AbstractFileAppender.java     | 204 +++++++++++++++++++
 .../test/AvailablePortSystemPropertyRule.java   |  81 ++++++++
 .../logging/log4j/test/RuleChainFactory.java    |  44 ++++
 14 files changed, 1173 insertions(+), 183 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/TestMarkers.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/TestMarkers.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/TestMarkers.java
new file mode 100644
index 0000000..29ba32c
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/TestMarkers.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Markers useful in tests.
+ */
+public class TestMarkers {
+
+	public static final Marker LIFE_CYCLE = MarkerManager.getMarker("LIFECYCLE");
+	public static final Marker TEST = MarkerManager.getMarker("TEST");
+	public static final Marker TEST_RULE = MarkerManager.getMarker("TEST_RULE").addParents(TEST);
+	public static final Marker TEST_RULE_LIFE_CYCLE = MarkerManager.getMarker("TEST_RULE_LIFE_CYCLE")
+			.addParents(TEST_RULE).addParents(LIFE_CYCLE);
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderIT.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderIT.java
new file mode 100644
index 0000000..135f568
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/AbstractJmsAppenderIT.java
@@ -0,0 +1,180 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+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.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+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.core.layout.MessageLayout;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.message.StringMapMessage;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Abstracts services for integration test for the JmsAppender using an embedded
+ * ActiveMQ broker. The client (appender) is set up once for the whole class.
+ * Subclasses decide whether the broker is reinitialized for each test method.
+ * This allows to test the ability of the JmsAppender to reconnect.
+ *
+ * The subclasses cannot be run in parallel.
+ */
+public abstract class AbstractJmsAppenderIT {
+
+	static class JmsQueueConsumer implements MessageListener {
+
+		private final CountDownLatch countDownLatch;
+		private final Collection<Object> events;
+		private final Class<? extends Message> messageClass;
+		private final int messageCount;
+
+		JmsQueueConsumer(final int messageCount, final Class<? extends Message> messageClass) {
+			this.messageCount = messageCount;
+			this.messageClass = messageClass;
+			this.countDownLatch = new CountDownLatch(messageCount);
+			this.events = new ArrayList<>(messageCount);
+		}
+
+		public void awaitAndAssertAllMessagesConsumed() throws InterruptedException {
+			countDownLatch.await(5, TimeUnit.SECONDS);
+			assertEquals(messageCount, events.size());
+		}
+
+		@Override
+		public void onMessage(final Message message) {
+			try {
+				try {
+					final Object event;
+					Assert.assertTrue(String.format("Expected type '%s' to be an instance of %s", message.getClass(),
+							messageClass), messageClass.isAssignableFrom(message.getClass()));
+					if (message instanceof ObjectMessage) {
+						event = ((ObjectMessage) message).getObject();
+					} else if (message instanceof javax.jms.MapMessage) {
+						event = message;
+					} else {
+						Assert.fail("Unexpected Message type: " + message);
+						event = null;
+					}
+					events.add(event);
+				} finally {
+					countDownLatch.countDown();
+				}
+			} catch (final JMSException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	static final String KEY_SERIALIZABLE_PACKAGES = "org.apache.activemq.SERIALIZABLE_PACKAGES";
+
+	private final JmsClientTestConfigRule jmsTestConfigRule;
+
+	public AbstractJmsAppenderIT(final JmsClientTestConfigRule jmsTestConfigRule) {
+		this.jmsTestConfigRule = jmsTestConfigRule;
+	}
+
+	protected JmsAppender getJmsAppender() {
+		return getJmsTestConfig().getJmsAppender();
+	}
+
+	protected JmsManager getJmsManager() {
+		return getJmsTestConfig().getJmsManager();
+	}
+
+	private JmsClientTestConfig getJmsTestConfig() {
+		return jmsTestConfigRule.getJmsClientTestConfig();
+	}
+
+	@Test
+	public void testLogMapMessageToQueue() throws Exception {
+		getJmsTestConfig().createAppender(MessageLayout.createLayout());
+		final int messageCount = 100;
+		final MessageConsumer messageConsumer = getJmsManager().createMessageConsumer();
+		try {
+		final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, javax.jms.MapMessage.class);
+		messageConsumer.setMessageListener(consumer);
+		final String messageText = "Hello, World!";
+		final String loggerName = this.getClass().getName();
+		for (int i = 0; i < messageCount; i++) {
+			final Map<String, String> map = new HashMap<>();
+			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
+				getJmsAppender().append(event);
+			}
+			consumer.awaitAndAssertAllMessagesConsumed();
+		} finally {
+			messageConsumer.close();
+		}
+	}
+
+	@Test
+	public void testLogObjectMessageToQueue() throws Exception {
+		getJmsTestConfig().createAppender(SerializedLayout.createLayout());
+		final int messageCount = 100;
+		final MessageConsumer messageConsumer = getJmsManager().createMessageConsumer();
+		try {
+			final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, ObjectMessage.class);
+			messageConsumer.setMessageListener(consumer);
+			final String messageText = "Hello, World!";
+			final String loggerName = this.getClass().getName();
+			for (int i = 0; i < messageCount; i++) {
+			// @formatter:off
+			final LogEvent event = Log4jLogEvent.newBuilder()
+					.setLoggerName(loggerName)
+					.setLoggerFqcn(loggerName)
+					.setLevel(Level.INFO)
+					.setMessage(new SimpleMessage(messageText))
+					.setThreadName(Thread.currentThread().getName())
+					.setTimeMillis(System.currentTimeMillis())
+					.build();
+			// @formatter:on
+				getJmsAppender().append(event);
+			}
+			consumer.awaitAndAssertAllMessagesConsumed();
+		} finally {
+			messageConsumer.close();
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/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
new file mode 100644
index 0000000..6f6949d
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceHelper.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.appender.mom.activemq;
+
+import java.io.IOException;
+
+import org.apache.activemq.broker.BrokerService;
+
+/**
+ * Helps starts an embedded Apache ActiveMQ service broker.
+ */
+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 void stopBrokerService(final BrokerService broker) throws IOException, Exception {
+		broker.deleteAllMessages();
+		broker.stop();
+		broker.waitUntilStopped();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceRule.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceRule.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceRule.java
new file mode 100644
index 0000000..445172e
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/ActiveMqBrokerServiceRule.java
@@ -0,0 +1,96 @@
+/*
+ * 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 org.apache.activemq.broker.BrokerService;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.TestMarkers;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * JUnit {@link TestRule} to manage an in-JVM Apache ActiveMQ broker with socket
+ * communications between clients and broker.
+ *
+ * @author <a href="mailto:ggregory@rocketsoftware.com">Gary Gregory</a>
+ */
+public class ActiveMqBrokerServiceRule implements TestRule {
+
+	static final Logger logger = LogManager.getLogger(ActiveMqBrokerServiceRule.class);
+
+	/**
+	 * Apache Active MQ uses this property name to lookup which port to use to
+	 * connect to a broker.
+	 */
+	static final String PORT_PROPERTY_NAME = "org.apache.activemq.AMQ_PORT";
+
+	private final String brokerName;
+
+	private String brokerUrlString;
+
+	private final String portPropertyName;
+
+	public ActiveMqBrokerServiceRule(final String brokerName, final String portPropertyName) {
+		this.brokerName = brokerName;
+		this.portPropertyName = portPropertyName;
+	}
+
+	@Override
+	public Statement apply(final Statement base, final Description description) {
+		return new Statement() {
+
+			@Override
+			public void evaluate() throws Throwable {
+				final BrokerService broker = ActiveMqBrokerServiceHelper.startBrokerService(brokerName, brokerUrlString,
+						Integer.parseInt(System.getProperty(portPropertyName)));
+				logger.debug(TestMarkers.TEST_RULE_LIFE_CYCLE, "{} started Apache Active MQ {}",
+						this.getClass().getSimpleName(), this);
+				try {
+					base.evaluate();
+				} finally {
+					ActiveMqBrokerServiceHelper.stopBrokerService(broker);
+					logger.debug(TestMarkers.TEST_RULE_LIFE_CYCLE, "{} stopped Apache Active MQ {}",
+							this.getClass().getSimpleName(), this);
+				}
+			}
+
+		};
+	}
+
+	public String getBrokerName() {
+		return brokerName;
+	}
+
+	public String getBrokerUrlString() {
+		return brokerUrlString;
+	}
+
+	@Override
+	public String toString() {
+		final StringBuilder builder = new StringBuilder();
+		builder.append("ActiveMqBrokerServiceRule [brokerName=");
+		builder.append(brokerName);
+		builder.append(", bindAddress=");
+		builder.append(brokerUrlString);
+		builder.append("]");
+		return builder.toString();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/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
new file mode 100644
index 0000000..7e53171
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectReConnectIT.java
@@ -0,0 +1,92 @@
+/*
+ * 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.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 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);
+	}
+
+	@Test
+	@Ignore
+	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");
+		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);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java
deleted file mode 100644
index 25b3252..0000000
--- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIT.java
+++ /dev/null
@@ -1,183 +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 static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-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.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.appender.mom.JmsManager;
-import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.core.layout.MessageLayout;
-import org.apache.logging.log4j.core.layout.SerializedLayout;
-import org.apache.logging.log4j.core.net.JndiManager;
-import org.apache.logging.log4j.message.StringMapMessage;
-import org.apache.logging.log4j.message.SimpleMessage;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * Integration test for JmsAppender using an embedded ActiveMQ broker.
- */
-@Category(Appenders.Jms.class)
-public class JmsAppenderIT {
-
-	private static final String KEY_SERIALIZABLE_PACKAGES = "org.apache.activemq.SERIALIZABLE_PACKAGES";
-
-	private JmsManager jmsManager;
-
-	private JmsAppender appender;
-
-	@Before
-	public void setUpClass() {
-		System.setProperty(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");
-		@SuppressWarnings("resource") // jndiManager is closed in tearDownClass() through the jmsManager
-		final 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);
-	}
-
-	@After
-	public void tearDownClass() {
-		if (jmsManager != null) {
-			jmsManager.close();
-		}
-		System.getProperties().remove(KEY_SERIALIZABLE_PACKAGES);
-	}
-
-	private void setUp(Layout<?> layout) throws Exception {
-		// @formatter:off
-		appender = JmsAppender.newBuilder()
-				.setName("JmsAppender")
-				.setLayout(layout)
-				.setIgnoreExceptions(true)
-				.setJmsManager(jmsManager)
-				.build();
-		// @formatter:off
-		appender.start();
-	}
-
-	@Test
-	public void testLogMapMessageToQueue() throws Exception {
-		setUp(MessageLayout.createLayout());
-		final int messageCount = 100;
-		final MessageConsumer messageConsumer = jmsManager.createMessageConsumer();
-		final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, javax.jms.MapMessage.class);
-		messageConsumer.setMessageListener(consumer);
-		final String messageText = "Hello, World!";
-		final String loggerName = this.getClass().getName();
-		for (int i = 0; i < messageCount; i++) {
-			Map<String, String> map = new HashMap<>();
-			map.put("messageText", messageText);
-			map.put("threadName", Thread.currentThread().getName());
-			final LogEvent event = Log4jLogEvent.newBuilder().setLoggerName(loggerName) //
-					.setLoggerFqcn(loggerName).setLevel(Level.INFO) //
-					.setMessage(new StringMapMessage(map)) //
-					.setTimeMillis(System.currentTimeMillis()).build();
-			appender.append(event);
-		}
-		consumer.awaitAndAssertAllMessagesConsumed();
-	}
-
-	@Test
-	public void testLogObjectMessageToQueue() throws Exception {
-		setUp(SerializedLayout.createLayout());
-		final int messageCount = 100;
-		final MessageConsumer messageConsumer = jmsManager.createMessageConsumer();
-		final JmsQueueConsumer consumer = new JmsQueueConsumer(messageCount, ObjectMessage.class);
-		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.newBuilder().setLoggerName(loggerName) //
-					.setLoggerFqcn(loggerName).setLevel(Level.INFO) //
-					.setMessage(new SimpleMessage(messageText)).setThreadName(Thread.currentThread().getName()) //
-					.setTimeMillis(System.currentTimeMillis()).build();
-			appender.append(event);
-		}
-		consumer.awaitAndAssertAllMessagesConsumed();
-	}
-
-	private static class JmsQueueConsumer implements MessageListener {
-
-		private final int messageCount;
-		private final Class<? extends Message> messageClass;
-		private final CountDownLatch countDownLatch;
-		private final Collection<Object> events;
-
-		private JmsQueueConsumer(final int messageCount, Class<? extends Message> messageClass) {
-			this.messageCount = messageCount;
-			this.messageClass = messageClass;
-			this.countDownLatch = new CountDownLatch(messageCount);
-			this.events = new ArrayList<>(messageCount);
-		}
-
-		@Override
-		public void onMessage(final Message message) {
-			try {
-				try {
-					final Object event;
-					Assert.assertTrue(String.format("Expected type '%s' to be an instance of %s", message.getClass(), messageClass),
-							messageClass.isAssignableFrom(message.getClass()));
-					if (message instanceof ObjectMessage) {
-						event = ((ObjectMessage) message).getObject();
-					} else if (message instanceof javax.jms.MapMessage) {
-						event = message;
-					} else {
-						Assert.fail("Unexpected Message type: " + message);
-						event = null;
-					}
-					events.add(event);
-				} finally {
-					countDownLatch.countDown();
-				}
-			} catch (final JMSException e) {
-				e.printStackTrace();
-			}
-		}
-
-		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/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderITcpConnectionIT.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderITcpConnectionIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderITcpConnectionIT.java
new file mode 100644
index 0000000..c32f911
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderITcpConnectionIT.java
@@ -0,0 +1,76 @@
+/*
+ * 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 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.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 an Appender would. This test appender is managed at
+ * the class level by a JmsTestConfigRule.
+ * <p>
+ * The test manages an Apache ActiveMQ broken embedded in this test. A new
+ * broker is started and stopped for each test method on the same port, which
+ * means that the JMS Appender needs to reconnect to JMS for the second test
+ * run, which ever that test maybe.
+ * </p>
+ */
+@Category(Appenders.Jms.class)
+public class JmsAppenderITcpConnectionIT extends AbstractJmsAppenderIT {
+
+	public static final AvailablePortSystemPropertyRule portRule = AvailablePortSystemPropertyRule
+			.create(ActiveMqBrokerServiceRule.PORT_PROPERTY_NAME);
+
+	public static final ActiveMqBrokerServiceRule activeMqBrokerServiceRule = new ActiveMqBrokerServiceRule(
+			JmsAppenderITcpConnectionIT.class.getName(), portRule.getName());
+
+	// "admin"/"admin" are the default Apache Active MQ creds.
+	public static final JmsClientTestConfigRule jmsClientTestConfigRule = new JmsClientTestConfigRule(
+			activeMqBrokerServiceRule, ActiveMQInitialContextFactory.class.getName(), "admin", "admin");
+
+	/**
+	 * We assign a port only ONCE ands start the broker ONCE for the whole test
+	 * suite.
+	 */
+	@ClassRule
+	public static final RuleChain ruleChain = RuleChainFactory.create(portRule, activeMqBrokerServiceRule,
+			jmsClientTestConfigRule);
+
+	@AfterClass
+	public static void afterClass() {
+		jmsClientTestConfigRule.getJmsClientTestConfig().stop();
+	}
+
+	@BeforeClass
+	public static void beforeClass() {
+		jmsClientTestConfigRule.getJmsClientTestConfig().start();
+	}
+
+	public JmsAppenderITcpConnectionIT() {
+		super(jmsClientTestConfigRule);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIVmConnectionIT.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIVmConnectionIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIVmConnectionIT.java
new file mode 100644
index 0000000..0178ea3
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderIVmConnectionIT.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.apache.activemq.jndi.ActiveMQInitialContextFactory;
+import org.apache.logging.log4j.categories.Appenders;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Integration test for JmsAppender using an embedded ActiveMQ broker with in
+ * direct VM communication between clients and broker.
+ * <p>
+ * This test manages a client connection to JMS like an Appender would. This
+ * test appender is managed at the class level by a JmsTestConfigRule.
+ * </p>
+ * <p>
+ * This test does not manage an Apache ActiveMQ broker explicitly, rather it
+ * lets ActiveMQ use its "vm" protocol.
+ * </p>
+ */
+@Category(Appenders.Jms.class)
+public class JmsAppenderIVmConnectionIT extends AbstractJmsAppenderIT {
+
+	@ClassRule
+	public static final JmsClientTestConfigRule jmsClientTestConfigRule = new JmsClientTestConfigRule(
+			ActiveMQInitialContextFactory.class.getName(), "vm://localhost?broker.persistent=false", null, null);
+
+	@AfterClass
+	public static void afterClass() {
+		jmsClientTestConfigRule.getJmsClientTestConfig().stop();
+	}
+
+	@BeforeClass
+	public static void beforeClass() {
+		jmsClientTestConfigRule.getJmsClientTestConfig().start();
+	}
+
+	public JmsAppenderIVmConnectionIT() {
+		super(jmsClientTestConfigRule);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderReConnectionIT.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderReConnectionIT.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderReConnectionIT.java
new file mode 100644
index 0000000..262a3da
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderReConnectionIT.java
@@ -0,0 +1,79 @@
+/*
+ * 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 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.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.
+ * <p>
+ * Tests that a JMS appender can connect to a broker AFTER Log4j startup.
+ * </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
+ * </p>
+ */
+@Ignore
+@Category(Appenders.Jms.class)
+public class JmsAppenderReConnectionIT extends AbstractJmsAppenderIT {
+
+	public static final AvailablePortSystemPropertyRule portRule = AvailablePortSystemPropertyRule
+			.create(ActiveMqBrokerServiceRule.PORT_PROPERTY_NAME);
+
+	@Rule
+	public final ActiveMqBrokerServiceRule activeMqBrokerServiceRule = new ActiveMqBrokerServiceRule(
+			JmsAppenderReConnectionIT.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");
+
+	/**
+	 * 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 JmsAppenderReConnectionIT() {
+		super(jmsClientTestConfigRule);
+	}
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/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
new file mode 100644
index 0000000..17ac788
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfig.java
@@ -0,0 +1,95 @@
+package org.apache.logging.log4j.core.appender.mom.activemq;
+
+import java.util.Properties;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.appender.mom.JmsAppender;
+import org.apache.logging.log4j.core.appender.mom.JmsManager;
+import org.apache.logging.log4j.core.net.JndiManager;
+
+/**
+ * All the JMS information and state needed to configure and get a test
+ * going.
+ */
+class JmsClientTestConfig {
+
+	private JmsAppender jmsAppender;
+	private final String jmsInitialContextFactoryClassName;
+	private JmsManager jmsManager;
+	private final String jmsPassword;
+	private final String jmsProviderUrlStr;
+	private final String jmsUserName;
+
+	JmsClientTestConfig(final String jmsInitialContextFactoryClassName, final String jmsProviderUrlStr, final String jmsUserName, final String jmsPassword) {
+		this.jmsInitialContextFactoryClassName = jmsInitialContextFactoryClassName;
+		this.jmsProviderUrlStr = jmsProviderUrlStr;
+		this.jmsUserName = jmsUserName;
+		this.jmsPassword = jmsPassword;
+	}
+
+	JmsAppender createAppender(final Layout<?> layout) {
+		// @formatter:off
+		jmsAppender = JmsAppender.newBuilder()
+				.setName("JmsAppender")
+				.setLayout(layout)
+				.setIgnoreExceptions(true)
+				.setJmsManager(jmsManager)
+				.build();
+		// @formatter:off
+		jmsAppender.start();
+		return jmsAppender;
+	}
+
+	JmsAppender getJmsAppender() {
+		return jmsAppender;
+	}
+
+	String getJmsInitialContextFactoryClassName() {
+		return jmsInitialContextFactoryClassName;
+	}
+
+	JmsManager getJmsManager() {
+		return jmsManager;
+	}
+
+	String 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);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfigRule.java
----------------------------------------------------------------------
diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfigRule.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfigRule.java
new file mode 100644
index 0000000..9cb2e8f
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsClientTestConfigRule.java
@@ -0,0 +1,88 @@
+package org.apache.logging.log4j.core.appender.mom.activemq;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit {@link TestRule} that builds a JmsTestConfig by getting a JMS broker
+ * URL string from either a ActiveMqBrokerServiceRule or the one it was given in
+ * the constructor.
+ */
+class JmsClientTestConfigRule implements TestRule {
+
+	final ActiveMqBrokerServiceRule activeMqBrokerServiceRule;
+	final String brokerUrlStr;
+	private JmsClientTestConfig jmsClientTestConfig;
+	final String jmsInitialContextFactoryClassName;
+	final String password;
+	final String userName;
+
+	public JmsClientTestConfigRule(final ActiveMqBrokerServiceRule activeMqBrokerServiceRule,
+			final String jmsInitialContextFactoryClassName, final String userName, final String password) {
+		this.activeMqBrokerServiceRule = activeMqBrokerServiceRule;
+		this.jmsInitialContextFactoryClassName = jmsInitialContextFactoryClassName;
+		this.brokerUrlStr = null;
+		this.userName = userName;
+		this.password = password;
+	}
+
+	public JmsClientTestConfigRule(final String jmsInitialContextFactoryClassName, final String brokerUrlStr, final String userName,
+			final String password) {
+		this.activeMqBrokerServiceRule = null;
+		this.jmsInitialContextFactoryClassName = jmsInitialContextFactoryClassName;
+		this.brokerUrlStr = brokerUrlStr;
+		this.userName = userName;
+		this.password = password;
+	}
+
+	@Override
+	public Statement apply(final Statement base, final Description description) {
+		return new Statement() {
+
+			@Override
+			public void evaluate() throws Throwable {
+				jmsClientTestConfig = new JmsClientTestConfig(jmsInitialContextFactoryClassName, getBrokerUrlString(),
+						userName, password);
+				try {
+					base.evaluate();
+				} finally {
+					// no tear down.
+				}
+			}
+
+			private String getBrokerUrlString() {
+				return brokerUrlStr != null ? brokerUrlStr : activeMqBrokerServiceRule.getBrokerUrlString();
+			}
+		};
+	}
+
+	ActiveMqBrokerServiceRule getActiveMqBrokerServiceRule() {
+		return activeMqBrokerServiceRule;
+	}
+
+	String getBrokerUrlStr() {
+		return brokerUrlStr;
+	}
+
+	JmsClientTestConfig getJmsClientTestConfig() {
+		return jmsClientTestConfig;
+	}
+
+	String getJmsInitialContextFactoryClassName() {
+		return jmsInitialContextFactoryClassName;
+	}
+
+	String getPassword() {
+		return password;
+	}
+
+	String getUserName() {
+		return userName;
+	}
+
+	void setJmsClientTestConfig(final JmsClientTestConfig jmsClientTestConfig) {
+		this.jmsClientTestConfig = jmsClientTestConfig;
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractFileAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractFileAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractFileAppender.java
new file mode 100644
index 0000000..7cad49c
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractFileAppender.java
@@ -0,0 +1,204 @@
+/*
+ * 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;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Core;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.Builder;
+import org.apache.logging.log4j.core.config.Configuration;
+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.validation.constraints.Required;
+import org.apache.logging.log4j.core.net.Advertiser;
+import org.apache.logging.log4j.core.util.Booleans;
+import org.apache.logging.log4j.core.util.Integers;
+
+/**
+ * Abstract File Appender.
+ */
+public abstract class AbstractFileAppender<M extends OutputStreamManager> extends AbstractOutputStreamAppender<M> {
+
+    /**
+     * Builds FileAppender instances.
+     * 
+     * @param <B>
+     *            The type to build
+     */
+    public abstract static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B> {
+
+        @PluginBuilderAttribute
+        @Required
+        private String fileName;
+
+        @PluginBuilderAttribute
+        private boolean append = true;
+
+        @PluginBuilderAttribute
+        private boolean locking;
+
+        @PluginBuilderAttribute
+        private boolean advertise;
+
+        @PluginBuilderAttribute
+        private String advertiseUri;
+
+        @PluginBuilderAttribute
+        private boolean createOnDemand;
+
+        @PluginBuilderAttribute
+        private String filePermissions;
+
+        @PluginBuilderAttribute
+        private String fileOwner;
+
+        @PluginBuilderAttribute
+        private String fileGroup;
+
+        public String getAdvertiseUri() {
+            return advertiseUri;
+        }
+
+        public String getFileName() {
+            return fileName;
+        }
+
+        public boolean isAdvertise() {
+            return advertise;
+        }
+
+        public boolean isAppend() {
+            return append;
+        }
+
+        public boolean isCreateOnDemand() {
+            return createOnDemand;
+        }
+
+        public boolean isLocking() {
+            return locking;
+        }
+
+        public String getFilePermissions() {
+            return filePermissions;
+        }
+
+        public String getFileOwner() {
+            return fileOwner;
+        }
+
+        public String getFileGroup() {
+            return fileGroup;
+        }
+
+        public B withAdvertise(final boolean advertise) {
+            this.advertise = advertise;
+            return asBuilder();
+        }
+
+        public B withAdvertiseUri(final String advertiseUri) {
+            this.advertiseUri = advertiseUri;
+            return asBuilder();
+        }
+
+        public B withAppend(final boolean append) {
+            this.append = append;
+            return asBuilder();
+        }
+
+        public B withFileName(final String fileName) {
+            this.fileName = fileName;
+            return asBuilder();
+        }
+
+        public B withCreateOnDemand(final boolean createOnDemand) {
+            this.createOnDemand = createOnDemand;
+            return asBuilder();
+        }
+
+        public B withLocking(final boolean locking) {
+            this.locking = locking;
+            return asBuilder();
+        }
+
+        public B withFilePermissions(final String filePermissions) {
+            this.filePermissions = filePermissions;
+            return asBuilder();
+        }
+
+        public B withFileOwner(final String fileOwner) {
+            this.fileOwner = fileOwner;
+            return asBuilder();
+        }
+
+        public B withFileGroup(final String fileGroup) {
+            this.fileGroup = fileGroup;
+            return asBuilder();
+        }
+
+    }
+    
+    private final String fileName;
+
+    private final Advertiser advertiser;
+
+    private final Object advertisement;
+
+    private AbstractFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
+            final M manager, final String filename, final boolean ignoreExceptions,
+            final boolean immediateFlush, final Advertiser advertiser) {
+
+        super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
+        if (advertiser != null) {
+            final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
+            configuration.putAll(manager.getContentFormat());
+            configuration.put("contentType", layout.getContentType());
+            configuration.put("name", name);
+            advertisement = advertiser.advertise(configuration);
+        } else {
+            advertisement = null;
+        }
+        this.fileName = filename;
+        this.advertiser = advertiser;
+    }
+
+    /**
+     * Returns the file name this appender is associated with.
+     * @return The File name.
+     */
+    public String getFileName() {
+        return this.fileName;
+    }
+
+    @Override
+    public boolean stop(final long timeout, final TimeUnit timeUnit) {
+        setStopping();
+        super.stop(timeout, timeUnit, false);
+        if (advertiser != null) {
+            advertiser.unadvertise(advertisement);
+        }
+        setStopped();
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
new file mode 100644
index 0000000..0098057
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/test/AvailablePortSystemPropertyRule.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit TestRule to discover an available port and save it in a system property. Useful for setting up tests using
+ * Apache Active MQ.
+ */
+public class AvailablePortSystemPropertyRule implements TestRule {
+
+    public static AvailablePortSystemPropertyRule create(final String name) {
+        return new AvailablePortSystemPropertyRule(name);
+    }
+
+    protected final String name;
+    protected int port;
+
+    protected AvailablePortSystemPropertyRule(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+        return new Statement() {
+
+            @Override
+            public void evaluate() throws Throwable {
+                final String oldValue = System.getProperty(name);
+                try {
+                    port = AvailablePortFinder.getNextAvailable();
+                    System.setProperty(name, Integer.toString(port));
+                    base.evaluate();
+                } finally {
+                    // Restore if previously set
+                    if (oldValue != null) {
+                        System.setProperty(name, oldValue);
+                    }
+                }
+            }
+        };
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("AvailablePortSystemPropertyRule [name=");
+        builder.append(name);
+        builder.append(", port=");
+        builder.append(port);
+        builder.append("]");
+        return builder.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/c9ae6a85/log4j-core/src/test/java/org/apache/logging/log4j/test/RuleChainFactory.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/RuleChainFactory.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/RuleChainFactory.java
new file mode 100644
index 0000000..6f601df
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/test/RuleChainFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.test;
+
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
+/**
+ * Builds JUnit {@link RuleChain}s.
+ */
+public class RuleChainFactory {
+
+    /**
+     * Creates a {@link RuleChain} where the rules are evaluated in the order you pass in.
+     * 
+     * @param testRules
+     *            test rules to evaluate
+     * @return a new rule chain.
+     */
+    public static RuleChain create(final TestRule... testRules) {
+        RuleChain ruleChain = RuleChain.outerRule(testRules[0]);
+        for (int idx = 1; idx < testRules.length; idx++) {
+            if (ruleChain != null) {
+                ruleChain = ruleChain.around(testRules[idx]);
+            }
+        }
+        return ruleChain;
+    }
+}