Skip to content

Commit afc94f1

Browse files
authored
Add support for ActiveMQ and Artemis (#7400)
The following implementations have been added: * `ActiveMQContainer` supports [ActiveMQ](https://hub.docker.com/r/apache/activemq-classic) * `ArtemisContainer` supports [Artemis](https://hub.docker.com/r/apache/activemq-artemis)
1 parent 9de26f1 commit afc94f1

File tree

14 files changed

+441
-0
lines changed

14 files changed

+441
-0
lines changed

.github/ISSUE_TEMPLATE/bug_report.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ body:
1414
description: Which Testcontainers module are you using?
1515
options:
1616
- Core
17+
- ActiveMQ
1718
- Azure
1819
- Cassandra
1920
- Clickhouse

.github/ISSUE_TEMPLATE/enhancement.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ body:
1414
description: For which Testcontainers module does the enhancement proposal apply?
1515
options:
1616
- Core
17+
- ActiveMQ
1718
- Azure
1819
- Cassandra
1920
- Clickhouse

.github/ISSUE_TEMPLATE/feature.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ body:
1414
description: Is this feature related to any of the existing modules?
1515
options:
1616
- Core
17+
- ActiveMQ
1718
- Azure
1819
- Cassandra
1920
- Clickhouse

.github/dependabot.yml

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ updates:
3131
open-pull-requests-limit: 10
3232

3333
# Explicit entry for each module
34+
- package-ecosystem: "gradle"
35+
directory: "/modules/activemq"
36+
schedule:
37+
interval: "monthly"
38+
open-pull-requests-limit: 10
3439
- package-ecosystem: "gradle"
3540
directory: "/modules/azure"
3641
schedule:

.github/labeler.yml

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
- gradle/wrapper/*
1616
- gradlew
1717
- gradlew.bat
18+
"modules/activemq":
19+
- changed-files:
20+
- any-glob-to-any-file:
21+
- modules/activemq/**/*
1822
"modules/azure":
1923
- changed-files:
2024
- any-glob-to-any-file:

.github/settings.yml

+3
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ labels:
106106
- name: help wanted
107107
color: '#fef2c0'
108108

109+
- name: modules/activemq
110+
color: '#006b75'
111+
109112
- name: modules/azure
110113
color: '#006b75'
111114

docs/modules/activemq.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# ActiveMQ
2+
3+
Testcontainers module for [ActiveMQ](https://hub.docker.com/r/apache/activemq-classic) and
4+
[Artemis](https://hub.docker.com/r/apache/activemq-artemis).
5+
6+
## ActiveMQContainer's usage examples
7+
8+
You can start an ActiveMQ Classic container instance from any Java application by using:
9+
10+
<!--codeinclude-->
11+
[Default ActiveMQ container](../../modules/activemq/src/test/java/org/testcontainers/activemq/ActiveMQContainerTest.java) inside_block:container
12+
<!--/codeinclude-->
13+
14+
With custom credentials:
15+
16+
<!--codeinclude-->
17+
[Setting custom credentials](../../modules/activemq/src/test/java/org/testcontainers/activemq/ActiveMQContainerTest.java) inside_block:settingCredentials
18+
<!--/codeinclude-->
19+
20+
## ArtemisContainer's usage examples
21+
22+
You can start an ActiveMQ Artemis container instance from any Java application by using:
23+
24+
<!--codeinclude-->
25+
[Default Artemis container](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:container
26+
<!--/codeinclude-->
27+
28+
With custom credentials:
29+
30+
<!--codeinclude-->
31+
[Setting custom credentials](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:settingCredentials
32+
<!--/codeinclude-->
33+
34+
With anonymous login:
35+
36+
<!--codeinclude-->
37+
[Allow anonymous login](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:enableAnonymousLogin
38+
<!--/codeinclude-->
39+
40+
## Adding this module to your project dependencies
41+
42+
Add the following dependency to your `pom.xml`/`build.gradle` file:
43+
44+
=== "Gradle"
45+
```groovy
46+
testImplementation "org.testcontainers:activemq:{{latest_version}}"
47+
```
48+
49+
=== "Maven"
50+
```xml
51+
<dependency>
52+
<groupId>org.testcontainers</groupId>
53+
<artifactId>activemq</artifactId>
54+
<version>{{latest_version}}</version>
55+
<scope>test</scope>
56+
</dependency>
57+
```

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ nav:
7373
- modules/databases/tidb.md
7474
- modules/databases/trino.md
7575
- modules/databases/yugabytedb.md
76+
- modules/activemq.md
7677
- modules/azure.md
7778
- modules/consul.md
7879
- modules/docker_compose.md

modules/activemq/build.gradle

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
description = "Testcontainers :: ActiveMQ"
2+
3+
dependencies {
4+
api project(':testcontainers')
5+
6+
testImplementation 'org.assertj:assertj-core:3.24.2'
7+
testImplementation "org.apache.activemq:activemq-client-jakarta:5.18.2"
8+
testImplementation "org.apache.activemq:artemis-jakarta-client:2.29.0"
9+
}
10+
11+
test {
12+
javaLauncher = javaToolchains.launcherFor {
13+
languageVersion = JavaLanguageVersion.of(11)
14+
}
15+
}
16+
17+
compileTestJava {
18+
javaCompiler = javaToolchains.compilerFor {
19+
languageVersion = JavaLanguageVersion.of(11)
20+
}
21+
options.release.set(11)
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.testcontainers.activemq;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.utility.DockerImageName;
6+
7+
import java.time.Duration;
8+
9+
/**
10+
* Testcontainers implementation for Apache ActiveMQ.
11+
* <p>
12+
* Exposed ports:
13+
* <ul>
14+
* <li>Console: 8161</li>
15+
* <li>TCP: 61616</li>
16+
* <li>AMQP: 5672</li>
17+
* <li>STOMP: 61613</li>
18+
* <li>MQTT: 1883</li>
19+
* <li>WS: 61614</li>
20+
* </ul>
21+
*/
22+
public class ActiveMQContainer extends GenericContainer<ActiveMQContainer> {
23+
24+
private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("apache/activemq-classic");
25+
26+
private static final int WEB_CONSOLE_PORT = 8161;
27+
28+
private static final int TCP_PORT = 61616;
29+
30+
private static final int AMQP_PORT = 5672;
31+
32+
private static final int STOMP_PORT = 61613;
33+
34+
private static final int MQTT_PORT = 1883;
35+
36+
private static final int WS_PORT = 61614;
37+
38+
private String username;
39+
40+
private String password;
41+
42+
public ActiveMQContainer(String image) {
43+
this(DockerImageName.parse(image));
44+
}
45+
46+
public ActiveMQContainer(DockerImageName dockerImageName) {
47+
super(dockerImageName);
48+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE);
49+
50+
withExposedPorts(WEB_CONSOLE_PORT, TCP_PORT, AMQP_PORT, STOMP_PORT, MQTT_PORT, WS_PORT);
51+
waitingFor(Wait.forLogMessage(".*Apache ActiveMQ.*started.*", 1).withStartupTimeout(Duration.ofMinutes(1)));
52+
}
53+
54+
@Override
55+
protected void configure() {
56+
if (this.username != null) {
57+
addEnv("ACTIVEMQ_CONNECTION_USER", this.username);
58+
}
59+
if (this.password != null) {
60+
addEnv("ACTIVEMQ_CONNECTION_PASSWORD", this.password);
61+
}
62+
}
63+
64+
public ActiveMQContainer withUser(String username) {
65+
this.username = username;
66+
return this;
67+
}
68+
69+
public ActiveMQContainer withPassword(String password) {
70+
this.password = password;
71+
return this;
72+
}
73+
74+
public String getBrokerUrl() {
75+
return String.format("tcp://%s:%s", getHost(), getMappedPort(TCP_PORT));
76+
}
77+
78+
public String getUser() {
79+
return this.username;
80+
}
81+
82+
public String getPassword() {
83+
return this.password;
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.testcontainers.activemq;
2+
3+
import org.testcontainers.containers.GenericContainer;
4+
import org.testcontainers.containers.wait.strategy.Wait;
5+
import org.testcontainers.utility.DockerImageName;
6+
7+
import java.time.Duration;
8+
9+
/**
10+
* Testcontainers implementation for Apache ActiveMQ Artemis.
11+
* <p>
12+
* Exposed ports:
13+
* <ul>
14+
* <li>Console: 8161</li>
15+
* <li>TCP: 61616</li>
16+
* <li>HORNETQ: 5445</li>
17+
* <li>AMQP: 5672</li>
18+
* <li>STOMP: 61613</li>
19+
* <li>MQTT: 1883</li>
20+
* <li>WS: 61614</li>
21+
* </ul>
22+
*/
23+
public class ArtemisContainer extends GenericContainer<ArtemisContainer> {
24+
25+
private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("apache/activemq-artemis");
26+
27+
private static final int WEB_CONSOLE_PORT = 8161;
28+
29+
// CORE,MQTT,AMQP,HORNETQ,STOMP,OPENWIRE
30+
private static final int TCP_PORT = 61616;
31+
32+
private static final int HORNETQ_STOMP_PORT = 5445;
33+
34+
private static final int AMQP_PORT = 5672;
35+
36+
private static final int STOMP_PORT = 61613;
37+
38+
private static final int MQTT_PORT = 1883;
39+
40+
private static final int WS_PORT = 61614;
41+
42+
private String username = "artemis";
43+
44+
private String password = "artemis";
45+
46+
public ArtemisContainer(String image) {
47+
this(DockerImageName.parse(image));
48+
}
49+
50+
public ArtemisContainer(DockerImageName dockerImageName) {
51+
super(dockerImageName);
52+
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE);
53+
54+
withExposedPorts(WEB_CONSOLE_PORT, TCP_PORT, HORNETQ_STOMP_PORT, AMQP_PORT, STOMP_PORT, MQTT_PORT, WS_PORT);
55+
waitingFor(Wait.forLogMessage(".*HTTP Server started.*", 1).withStartupTimeout(Duration.ofMinutes(1)));
56+
}
57+
58+
@Override
59+
protected void configure() {
60+
withEnv("ARTEMIS_USER", this.username);
61+
withEnv("ARTEMIS_PASSWORD", this.password);
62+
}
63+
64+
public ArtemisContainer withUser(String username) {
65+
this.username = username;
66+
return this;
67+
}
68+
69+
public ArtemisContainer withPassword(String password) {
70+
this.password = password;
71+
return this;
72+
}
73+
74+
public String getBrokerUrl() {
75+
return String.format("tcp://%s:%s", getHost(), getMappedPort(TCP_PORT));
76+
}
77+
78+
public String getUser() {
79+
return getEnvMap().get("ARTEMIS_USER");
80+
}
81+
82+
public String getPassword() {
83+
return getEnvMap().get("ARTEMIS_PASSWORD");
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.testcontainers.activemq;
2+
3+
import jakarta.jms.Connection;
4+
import jakarta.jms.ConnectionFactory;
5+
import jakarta.jms.Destination;
6+
import jakarta.jms.JMSException;
7+
import jakarta.jms.MessageConsumer;
8+
import jakarta.jms.MessageProducer;
9+
import jakarta.jms.Session;
10+
import jakarta.jms.TextMessage;
11+
import lombok.SneakyThrows;
12+
import org.apache.activemq.ActiveMQConnectionFactory;
13+
import org.junit.Test;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
public class ActiveMQContainerTest {
18+
19+
@Test
20+
public void test() throws JMSException {
21+
try ( // container {
22+
ActiveMQContainer activemq = new ActiveMQContainer("apache/activemq-classic:5.18.3")
23+
// }
24+
) {
25+
activemq.start();
26+
27+
assertThat(activemq.getUser()).isNull();
28+
assertThat(activemq.getPassword()).isNull();
29+
assertFunctionality(activemq, false);
30+
}
31+
}
32+
33+
@Test
34+
public void customCredentials() {
35+
try (
36+
// settingCredentials {
37+
ActiveMQContainer activemq = new ActiveMQContainer("apache/activemq-classic:5.18.3")
38+
.withUser("testcontainers")
39+
.withPassword("testcontainers")
40+
// }
41+
) {
42+
activemq.start();
43+
44+
assertThat(activemq.getUser()).isEqualTo("testcontainers");
45+
assertThat(activemq.getPassword()).isEqualTo("testcontainers");
46+
assertFunctionality(activemq, true);
47+
}
48+
}
49+
50+
@SneakyThrows
51+
private void assertFunctionality(ActiveMQContainer activemq, boolean useCredentials) {
52+
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(activemq.getBrokerUrl());
53+
Connection connection;
54+
if (useCredentials) {
55+
connection = connectionFactory.createConnection(activemq.getUser(), activemq.getPassword());
56+
} else {
57+
connection = connectionFactory.createConnection();
58+
}
59+
connection.start();
60+
61+
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
62+
63+
Destination destination = session.createQueue("test-queue");
64+
MessageProducer producer = session.createProducer(destination);
65+
66+
String contentMessage = "Testcontainers";
67+
TextMessage message = session.createTextMessage(contentMessage);
68+
producer.send(message);
69+
70+
MessageConsumer consumer = session.createConsumer(destination);
71+
TextMessage messageReceived = (TextMessage) consumer.receive();
72+
assertThat(messageReceived.getText()).isEqualTo(contentMessage);
73+
}
74+
}

0 commit comments

Comments
 (0)