Skip to content

Commit

Permalink
Add Hazelcast Example (#6117)
Browse files Browse the repository at this point in the history
A simple Hazelcast example using both a single container and a cluster with two nodes.

Fixes #6115
  • Loading branch information
tomazfernandes committed Nov 4, 2022
1 parent a25da3d commit 2b61912
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/examples.md
Expand Up @@ -2,6 +2,7 @@

Examples of different use cases provided by Testcontainers can be found below:

- [Hazelcast](https://github.com/testcontainers/testcontainers-java/tree/main/examples/hazelcast)
- [Kafka Cluster with multiple brokers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/kafka-cluster)
- [Linked containers](https://github.com/testcontainers/testcontainers-java/tree/main/examples/linked-container)
- [Neo4j](https://github.com/testcontainers/testcontainers-java/tree/main/examples/neo4j-container)
Expand Down
14 changes: 14 additions & 0 deletions examples/hazelcast/build.gradle
@@ -0,0 +1,14 @@
plugins {
id 'java'
}

repositories {
mavenCentral()
}

dependencies {
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'com.hazelcast:hazelcast:5.2.0'
testImplementation 'ch.qos.logback:logback-classic:1.3.4'
testImplementation 'org.assertj:assertj-core:3.23.1'
}
@@ -0,0 +1,125 @@
package org.testcontainers.examples;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.client.config.ClientConfig;
import com.hazelcast.core.HazelcastInstance;
import org.junit.After;
import org.junit.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.lifecycle.Startables;
import org.testcontainers.utility.DockerImageName;

import java.util.concurrent.BlockingQueue;

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

/**
* Examples with Hazelcast using both a single container and a cluster with two containers.
*/
public class HazelcastTest {

// Hazelcast values
private static final String HZ_IMAGE_NAME = "hazelcast/hazelcast:5.2.0";

private static final String HZ_CLUSTERNAME_ENV_NAME = "HZ_CLUSTERNAME";

private static final String HZ_NETWORK_JOIN_AZURE_ENABLED_ENV_NAME = "HZ_NETWORK_JOIN_AZURE_ENABLED";

private static final String HZ_NETWORK_JOIN_MULTICAST_ENABLED_ENV_NAME = "HZ_NETWORK_JOIN_MULTICAST_ENABLED";

private static final int DEFAULT_EXPOSED_PORT = 5701;

// Test values
private static final String CLUSTER_STARTUP_LOG_MESSAGE_REGEX = ".*Members \\{size:2.*";

private static final String HOST_PORT_SEPARATOR = ":";

private static final String TEST_QUEUE_NAME = "test-queue";

private static final String TEST_CLUSTER_NAME = "test-cluster";

private static final String TEST_VALUE = "Hello!";

private static final String FALSE_VALUE = "false";

private static final String TRUE_VALUE = "true";

@After
public void cleanUp() {
HazelcastClient.shutdownAll();
}

@Test
public void singleHazelcastContainer() {
try (
GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse(HZ_IMAGE_NAME))
.withExposedPorts(DEFAULT_EXPOSED_PORT)
) {
container.start();
assertThat(container.isRunning()).isTrue();

ClientConfig clientConfig = new ClientConfig();
clientConfig
.getNetworkConfig()
.addAddress(container.getHost() + HOST_PORT_SEPARATOR + container.getFirstMappedPort());
HazelcastInstance client = HazelcastClient.newHazelcastClient(clientConfig);

BlockingQueue<String> queue = client.getQueue(TEST_QUEUE_NAME);
queue.put(TEST_VALUE);
assertThat(queue.take()).isEqualTo(TEST_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted during singleHazelcastContainer test", e);
}
}

@Test
public void hazelcastCluster() {
Network network = Network.newNetwork();
try (
GenericContainer<?> container1 = new GenericContainer<>(DockerImageName.parse(HZ_IMAGE_NAME))
.withExposedPorts(DEFAULT_EXPOSED_PORT)
.withEnv(HZ_CLUSTERNAME_ENV_NAME, TEST_CLUSTER_NAME)
// Flags necessary to run on Github Actions which runs on an Azure VM
.withEnv(HZ_NETWORK_JOIN_AZURE_ENABLED_ENV_NAME, FALSE_VALUE)
.withEnv(HZ_NETWORK_JOIN_MULTICAST_ENABLED_ENV_NAME, TRUE_VALUE)
.waitingFor(Wait.forLogMessage(CLUSTER_STARTUP_LOG_MESSAGE_REGEX, 1))
.withNetwork(network);
GenericContainer<?> container2 = new GenericContainer<>(DockerImageName.parse(HZ_IMAGE_NAME))
.withExposedPorts(DEFAULT_EXPOSED_PORT)
.withEnv(HZ_CLUSTERNAME_ENV_NAME, TEST_CLUSTER_NAME)
// Flags necessary to run on Github Actions which runs on an Azure VM
.withEnv(HZ_NETWORK_JOIN_AZURE_ENABLED_ENV_NAME, FALSE_VALUE)
.withEnv(HZ_NETWORK_JOIN_MULTICAST_ENABLED_ENV_NAME, TRUE_VALUE)
.waitingFor(Wait.forLogMessage(CLUSTER_STARTUP_LOG_MESSAGE_REGEX, 1))
.withNetwork(network)
) {
Startables.deepStart(container1, container2).join();
assertThat(container1.isRunning()).isTrue();
assertThat(container2.isRunning()).isTrue();

ClientConfig clientConfig = new ClientConfig();
clientConfig
.setClusterName(TEST_CLUSTER_NAME)
.getNetworkConfig()
// Uncomment the next line to remove the "WARNING: ...Could not connect to member..." message
//.setSmartRouting(false)
.addAddress(container1.getHost() + HOST_PORT_SEPARATOR + container1.getFirstMappedPort())
.addAddress(container2.getHost() + HOST_PORT_SEPARATOR + container2.getFirstMappedPort());

HazelcastInstance client = HazelcastClient.newHazelcastClient(clientConfig);

assertThat(client.getCluster().getMembers()).hasSize(2);

BlockingQueue<String> queue = client.getQueue(TEST_QUEUE_NAME);
queue.put(TEST_VALUE);

assertThat(queue.take()).isEqualTo(TEST_VALUE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted during hazelcastCluster test", e);
}
}
}
14 changes: 14 additions & 0 deletions examples/hazelcast/src/test/resources/logback-test.xml
@@ -0,0 +1,14 @@
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level %logger - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
1 change: 1 addition & 0 deletions examples/settings.gradle
Expand Up @@ -32,6 +32,7 @@ include 'cucumber'
include 'spring-boot-kotlin-redis'
include 'immudb'
include 'zookeeper'
include 'hazelcast'

ext.isCI = System.getenv("CI") != null

Expand Down

0 comments on commit 2b61912

Please sign in to comment.