Discussion:
[02/29] Split out JMS appender and receiver into new log4j module.
Gary Gregory
2014-09-06 12:14:16 UTC
Permalink
What is the thought behind splitting JMS out of core?

Gary

<div>-------- Original message --------</div><div>From: ***@apache.org </div><div>Date:09/06/2014 00:58 (GMT-05:00) </div><div>To: ***@logging.apache.org </div><div>Subject: [02/29] Split out JMS appender and receiver into new log4j module. </div><div>
</div>http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j 2</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
+@Plugin(name = "JMSQueue", category = "Core", elementType = "appender", printObject = true)
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsQueueManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ * @param event The LogEvent.
+ */
+ @Override
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
+ * @param name The name of the Appender.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory.
+ * @param queueBindingName The name to use to locate the Queue.
+ * @param userName The user ID to use to create the Queue Connection.
+ * @param password The password to use to create the Queue Connection.
+ * @param layout The layout to use (defaults to SerializedLayout).
+ * @param filter The Filter or null.
+ * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
+ * they are propagated to the caller.
+ * @return The JmsQueueAppender.
+ */
+ @PluginFactory
+ public static JmsQueueAppender createAppender(
+ @PluginAttribute("name") final String name,
+ @PluginAttribute("factoryName") final String factoryName,
+ @PluginAttribute("providerURL") final String providerURL,
+ @PluginAttribute("urlPkgPrefixes") final String urlPkgPrefixes,
+ @PluginAttribute("securityPrincipalName") final String securityPrincipalName,
+ @PluginAttribute("securityCredentials") final String securityCredentials,
+ @PluginAttribute("factoryBindingName") final String factoryBindingName,
+ @PluginAttribute("queueBindingName") final String queueBindingName,
+ @PluginAttribute("userName") final String userName,
+ @PluginAttribute("password") final String password,
+ @PluginElement("Layout") Layout<? extends Serializable> layout,
+ @PluginElement("Filter") final Filter filter,
+ @PluginAttribute("ignoreExceptions") final String ignore) {
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsQueueManager manager = JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
new file mode 100644
index 0000000..9985746
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
+@Plugin(name = "JMSTopic", category = "Core", elementType = "appender", printObject = true)
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsTopicManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ * @param event The LogEvent.
+ */
+ @Override
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
+ * @param name The name of the Appender.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
+ * @param topicBindingName The name to use to locate the Topic.
+ * @param userName The userid to use to create the Topic Connection.
+ * @param password The password to use to create the Topic Connection.
+ * @param layout The layout to use (defaults to SerializedLayout).
+ * @param filter The Filter or null.
+ * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
+ * they are propagated to the caller.
+ * @return The JmsTopicAppender.
+ */
+ @PluginFactory
+ public static JmsTopicAppender createAppender(
+ @PluginAttribute("name") final String name,
+ @PluginAttribute("factoryName") final String factoryName,
+ @PluginAttribute("providerURL") final String providerURL,
+ @PluginAttribute("urlPkgPrefixes") final String urlPkgPrefixes,
+ @PluginAttribute("securityPrincipalName") final String securityPrincipalName,
+ @PluginAttribute("securityCredentials") final String securityCredentials,
+ @PluginAttribute("factoryBindingName") final String factoryBindingName,
+ @PluginAttribute("topicBindingName") final String topicBindingName,
+ @PluginAttribute("userName") final String userName,
+ @PluginAttribute("password") final String password,
+ @PluginElement("Layout") Layout<? extends Serializable> layout,
+ @PluginElement("Filters") final Filter filter,
+ @PluginAttribute("ignoreExceptions") final String ignore) {
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsTopicManager manager = JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ * @param name The name of the Appender.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @return the InitialContext.
+ * @throws NamingException if a naming error occurs.
+ */
+ protected static Context createContext(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials)
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName, providerURL, urlPkgPrefixes, securityPrincipalName,
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ * @param ctx The Context.
+ * @param name The name to locate.
+ * @return The object to be located.
+ * @throws NamingException If an error occurs locating the name.
+ */
+ protected static Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @return The Properties.
+ */
+ protected static Properties getEnvironment(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials) {
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been provided without a ProviderURL. " +
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set without SecurityCredentials. " +
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ * @param object The Object to sent.
+ * @throws Exception if an error occurs.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ * @param object The Object to send.
+ * @param session The Session.
+ * @param producer The MessageProducer.
+ * @throws Exception if an error occurs.
+ */
+ public synchronized void send(final Serializable object, final Session session, final MessageProducer producer)
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}", getName());
+ throw ex;
+ }
+ }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener implements javax.jms.MessageListener {
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger = LogManager.getLogger(this.getClass().getName());
+
+ /**
+ * Listener that receives the event.
+ * @param message The received message.
+ */
+ @Override
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage) message;
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " + object.getClass().getName() + ", was expecting LogEvent.");
+ }
+ } else {
+ logger.warn("Received message is of type " + message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming message.",
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ * @param ctx The Context.
+ * @param name The name of the object to locate.
+ * @return The object.
+ * @throws NamingException if an error occurs.
+ */
+ protected Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
new file mode 100644
index 0000000..6825282
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ * @param name The unique name of the connection.
+ * @param context The context.
+ * @param factoryBindingName The factory binding name.
+ * @param queueBindingName The queue binding name.
+ * @param userName The user name.
+ * @param password The credentials for the user.
+ * @param info The Queue connection info.
+ */
+ protected JmsQueueManager(final String name, final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @param factoryBindingName The name to locate in the Context that provides the QueueConnectionFactory.
+ * @param queueBindingName The name to use to locate the Queue.
+ * @param userName The userid to use to create the Queue Connection.
+ * @param password The password to use to create the Queue Connection.
+ * @return The JmsQueueManager.
+ */
+ public static JmsQueueManager getJmsQueueManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String queueBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsQueueManager");
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
+ }
+
+ @Override
+ public synchronized void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ @Override
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String queueBindingName, final String userName,
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final boolean suppress) throws Exception {
+ try {
+ final QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName, password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) lookup(context, queueBindingName);
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements ManagerFactory<JmsQueueManager, FactoryData> {
+
+ @Override
+ public JmsQueueManager createManager(final String name, final FactoryData data) {
+ try {
+ final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+ data.securityPrincipalName, data.securityCredentials);
+ final QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
+ data.password, true);
+ return new JmsQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
new file mode 100644
index 0000000..b231489
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ * @param qcfBindingName The QueueConnectionFactory binding name.
+ * @param queueBindingName The Queue binding name.
+ * @param username The userid to connect to the queue.
+ * @param password The password to connect to the queue.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String queueBindingName, final String username,
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory) lookup(ctx, qcfBindingName);
+ final QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ final QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver = queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ * @param args The command line arguments.
+ * @throws Exception if an error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName, username, password);
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, enc));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName QueueBindingName username password");
+ System.exit(1);
+ }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
new file mode 100644
index 0000000..08d7128
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for JMS Topic connections.
+ */
+public class JmsTopicManager extends AbstractJmsManager {
+
+ private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
+
+ private TopicInfo info;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+ /**
+ * Constructor.
+ * @param name The unique name of the connection.
+ * @param context The context.
+ * @param factoryBindingName The factory binding name.
+ * @param topicBindingName The queue binding name.
+ * @param userName The user name.
+ * @param password The credentials for the user.
+ * @param info The Queue connection info.
+ */
+ protected JmsTopicManager(final String name, final Context context, final String factoryBindingName,
+ final String topicBindingName, final String userName, final String password,
+ final TopicInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JSMTopicManager.
+ * @param factoryName The fully qualified class name of the InitialContextFactory.
+ * @param providerURL The URL of the provider to use.
+ * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory class that
+ * will create a URL context factory
+ * @param securityPrincipalName The name of the identity of the Principal.
+ * @param securityCredentials The security credentials of the Principal.
+ * @param factoryBindingName The name to locate in the Context that provides the TopicConnectionFactory.
+ * @param topicBindingName The name to use to locate the Topic.
+ * @param userName The userid to use to create the Topic Connection.
+ * @param password The password to use to create the Topic Connection.
+ * @return A JmsTopicManager.
+ */
+ public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String topicBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsTopicManager");
+ return null;
+ }
+ if (topicBindingName == null) {
+ LOGGER.error("No topic name provided for JmsTopicManager");
+ return null;
+ }
+
+ final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
+ }
+
+
+ @Override
+ public void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.publisher);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ @Override
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String topicBindingName,
+ final String userName, final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
Ralph Goers
2014-09-06 13:17:20 UTC
Permalink
I think he is doing it for OSGi. But if it has no third party dependencies I am not sure why it is necessary.

Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j 2</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsQueueManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
+ * will create a URL context factory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsQueueManager manager = JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
new file mode 100644
index 0000000..9985746
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsTopicManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
+ * will create a URL context factory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsTopicManager manager = JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
+ * will create a URL context factory
+ */
+ protected static Context createContext(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials)
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName, providerURL, urlPkgPrefixes, securityPrincipalName,
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
+ * will create a URL context factory
+ */
+ protected static Properties getEnvironment(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials) {
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been provided without a ProviderURL. " +
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set without SecurityCredentials. " +
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final Session session, final MessageProducer producer)
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}", getName());
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener implements javax.jms.MessageListener {
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger = LogManager.getLogger(this.getClass().getName());
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage) message;
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " + object.getClass().getName() + ", was expecting LogEvent.");
+ }
+ } else {
+ logger.warn("Received message is of type " + message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming message.",
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
new file mode 100644
index 0000000..6825282
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
+ * will create a URL context factory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String queueBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsQueueManager");
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
+ }
+
+ public synchronized void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String queueBindingName, final String userName,
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final boolean suppress) throws Exception {
+ try {
+ final QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName, password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) lookup(context, queueBindingName);
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements ManagerFactory<JmsQueueManager, FactoryData> {
+
+ public JmsQueueManager createManager(final String name, final FactoryData data) {
+ try {
+ final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+ data.securityPrincipalName, data.securityCredentials);
+ final QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
+ data.password, true);
+ return new JmsQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
new file mode 100644
index 0000000..b231489
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String queueBindingName, final String username,
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory) lookup(ctx, qcfBindingName);
+ final QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ final QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver = queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName, username, password);
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, enc));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName QueueBindingName username password");
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
new file mode 100644
index 0000000..08d7128
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for JMS Topic connections.
+ */
+public class JmsTopicManager extends AbstractJmsManager {
+
+ private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
+
+ private TopicInfo info;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+ /**
+ * Constructor.
+ */
+ protected JmsTopicManager(final String name, final Context context, final String factoryBindingName,
+ final String topicBindingName, final String userName, final String password,
+ final TopicInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JSMTopicManager.
+ * will create a URL context factory
+ */
+ public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String topicBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsTopicManager");
+ return null;
+ }
+ if (topicBindingName == null) {
+ LOGGER.error("No topic name provided for JmsTopicManager");
+ return null;
+ }
+
+ final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
+ }
+
+
+ public void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.publisher);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String topicBindingName,
+ final String userName, final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
Gary Gregory
2014-09-06 15:54:55 UTC
Permalink
Unless you count the JMS API jar which is not part of the JRE... but is that reason enough to have a new module?

Gary

<div>-------- Original message --------</div><div>From: Ralph Goers <***@apache.org> </div><div>Date:09/06/2014 09:17 (GMT-05:00) </div><div>To: Log4J Developers List <log4j-***@logging.apache.org> </div><div>Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module. </div><div>
</div>I think he is doing it for OSGi. But if it has no third party dependencies I am not sure why it is necessary.

Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j 2</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsQueueManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
+ * will create a URL context factory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsQueueManager manager = JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
new file mode 100644
index 0000000..9985746
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+ final JmsTopicManager manager, final boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
+ * will create a URL context factory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+ final JmsTopicManager manager = JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager, ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
+ * will create a URL context factory
+ */
+ protected static Context createContext(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials)
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName, providerURL, urlPkgPrefixes, securityPrincipalName,
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
+ * will create a URL context factory
+ */
+ protected static Properties getEnvironment(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials) {
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been provided without a ProviderURL. " +
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set without SecurityCredentials. " +
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final Session session, final MessageProducer producer)
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}", getName());
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener implements javax.jms.MessageListener {
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger = LogManager.getLogger(this.getClass().getName());
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage) message;
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " + object.getClass().getName() + ", was expecting LogEvent.");
+ }
+ } else {
+ logger.warn("Received message is of type " + message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming message.",
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
new file mode 100644
index 0000000..6825282
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new JMSQueueManagerFactory();
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
+ * will create a URL context factory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String queueBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsQueueManager");
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' + queueBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
+ }
+
+ public synchronized void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, queueBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String queueBindingName, final String userName,
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final String factoryBindingName,
+ final String queueBindingName, final String userName, final String password,
+ final boolean suppress) throws Exception {
+ try {
+ final QueueConnectionFactory factory = (QueueConnectionFactory) lookup(context, factoryBindingName);
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName, password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) lookup(context, queueBindingName);
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " + factoryBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " + queueBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession session, final QueueSender sender) {
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements ManagerFactory<JmsQueueManager, FactoryData> {
+
+ public JmsQueueManager createManager(final String name, final FactoryData data) {
+ try {
+ final Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+ data.securityPrincipalName, data.securityCredentials);
+ final QueueInfo info = connect(ctx, data.factoryBindingName, data.queueBindingName, data.userName,
+ data.password, true);
+ return new JmsQueueManager(name, ctx, data.factoryBindingName, data.queueBindingName,
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
new file mode 100644
index 0000000..b231489
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String queueBindingName, final String username,
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory) lookup(ctx, qcfBindingName);
+ final QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ final QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver = queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName, username, password);
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, enc));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName QueueBindingName username password");
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
----------------------------------------------------------------------
diff --git a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
new file mode 100644
index 0000000..08d7128
--- /dev/null
+++ b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for JMS Topic connections.
+ */
+public class JmsTopicManager extends AbstractJmsManager {
+
+ private static final JMSTopicManagerFactory FACTORY = new JMSTopicManagerFactory();
+
+ private TopicInfo info;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+ /**
+ * Constructor.
+ */
+ protected JmsTopicManager(final String name, final Context context, final String factoryBindingName,
+ final String topicBindingName, final String userName, final String password,
+ final TopicInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JSMTopicManager.
+ * will create a URL context factory
+ */
+ public static JmsTopicManager getJmsTopicManager(final String factoryName, final String providerURL,
+ final String urlPkgPrefixes, final String securityPrincipalName,
+ final String securityCredentials, final String factoryBindingName,
+ final String topicBindingName, final String userName,
+ final String password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for JmsTopicManager");
+ return null;
+ }
+ if (topicBindingName == null) {
+ LOGGER.error("No topic name provided for JmsTopicManager");
+ return null;
+ }
+
+ final String name = "JMSTopic:" + factoryBindingName + '.' + topicBindingName;
+ return getManager(name, FACTORY, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
+ }
+
+
+ public void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName, topicBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.publisher);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(), e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " + getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName, final String securityCredentials,
+ final String factoryBindingName, final String topicBindingName,
+ final String userName, final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-***@logging.apache.org
For additional commands, e-mail: log4j-dev-***@logging.apache.org
Matt Sicker
2014-09-06 16:08:38 UTC
Permalink
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html

Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j
module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..08d7128
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTopicManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for JMS Topic connections.
+ */
+public class JmsTopicManager extends AbstractJmsManager {
+
+ private static final JMSTopicManagerFactory FACTORY = new
JMSTopicManagerFactory();
Post by Gary Gregory
+
+ private TopicInfo info;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+ /**
+ * Constructor.
+ */
+ protected JmsTopicManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String topicBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final TopicInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JSMTopicManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsTopicManager getJmsTopicManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
topicBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsTopicManager");
Post by Gary Gregory
+ return null;
+ }
+ if (topicBindingName == null) {
+ LOGGER.error("No topic name provided for JmsTopicManager");
+ return null;
+ }
+
+ final String name = "JMSTopic:" + factoryBindingName + '.' +
topicBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password));
Post by Gary Gregory
+ }
+
+
+ public void send(final Serializable object) throws Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName,
topicBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.publisher);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String topicBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String topicBindingName,
Post by Gary Gregory
+ final String userName, final String
password) {
Post by Gary Gregory
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.topicBindingName = topicBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
---------------------------------------------------------------------
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-06 17:22:58 UTC
Permalink
Hi Matt, 

Feel free to document your thinking process on the ML since we can't banter around a water cooler...

I'm all for packaging log4j in a way to make it appealing to the widest audience. I am not fond personally of lots of little jars but it seems to be what OSGi wants. It's that correct? 

Gary

<div>-------- Original message --------</div><div>From: Matt Sicker <***@gmail.com> </div><div>Date:09/06/2014 12:08 (GMT-05:00) </div><div>To: Log4J Developers List <log4j-***@logging.apache.org> </div><div>Cc: Ralph Goers <***@apache.org> </div><div>Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module. </div><div>
</div>The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html

Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j
module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
Matt Sicker
2014-09-06 18:15:04 UTC
Permalink
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).

Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!

I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)

I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j
module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
{
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
{
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if
it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-06 18:56:21 UTC
Permalink
I agree with most of that but the flip side for logging is that we deliver all of log4j because it is the users of our products that decide how to integrate with our products and how logging is done is all up to the users. So our use case is give users all of log4j and let them configure it as they see fit. Which is why I want a all - in - one module where core is the impl.

Gary

<div>-------- Original message --------</div><div>From: Matt Sicker <***@gmail.com> </div><div>Date:09/06/2014 14:15 (GMT-05:00) </div><div>To: Log4J Developers List <log4j-***@logging.apache.org> </div><div>Cc: Ralph Goers <***@apache.org> </div><div>Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module. </div><div>
</div>The basic idea is that instead of so many optional dependencies (which still need to be specified in your build script or included as additional JARs), we can break out non-essential pieces with said dependencies into more easily used components. Most of log4j-core doesn't require outside dependencies, so that's a good thing! Sure, in a sense it could be broken up into tons of little modules, but besides the mass of JARs you get from that, I've found that most projects that are so finely split up like that have next to no documentation outside the core modules. Then again, that could be the same with monolithic projects, too. In that regard, I think Log4j is more like Spring where the entire project is rather well documented; however, the javadoc side of things is still lacking (which is understandable).

Basically, clear, concise documentation relevant to users and developers alike is key to having a bunch of features (or modules). There are projects like Apache Karaf which have like a bazillion features but tons of them are either undocumented or exist on entirely different projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and on). The documentation is the code so to say, and when that's split up into dozens of modules, good luck finding anything!

I also think that encouraging users to use build systems like Maven or Gradle instead of relying on IDE builds should be encouraged. However, yes, it is important to document how to properly pull all the necessary libraries together manually. I know we've still got various Ant-based builds at my work that require a lot of work to get new libraries added (like Camel, CXF, etc.), and as a user, that's a huge pain. However, I think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as with those projects, you tend to bring in tons of their modules (if not most of them); in Log4j, you only need the modules that correspond to the features you're using (which is probably in log4j-core most of the time; people still prefer logging to files). So instead of having a long chain of dependencies within our own project, anything outside log4j-core would only need third party dependencies/APIs rather than trying to figure out which set of Log4j dependencies are required for yet another Log4j dependency. That would be a much more modular system than the sort of modules we get from monoliths like Spring. (Speaking of Spring, to see just how ridiculous transitive dependencies have gotten, take a look at the Spring IO Platform project: <http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)

I also find that it helps testability when you separate things out a bit more. It can help identify weak areas that don't have many tests that would otherwise go unnoticed (without test coverage tools). It can help identify documentation gaps, too. A monolithic application can mask its lack of manuals or stability just out of its sheer size! I feel that way every time I work with a Java EE server (even though they all use OSGi nowadays) or proprietary libraries with even less documentation than your typical Incubator project. Plus, when things are modularised in a nice fashion (and not ad hoc), then it makes it that much easier for outside contributors to provide changes. Of course, that's more related to high cohesion and loose coupling, but having a module system helps enforce those goals.


On 6 September 2014 12:22, Gary Gregory <***@gmail.com> wrote:
Hi Matt,

Feel free to document your thinking process on the ML since we can't banter around a water cooler...

I'm all for packaging log4j in a way to make it appealing to the widest audience. I am not fond personally of lots of little jars but it seems to be what OSGi wants. It's that correct?

Gary


-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.

The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html

Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j
module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
Matt Sicker <***@gmail.com>
Matt Sicker
2014-09-06 19:44:22 UTC
Permalink
I could see a sort of megajar being useful one day in that case.
Post by Gary Gregory
I agree with most of that but the flip side for logging is that we deliver
all of log4j because it is the users of our products that decide how to
integrate with our products and how logging is done is all up to the users.
So our use case is give users all of log4j and let them configure it as
they see fit. Which is why I want a all - in - one module where core is the
impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if
it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
Matt Sicker <***@gmail.com>
Ralph Goers
2014-09-06 21:20:31 UTC
Permalink
I guess I prefer to follow the 80/20 rule - core should have all the functionality that 80% of the users will use. The other 20% - which necessarily require their own dependencies - can be found in outside jars. I donÂ’t mind telling a user that core has a few optional dependencies for a few optional features. But there should only be a handful of those.

JMS might fit into the same bucket as Flume but it could also be argued that it is similar to JDBC, so IÂ’m on the fence as to whether moving it out is the right thing to do.

Ralph
Post by Gary Gregory
I agree with most of that but the flip side for logging is that we deliver all of log4j because it is the users of our products that decide how to integrate with our products and how logging is done is all up to the users. So our use case is give users all of log4j and let them configure it as they see fit. Which is why I want a all - in - one module where core is the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which still need to be specified in your build script or included as additional JARs), we can break out non-essential pieces with said dependencies into more easily used components. Most of log4j-core doesn't require outside dependencies, so that's a good thing! Sure, in a sense it could be broken up into tons of little modules, but besides the mass of JARs you get from that, I've found that most projects that are so finely split up like that have next to no documentation outside the core modules. Then again, that could be the same with monolithic projects, too. In that regard, I think Log4j is more like Spring where the entire project is rather well documented; however, the javadoc side of things is still lacking (which is understandable).
Basically, clear, concise documentation relevant to users and developers alike is key to having a bunch of features (or modules). There are projects like Apache Karaf which have like a bazillion features but tons of them are either undocumented or exist on entirely different projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and on). The documentation is the code so to say, and when that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or Gradle instead of relying on IDE builds should be encouraged. However, yes, it is important to document how to properly pull all the necessary libraries together manually. I know we've still got various Ant-based builds at my work that require a lot of work to get new libraries added (like Camel, CXF, etc.), and as a user, that's a huge pain. However, I think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as with those projects, you tend to bring in tons of their modules (if not most of them); in Log4j, you only need the modules that correspond to the features you're using (which is probably in log4j-core most of the time; people still prefer logging to files). So instead of having a long chain of dependencies within our own project, anything outside log4j-core would only need third party dependencies/APIs rather than trying to figure out which set of Log4j dependencies are required for yet another Log4j dependency. That would be a much more modular system than the sort of modules we get from monoliths like Spring. (Speaking of Spring, to see just how ridiculous transitive dependencies have gotten, take a look at the Spring IO Platform project: <http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit more. It can help identify weak areas that don't have many tests that would otherwise go unnoticed (without test coverage tools). It can help identify documentation gaps, too. A monolithic application can mask its lack of manuals or stability just out of its sheer size! I feel that way every time I work with a Java EE server (even though they all use OSGi nowadays) or proprietary libraries with even less documentation than your typical Incubator project. Plus, when things are modularised in a nice fashion (and not ad hoc), then it makes it that much easier for outside contributors to provide changes. Of course, that's more related to high cohesion and loose coupling, but having a module system helps enforce those goals.
Hi Matt,
Feel free to document your thinking process on the ML since we can't banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest audience. I am not fond personally of lots of little jars but it seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
Matt Sicker
2014-09-06 21:47:10 UTC
Permalink
Honestly, I'd be more likely to use Log4j to log my JMS server rather than
the other way around. Plus, Flume is really awesome for distributed logging
without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued
that it is similar to JDBC, so I’m on the fence as to whether moving it out
is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we deliver
all of log4j because it is the users of our products that decide how to
integrate with our products and how logging is done is all up to the users.
So our use case is give users all of log4j and let them configure it as
they see fit. Which is why I want a all - in - one module where core is the
impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if
it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-11 11:30:20 UTC
Permalink
So... do we have consensus here?

Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that be
renamed to "Appender" singular?

The only advantage I see is not dragging in the JMS API jar...

Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather than
the other way around. Plus, Flume is really awesome for distributed logging
without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued
that it is similar to JDBC, so I’m on the fence as to whether moving it out
is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has
been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application
if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
E-Mail: ***@gmail.com | ***@apache.org
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Ralph Goers
2014-09-11 12:39:34 UTC
Permalink
It sounds like you and I have a similar point of view. If I had to vote it would be something like -0.5 - not a veto but really don't see why this is better.

Sent from my iPad
Post by Gary Gregory
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Honestly, I'd be more likely to use Log4j to log my JMS server rather than the other way around. Plus, Flume is really awesome for distributed logging without the hassle of setting up a network of brokers and other configuration fun when dealing with JMS on a large scale.
I guess I prefer to follow the 80/20 rule - core should have all the functionality that 80% of the users will use. The other 20% - which necessarily require their own dependencies - can be found in outside jars. I don’t mind telling a user that core has a few optional dependencies for a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued that it is similar to JDBC, so I’m on the fence as to whether moving it out is the right thing to do.
Ralph
Post by Gary Gregory
I agree with most of that but the flip side for logging is that we deliver all of log4j because it is the users of our products that decide how to integrate with our products and how logging is done is all up to the users. So our use case is give users all of log4j and let them configure it as they see fit. Which is why I want a all - in - one module where core is the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which still need to be specified in your build script or included as additional JARs), we can break out non-essential pieces with said dependencies into more easily used components. Most of log4j-core doesn't require outside dependencies, so that's a good thing! Sure, in a sense it could be broken up into tons of little modules, but besides the mass of JARs you get from that, I've found that most projects that are so finely split up like that have next to no documentation outside the core modules. Then again, that could be the same with monolithic projects, too. In that regard, I think Log4j is more like Spring where the entire project is rather well documented; however, the javadoc side of things is still lacking (which is understandable).
Basically, clear, concise documentation relevant to users and developers alike is key to having a bunch of features (or modules). There are projects like Apache Karaf which have like a bazillion features but tons of them are either undocumented or exist on entirely different projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and on). The documentation is the code so to say, and when that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or Gradle instead of relying on IDE builds should be encouraged. However, yes, it is important to document how to properly pull all the necessary libraries together manually. I know we've still got various Ant-based builds at my work that require a lot of work to get new libraries added (like Camel, CXF, etc.), and as a user, that's a huge pain. However, I think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as with those projects, you tend to bring in tons of their modules (if not most of them); in Log4j, you only need the modules that correspond to the features you're using (which is probably in log4j-core most of the time; people still prefer logging to files). So instead of having a long chain of dependencies within our own project, anything outside log4j-core would only need third party dependencies/APIs rather than trying to figure out which set of Log4j dependencies are required for yet another Log4j dependency. That would be a much more modular system than the sort of modules we get from monoliths like Spring. (Speaking of Spring, to see just how ridiculous transitive dependencies have gotten, take a look at the Spring IO Platform project: <http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit more. It can help identify weak areas that don't have many tests that would otherwise go unnoticed (without test coverage tools). It can help identify documentation gaps, too. A monolithic application can mask its lack of manuals or stability just out of its sheer size! I feel that way every time I work with a Java EE server (even though they all use OSGi nowadays) or proprietary libraries with even less documentation than your typical Incubator project. Plus, when things are modularised in a nice fashion (and not ad hoc), then it makes it that much easier for outside contributors to provide changes. Of course, that's more related to high cohesion and loose coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest audience. I am not fond personally of lots of little jars but it seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
JUnit in Action, Second Edition
Spring Batch in Action
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Matt Sicker
2014-09-11 13:29:13 UTC
Permalink
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to vote it
would be something like -0.5 - not a veto but really don't see why this is
better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that be
renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued
that it is similar to JDBC, so I’m on the fence as to whether moving it out
is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but
is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has
been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.'
+
Post by Gary Gregory
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application
if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-11 13:47:25 UTC
Permalink
I think the API module should have 0-deps, ideally, which we do. The OSGi
dep is "provided" so that's an exception.

For Core, I do not think we've ever settled on a policy. The policy could
be: if you have a runtime dep (test only deps are OK), then you cannot live
in Core. But that breaks right away since we bring in lots of deps just to
do XML and JSON IO. So that would have to be moved out. Bleh, seems like a
big mess.

Gary
Post by Matt Sicker
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to vote
it would be something like -0.5 - not a veto but really don't see why this
is better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that
be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued
that it is similar to JDBC, so I’m on the fence as to whether moving it out
is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and
developers alike is key to having a bunch of features (or modules). There
are projects like Apache Karaf which have like a bazillion features but
tons of them are either undocumented or exist on entirely different
projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld,
the list goes on and on). The documentation is the code so to say, and when
that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a
bit more. It can help identify weak areas that don't have many tests that
would otherwise go unnoticed (without test coverage tools). It can help
identify documentation gaps, too. A monolithic application can mask its
lack of manuals or stability just out of its sheer size! I feel that way
every time I work with a Java EE server (even though they all use OSGi
nowadays) or proprietary libraries with even less documentation than your
typical Incubator project. Plus, when things are modularised in a nice
fashion (and not ad hoc), then it makes it that much easier for outside
contributors to provide changes. Of course, that's more related to high
cohesion and loose coupling, but having a module system helps enforce those
goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the
widest audience. I am not fond personally of lots of little jars but it
seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but
is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Post by Gary Gregory
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the TopicConnectionFactory.
Topic.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has
been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been
set
Post by Gary Gregory
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This
class
Post by Gary Gregory
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage =
(ObjectMessage)
Post by Gary Gregory
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName +
'.' +
Post by Gary Gregory
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName,
Post by Gary Gregory
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx =
createContext(data.factoryName,
Post by Gary Gregory
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception
{
Post by Gary Gregory
Post by Gary Gregory
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application
if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
E-Mail: ***@gmail.com | ***@apache.org
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Matt Sicker
2014-09-11 19:04:05 UTC
Permalink
Plus the LMAX library for AsyncLogger (which I find to be a rather killer
feature) makes it somewhat awkward to truly enforce that policy.
Post by Gary Gregory
I think the API module should have 0-deps, ideally, which we do. The OSGi
dep is "provided" so that's an exception.
For Core, I do not think we've ever settled on a policy. The policy could
be: if you have a runtime dep (test only deps are OK), then you cannot live
in Core. But that breaks right away since we bring in lots of deps just to
do XML and JSON IO. So that would have to be moved out. Bleh, seems like a
big mess.
Gary
Post by Matt Sicker
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to vote
it would be something like -0.5 - not a veto but really don't see why this
is better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that
be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be
argued that it is similar to JDBC, so I’m on the fence as to whether moving
it out is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and
developers alike is key to having a bunch of features (or modules). There
are projects like Apache Karaf which have like a bazillion features but
tons of them are either undocumented or exist on entirely different
projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld,
the list goes on and on). The documentation is the code so to say, and when
that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a
bit more. It can help identify weak areas that don't have many tests that
would otherwise go unnoticed (without test coverage tools). It can help
identify documentation gaps, too. A monolithic application can mask its
lack of manuals or stability just out of its sheer size! I feel that way
every time I work with a Java EE server (even though they all use OSGi
nowadays) or proprietary libraries with even less documentation than your
typical Incubator project. Plus, when things are modularised in a nice
fashion (and not ad hoc), then it makes it that much easier for outside
contributors to provide changes. Of course, that's more related to high
cohesion and loose coupling, but having a module system helps enforce those
goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the
widest audience. I am not fond personally of lots of little jars but it
seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE...
but is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Post by Gary Gregory
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the TopicConnectionFactory.
Topic.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager
{
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final
String
Post by Gary Gregory
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has
been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been
set
Post by Gary Gregory
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object,
final
Post by Gary Gregory
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This
class
Post by Gary Gregory
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends
LogEventListener
Post by Gary Gregory
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage =
(ObjectMessage)
Post by Gary Gregory
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final
Post by Gary Gregory
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName +
'.' +
Post by Gary Gregory
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName,
Post by Gary Gregory
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx =
createContext(data.factoryName,
Post by Gary Gregory
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws
Exception {
Post by Gary Gregory
Post by Gary Gregory
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the
application if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName
QueueBindingName
Post by Gary Gregory
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-11 19:15:50 UTC
Permalink
Right, so we can table this discussion. Unless we want to put JMS back into
its 2.0.x location...

Gary
Post by Matt Sicker
Plus the LMAX library for AsyncLogger (which I find to be a rather killer
feature) makes it somewhat awkward to truly enforce that policy.
Post by Gary Gregory
I think the API module should have 0-deps, ideally, which we do. The OSGi
dep is "provided" so that's an exception.
For Core, I do not think we've ever settled on a policy. The policy could
be: if you have a runtime dep (test only deps are OK), then you cannot live
in Core. But that breaks right away since we bring in lots of deps just to
do XML and JSON IO. So that would have to be moved out. Bleh, seems like a
big mess.
Gary
Post by Matt Sicker
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to vote
it would be something like -0.5 - not a veto but really don't see why this
is better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that
be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be
argued that it is similar to JDBC, so I’m on the fence as to whether moving
it out is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies
(which still need to be specified in your build script or included as
additional JARs), we can break out non-essential pieces with said
dependencies into more easily used components. Most of log4j-core doesn't
require outside dependencies, so that's a good thing! Sure, in a sense it
could be broken up into tons of little modules, but besides the mass of
JARs you get from that, I've found that most projects that are so finely
split up like that have next to no documentation outside the core modules.
Then again, that could be the same with monolithic projects, too. In that
regard, I think Log4j is more like Spring where the entire project is
rather well documented; however, the javadoc side of things is still
lacking (which is understandable).
Basically, clear, concise documentation relevant to users and
developers alike is key to having a bunch of features (or modules). There
are projects like Apache Karaf which have like a bazillion features but
tons of them are either undocumented or exist on entirely different
projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld,
the list goes on and on). The documentation is the code so to say, and when
that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven
or Gradle instead of relying on IDE builds should be encouraged. However,
yes, it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a
bit more. It can help identify weak areas that don't have many tests that
would otherwise go unnoticed (without test coverage tools). It can help
identify documentation gaps, too. A monolithic application can mask its
lack of manuals or stability just out of its sheer size! I feel that way
every time I work with a Java EE server (even though they all use OSGi
nowadays) or proprietary libraries with even less documentation than your
typical Incubator project. Plus, when things are modularised in a nice
fashion (and not ad hoc), then it makes it that much easier for outside
contributors to provide changes. Of course, that's more related to high
cohesion and loose coupling, but having a module system helps enforce those
goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the
widest audience. I am not fond personally of lots of little jars but it
seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE...
but is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Post by Gary Gregory
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the TopicConnectionFactory.
Topic.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends
AbstractManager {
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final
String
Post by Gary Gregory
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name
has been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been
set
Post by Gary Gregory
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object,
final
Post by Gary Gregory
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This
class
Post by Gary Gregory
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends
LogEventListener
Post by Gary Gregory
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage =
(ObjectMessage)
Post by Gary Gregory
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String
name)
Post by Gary Gregory
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final
Post by Gary Gregory
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final
String
Post by Gary Gregory
factoryName, final String providerURL,
Post by Gary Gregory
+ final
String
Post by Gary Gregory
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final
String
Post by Gary Gregory
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final
String
Post by Gary Gregory
queueBindingName, final String userName,
Post by Gary Gregory
+ final
String
Post by Gary Gregory
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName +
'.' +
Post by Gary Gregory
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final
String
Post by Gary Gregory
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName,
final
Post by Gary Gregory
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context,
final
Post by Gary Gregory
String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName,
Post by Gary Gregory
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress)
throws
Post by Gary Gregory
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue "
+
Post by Gary Gregory
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx =
createContext(data.factoryName,
Post by Gary Gregory
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or
Post by Gary Gregory
implied.
Post by Gary Gregory
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws
Exception {
Post by Gary Gregory
Post by Gary Gregory
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the
application if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName
QueueBindingName
Post by Gary Gregory
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
E-Mail: ***@gmail.com | ***@apache.org
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Ralph Goers
2014-09-12 04:25:05 UTC
Permalink
I kind of got the impression that the point of this discussion was that you wanted jms put back into core.

Ralph
Right, so we can table this discussion. Unless we want to put JMS back into its 2.0.x location...
Gary
Plus the LMAX library for AsyncLogger (which I find to be a rather killer feature) makes it somewhat awkward to truly enforce that policy.
I think the API module should have 0-deps, ideally, which we do. The OSGi dep is "provided" so that's an exception.
For Core, I do not think we've ever settled on a policy. The policy could be: if you have a runtime dep (test only deps are OK), then you cannot live in Core. But that breaks right away since we bring in lots of deps just to do XML and JSON IO. So that would have to be moved out. Bleh, seems like a big mess.
Gary
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA appender is in there, and it would be weird to split that out from the JDBC ones even though only JDBC is in the JDK. Other than the JPA appender, what other appenders are in core that require optional dependencies? I think the SMTP appender requires JavaMail, so there's one more.
It sounds like you and I have a similar point of view. If I had to vote it would be something like -0.5 - not a veto but really don't see why this is better.
Sent from my iPad
Post by Gary Gregory
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Honestly, I'd be more likely to use Log4j to log my JMS server rather than the other way around. Plus, Flume is really awesome for distributed logging without the hassle of setting up a network of brokers and other configuration fun when dealing with JMS on a large scale.
I guess I prefer to follow the 80/20 rule - core should have all the functionality that 80% of the users will use. The other 20% - which necessarily require their own dependencies - can be found in outside jars. I donÂ’t mind telling a user that core has a few optional dependencies for a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued that it is similar to JDBC, so IÂ’m on the fence as to whether moving it out is the right thing to do.
Ralph
Post by Gary Gregory
I agree with most of that but the flip side for logging is that we deliver all of log4j because it is the users of our products that decide how to integrate with our products and how logging is done is all up to the users. So our use case is give users all of log4j and let them configure it as they see fit. Which is why I want a all - in - one module where core is the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which still need to be specified in your build script or included as additional JARs), we can break out non-essential pieces with said dependencies into more easily used components. Most of log4j-core doesn't require outside dependencies, so that's a good thing! Sure, in a sense it could be broken up into tons of little modules, but besides the mass of JARs you get from that, I've found that most projects that are so finely split up like that have next to no documentation outside the core modules. Then again, that could be the same with monolithic projects, too. In that regard, I think Log4j is more like Spring where the entire project is rather well documented; however, the javadoc side of things is still lacking (which is understandable).
Basically, clear, concise documentation relevant to users and developers alike is key to having a bunch of features (or modules). There are projects like Apache Karaf which have like a bazillion features but tons of them are either undocumented or exist on entirely different projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and on). The documentation is the code so to say, and when that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or Gradle instead of relying on IDE builds should be encouraged. However, yes, it is important to document how to properly pull all the necessary libraries together manually. I know we've still got various Ant-based builds at my work that require a lot of work to get new libraries added (like Camel, CXF, etc.), and as a user, that's a huge pain. However, I think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as with those projects, you tend to bring in tons of their modules (if not most of them); in Log4j, you only need the modules that correspond to the features you're using (which is probably in log4j-core most of the time; people still prefer logging to files). So instead of having a long chain of dependencies within our own project, anything outside log4j-core would only need third party dependencies/APIs rather than trying to figure out which set of Log4j dependencies are required for yet another Log4j dependency. That would be a much more modular system than the sort of modules we get from monoliths like Spring. (Speaking of Spring, to see just how ridiculous transitive dependencies have gotten, take a look at the Spring IO Platform project: <http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/>)
I also find that it helps testability when you separate things out a bit more. It can help identify weak areas that don't have many tests that would otherwise go unnoticed (without test coverage tools). It can help identify documentation gaps, too. A monolithic application can mask its lack of manuals or stability just out of its sheer size! I feel that way every time I work with a Java EE server (even though they all use OSGi nowadays) or proprietary libraries with even less documentation than your typical Incubator project. Plus, when things are modularised in a nice fashion (and not ad hoc), then it makes it that much easier for outside contributors to provide changes. Of course, that's more related to high cohesion and loose coupling, but having a module system helps enforce those goals.
Hi Matt,
Feel free to document your thinking process on the ML since we can't banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest audience. I am not fond personally of lots of little jars but it seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but is
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
I think he is doing it for OSGi. But if it has no third party dependencies
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
Post by Gary Gregory
----------------------------------------------------------------------
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.AppenderLoggingException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter filter,
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the TopicConnectionFactory.
Connection.
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions = Booleans.parseBoolean(ignore,
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String factoryName,
final String providerURL,
Post by Gary Gregory
+ final String urlPkgPrefixes,
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has been
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws Exception;
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object = objectMessage.getObject();
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing incoming
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context context,
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for JmsQueueManager");
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.' +
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new FactoryData(factoryName,
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " + getName(),
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName, final
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess = conn.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final QueueSession
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name, final
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+ data.securityPrincipalName,
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
----------------------------------------------------------------------
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version
2.0
Post by Gary Gregory
+ * (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.
Post by Gary Gregory
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation expects
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final String
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) ctx.lookup(queueBindingName);
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JmsQueueReceiver.");
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
JUnit in Action, Second Edition
Spring Batch in Action
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
JUnit in Action, Second Edition
Spring Batch in Action
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
JUnit in Action, Second Edition
Spring Batch in Action
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Matt Sicker
2014-09-12 05:00:01 UTC
Permalink
I'll merge it back into log4j-core.
Post by Ralph Goers
I kind of got the impression that the point of this discussion was that
you wanted jms put back into core.
Ralph
Right, so we can table this discussion. Unless we want to put JMS back
into its 2.0.x location...
Gary
Post by Matt Sicker
Plus the LMAX library for AsyncLogger (which I find to be a rather killer
feature) makes it somewhat awkward to truly enforce that policy.
Post by Gary Gregory
I think the API module should have 0-deps, ideally, which we do. The
OSGi dep is "provided" so that's an exception.
For Core, I do not think we've ever settled on a policy. The policy
could be: if you have a runtime dep (test only deps are OK), then you
cannot live in Core. But that breaks right away since we bring in lots of
deps just to do XML and JSON IO. So that would have to be moved out. Bleh,
seems like a big mess.
Gary
Post by Matt Sicker
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to
vote it would be something like -0.5 - not a veto but really don't see why
this is better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't
that be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be
argued that it is similar to JDBC, so I’m on the fence as to whether moving
it out is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies
(which still need to be specified in your build script or included as
additional JARs), we can break out non-essential pieces with said
dependencies into more easily used components. Most of log4j-core doesn't
require outside dependencies, so that's a good thing! Sure, in a sense it
could be broken up into tons of little modules, but besides the mass of
JARs you get from that, I've found that most projects that are so finely
split up like that have next to no documentation outside the core modules.
Then again, that could be the same with monolithic projects, too. In that
regard, I think Log4j is more like Spring where the entire project is
rather well documented; however, the javadoc side of things is still
lacking (which is understandable).
Basically, clear, concise documentation relevant to users and
developers alike is key to having a bunch of features (or modules). There
are projects like Apache Karaf which have like a bazillion features but
tons of them are either undocumented or exist on entirely different
projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld,
the list goes on and on). The documentation is the code so to say, and when
that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven
or Gradle instead of relying on IDE builds should be encouraged. However,
yes, it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a
bit more. It can help identify weak areas that don't have many tests that
would otherwise go unnoticed (without test coverage tools). It can help
identify documentation gaps, too. A monolithic application can mask its
lack of manuals or stability just out of its sheer size! I feel that way
every time I work with a Java EE server (even though they all use OSGi
nowadays) or proprietary libraries with even less documentation than your
typical Incubator project. Plus, when things are modularised in a nice
fashion (and not ad hoc), then it makes it that much easier for outside
contributors to provide changes. Of course, that's more related to high
cohesion and loose coupling, but having a module system helps enforce those
goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we
can't banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the
widest audience. I am not fond personally of lots of little jars but it
seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE...
but is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
On Sep 6, 2014, at 5:14 AM, Gary Gregory <
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
+ final JmsQueueManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Post by Gary Gregory
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
String
Post by Gary Gregory
securityCredentials,
factoryBindingName,
queueBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
+ final JmsTopicManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the TopicConnectionFactory.
Topic.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
String
Post by Gary Gregory
securityCredentials,
factoryBindingName,
topicBindingName,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly
configured
Post by Gary Gregory
through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends
AbstractManager {
Post by Gary Gregory
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
+ final String
securityCredentials)
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final
String
Post by Gary Gregory
name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
+ final String
urlPkgPrefixes, final String securityPrincipalName,
+ final String
securityCredentials) {
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name
has been
Post by Gary Gregory
provided without a ProviderURL. " +
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
+ } else {
+ LOGGER.warn("SecurityPrincipalName has
been set
Post by Gary Gregory
without SecurityCredentials. " +
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object,
final
Post by Gary Gregory
Session session, final MessageProducer producer)
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS
{}",
Post by Gary Gregory
getName());
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This
class
Post by Gary Gregory
expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends
LogEventListener
Post by Gary Gregory
implements javax.jms.MessageListener {
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage =
(ObjectMessage)
Post by Gary Gregory
message;
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type "
+
Post by Gary Gregory
object.getClass().getName() + ", was expecting LogEvent.");
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String
name)
Post by Gary Gregory
throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name +
"].");
Post by Gary Gregory
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
+ final String queueBindingName,
final
Post by Gary Gregory
String userName, final String password,
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Connection.
Post by Gary Gregory
Connection.
+ */
+ public static JmsQueueManager getJmsQueueManager(final
String
Post by Gary Gregory
factoryName, final String providerURL,
+ final
String
Post by Gary Gregory
urlPkgPrefixes, final String securityPrincipalName,
+ final
String
Post by Gary Gregory
securityCredentials, final String factoryBindingName,
+ final
String
Post by Gary Gregory
queueBindingName, final String userName,
+ final
String
Post by Gary Gregory
password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName +
'.' +
Post by Gary Gregory
queueBindingName;
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
+ }
+
+ public synchronized void send(final Serializable object)
throws
Post by Gary Gregory
Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final
String
Post by Gary Gregory
providerURL, final String urlPkgPrefixes,
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
+ final String factoryBindingName,
final
Post by Gary Gregory
String queueBindingName, final String userName,
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context,
final
Post by Gary Gregory
String factoryBindingName,
+ final String
queueBindingName,
Post by Gary Gregory
final String userName, final String password,
+ final boolean suppress)
throws
Post by Gary Gregory
Exception {
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
+ final QueueSender sender =
sess.createSender(queue);
Post by Gary Gregory
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory "
+
Post by Gary Gregory
factoryBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue
" +
Post by Gary Gregory
queueBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
+
+ public JmsQueueManager createManager(final String
name, final
Post by Gary Gregory
FactoryData data) {
+ try {
+ final Context ctx =
createContext(data.factoryName,
Post by Gary Gregory
data.providerURL, data.urlPkgPrefixes,
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
binding name.
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws
Exception {
Post by Gary Gregory
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit"))
{
Post by Gary Gregory
+ System.out.println("Exiting. Kill the
application if it
Post by Gary Gregory
does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName
QueueBindingName
Post by Gary Gregory
username password");
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
Matt Sicker <***@gmail.com>
Matt Sicker
2014-09-12 13:19:36 UTC
Permalink
Done. Added a mom package (message oriented middleware) due to future
additions of other messaging protocols (like AMQP, and STOMP; ActiveMQ has
a project called Apollo that deals with these two).
Post by Matt Sicker
I'll merge it back into log4j-core.
Post by Ralph Goers
I kind of got the impression that the point of this discussion was that
you wanted jms put back into core.
Ralph
Right, so we can table this discussion. Unless we want to put JMS back
into its 2.0.x location...
Gary
Post by Matt Sicker
Plus the LMAX library for AsyncLogger (which I find to be a rather
killer feature) makes it somewhat awkward to truly enforce that policy.
Post by Gary Gregory
I think the API module should have 0-deps, ideally, which we do. The
OSGi dep is "provided" so that's an exception.
For Core, I do not think we've ever settled on a policy. The policy
could be: if you have a runtime dep (test only deps are OK), then you
cannot live in Core. But that breaks right away since we bring in lots of
deps just to do XML and JSON IO. So that would have to be moved out. Bleh,
seems like a big mess.
Gary
Post by Matt Sicker
Well, I wouldn't mind putting it back in log4j-core. I mean, the JPA
appender is in there, and it would be weird to split that out from the JDBC
ones even though only JDBC is in the JDK. Other than the JPA appender, what
other appenders are in core that require optional dependencies? I think the
SMTP appender requires JavaMail, so there's one more.
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to
vote it would be something like -0.5 - not a veto but really don't see why
this is better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't
that be renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server
rather than the other way around. Plus, Flume is really awesome for
distributed logging without the hassle of setting up a network of brokers
and other configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all
the functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be
argued that it is similar to JDBC, so I’m on the fence as to whether moving
it out is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j module.
The basic idea is that instead of so many optional dependencies
(which still need to be specified in your build script or included as
additional JARs), we can break out non-essential pieces with said
dependencies into more easily used components. Most of log4j-core doesn't
require outside dependencies, so that's a good thing! Sure, in a sense it
could be broken up into tons of little modules, but besides the mass of
JARs you get from that, I've found that most projects that are so finely
split up like that have next to no documentation outside the core modules.
Then again, that could be the same with monolithic projects, too. In that
regard, I think Log4j is more like Spring where the entire project is
rather well documented; however, the javadoc side of things is still
lacking (which is understandable).
Basically, clear, concise documentation relevant to users and
developers alike is key to having a bunch of features (or modules). There
are projects like Apache Karaf which have like a bazillion features but
tons of them are either undocumented or exist on entirely different
projects (like ops4j, Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld,
the list goes on and on). The documentation is the code so to say, and when
that's split up into dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven
or Gradle instead of relying on IDE builds should be encouraged. However,
yes, it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out
a bit more. It can help identify weak areas that don't have many tests that
would otherwise go unnoticed (without test coverage tools). It can help
identify documentation gaps, too. A monolithic application can mask its
lack of manuals or stability just out of its sheer size! I feel that way
every time I work with a Java EE server (even though they all use OSGi
nowadays) or proprietary libraries with even less documentation than your
typical Incubator project. Plus, when things are modularised in a nice
fashion (and not ad hoc), then it makes it that much easier for outside
contributors to provide changes. Of course, that's more related to high
cohesion and loose coupling, but having a module system helps enforce those
goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we
can't banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the
widest audience. I am not fond personally of lots of little jars but it
seems to be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE...
but is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into
new log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
On Sep 6, 2014, at 5:14 AM, Gary Gregory <
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
"
Post by Gary Gregory
+ xsi:schemaLocation="
http://maven.apache.org/POM/4.0.0
Post by Gary Gregory
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import
org.apache.logging.log4j.core.appender.AbstractAppender;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
+ final JmsQueueManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
the
Post by Gary Gregory
InitialContextFactory.
package prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Post by Gary Gregory
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
String
Post by Gary Gregory
securityCredentials,
String
Post by Gary Gregory
factoryBindingName,
queueBindingName,
userName,
password,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import
org.apache.logging.log4j.core.appender.AbstractAppender;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginElement;
Post by Gary Gregory
+import
org.apache.logging.log4j.core.config.plugins.PluginFactory;
Post by Gary Gregory
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import
org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
Post by Gary Gregory
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
+ final JmsTopicManager manager,
final
Post by Gary Gregory
boolean ignoreExceptions) {
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
the
Post by Gary Gregory
InitialContextFactory.
package prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the TopicConnectionFactory.
Topic.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
String
Post by Gary Gregory
securityPrincipalName,
String
Post by Gary Gregory
securityCredentials,
String
Post by Gary Gregory
factoryBindingName,
topicBindingName,
userName,
password,
Serializable>
Post by Gary Gregory
layout,
ignore) {
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for
JmsQueueAppender");
Post by Gary Gregory
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout,
manager,
Post by Gary Gregory
ignoreExceptions);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly
configured
Post by Gary Gregory
through your log4j2 configuration file.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends
AbstractManager {
Post by Gary Gregory
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
the
Post by Gary Gregory
InitialContextFactory.
package prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
+ final String
securityCredentials)
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
+
securityCredentials);
Post by Gary Gregory
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
the name.
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final
String
Post by Gary Gregory
name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
the
Post by Gary Gregory
InitialContextFactory.
package prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
+ final String
urlPkgPrefixes, final String securityPrincipalName,
+ final String
securityCredentials) {
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name
has been
Post by Gary Gregory
provided without a ProviderURL. " +
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
+ } else {
+ LOGGER.warn("SecurityPrincipalName has
been set
Post by Gary Gregory
without SecurityCredentials. " +
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object,
final
Post by Gary Gregory
Session session, final MessageProducer producer)
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS
{}",
Post by Gary Gregory
getName());
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This
class
Post by Gary Gregory
expects all messages to be serialized log events.
+ */
+public abstract class AbstractJmsReceiver extends
LogEventListener
Post by Gary Gregory
implements javax.jms.MessageListener {
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage =
(ObjectMessage)
Post by Gary Gregory
message;
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type
" +
Post by Gary Gregory
object.getClass().getName() + ", was expecting LogEvent.");
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String
name)
Post by Gary Gregory
throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name +
"].");
Post by Gary Gregory
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final
Context context,
Post by Gary Gregory
final String factoryBindingName,
+ final String queueBindingName,
final
Post by Gary Gregory
String userName, final String password,
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
the
Post by Gary Gregory
InitialContextFactory.
package prefixes
Post by Gary Gregory
for the class name of the factory class that
+ * will create a URL context factory
of the
Post by Gary Gregory
Principal.
the
Post by Gary Gregory
Principal.
Context that
Post by Gary Gregory
provides the QueueConnectionFactory.
Queue.
Connection.
Post by Gary Gregory
Connection.
+ */
+ public static JmsQueueManager getJmsQueueManager(final
String
Post by Gary Gregory
factoryName, final String providerURL,
+ final
String
Post by Gary Gregory
urlPkgPrefixes, final String securityPrincipalName,
+ final
String
Post by Gary Gregory
securityCredentials, final String factoryBindingName,
+ final
String
Post by Gary Gregory
queueBindingName, final String userName,
+ final
String
Post by Gary Gregory
password) {
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName
+ '.' +
Post by Gary Gregory
queueBindingName;
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
+ }
+
+ public synchronized void send(final Serializable object)
throws
Post by Gary Gregory
Exception {
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final
String
Post by Gary Gregory
providerURL, final String urlPkgPrefixes,
+ final String
securityPrincipalName, final
Post by Gary Gregory
String securityCredentials,
+ final String factoryBindingName,
final
Post by Gary Gregory
String queueBindingName, final String userName,
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName =
securityPrincipalName;
Post by Gary Gregory
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context,
final
Post by Gary Gregory
String factoryBindingName,
+ final String
queueBindingName,
Post by Gary Gregory
final String userName, final String password,
+ final boolean suppress)
throws
Post by Gary Gregory
Exception {
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
+ final QueueSender sender =
sess.createSender(queue);
Post by Gary Gregory
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory
" +
Post by Gary Gregory
factoryBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue
" +
Post by Gary Gregory
queueBindingName, ex);
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
+
+ public JmsQueueManager createManager(final String
name, final
Post by Gary Gregory
FactoryData data) {
+ try {
+ final Context ctx =
createContext(data.factoryName,
Post by Gary Gregory
data.providerURL, data.urlPkgPrefixes,
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
or more
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file
distributed with
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache
license, Version
Post by Gary Gregory
2.0
+ * (the "License"); you may not use this file except in
compliance with
Post by Gary Gregory
+ * 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
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or
Post by Gary Gregory
implied.
+ * See the license for the specific language governing
permissions and
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
binding name.
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName,
final String
Post by Gary Gregory
queueBindingName, final String username,
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws
Exception {
Post by Gary Gregory
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null ||
line.equalsIgnoreCase("exit")) {
Post by Gary Gregory
+ System.out.println("Exiting. Kill the
application if it
Post by Gary Gregory
does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName
QueueBindingName
Post by Gary Gregory
username password");
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
--
Matt Sicker <***@gmail.com>
Gary Gregory
2014-09-11 13:39:57 UTC
Permalink
Check. It seems we have settled on a status quo then.

Issues:
- We now have _moved_ code out of a module into a new one. This means that
users MUST update their POMs from 2.0.x to 2.1. Is that a jar-hell problem?
How is that not the same issue as us deciding not to rename the Flume NG
module to Flume.
- Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that
be renamed to "Appender" singular?

Gary
Post by Ralph Goers
It sounds like you and I have a similar point of view. If I had to vote it
would be something like -0.5 - not a veto but really don't see why this is
better.
Sent from my iPad
So... do we have consensus here?
Right now we have a "Apache Log4j JMS Appenders" module. Shouldn't that be
renamed to "Appender" singular?
The only advantage I see is not dragging in the JMS API jar...
Gary
Post by Matt Sicker
Honestly, I'd be more likely to use Log4j to log my JMS server rather
than the other way around. Plus, Flume is really awesome for distributed
logging without the hassle of setting up a network of brokers and other
configuration fun when dealing with JMS on a large scale.
Post by Ralph Goers
I guess I prefer to follow the 80/20 rule - core should have all the
functionality that 80% of the users will use. The other 20% - which
necessarily require their own dependencies - can be found in outside jars.
I don’t mind telling a user that core has a few optional dependencies for
a few optional features. But there should only be a handful of those.
JMS might fit into the same bucket as Flume but it could also be argued
that it is similar to JDBC, so I’m on the fence as to whether moving it out
is the right thing to do.
Ralph
I agree with most of that but the flip side for logging is that we
deliver all of log4j because it is the users of our products that decide
how to integrate with our products and how logging is done is all up to the
users. So our use case is give users all of log4j and let them configure it
as they see fit. Which is why I want a all - in - one module where core is
the impl.
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 14:15 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The basic idea is that instead of so many optional dependencies (which
still need to be specified in your build script or included as additional
JARs), we can break out non-essential pieces with said dependencies into
more easily used components. Most of log4j-core doesn't require outside
dependencies, so that's a good thing! Sure, in a sense it could be broken
up into tons of little modules, but besides the mass of JARs you get from
that, I've found that most projects that are so finely split up like that
have next to no documentation outside the core modules. Then again, that
could be the same with monolithic projects, too. In that regard, I think
Log4j is more like Spring where the entire project is rather well
documented; however, the javadoc side of things is still lacking (which is
understandable).
Basically, clear, concise documentation relevant to users and developers
alike is key to having a bunch of features (or modules). There are projects
like Apache Karaf which have like a bazillion features but tons of them are
either undocumented or exist on entirely different projects (like ops4j,
Felix, Aries, Geronimo, OpenEJB, OpenWebBeans, Weld, the list goes on and
on). The documentation is the code so to say, and when that's split up into
dozens of modules, good luck finding anything!
I also think that encouraging users to use build systems like Maven or
Gradle instead of relying on IDE builds should be encouraged. However, yes,
it is important to document how to properly pull all the necessary
libraries together manually. I know we've still got various Ant-based
builds at my work that require a lot of work to get new libraries added
(like Camel, CXF, etc.), and as a user, that's a huge pain. However, I
think Log4j is a bit different than projects like Spring/CXF/Camel/Mule as
with those projects, you tend to bring in tons of their modules (if not
most of them); in Log4j, you only need the modules that correspond to the
features you're using (which is probably in log4j-core most of the time;
people still prefer logging to files). So instead of having a long chain of
dependencies within our own project, anything outside log4j-core would only
need third party dependencies/APIs rather than trying to figure out which
set of Log4j dependencies are required for yet another Log4j dependency.
That would be a much more modular system than the sort of modules we get
from monoliths like Spring. (Speaking of Spring, to see just how ridiculous
transitive dependencies have gotten, take a look at the Spring IO Platform
project: <
http://docs.spring.io/platform/docs/1.0.2.RELEASE/reference/htmlsingle/
Post by Gary Gregory
)
I also find that it helps testability when you separate things out a bit
more. It can help identify weak areas that don't have many tests that would
otherwise go unnoticed (without test coverage tools). It can help identify
documentation gaps, too. A monolithic application can mask its lack of
manuals or stability just out of its sheer size! I feel that way every time
I work with a Java EE server (even though they all use OSGi nowadays) or
proprietary libraries with even less documentation than your typical
Incubator project. Plus, when things are modularised in a nice fashion (and
not ad hoc), then it makes it that much easier for outside contributors to
provide changes. Of course, that's more related to high cohesion and loose
coupling, but having a module system helps enforce those goals.
Post by Gary Gregory
Hi Matt,
Feel free to document your thinking process on the ML since we can't
banter around a water cooler...
I'm all for packaging log4j in a way to make it appealing to the widest
audience. I am not fond personally of lots of little jars but it seems to
be what OSGi wants. It's that correct?
Gary
-------- Original message --------
From: Matt Sicker
Date:09/06/2014 12:08 (GMT-05:00)
To: Log4J Developers List
Cc: Ralph Goers
Subject: Re: [02/29] Split out JMS appender and receiver into new log4j module.
The nice thing about it being separate from core is that you no longer have
to make the JMS API optional, so this helps transitive dependency
resolution. I already fixed the OSGi problem while it was in core (just had
to make it optional as well as provided). Was it part of log4j 1.2?
http://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq.html
Plus, log4j-web is split out, too, though now I'm starting to reconsider
that.
Post by Gary Gregory
Unless you count the JMS API jar which is not part of the JRE... but
is
Post by Gary Gregory
that reason enough to have a new module?
Gary
-------- Original message --------
From: Ralph Goers
Date:09/06/2014 09:17 (GMT-05:00)
To: Log4J Developers List
Subject: Re: [02/29] Split out JMS appender and receiver into new
log4j
Post by Gary Gregory
module.
I think he is doing it for OSGi. But if it has no third party
dependencies
Post by Gary Gregory
I am not sure why it is necessary.
Sent from my iPad
Post by Gary Gregory
What is the thought behind splitting JMS out of core?
Gary
-------- Original message --------
Date:09/06/2014 00:58 (GMT-05:00)
Subject: [02/29] Split out JMS appender and receiver into new log4j
module.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/pom.xml
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git a/log4j-mom/pom.xml b/log4j-mom/pom.xml
new file mode 100644
index 0000000..6c3f760
--- /dev/null
+++ b/log4j-mom/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
Post by Gary Gregory
+ <parent>
+ <artifactId>log4j</artifactId>
+ <groupId>org.apache.logging.log4j</groupId>
+ <version>2.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>log4j-mom</artifactId>
+ <name>Log4j 2 MOM Plugins</name>
+ <description>Message Oriented Middleware plugins for Log4j
2</description>
Post by Gary Gregory
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.jms</groupId>
+ <artifactId>jboss-jms-api_1.1_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..284e1f6
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsQueueAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsQueueManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Queue.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsQueueAppender extends AbstractAppender {
+
+ private final JmsQueueManager manager;
+
+ private JmsQueueAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsQueueManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsQueueAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsQueueAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
queueBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsQueueManager manager =
JmsQueueManager.getJmsQueueManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsQueueAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..9985746
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/JmsTopicAppender.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.appender;
+
+import java.io.Serializable;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import
org.apache.logging.log4j.core.appender.AppenderLoggingException;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import
org.apache.logging.log4j.core.config.plugins.PluginAttribute;
Post by Gary Gregory
Post by Gary Gregory
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.mom.jms.receiver.JmsTopicManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * Appender to write to a JMS Topic.
+ */
"appender",
Post by Gary Gregory
printObject = true)
Post by Gary Gregory
+public final class JmsTopicAppender extends AbstractAppender {
+
+ private final JmsTopicManager manager;
+
+ private JmsTopicAppender(final String name, final Filter
filter,
Post by Gary Gregory
final Layout<? extends Serializable> layout,
Post by Gary Gregory
+ final JmsTopicManager manager, final
boolean ignoreExceptions) {
Post by Gary Gregory
+ super(name, filter, layout, ignoreExceptions);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ */
+ public void append(final LogEvent event) {
+ try {
+ manager.send(getLayout().toSerializable(event));
+ } catch (final Exception ex) {
+ throw new AppenderLoggingException(ex);
+ }
+ }
+
+ /**
+ * Create a JmsTopicAppender.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the TopicConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
SerializedLayout).
encountered
Post by Gary Gregory
when appending events are logged; otherwise
Post by Gary Gregory
+ * they are propagated to the caller.
+ */
+ public static JmsTopicAppender createAppender(
factoryName,
providerURL,
Post by Gary Gregory
urlPkgPrefixes,
securityPrincipalName,
securityCredentials,
factoryBindingName,
topicBindingName,
layout,
ignore) {
Post by Gary Gregory
Post by Gary Gregory
+
+ if (name == null) {
+ LOGGER.error("No name provided for JmsQueueAppender");
+ return null;
+ }
+ final boolean ignoreExceptions =
Booleans.parseBoolean(ignore,
Post by Gary Gregory
true);
Post by Gary Gregory
+ final JmsTopicManager manager =
JmsTopicManager.getJmsTopicManager(factoryName, providerURL,
urlPkgPrefixes,
Post by Gary Gregory
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, topicBindingName, userName, password);
Post by Gary Gregory
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = SerializedLayout.createLayout();
+ }
+ return new JmsTopicAppender(name, filter, layout, manager,
ignoreExceptions);
Post by Gary Gregory
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..0c7a4ff
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/appender/package-info.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+/**
+ * Appender classes for using JMS. These are directly configured
through your log4j2 configuration file.
Post by Gary Gregory
+ */
+package org.apache.logging.log4j.mom.jms.appender;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..d3ae2a3
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageProducer;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+/**
+ * Base Class for Managers of JMS connections.
+ */
+public abstract class AbstractJmsManager extends AbstractManager {
+
+ /**
+ * The Constructor.
+ */
+ public AbstractJmsManager(final String name) {
+ super(name);
+ }
+
+ /**
+ * Create the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Context createContext(final String
factoryName,
Post by Gary Gregory
final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes,
Post by Gary Gregory
final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials)
Post by Gary Gregory
+ throws NamingException {
+
+ final Properties props = getEnvironment(factoryName,
providerURL, urlPkgPrefixes, securityPrincipalName,
Post by Gary Gregory
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ /**
+ * Looks up the name in the context.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ protected static Object lookup(final Context ctx, final String
name) throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ LOGGER.warn("Could not find name [{}].", name);
+ throw e;
+ }
+ }
+
+ /**
+ * Sets up the properties to pass to the InitialContext.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
Post by Gary Gregory
+ */
+ protected static Properties getEnvironment(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials) {
Post by Gary Gregory
+ final Properties props = new Properties();
+ if (factoryName != null) {
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
factoryName);
Post by Gary Gregory
Post by Gary Gregory
+ if (providerURL != null) {
+ props.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LOGGER.warn("The InitialContext factory name has
been
Post by Gary Gregory
provided without a ProviderURL. " +
Post by Gary Gregory
+ "This is likely to cause problems");
+ }
+ if (urlPkgPrefixes != null) {
+ props.put(Context.URL_PKG_PREFIXES,
urlPkgPrefixes);
Post by Gary Gregory
Post by Gary Gregory
+ }
+ if (securityPrincipalName != null) {
+ props.put(Context.SECURITY_PRINCIPAL,
securityPrincipalName);
Post by Gary Gregory
+ if (securityCredentials != null) {
+ props.put(Context.SECURITY_CREDENTIALS,
securityCredentials);
Post by Gary Gregory
+ } else {
+ LOGGER.warn("SecurityPrincipalName has been set
without SecurityCredentials. " +
Post by Gary Gregory
+ "This is likely to cause problems.");
+ }
+ }
+ return props;
+ }
+ return null;
+ }
+
+ /**
+ * Send the message.
+ */
+ public abstract void send(Serializable object) throws
Exception;
Post by Gary Gregory
Post by Gary Gregory
+
+ /**
+ * Send the Object.
+ */
+ public synchronized void send(final Serializable object, final
Session session, final MessageProducer producer)
Post by Gary Gregory
+ throws Exception {
+ try {
+ Message msg;
+ if (object instanceof String) {
+ msg = session.createTextMessage();
+ ((TextMessage) msg).setText((String) object);
+ } else {
+ msg = session.createObjectMessage();
+ ((ObjectMessage) msg).setObject(object);
+ }
+ producer.send(msg);
+ } catch (final JMSException ex) {
+ LOGGER.error("Could not publish message via JMS {}",
getName());
Post by Gary Gregory
+ throw ex;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..bf86c65
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/AbstractJmsReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LogEventListener;
+
+/**
+ * Abstract base class for receiving LogEvents over JMS. This class
expects all messages to be serialized log events.
Post by Gary Gregory
+ */
+public abstract class AbstractJmsReceiver extends LogEventListener
implements javax.jms.MessageListener {
Post by Gary Gregory
+
+ /**
+ * Logger to capture diagnostics.
+ */
+ protected Logger logger =
LogManager.getLogger(this.getClass().getName());
Post by Gary Gregory
+
+ /**
+ * Listener that receives the event.
+ */
+ public void onMessage(final javax.jms.Message message) {
+ try {
+ if (message instanceof ObjectMessage) {
+ final ObjectMessage objectMessage = (ObjectMessage)
message;
Post by Gary Gregory
+ final Serializable object =
objectMessage.getObject();
Post by Gary Gregory
Post by Gary Gregory
+ if (object instanceof LogEvent) {
+ log((LogEvent) object);
+ } else {
+ logger.warn("Received message is of type " +
object.getClass().getName() + ", was expecting LogEvent.");
Post by Gary Gregory
+ }
+ } else {
+ logger.warn("Received message is of type " +
message.getJMSType()
Post by Gary Gregory
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (final JMSException jmse) {
+ logger.error("Exception thrown while processing
incoming
Post by Gary Gregory
message.",
Post by Gary Gregory
+ jmse);
+ }
+ }
+
+ /**
+ * Looks up an object from the Context.
+ */
+ protected Object lookup(final Context ctx, final String name)
throws NamingException {
Post by Gary Gregory
+ try {
+ return ctx.lookup(name);
+ } catch (final NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..6825282
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueManager.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.Serializable;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+/**
+ * Manager for a JMS Queue.
+ */
+public class JmsQueueManager extends AbstractJmsManager {
+
+ private static final JMSQueueManagerFactory FACTORY = new
JMSQueueManagerFactory();
Post by Gary Gregory
+
+ private QueueInfo info;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+ private final Context context;
+
+ /**
+ * The Constructor.
+ */
+ protected JmsQueueManager(final String name, final Context
context,
Post by Gary Gregory
final String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName, final
String userName, final String password,
Post by Gary Gregory
+ final QueueInfo info) {
+ super(name);
+ this.context = context;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ this.info = info;
+ }
+
+ /**
+ * Obtain a JmsQueueManager.
InitialContextFactory.
prefixes
Post by Gary Gregory
for the class name of the factory class that
Post by Gary Gregory
+ * will create a URL context factory
Principal.
Principal.
that
Post by Gary Gregory
provides the QueueConnectionFactory.
Connection.
Post by Gary Gregory
Connection.
Post by Gary Gregory
+ */
+ public static JmsQueueManager getJmsQueueManager(final String
factoryName, final String providerURL,
Post by Gary Gregory
+ final String
urlPkgPrefixes, final String securityPrincipalName,
Post by Gary Gregory
+ final String
securityCredentials, final String factoryBindingName,
Post by Gary Gregory
+ final String
queueBindingName, final String userName,
Post by Gary Gregory
+ final String
password) {
Post by Gary Gregory
+
+ if (factoryBindingName == null) {
+ LOGGER.error("No factory name provided for
JmsQueueManager");
Post by Gary Gregory
+ return null;
+ }
+ if (queueBindingName == null) {
+ LOGGER.error("No topic name provided for
JmsQueueManager");
Post by Gary Gregory
Post by Gary Gregory
+ return null;
+ }
+
+ final String name = "JMSQueue:" + factoryBindingName + '.'
+
Post by Gary Gregory
queueBindingName;
Post by Gary Gregory
+ return getManager(name, FACTORY, new
FactoryData(factoryName,
Post by Gary Gregory
providerURL, urlPkgPrefixes,
Post by Gary Gregory
+ securityPrincipalName, securityCredentials,
factoryBindingName, queueBindingName, userName, password));
Post by Gary Gregory
+ }
+
+ public synchronized void send(final Serializable object) throws
Exception {
Post by Gary Gregory
+ if (info == null) {
+ info = connect(context, factoryBindingName,
queueBindingName, userName, password, false);
Post by Gary Gregory
+ }
+ try {
+ super.send(object, info.session, info.sender);
+ } catch (final Exception ex) {
+ cleanup(true);
+ throw ex;
+ }
+ }
+
+ public void releaseSub() {
+ if (info != null) {
+ cleanup(false);
+ }
+ }
+
+ private void cleanup(final boolean quiet) {
+ try {
+ info.session.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing session for " +
getName(),
Post by Gary Gregory
e);
Post by Gary Gregory
+ }
+ }
+ try {
+ info.conn.close();
+ } catch (final Exception e) {
+ if (!quiet) {
+ LOGGER.error("Error closing connection for " +
getName(), e);
Post by Gary Gregory
+ }
+ }
+ info = null;
+ }
+
+ /**
+ * Data for the factory.
+ */
+ private static class FactoryData {
+ private final String factoryName;
+ private final String providerURL;
+ private final String urlPkgPrefixes;
+ private final String securityPrincipalName;
+ private final String securityCredentials;
+ private final String factoryBindingName;
+ private final String queueBindingName;
+ private final String userName;
+ private final String password;
+
+ public FactoryData(final String factoryName, final String
providerURL, final String urlPkgPrefixes,
Post by Gary Gregory
+ final String securityPrincipalName,
final
Post by Gary Gregory
String securityCredentials,
Post by Gary Gregory
+ final String factoryBindingName, final
String queueBindingName, final String userName,
Post by Gary Gregory
+ final String password) {
+ this.factoryName = factoryName;
+ this.providerURL = providerURL;
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ this.securityPrincipalName = securityPrincipalName;
+ this.securityCredentials = securityCredentials;
+ this.factoryBindingName = factoryBindingName;
+ this.queueBindingName = queueBindingName;
+ this.userName = userName;
+ this.password = password;
+ }
+ }
+
+ private static QueueInfo connect(final Context context, final
String factoryBindingName,
Post by Gary Gregory
+ final String queueBindingName,
final String userName, final String password,
Post by Gary Gregory
+ final boolean suppress) throws
Exception {
Post by Gary Gregory
+ try {
+ final QueueConnectionFactory factory =
(QueueConnectionFactory) lookup(context, factoryBindingName);
Post by Gary Gregory
+ QueueConnection conn;
+ if (userName != null) {
+ conn = factory.createQueueConnection(userName,
password);
Post by Gary Gregory
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ final QueueSession sess =
conn.createQueueSession(false,
Post by Gary Gregory
Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue) lookup(context,
queueBindingName);
Post by Gary Gregory
+ final QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new QueueInfo(conn, sess, sender);
+ } catch (final NamingException ex) {
+ LOGGER.warn("Unable to locate connection factory " +
factoryBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ } catch (final JMSException ex) {
+ LOGGER.warn("Unable to create connection to queue " +
queueBindingName, ex);
Post by Gary Gregory
+ if (!suppress) {
+ throw ex;
+ }
+ }
+ return null;
+ }
+
+ /** Queue connection information */
+ private static class QueueInfo {
+ private final QueueConnection conn;
+ private final QueueSession session;
+ private final QueueSender sender;
+
+ public QueueInfo(final QueueConnection conn, final
QueueSession
Post by Gary Gregory
session, final QueueSender sender) {
Post by Gary Gregory
+ this.conn = conn;
+ this.session = session;
+ this.sender = sender;
+ }
+ }
+
+ /**
+ * Factory to create the JmsQueueManager.
+ */
+ private static class JMSQueueManagerFactory implements
ManagerFactory<JmsQueueManager, FactoryData> {
Post by Gary Gregory
+
+ public JmsQueueManager createManager(final String name,
final
Post by Gary Gregory
FactoryData data) {
Post by Gary Gregory
+ try {
+ final Context ctx = createContext(data.factoryName,
data.providerURL, data.urlPkgPrefixes,
Post by Gary Gregory
+
data.securityPrincipalName,
Post by Gary Gregory
data.securityCredentials);
Post by Gary Gregory
+ final QueueInfo info = connect(ctx,
data.factoryBindingName, data.queueBindingName, data.userName,
Post by Gary Gregory
+ data.password, true);
+ return new JmsQueueManager(name, ctx,
data.factoryBindingName, data.queueBindingName,
Post by Gary Gregory
+ data.userName, data.password, info);
+ } catch (final NamingException ex) {
+ LOGGER.error("Unable to locate resource", ex);
+ } catch (final Exception ex) {
+ LOGGER.error("Unable to connect", ex);
+ }
+
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
----------------------------------------------------------------------
Post by Gary Gregory
Post by Gary Gregory
diff --git
a/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
new file mode 100644
index 0000000..b231489
--- /dev/null
+++
b/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsQueueReceiver.java
Post by Gary Gregory
Post by Gary Gregory
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or
more
Post by Gary Gregory
Post by Gary Gregory
+ * contributor license agreements. See the NOTICE file distributed
with
Post by Gary Gregory
Post by Gary Gregory
+ * this work for additional information regarding copyright
ownership.
Post by Gary Gregory
Post by Gary Gregory
+ * The ASF licenses this file to You under the Apache license,
Version
Post by Gary Gregory
2.0
Post by Gary Gregory
+ * (the "License"); you may not use this file except in compliance
with
Post by Gary Gregory
Post by Gary Gregory
+ * 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
Post by Gary Gregory
Post by Gary Gregory
+ * distributed under the License is distributed on an "AS IS"
BASIS,
Post by Gary Gregory
Post by Gary Gregory
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
Post by Gary Gregory
+ * See the license for the specific language governing permissions
and
Post by Gary Gregory
Post by Gary Gregory
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.mom.jms.receiver;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+
+import javax.jms.JMSException;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueReceiver;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Receives Log Events over a JMS Queue. This implementation
expects
Post by Gary Gregory
that all messages will
Post by Gary Gregory
+ * contain a serialized LogEvent.
+ */
+public class JmsQueueReceiver extends AbstractJmsReceiver {
+
+ /**
+ * Constructor.
name.
Post by Gary Gregory
Post by Gary Gregory
+ */
+ public JmsQueueReceiver(final String qcfBindingName, final
String
Post by Gary Gregory
queueBindingName, final String username,
Post by Gary Gregory
+ final String password) {
+
+ try {
+ final Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory)
lookup(ctx, qcfBindingName);
Post by Gary Gregory
+ final QueueConnection queueConnection =
queueConnectionFactory.createQueueConnection(username, password);
Post by Gary Gregory
+ queueConnection.start();
+ final QueueSession queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Post by Gary Gregory
+ final Queue queue = (Queue)
ctx.lookup(queueBindingName);
Post by Gary Gregory
Post by Gary Gregory
+ final QueueReceiver queueReceiver =
queueSession.createReceiver(queue);
Post by Gary Gregory
+ queueReceiver.setMessageListener(this);
+ } catch (final JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (final RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ /**
+ * Main startup for the receiver.
+ */
+ public static void main(final String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ final String qcfBindingName = args[0];
+ final String queueBindingName = args[1];
+ final String username = args[2];
+ final String password = args[3];
+
+ new JmsQueueReceiver(qcfBindingName, queueBindingName,
username, password);
Post by Gary Gregory
+
+ final Charset enc = Charset.defaultCharset();
+ final BufferedReader stdin = new BufferedReader(new
InputStreamReader(System.in, enc));
Post by Gary Gregory
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit
JmsQueueReceiver.");
Post by Gary Gregory
Post by Gary Gregory
+ while (true) {
+ final String line = stdin.readLine();
+ if (line == null || line.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application
if it
Post by Gary Gregory
does not exit "
Post by Gary Gregory
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+
+ private static void usage(final String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " +
JmsQueueReceiver.class.getName()
Post by Gary Gregory
+ + " QueueConnectionFactoryBindingName QueueBindingName
username password");
Post by Gary Gregory
+ System.exit(1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/1274408e/log4j-mom/src/main/java/org/apache/logging/log4j/mom/jms/receiver/JmsTop
--
--
--
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
--
E-Mail: ***@gmail.com | ***@apache.org
Java Persistence with Hibernate, Second Edition
<http://www.manning.com/bauer3/>
JUnit in Action, Second Edition <http://www.manning.com/tahchiev/>
Spring Batch in Action <http://www.manning.com/templier/>
Blog: http://garygregory.wordpress.com
Home: http://garygregory.com/
Tweet! http://twitter.com/GaryGregory
Loading...