Skip to content

Commit 5679f45

Browse files
committed
Add a Jakarta JMS Appender apache#2295
1 parent 7d08222 commit 5679f45

File tree

12 files changed

+1064
-10
lines changed

12 files changed

+1064
-10
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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 static org.mockito.ArgumentMatchers.anyLong;
20+
import static org.mockito.ArgumentMatchers.anyString;
21+
import static org.mockito.ArgumentMatchers.eq;
22+
import static org.mockito.ArgumentMatchers.isA;
23+
import static org.mockito.BDDMockito.given;
24+
import static org.mockito.BDDMockito.then;
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.times;
27+
28+
import jakarta.jms.Connection;
29+
import jakarta.jms.ConnectionFactory;
30+
import jakarta.jms.Destination;
31+
import jakarta.jms.MapMessage;
32+
import jakarta.jms.MessageProducer;
33+
import jakarta.jms.ObjectMessage;
34+
import jakarta.jms.Session;
35+
import jakarta.jms.TextMessage;
36+
import java.io.Serializable;
37+
import java.util.Map;
38+
import java.util.concurrent.ConcurrentHashMap;
39+
import org.apache.logging.log4j.Level;
40+
import org.apache.logging.log4j.core.LogEvent;
41+
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
42+
import org.apache.logging.log4j.core.test.categories.Appenders;
43+
import org.apache.logging.log4j.core.test.junit.JndiRule;
44+
import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
45+
import org.apache.logging.log4j.message.Message;
46+
import org.apache.logging.log4j.message.SimpleMessage;
47+
import org.apache.logging.log4j.message.StringMapMessage;
48+
import org.junit.AfterClass;
49+
import org.junit.Before;
50+
import org.junit.BeforeClass;
51+
import org.junit.Rule;
52+
import org.junit.Test;
53+
import org.junit.experimental.categories.Category;
54+
import org.junit.rules.RuleChain;
55+
56+
@Category(Appenders.Jms.class)
57+
public class JmsAppenderTest {
58+
59+
private static final String CONNECTION_FACTORY_NAME = "jms/connectionFactory";
60+
private static final String QUEUE_FACTORY_NAME = "jms/queues";
61+
private static final String TOPIC_FACTORY_NAME = "jms/topics";
62+
private static final String DESTINATION_NAME = "jms/destination";
63+
private static final String DESTINATION_NAME_ML = "jms/destination-ml";
64+
private static final String QUEUE_NAME = "jms/queue";
65+
private static final String TOPIC_NAME = "jms/topic";
66+
private static final String LOG_MESSAGE = "Hello, world!";
67+
68+
private final ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
69+
private final Connection connection = mock(Connection.class);
70+
private final Session session = mock(Session.class);
71+
private final Destination destination = mock(Destination.class);
72+
private final Destination destinationMl = mock(Destination.class);
73+
private final MessageProducer messageProducer = mock(MessageProducer.class);
74+
private final MessageProducer messageProducerMl = mock(MessageProducer.class);
75+
private final TextMessage textMessage = mock(TextMessage.class);
76+
private final ObjectMessage objectMessage = mock(ObjectMessage.class);
77+
private final MapMessage mapMessage = mock(MapMessage.class);
78+
79+
private final JndiRule jndiRule = new JndiRule(createBindings());
80+
private final LoggerContextRule ctx = new LoggerContextRule("JmsJakartaAppenderTest.xml");
81+
82+
@Rule
83+
public RuleChain rules = RuleChain.outerRule(jndiRule).around(ctx);
84+
85+
@AfterClass
86+
public static void afterClass() throws Exception {
87+
System.clearProperty("log4j2.enableJndiJms");
88+
}
89+
90+
@BeforeClass
91+
public static void beforeClass() throws Exception {
92+
System.setProperty("log4j2.enableJndiJms", "true");
93+
}
94+
95+
public JmsAppenderTest() throws Exception {
96+
// this needs to set up before LoggerContextRule
97+
given(connectionFactory.createConnection()).willReturn(connection);
98+
given(connectionFactory.createConnection(anyString(), anyString())).willThrow(IllegalArgumentException.class);
99+
given(connection.createSession(eq(false), eq(Session.AUTO_ACKNOWLEDGE))).willReturn(session);
100+
given(session.createProducer(eq(destination))).willReturn(messageProducer);
101+
given(session.createProducer(eq(destinationMl))).willReturn(messageProducerMl);
102+
given(session.createTextMessage(anyString())).willReturn(textMessage);
103+
given(session.createObjectMessage(isA(Serializable.class))).willReturn(objectMessage);
104+
given(session.createMapMessage()).willReturn(mapMessage);
105+
}
106+
107+
private Map<String, Object> createBindings() {
108+
final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
109+
map.put(CONNECTION_FACTORY_NAME, connectionFactory);
110+
map.put(DESTINATION_NAME, destination);
111+
map.put(DESTINATION_NAME_ML, destinationMl);
112+
map.put(QUEUE_FACTORY_NAME, connectionFactory);
113+
map.put(QUEUE_NAME, destination);
114+
map.put(TOPIC_FACTORY_NAME, connectionFactory);
115+
map.put(TOPIC_NAME, destination);
116+
return map;
117+
}
118+
119+
private Log4jLogEvent createLogEvent() {
120+
return createLogEvent(new SimpleMessage(LOG_MESSAGE));
121+
}
122+
123+
private Log4jLogEvent createLogEvent(final Message message) {
124+
// @formatter:off
125+
return Log4jLogEvent.newBuilder()
126+
.setLoggerName(JmsAppenderTest.class.getName())
127+
.setLoggerFqcn(JmsAppenderTest.class.getName())
128+
.setLevel(Level.INFO)
129+
.setMessage(message)
130+
.build();
131+
// @formatter:on
132+
}
133+
134+
private Log4jLogEvent createMapMessageLogEvent() {
135+
final StringMapMessage mapMessage = new StringMapMessage();
136+
return createLogEvent(mapMessage.with("testMesage", LOG_MESSAGE));
137+
}
138+
139+
@Before
140+
public void setUp() throws Exception {
141+
// we have 4 appenders all connecting to the same ConnectionFactory
142+
then(connection).should(times(4)).start();
143+
}
144+
145+
@Test
146+
public void testAppendToQueue() throws Exception {
147+
final JmsAppender appender = (JmsAppender) ctx.getRequiredAppender("JmsAppender");
148+
final LogEvent event = createLogEvent();
149+
appender.append(event);
150+
then(session).should().createTextMessage(eq(LOG_MESSAGE));
151+
then(textMessage).should().setJMSTimestamp(anyLong());
152+
then(messageProducer).should().send(textMessage);
153+
appender.stop();
154+
then(session).should().close();
155+
then(connection).should().close();
156+
}
157+
158+
@Test
159+
public void testAppendToQueueWithMessageLayout() throws Exception {
160+
final JmsAppender appender = (JmsAppender) ctx.getRequiredAppender("JmsAppender-MessageLayout");
161+
final LogEvent event = createMapMessageLogEvent();
162+
appender.append(event);
163+
then(session).should().createMapMessage();
164+
then(mapMessage).should().setJMSTimestamp(anyLong());
165+
then(messageProducerMl).should().send(mapMessage);
166+
appender.stop();
167+
then(session).should().close();
168+
then(connection).should().close();
169+
}
170+
171+
@Test
172+
public void testJmsQueueAppenderCompatibility() throws Exception {
173+
final JmsAppender appender = (JmsAppender) ctx.getRequiredAppender("JmsQueueAppender");
174+
final LogEvent expected = createLogEvent();
175+
appender.append(expected);
176+
then(session).should().createObjectMessage(eq(expected));
177+
then(objectMessage).should().setJMSTimestamp(anyLong());
178+
then(messageProducer).should().send(objectMessage);
179+
appender.stop();
180+
then(session).should().close();
181+
then(connection).should().close();
182+
}
183+
184+
@Test
185+
public void testJmsTopicAppenderCompatibility() throws Exception {
186+
final JmsAppender appender = (JmsAppender) ctx.getRequiredAppender("JmsTopicAppender");
187+
final LogEvent expected = createLogEvent();
188+
appender.append(expected);
189+
then(session).should().createObjectMessage(eq(expected));
190+
then(objectMessage).should().setJMSTimestamp(anyLong());
191+
then(messageProducer).should().send(objectMessage);
192+
appender.stop();
193+
then(session).should().close();
194+
then(connection).should().close();
195+
}
196+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
<Configuration name="JmsAppenderTest" status="OFF">
19+
<Appenders>
20+
<JMS-Jakarta name="JmsAppender"
21+
factoryBindingName="jms/connectionFactory"
22+
destinationBindingName="jms/destination">
23+
<PatternLayout pattern="%m"/>
24+
</JMS-Jakarta>
25+
<JMS-Jakarta name="JmsAppender-MessageLayout"
26+
factoryBindingName="jms/connectionFactory"
27+
destinationBindingName="jms/destination-ml">
28+
<MessageLayout />
29+
</JMS-Jakarta>
30+
<JMS-Jakarta name="JmsQueueAppender"
31+
factoryBindingName="jms/queues"
32+
queueBindingName="jms/queue">
33+
<SerializedLayout/>
34+
</JMS-Jakarta>
35+
<JMS-Jakarta name="JmsTopicAppender"
36+
factoryBindingName="jms/topics"
37+
topicBindingName="jms/topic">
38+
<SerializedLayout/>
39+
</JMS-Jakarta>
40+
</Appenders>
41+
<Loggers>
42+
<Root level="info">
43+
<AppenderRef ref="JmsQueueAppender"/>
44+
</Root>
45+
</Loggers>
46+
</Configuration>

log4j-core/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
com.conversantmedia.util.concurrent;resolution:=optional;
6060
com.fasterxml.jackson.*;resolution:=optional,
6161
com.lmax.disruptor.*;resolution:=optional,
62+
jakarta.jms;version="3.0";resolution:=optional,
6263
javax.activation;resolution:=optional,
6364
javax.jms;version="[1.1,3)";resolution:=optional,
6465
javax.mail.*;version="[1.6,2)";resolution:=optional,
@@ -121,6 +122,12 @@
121122
<scope>provided</scope>
122123
<optional>true</optional>
123124
</dependency>
125+
<dependency>
126+
<groupId>jakarta.jms</groupId>
127+
<artifactId>jakarta.jms-api</artifactId>
128+
<scope>provided</scope>
129+
<optional>true</optional>
130+
</dependency>
124131
<!-- Required for SMTPAppender -->
125132
<dependency>
126133
<groupId>javax.mail</groupId>

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

Lines changed: 7 additions & 4 deletions
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 for both queues and topics. 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 {@link 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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
* JMS connection and session manager. Can be used to access MessageProducer, MessageConsumer, and Message objects
4646
* involving a configured ConnectionFactory and Destination.
4747
* </p>
48+
*
49+
* @deprecated Use {@link org.apache.logging.log4j.core.appender.mom.jakarta.JmsManager}.
4850
*/
51+
@Deprecated
4952
public class JmsManager extends AbstractManager {
5053

5154
public static class JmsManagerConfiguration {

0 commit comments

Comments
 (0)