Skip to content

Commit

Permalink
Add support for ActiveMQ and Artemis (#7400)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
eddumelendez committed Jan 23, 2024
1 parent 9de26f1 commit afc94f1
Show file tree
Hide file tree
Showing 14 changed files with 441 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ body:
description: Which Testcontainers module are you using?
options:
- Core
- ActiveMQ
- Azure
- Cassandra
- Clickhouse
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/enhancement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ body:
description: For which Testcontainers module does the enhancement proposal apply?
options:
- Core
- ActiveMQ
- Azure
- Cassandra
- Clickhouse
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ body:
description: Is this feature related to any of the existing modules?
options:
- Core
- ActiveMQ
- Azure
- Cassandra
- Clickhouse
Expand Down
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ updates:
open-pull-requests-limit: 10

# Explicit entry for each module
- package-ecosystem: "gradle"
directory: "/modules/activemq"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
- package-ecosystem: "gradle"
directory: "/modules/azure"
schedule:
Expand Down
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
- gradle/wrapper/*
- gradlew
- gradlew.bat
"modules/activemq":
- changed-files:
- any-glob-to-any-file:
- modules/activemq/**/*
"modules/azure":
- changed-files:
- any-glob-to-any-file:
Expand Down
3 changes: 3 additions & 0 deletions .github/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ labels:
- name: help wanted
color: '#fef2c0'

- name: modules/activemq
color: '#006b75'

- name: modules/azure
color: '#006b75'

Expand Down
57 changes: 57 additions & 0 deletions docs/modules/activemq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ActiveMQ

Testcontainers module for [ActiveMQ](https://hub.docker.com/r/apache/activemq-classic) and
[Artemis](https://hub.docker.com/r/apache/activemq-artemis).

## ActiveMQContainer's usage examples

You can start an ActiveMQ Classic container instance from any Java application by using:

<!--codeinclude-->
[Default ActiveMQ container](../../modules/activemq/src/test/java/org/testcontainers/activemq/ActiveMQContainerTest.java) inside_block:container
<!--/codeinclude-->

With custom credentials:

<!--codeinclude-->
[Setting custom credentials](../../modules/activemq/src/test/java/org/testcontainers/activemq/ActiveMQContainerTest.java) inside_block:settingCredentials
<!--/codeinclude-->

## ArtemisContainer's usage examples

You can start an ActiveMQ Artemis container instance from any Java application by using:

<!--codeinclude-->
[Default Artemis container](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:container
<!--/codeinclude-->

With custom credentials:

<!--codeinclude-->
[Setting custom credentials](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:settingCredentials
<!--/codeinclude-->

With anonymous login:

<!--codeinclude-->
[Allow anonymous login](../../modules/activemq/src/test/java/org/testcontainers/activemq/ArtemisContainerTest.java) inside_block:enableAnonymousLogin
<!--/codeinclude-->

## Adding this module to your project dependencies

Add the following dependency to your `pom.xml`/`build.gradle` file:

=== "Gradle"
```groovy
testImplementation "org.testcontainers:activemq:{{latest_version}}"
```

=== "Maven"
```xml
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>activemq</artifactId>
<version>{{latest_version}}</version>
<scope>test</scope>
</dependency>
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ nav:
- modules/databases/tidb.md
- modules/databases/trino.md
- modules/databases/yugabytedb.md
- modules/activemq.md
- modules/azure.md
- modules/consul.md
- modules/docker_compose.md
Expand Down
22 changes: 22 additions & 0 deletions modules/activemq/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
description = "Testcontainers :: ActiveMQ"

dependencies {
api project(':testcontainers')

testImplementation 'org.assertj:assertj-core:3.24.2'
testImplementation "org.apache.activemq:activemq-client-jakarta:5.18.2"
testImplementation "org.apache.activemq:artemis-jakarta-client:2.29.0"
}

test {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(11)
}
}

compileTestJava {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(11)
}
options.release.set(11)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.testcontainers.activemq;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import java.time.Duration;

/**
* Testcontainers implementation for Apache ActiveMQ.
* <p>
* Exposed ports:
* <ul>
* <li>Console: 8161</li>
* <li>TCP: 61616</li>
* <li>AMQP: 5672</li>
* <li>STOMP: 61613</li>
* <li>MQTT: 1883</li>
* <li>WS: 61614</li>
* </ul>
*/
public class ActiveMQContainer extends GenericContainer<ActiveMQContainer> {

private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("apache/activemq-classic");

private static final int WEB_CONSOLE_PORT = 8161;

private static final int TCP_PORT = 61616;

private static final int AMQP_PORT = 5672;

private static final int STOMP_PORT = 61613;

private static final int MQTT_PORT = 1883;

private static final int WS_PORT = 61614;

private String username;

private String password;

public ActiveMQContainer(String image) {
this(DockerImageName.parse(image));
}

public ActiveMQContainer(DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE);

withExposedPorts(WEB_CONSOLE_PORT, TCP_PORT, AMQP_PORT, STOMP_PORT, MQTT_PORT, WS_PORT);
waitingFor(Wait.forLogMessage(".*Apache ActiveMQ.*started.*", 1).withStartupTimeout(Duration.ofMinutes(1)));
}

@Override
protected void configure() {
if (this.username != null) {
addEnv("ACTIVEMQ_CONNECTION_USER", this.username);
}
if (this.password != null) {
addEnv("ACTIVEMQ_CONNECTION_PASSWORD", this.password);
}
}

public ActiveMQContainer withUser(String username) {
this.username = username;
return this;
}

public ActiveMQContainer withPassword(String password) {
this.password = password;
return this;
}

public String getBrokerUrl() {
return String.format("tcp://%s:%s", getHost(), getMappedPort(TCP_PORT));
}

public String getUser() {
return this.username;
}

public String getPassword() {
return this.password;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.testcontainers.activemq;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import java.time.Duration;

/**
* Testcontainers implementation for Apache ActiveMQ Artemis.
* <p>
* Exposed ports:
* <ul>
* <li>Console: 8161</li>
* <li>TCP: 61616</li>
* <li>HORNETQ: 5445</li>
* <li>AMQP: 5672</li>
* <li>STOMP: 61613</li>
* <li>MQTT: 1883</li>
* <li>WS: 61614</li>
* </ul>
*/
public class ArtemisContainer extends GenericContainer<ArtemisContainer> {

private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("apache/activemq-artemis");

private static final int WEB_CONSOLE_PORT = 8161;

// CORE,MQTT,AMQP,HORNETQ,STOMP,OPENWIRE
private static final int TCP_PORT = 61616;

private static final int HORNETQ_STOMP_PORT = 5445;

private static final int AMQP_PORT = 5672;

private static final int STOMP_PORT = 61613;

private static final int MQTT_PORT = 1883;

private static final int WS_PORT = 61614;

private String username = "artemis";

private String password = "artemis";

public ArtemisContainer(String image) {
this(DockerImageName.parse(image));
}

public ArtemisContainer(DockerImageName dockerImageName) {
super(dockerImageName);
dockerImageName.assertCompatibleWith(DEFAULT_IMAGE);

withExposedPorts(WEB_CONSOLE_PORT, TCP_PORT, HORNETQ_STOMP_PORT, AMQP_PORT, STOMP_PORT, MQTT_PORT, WS_PORT);
waitingFor(Wait.forLogMessage(".*HTTP Server started.*", 1).withStartupTimeout(Duration.ofMinutes(1)));
}

@Override
protected void configure() {
withEnv("ARTEMIS_USER", this.username);
withEnv("ARTEMIS_PASSWORD", this.password);
}

public ArtemisContainer withUser(String username) {
this.username = username;
return this;
}

public ArtemisContainer withPassword(String password) {
this.password = password;
return this;
}

public String getBrokerUrl() {
return String.format("tcp://%s:%s", getHost(), getMappedPort(TCP_PORT));
}

public String getUser() {
return getEnvMap().get("ARTEMIS_USER");
}

public String getPassword() {
return getEnvMap().get("ARTEMIS_PASSWORD");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.testcontainers.activemq;

import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.MessageConsumer;
import jakarta.jms.MessageProducer;
import jakarta.jms.Session;
import jakarta.jms.TextMessage;
import lombok.SneakyThrows;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class ActiveMQContainerTest {

@Test
public void test() throws JMSException {
try ( // container {
ActiveMQContainer activemq = new ActiveMQContainer("apache/activemq-classic:5.18.3")
// }
) {
activemq.start();

assertThat(activemq.getUser()).isNull();
assertThat(activemq.getPassword()).isNull();
assertFunctionality(activemq, false);
}
}

@Test
public void customCredentials() {
try (
// settingCredentials {
ActiveMQContainer activemq = new ActiveMQContainer("apache/activemq-classic:5.18.3")
.withUser("testcontainers")
.withPassword("testcontainers")
// }
) {
activemq.start();

assertThat(activemq.getUser()).isEqualTo("testcontainers");
assertThat(activemq.getPassword()).isEqualTo("testcontainers");
assertFunctionality(activemq, true);
}
}

@SneakyThrows
private void assertFunctionality(ActiveMQContainer activemq, boolean useCredentials) {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(activemq.getBrokerUrl());
Connection connection;
if (useCredentials) {
connection = connectionFactory.createConnection(activemq.getUser(), activemq.getPassword());
} else {
connection = connectionFactory.createConnection();
}
connection.start();

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

Destination destination = session.createQueue("test-queue");
MessageProducer producer = session.createProducer(destination);

String contentMessage = "Testcontainers";
TextMessage message = session.createTextMessage(contentMessage);
producer.send(message);

MessageConsumer consumer = session.createConsumer(destination);
TextMessage messageReceived = (TextMessage) consumer.receive();
assertThat(messageReceived.getText()).isEqualTo(contentMessage);
}
}
Loading

0 comments on commit afc94f1

Please sign in to comment.