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/26 01:58:23 UTC
logging-log4j2 git commit: [LOG4J2-1934] JMS Appender does not know
how to recover from a broken connection.
Repository: logging-log4j2
Updated Branches:
refs/heads/master f1778a02c -> 8573c8e56
[LOG4J2-1934] JMS Appender does not know how to recover from a broken
connection.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/8573c8e5
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/8573c8e5
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/8573c8e5
Branch: refs/heads/master
Commit: 8573c8e563c2b734f17e6c7f4a141b7cfb80a286
Parents: f1778a0
Author: Gary Gregory <gg...@apache.org>
Authored: Sun Jun 25 18:58:19 2017 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Sun Jun 25 18:58:19 2017 -0700
----------------------------------------------------------------------
.../JmsAppenderConnectPostStartupIT.java | 79 ++++++
.../activemq/JmsAppenderConnectReConnectIT.java | 3 -
.../mom/activemq/JmsAppenderReConnectionIT.java | 79 ------
.../log4j/core/appender/mom/JmsAppender.java | 275 ++++++++++++-------
.../log4j/core/appender/mom/JmsManager.java | 166 +++++------
src/changes/changes.xml | 3 +
6 files changed, 350 insertions(+), 255 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8573c8e5/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
new file mode 100644
index 0000000..a7391ba
--- /dev/null
+++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderConnectPostStartupIT.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 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");
+
+ /**
+ * 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 JmsAppenderConnectPostStartupIT() {
+ super(jmsClientTestConfigRule);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8573c8e5/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 7e53171..82b8f39 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
@@ -29,7 +29,6 @@ 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;
@@ -41,7 +40,6 @@ import org.junit.experimental.categories.Category;
* connection. See https://issues.apache.org/jira/browse/LOG4J2-1934
* </p>
*/
-@Ignore
@Category(Appenders.Jms.class)
public class JmsAppenderConnectReConnectIT {
@@ -64,7 +62,6 @@ public class JmsAppenderConnectReConnectIT {
}
@Test
- @Ignore
public void testConnectReConnect() throws Exception {
// Start broker
final int port = AvailablePortFinder.getNextAvailable();
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8573c8e5/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
deleted file mode 100644
index 262a3da..0000000
--- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/appender/mom/activemq/JmsAppenderReConnectionIT.java
+++ /dev/null
@@ -1,79 +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 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/8573c8e5/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 6ca0296..737d24a 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,18 +18,20 @@
package org.apache.logging.log4j.core.appender.mom;
import java.io.Serializable;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.jms.JMSException;
import javax.jms.Message;
-import javax.jms.MessageProducer;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.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;
import org.apache.logging.log4j.core.config.plugins.PluginAliases;
@@ -39,51 +41,16 @@ 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.layout.SerializedLayout;
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,
* configurations set up for the 2.0 version of the JMS appenders will still work.
*/
@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
-@PluginAliases({"JMSQueue", "JMSTopic"})
+@PluginAliases({ "JMSQueue", "JMSTopic" })
public class JmsAppender extends AbstractAppender {
- private final JmsManager manager;
- private final MessageProducer producer;
-
- protected JmsAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
- final boolean ignoreExceptions, final JmsManager manager)
- throws JMSException {
- super(name, filter, layout, ignoreExceptions);
- this.manager = manager;
- this.producer = this.manager.createMessageProducer();
- }
-
- @Override
- public void append(final LogEvent event) {
- try {
- final Message message = this.manager.createMessage(getLayout().toSerializable(event));
- message.setJMSTimestamp(event.getTimeMillis());
- this.producer.send(message);
- } catch (final JMSException e) {
- throw new AppenderLoggingException(e);
- }
- }
-
- @Override
- public boolean stop(final long timeout, final TimeUnit timeUnit) {
- setStopping();
- boolean stopped = super.stop(timeout, timeUnit, false);
- stopped &= this.manager.stop(timeout, timeUnit);
- setStopped();
- return stopped;
- }
-
- @PluginBuilderFactory
- public static Builder newBuilder() {
- return new Builder();
- }
-
public static class Builder implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
@PluginBuilderAttribute
@@ -110,7 +77,7 @@ public class JmsAppender extends AbstractAppender {
private String factoryBindingName;
@PluginBuilderAttribute
- @PluginAliases({"queueBindingName", "topicBindingName"})
+ @PluginAliases({ "queueBindingName", "topicBindingName" })
@Required(message = "A javax.jms.Destination JNDI name must be specified")
private String destinationBindingName;
@@ -126,114 +93,123 @@ 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;
+
@PluginBuilderAttribute
private boolean ignoreExceptions = true;
-
+
// Programmatic access only for now.
private JmsManager jmsManager;
private Builder() {
}
- public Builder setName(final String name) {
- this.name = name;
- return this;
+ @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender
+ @Override
+ public JmsAppender build() {
+ JmsManager actualJmsManager = jmsManager;
+ JndiManager jndiManager = null;
+ JmsManagerConfiguration configuration = null;
+ if (actualJmsManager == null) {
+ jndiManager = JndiManager.getJndiManager(factoryName, providerUrl, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, null);
+ configuration = new JmsManagerConfiguration(jndiManager, factoryBindingName, destinationBindingName,
+ userName, password);
+ actualJmsManager = AbstractManager.getManager(name, JmsManager.FACTORY, configuration);
+ }
+ // TODO Try to reconnect later by letting the manager be null?
+ if (actualJmsManager == null) {
+ // JmsManagerFactory has already logged an ERROR.
+ return null;
+ }
+ return new JmsAppender(name, filter, layout, ignoreExceptions, reconnectOnExceptionMessages,
+ reconnectAttempts, reconnectIntervalMillis, actualJmsManager);
}
- public Builder setFactoryName(final String factoryName) {
- this.factoryName = factoryName;
+ public Builder setDestinationBindingName(final String destinationBindingName) {
+ this.destinationBindingName = destinationBindingName;
return this;
}
- public Builder setProviderUrl(final String providerUrl) {
- this.providerUrl = providerUrl;
+ public Builder setFactoryBindingName(final String factoryBindingName) {
+ this.factoryBindingName = factoryBindingName;
return this;
}
- public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) {
- this.urlPkgPrefixes = urlPkgPrefixes;
+ public Builder setFactoryName(final String factoryName) {
+ this.factoryName = factoryName;
return this;
}
- public Builder setSecurityPrincipalName(final String securityPrincipalName) {
- this.securityPrincipalName = securityPrincipalName;
+ public Builder setFilter(final Filter filter) {
+ this.filter = filter;
return this;
}
- public Builder setSecurityCredentials(final String securityCredentials) {
- this.securityCredentials = securityCredentials;
+ public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
+ this.ignoreExceptions = ignoreExceptions;
return this;
}
- public Builder setFactoryBindingName(final String factoryBindingName) {
- this.factoryBindingName = factoryBindingName;
+ public Builder setJmsManager(final JmsManager jmsManager) {
+ this.jmsManager = jmsManager;
return this;
}
- public Builder setDestinationBindingName(final String destinationBindingName) {
- this.destinationBindingName = destinationBindingName;
+ public Builder setLayout(final Layout<? extends Serializable> layout) {
+ this.layout = layout;
return this;
}
- /**
- * @deprecated Use {@link #setUserName(String)}.
- */
- @Deprecated
- public Builder setUsername(final String username) {
- this.userName = username;
+ public Builder setName(final String name) {
+ this.name = name;
return this;
}
- public Builder setUserName(final String userName) {
- this.userName = userName;
+ public Builder setPassword(final String password) {
+ this.password = password;
return this;
}
- public Builder setPassword(final String password) {
- this.password = password;
+ public Builder setProviderUrl(final String providerUrl) {
+ this.providerUrl = providerUrl;
return this;
}
- public Builder setLayout(final Layout<? extends Serializable> layout) {
- this.layout = layout;
+ public Builder setSecurityCredentials(final String securityCredentials) {
+ this.securityCredentials = securityCredentials;
return this;
}
- public Builder setFilter(final Filter filter) {
- this.filter = filter;
+ public Builder setSecurityPrincipalName(final String securityPrincipalName) {
+ this.securityPrincipalName = securityPrincipalName;
return this;
}
- public Builder setJmsManager(final JmsManager jmsManager) {
- this.jmsManager = jmsManager;
+ public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) {
+ this.urlPkgPrefixes = urlPkgPrefixes;
return this;
}
- public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
- this.ignoreExceptions = ignoreExceptions;
+ /**
+ * @deprecated Use {@link #setUserName(String)}.
+ */
+ @Deprecated
+ public Builder setUsername(final String username) {
+ this.userName = username;
return this;
}
- @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender
- @Override
- public JmsAppender build() {
- JmsManager actualJmsManager = jmsManager;
- JndiManager jndiManager = null;
- if (actualJmsManager == null) {
- jndiManager = JndiManager.getJndiManager(factoryName, providerUrl, urlPkgPrefixes,
- securityPrincipalName, securityCredentials, null);
- actualJmsManager = JmsManager.getJmsManager(name, jndiManager, factoryBindingName,
- destinationBindingName, userName, password);
- }
- try {
- return new JmsAppender(name, filter, layout, ignoreExceptions, actualJmsManager);
- } catch (final JMSException e) {
- LOGGER.error("Error creating JmsAppender [{}].", name, e);
- if (jndiManager != null) {
- jndiManager.stop(500, TimeUnit.MILLISECONDS);
- }
- return null;
- }
+ public Builder setUserName(final String userName) {
+ this.userName = userName;
+ return this;
}
/**
@@ -249,6 +225,115 @@ public class JmsAppender extends AbstractAppender {
+ jmsManager + "]";
}
+ public void setReconnectOnExceptionMessage(final String[] reconnectOnExceptionMessage) {
+ this.reconnectOnExceptionMessages = reconnectOnExceptionMessage;
+ }
+
+ }
+
+ @PluginBuilderFactory
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ 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
+ */
+ @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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+
+ @Override
+ public boolean stop(final long timeout, final TimeUnit timeUnit) {
+ setStopping();
+ boolean stopped = super.stop(timeout, timeUnit, false);
+ stopped &= this.manager.stop(timeout, timeUnit);
+ setStopped();
+ return stopped;
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8573c8e5/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 5677b86..b6ad13c 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
@@ -44,31 +44,47 @@ import org.apache.logging.log4j.util.BiConsumer;
*/
public class JmsManager extends AbstractManager {
- private static final Logger LOGGER = StatusLogger.getLogger();
+ static class JmsManagerConfiguration {
+ private final JndiManager jndiManager;
+ private final String connectionFactoryName;
+ private final String destinationName;
+ private final String userName;
+ private final String password;
- private static final JmsManagerFactory FACTORY = new JmsManagerFactory();
+ JmsManagerConfiguration(final JndiManager jndiManager, final String connectionFactoryName, final String destinationName,
+ final String userName, final String password) {
+ this.jndiManager = jndiManager;
+ this.connectionFactoryName = connectionFactoryName;
+ this.destinationName = destinationName;
+ this.userName = userName;
+ this.password = password;
+ }
- private final JndiManager jndiManager;
- private final Connection connection;
- private final Session session;
- private final Destination destination;
+ /**
+ * Does not include the password.
+ */
+ @Override
+ public String toString() {
+ return "JmsConfiguration [jndiManager=" + jndiManager + ", connectionFactoryName=" + connectionFactoryName
+ + ", destinationName=" + destinationName + ", userName=" + userName + "]";
+ }
+ }
- private JmsManager(final String name, final JndiManager jndiManager, final String connectionFactoryName,
- final String destinationName, final String userName, final String password)
- throws NamingException, JMSException {
- super(null, name);
- this.jndiManager = jndiManager;
- final ConnectionFactory connectionFactory = this.jndiManager.lookup(connectionFactoryName);
- if (userName != null && password != null) {
- this.connection = connectionFactory.createConnection(userName, password);
- } else {
- this.connection = connectionFactory.createConnection();
+ private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsManagerConfiguration> {
+
+ @Override
+ public JmsManager createManager(final String name, final JmsManagerConfiguration data) {
+ try {
+ return new JmsManager(name, data);
+ } catch (final Exception e) {
+ LOGGER.error("Error creating JmsManager using JmsManagerConfiguration [{}]", data, e);
+ return null;
+ }
}
- this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
- this.destination = this.jndiManager.lookup(destinationName);
- this.connection.start();
}
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ static final JmsManagerFactory FACTORY = new JmsManagerFactory();
/**
* Gets a JmsManager using the specified configuration parameters.
*
@@ -83,29 +99,36 @@ public class JmsManager extends AbstractManager {
public static JmsManager getJmsManager(final String name, final JndiManager jndiManager,
final String connectionFactoryName, final String destinationName,
final String userName, final String password) {
- final JmsConfiguration configuration = new JmsConfiguration(jndiManager, connectionFactoryName, destinationName,
+ final JmsManagerConfiguration configuration = new JmsManagerConfiguration(jndiManager, connectionFactoryName, destinationName,
userName, password);
return getManager(name, FACTORY, configuration);
}
+ private final JndiManager jndiManager;
+ private final Connection connection;
+ private final Session session;
- /**
- * Creates a MessageConsumer on this Destination using the current Session.
- *
- * @return A MessageConsumer on this Destination.
- * @throws JMSException
- */
- public MessageConsumer createMessageConsumer() throws JMSException {
- return this.session.createConsumer(this.destination);
- }
- /**
- * Creates a MessageProducer on this Destination using the current Session.
- *
- * @return A MessageProducer on this Destination.
- * @throws JMSException
- */
- public MessageProducer createMessageProducer() throws JMSException {
- return this.session.createProducer(this.destination);
+ private final Destination destination;
+
+ private final JmsManagerConfiguration configuration;
+
+ private final MessageProducer producer;
+
+ private JmsManager(final String name, final JmsManagerConfiguration configuration)
+ throws NamingException, JMSException {
+ 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);
+ } else {
+ this.connection = connectionFactory.createConnection();
+ }
+ this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ this.destination = this.jndiManager.lookup(configuration.destinationName);
+ this.producer = createMessageProducer();
+ this.connection.start();
}
/**
@@ -138,6 +161,31 @@ public class JmsManager extends AbstractManager {
return this.session.createObjectMessage(object);
}
+ /**
+ * Creates a MessageConsumer on this Destination using the current Session.
+ *
+ * @return A MessageConsumer on this Destination.
+ * @throws JMSException
+ */
+ public MessageConsumer createMessageConsumer() throws JMSException {
+ return this.session.createConsumer(this.destination);
+ }
+
+
+ /**
+ * Creates a MessageProducer on this Destination using the current Session.
+ *
+ * @return A MessageProducer on this Destination.
+ * @throws JMSException
+ */
+ public MessageProducer createMessageProducer() throws JMSException {
+ return this.session.createProducer(this.destination);
+ }
+
+ JmsManagerConfiguration getJmsManagerConfiguration() {
+ return configuration;
+ }
+
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.
log4jMapMessage.forEach(new BiConsumer<String, Object>() {
@@ -145,7 +193,7 @@ public class JmsManager extends AbstractManager {
public void accept(final String key, final Object value) {
try {
jmsMapMessage.setObject(key, value);
- } catch (JMSException e) {
+ } catch (final JMSException e) {
throw new IllegalArgumentException(String.format("%s mapping key '%s' to value '%s': %s",
e.getClass(), key, value, e.getLocalizedMessage()), e);
}
@@ -154,7 +202,6 @@ public class JmsManager extends AbstractManager {
return jmsMapMessage;
}
-
@Override
protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
boolean closed = true;
@@ -173,45 +220,8 @@ public class JmsManager extends AbstractManager {
return closed && this.jndiManager.stop(timeout, timeUnit);
}
- private static class JmsConfiguration {
- private final JndiManager jndiManager;
- private final String connectionFactoryName;
- private final String destinationName;
- private final String userName;
- private final String password;
-
- private JmsConfiguration(final JndiManager jndiManager, final String connectionFactoryName, final String destinationName,
- final String userName, final String password) {
- this.jndiManager = jndiManager;
- this.connectionFactoryName = connectionFactoryName;
- this.destinationName = destinationName;
- this.userName = userName;
- this.password = password;
- }
-
- /**
- * Does not include the password.
- */
- @Override
- public String toString() {
- return "JmsConfiguration [jndiManager=" + jndiManager + ", connectionFactoryName=" + connectionFactoryName
- + ", destinationName=" + destinationName + ", userName=" + userName + "]";
- }
- }
-
- private static class JmsManagerFactory implements ManagerFactory<JmsManager, JmsConfiguration> {
-
- @Override
- public JmsManager createManager(final String name, final JmsConfiguration data) {
- try {
- return new JmsManager(name, data.jndiManager, data.connectionFactoryName, data.destinationName,
- data.userName, data.password);
- } catch (final Exception e) {
- LOGGER.error("Error creating JmsManager using ConnectionFactory [{}] and Destination [{}].",
- data.connectionFactoryName, data.destinationName, e);
- return null;
- }
- }
+ void send(final Message message) throws JMSException {
+ producer.send(message);
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/8573c8e5/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 55627e8..ebd9311 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -49,6 +49,9 @@
<action issue="LOG4J2-1945" dev="ggregory" type="add">
Generate source jas for all test jars.
</action>
+ <action issue="LOG4J2-1934" dev="ggregory" type="add">
+ JMS Appender does not know how to recover from a broken connection.
+ </action>
<action issue="LOG4J2-1874" dev="rpopma" type="add" due-to="Roman Leventov">
Added methods ::writeBytes(ByteBuffer) and ::writeBytes(byte[], int, int) to ByteBufferDestination interface and use these methods in TextEncoderHelper where possible to prepare for future enhancements to reduce lock contention.
</action>