Skip to content

Commit 43a0e29

Browse files
authoredDec 7, 2024··
Add a Jakarta JMS Appender #2295 (#3247)
1 parent df91908 commit 43a0e29

File tree

15 files changed

+1127
-12
lines changed

15 files changed

+1127
-12
lines changed
 

‎.github/dependabot.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ updates:
5959
- "/log4j-docker"
6060
- "/log4j-fuzz-test"
6161
- "/log4j-iostreams"
62+
- "/log4j-jakarta-jms"
6263
- "/log4j-jakarta-smtp"
6364
- "/log4j-jakarta-web"
6465
- "/log4j-jcl"

‎log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,14 @@
3737
import org.apache.logging.log4j.core.net.JndiManager;
3838

3939
/**
40-
* Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However,
41-
* configurations set up for the 2.0 version of the JMS appenders will still work.
40+
* Javax JMS Appender plugin. This Appender replaces the previous split classes.
41+
* Configurations set up for the 2.0 version of the JMS appenders will still work.
42+
*
43+
* @deprecated Use {@code org.apache.logging.log4j.core.appender.mom.jakarta.JmsAppender}.
4244
*/
43-
@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
44-
@PluginAliases({"JMSQueue", "JMSTopic"})
45+
@Deprecated
46+
@Plugin(name = "JMS-Javax", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
47+
@PluginAliases({"JMS", "JMSQueue", "JMSTopic"})
4548
public class JmsAppender extends AbstractAppender {
4649

4750
public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>

‎log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242
* Consider this class <b>private</b>; it is only <b>public</b> for access by integration tests.
4343
*
4444
* <p>
45-
* JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects
46-
* involving a configured ConnectionFactory and Destination.
45+
* JMS connection and destination manager. Uses a MessageProducer to send log events to a JMS Destination.
4746
* </p>
47+
*
48+
* @deprecated Use {@code org.apache.logging.log4j.core.appender.mom.jakarta.JmsManager}.
4849
*/
50+
@Deprecated
4951
public class JmsManager extends AbstractManager {
5052

5153
public static class JmsManagerConfiguration {

‎log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/package-info.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* @since 2.1
2222
*/
2323
@Export
24-
@Version("2.20.1")
24+
@Version("2.25.0")
2525
package org.apache.logging.log4j.core.appender.mom;
2626

2727
import org.osgi.annotation.bundle.Export;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file is here to activate the `plugin-processing` Maven profile.

‎log4j-jakarta-jms/pom.xml

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to you under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing, software
13+
~ distributed under the License is distributed on an "AS IS" BASIS,
14+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
~ See the License for the specific language governing permissions and
16+
~ limitations under the License.
17+
-->
18+
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
<modelVersion>4.0.0</modelVersion>
20+
<parent>
21+
<groupId>org.apache.logging.log4j</groupId>
22+
<artifactId>log4j</artifactId>
23+
<version>${revision}</version>
24+
<relativePath>../log4j-parent</relativePath>
25+
</parent>
26+
<artifactId>log4j-jakarta-jms</artifactId>
27+
<name>Apache Log4j Jakarta JMS</name>
28+
<description>Apache Log4j Java Message Service (JMS), version for Jakarta.</description>
29+
<properties>
30+
<!-- This is a new module in 2.25.0, remove this property after the release. -->
31+
<bnd.baseline.skip>true</bnd.baseline.skip>
32+
<!-- OSGi and JPMS options -->
33+
<bnd-module-name>org.apache.logging.log4j.jakarta.jms</bnd-module-name>
34+
<Fragment-Host>org.apache.logging.log4j.core</Fragment-Host>
35+
</properties>
36+
<dependencies>
37+
<dependency>
38+
<groupId>jakarta.jms</groupId>
39+
<artifactId>jakarta.jms-api</artifactId>
40+
<scope>provided</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.apache.logging.log4j</groupId>
44+
<artifactId>log4j-api</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.apache.logging.log4j</groupId>
48+
<artifactId>log4j-core</artifactId>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.junit.vintage</groupId>
52+
<artifactId>junit-vintage-engine</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.junit.jupiter</groupId>
57+
<artifactId>junit-jupiter-engine</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.apache.logging.log4j</groupId>
62+
<artifactId>log4j-core-test</artifactId>
63+
<scope>test</scope>
64+
</dependency>
65+
<dependency>
66+
<groupId>commons-logging</groupId>
67+
<artifactId>commons-logging</artifactId>
68+
<scope>test</scope>
69+
</dependency>
70+
</dependencies>
71+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.core.appender.mom.jakarta;
18+
19+
import jakarta.jms.JMSException;
20+
import java.io.Serializable;
21+
import java.util.Properties;
22+
import java.util.concurrent.TimeUnit;
23+
import org.apache.logging.log4j.core.Appender;
24+
import org.apache.logging.log4j.core.Filter;
25+
import org.apache.logging.log4j.core.Layout;
26+
import org.apache.logging.log4j.core.LogEvent;
27+
import org.apache.logging.log4j.core.appender.AbstractAppender;
28+
import org.apache.logging.log4j.core.appender.AbstractManager;
29+
import org.apache.logging.log4j.core.appender.mom.jakarta.JmsManager.JmsManagerConfiguration;
30+
import org.apache.logging.log4j.core.config.Node;
31+
import org.apache.logging.log4j.core.config.Property;
32+
import org.apache.logging.log4j.core.config.plugins.Plugin;
33+
import org.apache.logging.log4j.core.config.plugins.PluginAliases;
34+
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
35+
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
36+
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
37+
import org.apache.logging.log4j.core.net.JndiManager;
38+
39+
/**
40+
* Jakarta JMS Appender plugin for both queues and topics.
41+
*/
42+
@Plugin(name = "JMS-Jakarta", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true)
43+
public final class JmsAppender extends AbstractAppender {
44+
45+
public static final class Builder extends AbstractAppender.Builder<Builder>
46+
implements org.apache.logging.log4j.core.util.Builder<JmsAppender> {
47+
48+
public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000;
49+
50+
@PluginBuilderAttribute
51+
private String factoryName;
52+
53+
@PluginBuilderAttribute
54+
private String providerUrl;
55+
56+
@PluginBuilderAttribute
57+
private String urlPkgPrefixes;
58+
59+
@PluginBuilderAttribute
60+
private String securityPrincipalName;
61+
62+
@PluginBuilderAttribute(sensitive = true)
63+
private String securityCredentials;
64+
65+
@PluginBuilderAttribute
66+
@Required(message = "A jakarta.jms.ConnectionFactory JNDI name must be specified")
67+
private String factoryBindingName;
68+
69+
@PluginBuilderAttribute
70+
@PluginAliases({"queueBindingName", "topicBindingName"})
71+
@Required(message = "A jakarta.jms.Destination JNDI name must be specified")
72+
private String destinationBindingName;
73+
74+
@PluginBuilderAttribute
75+
private String userName;
76+
77+
@PluginBuilderAttribute(sensitive = true)
78+
private char[] password;
79+
80+
@PluginBuilderAttribute
81+
private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS;
82+
83+
@PluginBuilderAttribute
84+
private boolean immediateFail;
85+
86+
// Programmatic access only for now.
87+
private JmsManager jmsManager;
88+
89+
private Builder() {}
90+
91+
@SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender
92+
@Override
93+
public JmsAppender build() {
94+
JmsManager actualJmsManager = jmsManager;
95+
JmsManagerConfiguration configuration = null;
96+
if (actualJmsManager == null) {
97+
final Properties jndiProperties = JndiManager.createProperties(
98+
factoryName, providerUrl, urlPkgPrefixes, securityPrincipalName, securityCredentials, null);
99+
configuration = new JmsManagerConfiguration(
100+
jndiProperties,
101+
factoryBindingName,
102+
destinationBindingName,
103+
userName,
104+
password,
105+
immediateFail,
106+
reconnectIntervalMillis);
107+
actualJmsManager = AbstractManager.getManager(getName(), JmsManager.FACTORY, configuration);
108+
}
109+
if (actualJmsManager == null) {
110+
// JmsManagerFactory has already logged an ERROR.
111+
return null;
112+
}
113+
final Layout<? extends Serializable> layout = getLayout();
114+
if (layout == null) {
115+
LOGGER.error("No layout provided for JmsAppender");
116+
return null;
117+
}
118+
try {
119+
return new JmsAppender(
120+
getName(), getFilter(), layout, isIgnoreExceptions(), getPropertyArray(), actualJmsManager);
121+
} catch (final JMSException e) {
122+
// Never happens since the ctor no longer actually throws a JMSException.
123+
throw new IllegalStateException(e);
124+
}
125+
}
126+
127+
public Builder setDestinationBindingName(final String destinationBindingName) {
128+
this.destinationBindingName = destinationBindingName;
129+
return this;
130+
}
131+
132+
public Builder setFactoryBindingName(final String factoryBindingName) {
133+
this.factoryBindingName = factoryBindingName;
134+
return this;
135+
}
136+
137+
public Builder setFactoryName(final String factoryName) {
138+
this.factoryName = factoryName;
139+
return this;
140+
}
141+
142+
public Builder setImmediateFail(final boolean immediateFail) {
143+
this.immediateFail = immediateFail;
144+
return this;
145+
}
146+
147+
public Builder setJmsManager(final JmsManager jmsManager) {
148+
this.jmsManager = jmsManager;
149+
return this;
150+
}
151+
152+
public Builder setPassword(final char[] password) {
153+
this.password = password;
154+
return this;
155+
}
156+
157+
public Builder setProviderUrl(final String providerUrl) {
158+
this.providerUrl = providerUrl;
159+
return this;
160+
}
161+
162+
public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) {
163+
this.reconnectIntervalMillis = reconnectIntervalMillis;
164+
return this;
165+
}
166+
167+
public Builder setSecurityCredentials(final String securityCredentials) {
168+
this.securityCredentials = securityCredentials;
169+
return this;
170+
}
171+
172+
public Builder setSecurityPrincipalName(final String securityPrincipalName) {
173+
this.securityPrincipalName = securityPrincipalName;
174+
return this;
175+
}
176+
177+
public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) {
178+
this.urlPkgPrefixes = urlPkgPrefixes;
179+
return this;
180+
}
181+
182+
public Builder setUserName(final String userName) {
183+
this.userName = userName;
184+
return this;
185+
}
186+
187+
/**
188+
* Does not include the password.
189+
*/
190+
@Override
191+
public String toString() {
192+
return "Builder [name=" + getName() + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl
193+
+ ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName
194+
+ ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName
195+
+ ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout="
196+
+ getLayout() + ", filter=" + getFilter() + ", ignoreExceptions=" + isIgnoreExceptions()
197+
+ ", jmsManager=" + jmsManager + "]";
198+
}
199+
}
200+
201+
@PluginBuilderFactory
202+
public static Builder newBuilder() {
203+
return new Builder();
204+
}
205+
206+
private volatile JmsManager manager;
207+
208+
/**
209+
* Constructs a new instance.
210+
*
211+
* @throws JMSException not thrown as of 2.9 but retained in the signature for compatibility, will be removed in 3.0
212+
*/
213+
private JmsAppender(
214+
final String name,
215+
final Filter filter,
216+
final Layout<? extends Serializable> layout,
217+
final boolean ignoreExceptions,
218+
final Property[] properties,
219+
final JmsManager manager)
220+
throws JMSException {
221+
super(name, filter, layout, ignoreExceptions, properties);
222+
this.manager = manager;
223+
}
224+
225+
@Override
226+
public void append(final LogEvent event) {
227+
this.manager.send(event, toSerializable(event));
228+
}
229+
230+
public JmsManager getManager() {
231+
return manager;
232+
}
233+
234+
@Override
235+
public boolean stop(final long timeout, final TimeUnit timeUnit) {
236+
setStopping();
237+
boolean stopped = super.stop(timeout, timeUnit, false);
238+
stopped &= this.manager.stop(timeout, timeUnit);
239+
setStopped();
240+
return stopped;
241+
}
242+
}

0 commit comments

Comments
 (0)
Please sign in to comment.