Add JMS appenders
git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers@1137270 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/log4j2-core/pom.xml b/log4j2-core/pom.xml
index 8e41ec1..e9e9066 100644
--- a/log4j2-core/pom.xml
+++ b/log4j2-core/pom.xml
@@ -31,12 +31,6 @@
<url>http://logging.apache.org/log4j/experimental</url>
<dependencies>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.3.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.apache.logging</groupId>
<artifactId>log4j2-api</artifactId>
<version>1.99.0-SNAPSHOT</version>
@@ -64,6 +58,18 @@
<artifactId>slf4j-api</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.3.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockejb</groupId>
+ <artifactId>mockejb</artifactId>
+ <version>0.6-beta2</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
@@ -74,7 +80,12 @@
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jms_1.1_spec</artifactId>
+ <version>1.0</version>
+ <optional>true</optional>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/Layout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/Layout.java
index fe28fb8..e35754d 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/Layout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/Layout.java
@@ -1,12 +1,14 @@
package org.apache.logging.log4j.core;
+import java.io.Serializable;
+
/**
* @doubt There is still a need for a character-based layout for character based event sinks (databases, etc).
* Would introduce an EventEncoder, EventRenderer or something similar for the logging event to byte encoding.
* (RG) A layout can be configured with a Charset and then Strings can be converted to byte arrays. OTOH, it isn't
* possible to write byte arrays as character streams.
*/
-public interface Layout {
+public interface Layout<T extends Serializable> {
// Note that the line.separator property can be looked up even by
// applets.
/**
@@ -26,6 +28,13 @@
byte[] format(LogEvent event);
/**
+ * Formats the event as an Object that can be serialized.
+ * @param event The Logging Event.
+ * @return The formatted event.
+ */
+ T formatAs(LogEvent event);
+
+ /**
* Returns the header for the layout format.
* @return The header.
* @doubt the concept of header and footer is not universal, should not be on the base interface.
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
index 442e87d..9fe048c 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/LogEvent.java
@@ -4,13 +4,14 @@
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.message.Message;
+import java.io.Serializable;
import java.util.Map;
import java.util.Stack;
/**
*
*/
-public interface LogEvent {
+public interface LogEvent extends Serializable {
/**
* Get level.
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
index 169a3cb..4edd2f2 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
@@ -53,6 +53,9 @@
T manager = (T) map.get(name);
if (manager == null) {
manager = factory.createManager(name, data);
+ if (manager == null) {
+ throw new IllegalStateException("Unable to create a manager");
+ }
map.put(name, manager);
}
manager.count++;
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java
new file mode 100644
index 0000000..86e2c4b
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSQueueAppender.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.filter.Filters;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.core.net.JMSQueueManager;
+
+/**
+ *
+ */
+@Plugin(name="JMSQueue",type="Core",elementType="appender",printObject=true)
+public class JMSQueueAppender extends AppenderBase {
+
+ private final JMSQueueManager manager;
+
+ public JMSQueueAppender(String name, Filters filters, Layout layout, JMSQueueManager manager) {
+ super(name, filters, layout);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ * @param event The LogEvent.
+ */
+ public void append(LogEvent event) {
+ try {
+ manager.send(getLayout().formatAs(event));
+ } catch (Exception ex) {
+ throw new AppenderRuntimeException(ex);
+ }
+ }
+
+ @PluginFactory
+ public static JMSQueueAppender createAppender(@PluginAttr("factoryName") String factoryName,
+ @PluginAttr("providerURL") String providerURL,
+ @PluginAttr("urlPkgPrefixes") String urlPkgPrefixes,
+ @PluginAttr("securityPrincipalName") String securityPrincipalName,
+ @PluginAttr("securityCredentials") String securityCredentials,
+ @PluginAttr("factoryBindingName") String factoryBindingName,
+ @PluginAttr("queueBindingName") String queueBindingName,
+ @PluginAttr("userName") String userName,
+ @PluginAttr("password") String password,
+ @PluginElement("layout") Layout layout,
+ @PluginElement("filters") Filters filters) {
+
+ String name = "JMSQueue" + factoryBindingName + "." + queueBindingName;
+ 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, filters, layout, manager);
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSTopicAppender.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSTopicAppender.java
new file mode 100644
index 0000000..d9c73d1
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/appender/JMSTopicAppender.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.appender;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttr;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.filter.Filters;
+import org.apache.logging.log4j.core.layout.SerializedLayout;
+import org.apache.logging.log4j.core.net.JMSTopicManager;
+
+/**
+ *
+ */
+@Plugin(name="JMSTopic",type="Core",elementType="appender",printObject=true)
+public class JMSTopicAppender extends AppenderBase {
+
+ private final JMSTopicManager manager;
+
+ public JMSTopicAppender(String name, Filters filters, Layout layout, JMSTopicManager manager) {
+ super(name, filters, layout);
+ this.manager = manager;
+ }
+
+ /**
+ * Actual writing occurs here.
+ * <p/>
+ * @param event The LogEvent.
+ */
+ public void append(LogEvent event) {
+ try {
+ manager.send(getLayout().formatAs(event));
+ } catch (Exception ex) {
+ throw new AppenderRuntimeException(ex);
+ }
+ }
+
+ @PluginFactory
+ public static JMSTopicAppender createAppender(@PluginAttr("factoryName") String factoryName,
+ @PluginAttr("providerURL") String providerURL,
+ @PluginAttr("urlPkgPrefixes") String urlPkgPrefixes,
+ @PluginAttr("securityPrincipalName") String securityPrincipalName,
+ @PluginAttr("securityCredentials") String securityCredentials,
+ @PluginAttr("factoryBindingName") String factoryBindingName,
+ @PluginAttr("topicBindingName") String topicBindingName,
+ @PluginAttr("userName") String userName,
+ @PluginAttr("password") String password,
+ @PluginElement("layout") Layout layout,
+ @PluginElement("filters") Filters filters) {
+
+ String name = "JMSTopic" + factoryBindingName + "." + topicBindingName;
+ 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, filters, layout, manager);
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java
index 048ad8c..ed4443e 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java
@@ -44,10 +44,13 @@
root.setLevel(level);
}
- public class BasicLayout implements Layout {
+ public class BasicLayout implements Layout<String> {
public byte[] format(LogEvent event) {
- String result = event.getMessage().getFormattedMessage() + "\n";
- return result.getBytes();
+ return formatAs(event).getBytes();
+ }
+
+ public String formatAs(LogEvent event) {
+ return event.getMessage().getFormattedMessage() + "\n";
}
public byte[] getHeader() {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
new file mode 100644
index 0000000..7a7cf2d
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.layout;
+
+import org.apache.logging.log4j.core.LogEvent;
+
+import java.nio.charset.Charset;
+
+/**
+ *
+ */
+public abstract class AbstractStringLayout extends LayoutBase<String> {
+
+ /**
+ * The charset of the formatted message.
+ */
+ private Charset charset;
+
+ protected AbstractStringLayout(Charset charset) {
+ this.charset = charset;
+ }
+
+ public byte[] format(LogEvent event) {
+ return formatAs(event).getBytes(charset);
+ }
+
+ protected Charset getCharset() {
+ return charset;
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/HTMLLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/HTMLLayout.java
index 45ae6d8..7166bd3 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/HTMLLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/HTMLLayout.java
@@ -40,7 +40,7 @@
* non ASCII characters could result in corrupted log files.
*/
@Plugin(name="HTMLLayout",type="Core",elementType="layout",printObject=true)
-public class HTMLLayout extends LayoutBase {
+public class HTMLLayout extends AbstractStringLayout {
protected static final int BUF_SIZE = 256;
@@ -57,16 +57,14 @@
protected final String contentType;
- protected final Charset charset;
-
public HTMLLayout(boolean locationInfo, String title, String contentType, Charset charset) {
+ super(charset);
this.locationInfo = locationInfo;
this.title = title;
this.contentType = contentType;
- this.charset = charset;
}
- public byte[] format(LogEvent event) {
+ public String formatAs(LogEvent event) {
StringBuilder sbuf = new StringBuilder(BUF_SIZE);
sbuf.append(LINE_SEP).append("<tr>").append(LINE_SEP);
@@ -138,7 +136,7 @@
sbuf.append("</td></tr>").append(LINE_SEP);
}
- return sbuf.toString().getBytes(charset);
+ return sbuf.toString();
}
void appendThrowableAsHTML(Throwable throwable, StringBuilder sbuf) {
@@ -211,7 +209,7 @@
}
sbuf.append("<th>Message</th>").append(LINE_SEP);
sbuf.append("</tr>").append(LINE_SEP);
- return sbuf.toString().getBytes(charset);
+ return sbuf.toString().getBytes(getCharset());
}
/**
@@ -223,7 +221,7 @@
sbuf.append("</table>").append(LINE_SEP);
sbuf.append("<br>").append(LINE_SEP);
sbuf.append("</body></html>");
- return sbuf.toString().getBytes(charset);
+ return sbuf.toString().getBytes(getCharset());
}
@PluginFactory
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/LayoutBase.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/LayoutBase.java
index 61713ec..d5c9f34 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/LayoutBase.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/LayoutBase.java
@@ -20,10 +20,12 @@
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.internal.StatusLogger;
+import java.io.Serializable;
+
/**
*
*/
-public abstract class LayoutBase implements Layout {
+public abstract class LayoutBase<T extends Serializable> implements Layout<T> {
protected byte[] header;
protected byte[] footer;
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
index 32fd2d9..3930af0 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
@@ -388,7 +388,7 @@
* Engineering Approach", ISBN 0-387-97389-3.
*/
@Plugin(name="PatternLayout",type="Core",elementType="layout",printObject=true)
-public class PatternLayout extends LayoutBase {
+public class PatternLayout extends AbstractStringLayout {
/**
* Default pattern string for log output. Currently set to the
* string <b>"%m%n"</b> which just prints the application supplied
@@ -428,11 +428,6 @@
private boolean handlesExceptions;
/**
- * The charset of the formatted message.
- */
- private Charset charset;
-
- /**
* Constructs a EnhancedPatternLayout using the DEFAULT_LAYOUT_PATTERN.
* <p/>
* The default pattern just produces the application supplied message.
@@ -456,9 +451,8 @@
* @param pattern conversion pattern.
*/
public PatternLayout(final String pattern, final Charset charset) {
-
+ super(charset);
this.conversionPattern = pattern;
- this.charset = charset;
PatternParser parser = createPatternParser();
converters = parser.parse((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern);
handlesExceptions = parser.handlesExceptions();
@@ -487,12 +481,12 @@
*
* @param event logging event to be formatted.
*/
- public byte[] format(final LogEvent event) {
+ public String formatAs(final LogEvent event) {
StringBuilder buf = new StringBuilder();
for (PatternConverter c : converters) {
c.format(event, buf);
}
- return buf.toString().getBytes(charset);
+ return buf.toString();
}
private PatternParser createPatternParser() {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/RFC5424Layout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/RFC5424Layout.java
index 496e58f..f591254 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/RFC5424Layout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/RFC5424Layout.java
@@ -45,14 +45,13 @@
*
*/
@Plugin(name="RFC5424Layout",type="Core",elementType="layout",printObject=true)
-public class RFC5424Layout extends LayoutBase {
+public class RFC5424Layout extends AbstractStringLayout {
private final Facility facility;
private final String defaultId;
private final Integer enterpriseNumber;
private final boolean includeMDC;
private final String mdcId;
- private final Charset charset;
private final String localHostName;
private final String appName;
private final String messageId;
@@ -75,6 +74,7 @@
public RFC5424Layout(Facility facility, String id, int ein, boolean includeMDC, boolean includeNL, String mdcId,
String appName, String messageId, String excludes, String includes, Charset charset) {
+ super(charset);
this.facility = facility;
this.defaultId = id == null ? DEFAULT_ID : id;
this.enterpriseNumber = ein;
@@ -83,7 +83,6 @@
this.mdcId = mdcId;
this.appName = appName;
this.messageId = messageId;
- this.charset = charset;
this.localHostName = getLocalHostname();
ListChecker c = null;
if (excludes != null) {
@@ -123,7 +122,7 @@
/**
* Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the RFC 5424 Syslog specification.
*/
- public byte[] format(final LogEvent event) {
+ public String formatAs(final LogEvent event) {
Message msg = event.getMessage();
boolean isStructured = msg instanceof StructuredDataMessage;
StringBuilder buf = new StringBuilder();
@@ -176,7 +175,7 @@
if (includeNewLine) {
buf.append("\n");
}
- return buf.toString().getBytes(charset);
+ return buf.toString();
}
protected String getProcId() {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SerializedLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SerializedLayout.java
index 70bf498..59ee3d8 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SerializedLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SerializedLayout.java
@@ -42,7 +42,7 @@
*
*/
@Plugin(name="SerializedLayout",type="Core",elementType="layout",printObject=true)
-public class SerializedLayout extends LayoutBase {
+public class SerializedLayout extends LayoutBase<LogEvent> {
private static int count = 0;
private static byte[] header;
@@ -75,6 +75,10 @@
return baos.toByteArray();
}
+ public LogEvent formatAs(final LogEvent event) {
+ return event;
+ }
+
@PluginFactory
public static SerializedLayout createLayout() {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
index d13803f..75b86e9 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java
@@ -35,9 +35,8 @@
*
*/
@Plugin(name="SyslogLayout",type="Core",elementType="layout",printObject=true)
-public class SyslogLayout extends LayoutBase {
+public class SyslogLayout extends AbstractStringLayout {
- private final Charset charset;
private final Facility facility;
private final boolean includeNewLine;
@@ -52,15 +51,15 @@
public SyslogLayout(Facility facility, boolean includeNL, Charset c) {
+ super(c);
this.facility = facility;
this.includeNewLine = includeNL;
- this.charset = c;
}
/**
* Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the log4j.dtd.
*/
- public byte[] format(final LogEvent event) {
+ public String formatAs(final LogEvent event) {
StringBuilder buf = new StringBuilder();
buf.append("<");
@@ -74,7 +73,7 @@
if (includeNewLine) {
buf.append("\n");
}
- return buf.toString().getBytes(charset);
+ return buf.toString();
}
/**
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java
index 169d1cb..2a04a13 100644
--- a/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/layout/XMLLayout.java
@@ -69,7 +69,7 @@
* log files.
*/
@Plugin(name="XMLLayout",type="Core",elementType="layout",printObject=true)
-public class XMLLayout extends LayoutBase {
+public class XMLLayout extends AbstractStringLayout {
private final boolean locationInfo;
private final boolean properties;
@@ -77,11 +77,9 @@
protected static final int DEFAULT_SIZE = 256;
- protected final Charset charset;
-
public XMLLayout(boolean locationInfo, boolean properties, boolean complete, Charset charset) {
+ super(charset);
this.locationInfo = locationInfo;
- this.charset = charset;
this.properties = properties;
this.complete = complete;
}
@@ -89,7 +87,7 @@
/**
* Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the log4j.dtd.
*/
- public byte[] format(final LogEvent event) {
+ public String formatAs(final LogEvent event) {
StringBuilder buf = new StringBuilder(DEFAULT_SIZE);
// We yield to the \r\n heresy.
@@ -158,7 +156,7 @@
buf.append("</log4j:event>\r\n\r\n");
- return buf.toString().getBytes(charset);
+ return buf.toString();
}
/**
@@ -172,7 +170,7 @@
StringBuilder sbuf = new StringBuilder();
sbuf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
sbuf.append("<log4j:eventSet xmlns:log4j=\"http://jakarta.apache.org/log4j/\">\r\n");
- return sbuf.toString().getBytes(charset);
+ return sbuf.toString().getBytes(getCharset());
}
@@ -186,7 +184,7 @@
}
StringBuilder sbuf = new StringBuilder();
sbuf.append("</log4j:eventSet>\r\n");
- return sbuf.toString().getBytes(charset);
+ return sbuf.toString().getBytes(getCharset());
}
List<String> getThrowableString(Throwable throwable) {
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSManager.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSManager.java
new file mode 100644
index 0000000..f076f06
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSManager.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.core.appender.AbstractManager;
+
+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 java.io.Serializable;
+import java.util.Properties;
+
+/**
+ *
+ */
+public abstract class AbstractJMSManager extends AbstractManager {
+
+ public AbstractJMSManager(String name) {
+ super(name);
+ }
+
+ protected static Context createContext(String factoryName, String providerURL, String urlPkgPrefixes,
+ String securityPrincipalName, String securityCredentials)
+ throws NamingException {
+
+ Properties props = getEnvironment(factoryName, providerURL, urlPkgPrefixes, securityPrincipalName,
+ securityCredentials);
+ return new InitialContext(props);
+ }
+
+ protected static Object lookup(Context ctx, String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch(NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+ protected static Properties getEnvironment(String factoryName, String providerURL, String urlPkgPrefixes,
+ String securityPrincipalName, String securityCredentials) {
+ 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 InitalContext 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;
+ }
+
+ public abstract void send(Serializable Object) throws Exception;
+
+
+ public synchronized void send(Serializable object, Session session, 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 (JMSException ex) {
+ logger.error("Could not publish message via JMS " + getName());
+ throw ex;
+ }
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSReceiver.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSReceiver.java
new file mode 100644
index 0000000..9973bdc
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/AbstractJMSReceiver.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.AbstractServer;
+import org.apache.logging.log4j.core.LogEvent;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.naming.Context;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+/**
+ *
+ */
+public abstract class AbstractJMSReceiver extends AbstractServer implements javax.jms.MessageListener {
+
+
+ protected Logger logger = LogManager.getLogger(this.getClass().getName());
+
+ public void onMessage(javax.jms.Message message) {
+
+ try {
+ if (message instanceof ObjectMessage) {
+ ObjectMessage objectMessage = (ObjectMessage) message;
+ log((LogEvent) objectMessage.getObject());
+ } else {
+ logger.warn("Received message is of type " + message.getJMSType()
+ + ", was expecting ObjectMessage.");
+ }
+ } catch (JMSException jmse) {
+ logger.error("Exception thrown while processing incoming message.",
+ jmse);
+ }
+ }
+
+
+ protected Object lookup(Context ctx, String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch (NameNotFoundException e) {
+ logger.error("Could not find name [" + name + "].");
+ throw e;
+ }
+ }
+
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java
new file mode 100644
index 0000000..d39cc31
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueManager.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+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 java.io.Serializable;
+
+/**
+ *
+ */
+public class JMSQueueManager extends AbstractJMSManager {
+
+ private QueueConnection queueConnection;
+ private QueueSession queueSession;
+ private QueueSender queueSender;
+
+
+ private static ManagerFactory factory = new JMSTopicManagerFactory();
+
+ public static JMSQueueManager getJMSQueueManager(String factoryName, String providerURL, String urlPkgPrefixes,
+ String securityPrincipalName, String securityCredentials,
+ String factoryBindingName, String queueBindingName,
+ String userName, 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;
+ }
+
+ String name = "JMSQueue:" + factoryBindingName + "." + queueBindingName;
+ return (JMSQueueManager) getManager(name, factory, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, queueBindingName, userName, password));
+ }
+
+ public JMSQueueManager(String name, QueueConnection conn, QueueSession sess, QueueSender sender) {
+ super(name);
+ this.queueConnection = conn;
+ this.queueSession = sess;
+ this.queueSender = sender;
+ }
+
+ @Override
+ public void send(Serializable object) throws Exception {
+ super.send(object, queueSession, queueSender);
+ }
+
+ @Override
+ public void releaseSub() {
+ try {
+ if (queueSession != null) {
+ queueSession.close();
+ }
+ if (queueConnection != null) {
+ queueConnection.close();
+ }
+ } catch (JMSException ex) {
+ logger.error("Error closing " + getName(), ex);
+ }
+ }
+
+
+ private static class FactoryData {
+ String factoryName;
+ String providerURL;
+ String urlPkgPrefixes;
+ String securityPrincipalName;
+ String securityCredentials;
+ String factoryBindingName;
+ String queueBindingName;
+ String userName;
+ String password;
+
+ public FactoryData(String factoryName, String providerURL, String urlPkgPrefixes, String securityPrincipalName,
+ String securityCredentials, String factoryBindingName, String queueBindingName,
+ String userName, 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 class JMSTopicManagerFactory implements ManagerFactory<JMSQueueManager, FactoryData> {
+
+ public JMSQueueManager createManager(String name, FactoryData data) {
+ try {
+ Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+ data.securityPrincipalName, data.securityCredentials);
+ QueueConnectionFactory factory = (QueueConnectionFactory) lookup(ctx, data.factoryBindingName);
+ QueueConnection conn;
+ if (data.userName != null) {
+ conn = factory.createQueueConnection(data.userName, data.password);
+ } else {
+ conn = factory.createQueueConnection();
+ }
+ QueueSession sess = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = (Queue) lookup(ctx, data.queueBindingName);
+ QueueSender sender = sess.createSender(queue);
+ conn.start();
+ return new JMSQueueManager(name, conn, sess, sender);
+
+ } catch (NamingException ex) {
+
+ } catch (JMSException jmsex) {
+
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueReceiver.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueReceiver.java
new file mode 100644
index 0000000..fd1dd51
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSQueueReceiver.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+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;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ *
+ */
+public class JMSQueueReceiver extends AbstractJMSReceiver {
+
+ static public void main(String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ String qcfBindingName = args[0];
+ String queueBindingName = args[1];
+ String username = args[2];
+ String password = args[3];
+
+ new JMSQueueReceiver(qcfBindingName, queueBindingName, username, password);
+
+ BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JMSQueueReceiver.");
+ while (true) {
+ String s = stdin.readLine();
+ if (s.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+ public JMSQueueReceiver(String qcfBindingName, String queueBindingName, String username, String password) {
+
+ try {
+ Context ctx = new InitialContext();
+ QueueConnectionFactory queueConnectionFactory;
+ queueConnectionFactory = (QueueConnectionFactory) lookup(ctx,qcfBindingName);
+ QueueConnection queueConnection = queueConnectionFactory.createQueueConnection(username, password);
+ queueConnection.start();
+ QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = (Queue) ctx.lookup(queueBindingName);
+ QueueReceiver queueReceiver = queueSession.createReceiver(queue);
+ queueReceiver.setMessageListener(this);
+ } catch (JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JMSQueueReceiver.class.getName()
+ + " QueueConnectionFactoryBindingName QueueBindingName username password");
+ System.exit(1);
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java
new file mode 100644
index 0000000..c602475
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicManager.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.core.appender.ManagerFactory;
+
+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.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 java.io.Serializable;
+
+/**
+ *
+ */
+public class JMSTopicManager extends AbstractJMSManager {
+
+ private TopicConnection topicConnection;
+ private TopicSession topicSession;
+ private TopicPublisher topicPublisher;
+
+
+ private static ManagerFactory factory = new JMSTopicManagerFactory();
+
+ public static JMSTopicManager getJMSTopicManager(String factoryName, String providerURL, String urlPkgPrefixes,
+ String securityPrincipalName, String securityCredentials,
+ String factoryBindingName, String topicBindingName,
+ String userName, 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;
+ }
+
+ String name = "JMSTopic:" + factoryBindingName + "." + topicBindingName;
+ return (JMSTopicManager) getManager(name, factory, new FactoryData(factoryName, providerURL, urlPkgPrefixes,
+ securityPrincipalName, securityCredentials, factoryBindingName, topicBindingName, userName, password));
+ }
+
+ public JMSTopicManager(String name, TopicConnection conn, TopicSession sess, TopicPublisher pub) {
+ super(name);
+ this.topicConnection = conn;
+ this.topicSession = sess;
+ this.topicPublisher = pub;
+ }
+
+ @Override
+ public void send(Serializable object) throws Exception {
+ super.send(object, topicSession, topicPublisher);
+ }
+
+ @Override
+ public void releaseSub() {
+ try {
+ if (topicSession != null) {
+ topicSession.close();
+ }
+ if (topicConnection != null) {
+ topicConnection.close();
+ }
+ } catch (JMSException ex) {
+ logger.error("Error closing " + getName(), ex);
+ }
+ }
+
+
+ private static class FactoryData {
+ String factoryName;
+ String providerURL;
+ String urlPkgPrefixes;
+ String securityPrincipalName;
+ String securityCredentials;
+ String factoryBindingName;
+ String topicBindingName;
+ String userName;
+ String password;
+
+ public FactoryData(String factoryName, String providerURL, String urlPkgPrefixes, String securityPrincipalName,
+ String securityCredentials, String factoryBindingName, String topicBindingName,
+ String userName, 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;
+ }
+ }
+
+ private static class JMSTopicManagerFactory implements ManagerFactory<JMSTopicManager, FactoryData> {
+
+ public JMSTopicManager createManager(String name, FactoryData data) {
+ try {
+ Context ctx = createContext(data.factoryName, data.providerURL, data.urlPkgPrefixes,
+ data.securityPrincipalName, data.securityCredentials);
+ TopicConnectionFactory factory = (TopicConnectionFactory) lookup(ctx, data.factoryBindingName);
+ TopicConnection conn;
+ if (data.userName != null) {
+ conn = factory.createTopicConnection(data.userName, data.password);
+ } else {
+ conn = factory.createTopicConnection();
+ }
+ TopicSession sess = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = (Topic) lookup(ctx, data.topicBindingName);
+ TopicPublisher pub = sess.createPublisher(topic);
+ conn.start();
+ return new JMSTopicManager(name, conn, sess, pub);
+ } catch (NamingException ex) {
+ logger.error("Bad Name " + data.topicBindingName, ex);
+ } catch (JMSException jmsex) {
+ logger.error("Unable to create publisher ", jmsex);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicReceiver.java b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicReceiver.java
new file mode 100644
index 0000000..42ddd5f
--- /dev/null
+++ b/log4j2-core/src/main/java/org/apache/logging/log4j/core/net/JMSTopicReceiver.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.AbstractServer;
+import org.apache.logging.log4j.core.LogEvent;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ *
+ */
+public class JMSTopicReceiver extends AbstractJMSReceiver {
+
+ static public void main(String[] args) throws Exception {
+ if (args.length != 4) {
+ usage("Wrong number of arguments.");
+ }
+
+ String tcfBindingName = args[0];
+ String topicBindingName = args[1];
+ String username = args[2];
+ String password = args[3];
+
+ new JMSTopicReceiver(tcfBindingName, topicBindingName, username, password);
+
+ BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JMSTopicReceiver.");
+ while (true) {
+ String s = stdin.readLine();
+ if (s.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+ public JMSTopicReceiver(String tcfBindingName, String topicBindingName, String username, String password) {
+ try {
+ Context ctx = new InitialContext();
+ TopicConnectionFactory topicConnectionFactory;
+ topicConnectionFactory = (TopicConnectionFactory) lookup(ctx, tcfBindingName);
+ TopicConnection topicConnection = topicConnectionFactory.createTopicConnection(username, password);
+ topicConnection.start();
+ TopicSession topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
+ Topic topic = (Topic) ctx.lookup(topicBindingName);
+ TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
+ topicSubscriber.setMessageListener(this);
+ } catch (JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch (RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JMSTopicReceiver.class.getName()
+ + " TopicConnectionFactoryBindingName TopicBindingName username password");
+ System.exit(1);
+ }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSQueueTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSQueueTest.java
new file mode 100644
index 0000000..400ed28
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSQueueTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.JMSQueueAppender;
+import org.apache.logging.log4j.core.appender.JMSTopicAppender;
+import org.apache.logging.log4j.core.appender.ListAppender;
+import org.apache.logging.log4j.core.filter.FilterBase;
+import org.apache.logging.log4j.core.filter.Filters;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.internal.StatusConsoleListener;
+import org.apache.logging.log4j.internal.StatusLogger;
+import org.mockejb.jndi.*;
+import org.mockejb.jms.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class JMSQueueTest {
+
+ private static final String FACTORY_NAME = "TestQueueConnectionFactory";
+ private static final String QUEUE_NAME = "TestQueue";
+
+ private static Context context;
+ private static AbstractJMSReceiver receiver;
+
+ LoggerContext ctx = (LoggerContext) LogManager.getContext();
+ Logger root = ctx.getLogger("JMSQueueTest");
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ // MockContextFactory becomes the primary JNDI provider
+ StatusConsoleListener l = new StatusConsoleListener(Level.ERROR);
+ StatusLogger.getLogger().registerListener(l);
+ MockContextFactory.setAsInitial();
+ context = new InitialContext();
+ context.rebind(FACTORY_NAME, new QueueConnectionFactoryImpl() );
+ context.rebind(QUEUE_NAME, new MockQueue(QUEUE_NAME));
+ ((LoggerContext) LogManager.getContext()).reconfigure();
+ receiver = new JMSQueueReceiver(FACTORY_NAME, QUEUE_NAME, null, null);
+ }
+
+ @AfterClass
+ public static void cleanupClass() {
+ StatusLogger.getLogger().reset();
+ }
+
+ @After
+ public void teardown() {
+ Map<String,Appender> map = root.getAppenders();
+ for (Map.Entry<String, Appender> entry : map.entrySet()) {
+ Appender app = entry.getValue();
+ root.removeAppender(app);
+ app.stop();
+ }
+ }
+
+ @Test
+ public void testServer() throws Exception {
+ Filter clientFilter = new MessageFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
+ Filter serverFilter = new MessageFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
+ Filters clientFilters = Filters.createFilters(new Filter[] {clientFilter});
+ JMSQueueAppender appender = JMSQueueAppender.createAppender(null, null, null, null, null, FACTORY_NAME,
+ QUEUE_NAME, null, null, null, clientFilters);
+ appender.start();
+ Filters serverFilters = Filters.createFilters(new Filter[] {serverFilter});
+ ListAppender listApp = new ListAppender("Events", serverFilters, null, false, false);
+ listApp.start();
+ PatternLayout layout = new PatternLayout("%m %ex%n");
+ ConsoleAppender console = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console");
+ console.start();
+ Logger serverLogger = ctx.getLogger(JMSTopicReceiver.class.getName());
+ serverLogger.addAppender(console);
+ serverLogger.setAdditive(false);
+
+
+ // set appender on root and set level to debug
+ root.addAppender(listApp);
+ root.addAppender(appender);
+ root.setAdditive(false);
+ root.setLevel(Level.DEBUG);
+ root.debug("This is a test message");
+ Thread.sleep(100);
+ List<LogEvent> events = listApp.getEvents();
+ assertNotNull("No event retrieved", events);
+ assertTrue("No events retrieved", events.size() > 0);
+ assertTrue("Incorrect event", events.get(0).getMessage().getFormattedMessage().equals("This is a test message"));
+ }
+
+ private class MessageFilter extends FilterBase {
+ public MessageFilter(Result onMatch, Result onMismatch) {
+ super(onMatch, onMismatch);
+ }
+
+ public Result filter(LogEvent event) {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ for (StackTraceElement element : stackTrace) {
+ if (element.getMethodName().equals("onMessage")) {
+ return onMatch;
+ } else if (element.getMethodName().equals("testServer")) {
+ return onMismatch;
+ }
+ }
+ return onMismatch;
+ }
+ }
+}
diff --git a/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSTopicTest.java b/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSTopicTest.java
new file mode 100644
index 0000000..25c46b2
--- /dev/null
+++ b/log4j2-core/src/test/java/org/apache/logging/log4j/core/net/JMSTopicTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.net;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.JMSTopicAppender;
+import org.apache.logging.log4j.core.appender.ListAppender;
+import org.apache.logging.log4j.core.filter.FilterBase;
+import org.apache.logging.log4j.core.filter.Filters;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.internal.StatusConsoleListener;
+import org.apache.logging.log4j.internal.StatusLogger;
+import org.mockejb.jndi.*;
+import org.mockejb.jms.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class JMSTopicTest {
+
+ private static final String FACTORY_NAME = "TestTopicConnectionFactory";
+ private static final String TOPIC_NAME = "TestTopic";
+
+ private static Context context;
+ private static AbstractJMSReceiver receiver;
+
+ LoggerContext ctx = (LoggerContext) LogManager.getContext();
+ Logger root = ctx.getLogger("JMSTopicTest");
+
+ @BeforeClass
+ public static void setupClass() throws Exception {
+ // MockContextFactory becomes the primary JNDI provider
+ StatusConsoleListener l = new StatusConsoleListener(Level.ERROR);
+ StatusLogger.getLogger().registerListener(l);
+ MockContextFactory.setAsInitial();
+ context = new InitialContext();
+ context.rebind(FACTORY_NAME, new TopicConnectionFactoryImpl() );
+ context.rebind(TOPIC_NAME, new MockTopic(TOPIC_NAME) );
+ ((LoggerContext) LogManager.getContext()).reconfigure();
+ receiver = new JMSTopicReceiver(FACTORY_NAME, TOPIC_NAME, null, null);
+ }
+
+ @AfterClass
+ public static void cleanupClass() {
+ StatusLogger.getLogger().reset();
+ }
+
+ @After
+ public void teardown() {
+ Map<String,Appender> map = root.getAppenders();
+ for (Map.Entry<String, Appender> entry : map.entrySet()) {
+ Appender app = entry.getValue();
+ root.removeAppender(app);
+ app.stop();
+ }
+ }
+
+ @Test
+ public void testServer() throws Exception {
+ Filter clientFilter = new MessageFilter(Filter.Result.NEUTRAL, Filter.Result.DENY);
+ Filter serverFilter = new MessageFilter(Filter.Result.DENY, Filter.Result.NEUTRAL);
+ Filters clientFilters = Filters.createFilters(new Filter[] {clientFilter});
+ JMSTopicAppender appender = JMSTopicAppender.createAppender(null, null, null, null, null, FACTORY_NAME,
+ TOPIC_NAME, null, null, null, clientFilters);
+ appender.start();
+ Filters serverFilters = Filters.createFilters(new Filter[] {serverFilter});
+ ListAppender listApp = new ListAppender("Events", serverFilters, null, false, false);
+ listApp.start();
+ PatternLayout layout = new PatternLayout("%m %ex%n");
+ ConsoleAppender console = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console");
+ console.start();
+ Logger serverLogger = ctx.getLogger(JMSTopicReceiver.class.getName());
+ serverLogger.addAppender(console);
+ serverLogger.setAdditive(false);
+
+
+ // set appender on root and set level to debug
+ root.addAppender(listApp);
+ root.addAppender(appender);
+ root.setAdditive(false);
+ root.setLevel(Level.DEBUG);
+ root.debug("This is a test message");
+ Thread.sleep(100);
+ List<LogEvent> events = listApp.getEvents();
+ assertNotNull("No event retrieved", events);
+ assertTrue("No events retrieved", events.size() > 0);
+ assertTrue("Incorrect event", events.get(0).getMessage().getFormattedMessage().equals("This is a test message"));
+ }
+
+ private class MessageFilter extends FilterBase {
+ public MessageFilter(Result onMatch, Result onMismatch) {
+ super(onMatch, onMismatch);
+ }
+
+ public Result filter(LogEvent event) {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ for (StackTraceElement element : stackTrace) {
+ if (element.getMethodName().equals("onMessage")) {
+ return onMatch;
+ } else if (element.getMethodName().equals("testServer")) {
+ return onMismatch;
+ }
+ }
+ return onMismatch;
+ }
+ }
+}