diff --git a/src/main/java/de/rpr/testcontainers/infinispan/ClusteredInfinispanContainer.java b/src/main/java/de/rpr/testcontainers/infinispan/ClusteredInfinispanContainer.java new file mode 100644 index 0000000..705d8b9 --- /dev/null +++ b/src/main/java/de/rpr/testcontainers/infinispan/ClusteredInfinispanContainer.java @@ -0,0 +1,57 @@ +package de.rpr.testcontainers.infinispan; + +import de.rpr.testcontainers.infinispan.transport.NoTopologyStateTransferTransportFactory; +import org.infinispan.client.hotrod.impl.transport.TransportFactory; +import org.testcontainers.containers.BindMode; + +import java.util.Optional; + +/** + * An implementation of the {@link org.testcontainers.containers.GenericContainer} class that can be + * used to easily instantiate an Infinispan server for integration tests. + */ +@SuppressWarnings("ALL") +public class ClusteredInfinispanContainer extends InfinispanContainer { + + private static final String IMAGE_NAME = "jboss/infinispan-server"; + + /** + * Construct an instance using the latest Infinispan image version. + */ + public ClusteredInfinispanContainer() { + this(IMAGE_NAME + ":latest"); + } + + /** + * Construct an instance using the specifice image name. + * + * @param imageName The image name, must contain a version reference + */ + public ClusteredInfinispanContainer(final String imageName) { + super(imageName); + } + + /** + * Use a custom {@link TransportFactory} to disable the Hotrod Topology-State-Transfer. + * + * @return + */ + @Override + protected Optional> getTransportFactory() { + return Optional.of(NoTopologyStateTransferTransportFactory.class); + } + + /** + * Links a configuration file for a clustered Infinispan server into the container. The configuration file format needs to match the server version. + * + * @param filenameFromClasspath The filename containing the standalone configuration. + * @return The container itself + */ + public ClusteredInfinispanContainer withClusteredConfiguration(final String filenameFromClasspath) { + return (ClusteredInfinispanContainer) withClasspathResourceMapping( + filenameFromClasspath, + "/opt/jboss/infinispan-server/standalone/configuration/clustered.xml", + BindMode.READ_ONLY); + } + +} diff --git a/src/main/java/de/rpr/testcontainers/infinispan/InfinispanContainer.java b/src/main/java/de/rpr/testcontainers/infinispan/InfinispanContainer.java index c443dd5..9de7381 100644 --- a/src/main/java/de/rpr/testcontainers/infinispan/InfinispanContainer.java +++ b/src/main/java/de/rpr/testcontainers/infinispan/InfinispanContainer.java @@ -3,41 +3,19 @@ package de.rpr.testcontainers.infinispan; import com.github.dockerjava.api.command.InspectContainerResponse; import org.infinispan.client.hotrod.ProtocolVersion; import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.configuration.Configuration; import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; import org.infinispan.client.hotrod.exceptions.HotRodClientException; +import org.infinispan.client.hotrod.impl.transport.TransportFactory; import org.junit.runner.Description; -import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.Wait; import java.util.*; import java.util.stream.IntStream; -/** - * An implementation of the {@link org.testcontainers.containers.GenericContainer} class that can be - * used to easily instantiate an Infinispan server for integration tests. - * - * @param - */ @SuppressWarnings("ALL") -public class InfinispanContainer extends GenericContainer { - - private static final String IMAGE_NAME = "jboss/infinispan-server"; - public static final String STANDALONE_MODE_CMD = "standalone"; - private final String infinispanServerVersion; - - /* - * An enumeration of the endpoints provided by Infinispan, that this container provides access to. - */ - private enum InfinispanEndpoints { - HOTROD(11222); - - private final int protocolPort; - - private InfinispanEndpoints(final int port) { - this.protocolPort = port; - } - } +public abstract class InfinispanContainer> extends GenericContainer { private static final Set incompatibleProtocolVersions = new HashSet<>(); @@ -48,79 +26,98 @@ public class InfinispanContainer extends GenericContainer { incompatibleProtocolVersions.add(ProtocolVersion.PROTOCOL_VERSION_13); } - private ProtocolVersion protocolVersion; - private Collection cacheNames = new ArrayList<>(); - - private RemoteCacheManager cacheManager; - - /** - * Construct an instance using the latest Infinispan image version. + /* + * An enumeration of the endpoints provided by Infinispan, that this container provides access to. */ - public InfinispanContainer() { - this(IMAGE_NAME + ":latest"); + protected enum InfinispanEndpoints { + HOTROD(11222); + + private final int protocolPort; + + InfinispanEndpoints(final int port) { + this.protocolPort = port; + } + + public int getProtocolPort() { + return protocolPort; + } + + } + + protected RemoteCacheManager cacheManager; + + private final String infinispanServerVersion; + private final Collection cacheNames = new ArrayList<>(); + private ProtocolVersion protocolVersion; + + private boolean isProtocolConflict() { + return incompatibleProtocolVersions.contains(getProtocolVersion()); + } + + private boolean isMajorVersionConflict() { + return IntStream.range(1, 8) + .anyMatch(majorVersion -> getInfinispanServerVersion().startsWith(Integer.toString(majorVersion))); + } + + private boolean isMinorVersionConflict() { + boolean minorVersionConflict = false; + if (getInfinispanServerVersion().startsWith("9.0")) { + minorVersionConflict = true; + } + return minorVersionConflict; } - /** - * Construct n instance using the specifice image name. - * - * @param imageName The image name, must contain a version reference - */ public InfinispanContainer(final String imageName) { super(imageName); this.infinispanServerVersion = imageName.split(":")[1]; - this.withCommand(STANDALONE_MODE_CMD); - withExposedPorts(Arrays.stream(InfinispanEndpoints.values()).map(endpoint -> endpoint.protocolPort).toArray(Integer[]::new)); + withExposedPorts(Arrays.stream(InfinispanEndpoints.values()) + .map(endpoint -> endpoint.getProtocolPort()).toArray(Integer[]::new)); this.waitStrategy = Wait.forListeningPort(); } - /** - * Overloading, because we want to make sure that the "standalone" command is always present. - *

- * The {@link org.testcontainers.containers.GenericContainer#setCommand} method splits on empty string. - * In order to avoid dependency of that behaviour, we set the cmd first, then getting the commandParts - * and ensuring that it contains the "standalone" command. - *

- * - * @param cmd The command(s) to set. {@link org.testcontainers.containers.GenericContainer#setCommand} - * @return The container instance - */ - @Override - public InfinispanContainer withCommand(String cmd) { - super.setCommand(cmd); - this.withCommand(ensureStandaloneCommand(getCommandParts())); - return self(); - } - - /** - * Overloading, because we want to make sure that the "standalone" command is always present. - * - * @param cmd - * @return - */ - @Override - public InfinispanContainer withCommand(String... commandParts) { - this.setCommand(ensureStandaloneCommand(commandParts)); - return self(); - } - - private String[] ensureStandaloneCommand(final String[] commandParts) { - List commands = Arrays.asList(commandParts); - if (commands.contains(STANDALONE_MODE_CMD)) { - return commands.toArray(new String[0]); - } else { - commands.add(STANDALONE_MODE_CMD); - return commands.toArray(new String[0]); - } - } public InfinispanContainer withProtocolVersion(final ProtocolVersion protocolVersion) { this.protocolVersion = protocolVersion; return this; } + @Override + protected void finished(final Description description) { + if (cacheManager != null) { + cacheManager.stop(); + } + super.finished(description); + } + + /** + * Retrieve the Hotrod endpoint address used to connect to the Infinispan instance inside the container. + * + * @return A String of the format [ipaddress]:[port] + */ + public String getHotrodEndpointConnectionString() { + return getContainerIpAddress() + ":" + getMappedPort(StandaloneInfinispanContainer.InfinispanEndpoints.HOTROD.protocolPort); + } + + /** + * Retrieve a preconfigured {@link org.infinispan.client.hotrod.RemoteCacheManager}. + * + * @return A cacheManager + */ + public RemoteCacheManager getCacheManager() { + return cacheManager; + } + + protected ProtocolVersion getProtocolVersion() { + return protocolVersion != null ? protocolVersion : ProtocolVersion.PROTOCOL_VERSION_26; + } + + protected String getInfinispanServerVersion() { + return infinispanServerVersion; + } + /** * Defines caches that should be created after the container has started. * @@ -147,88 +144,45 @@ public class InfinispanContainer extends GenericContainer { throw new IllegalStateException("Programmatic cache creation only works with InfinispanServer version >= 9.1.0!"); } - this.cacheNames = cacheNames; + this.cacheNames.clear(); + this.cacheNames.addAll(cacheNames); return this; } - /** - * Links a configuration file for a standalone Infinispan server into the container. The configuration file format needs to match the server version. - * - * @param filenameFromClasspath The filename containing the standalone configuration. - * @return The container itself - */ - public InfinispanContainer withStandaloneConfiguration(final String filenameFromClasspath) { - return withClasspathResourceMapping( - filenameFromClasspath, - "/opt/jboss/infinispan-server/standalone/configuration/standalone.xml", - BindMode.READ_ONLY); - } - - @Override protected void containerIsStarted(final InspectContainerResponse containerInfo) { - cacheManager = new RemoteCacheManager(new ConfigurationBuilder() - .addServers(getHotrodEndpointConnectionString()) - .version(getProtocolVersion()) - .build()); + cacheManager = new RemoteCacheManager(getCacheManagerConfiguration()); if (cacheManager == null) { throw new IllegalStateException("Couldn't instantiate cacheManager"); } this.cacheNames.forEach(this::createCache); } + private Configuration getCacheManagerConfiguration() { + ConfigurationBuilder configBuilder = new ConfigurationBuilder() + .addServers(getHotrodEndpointConnectionString()) + .version(getProtocolVersion()); + getTransportFactory().ifPresent(transportFactory -> { + configBuilder.transportFactory(transportFactory); + }); + return configBuilder.build(); + } + private void createCache(final String cacheName) { try { - cacheManager.administration().createCache(cacheName, null); + getCacheManager().administration().createCache(cacheName, null); } catch (HotRodClientException e) { logger().error("Couldn't create cache '{}'", cacheName, e); } } - @Override - protected void finished(final Description description) { - if (cacheManager != null) { - cacheManager.stop(); - } - super.finished(description); - } - - private ProtocolVersion getProtocolVersion() { - return protocolVersion != null ? protocolVersion : ProtocolVersion.PROTOCOL_VERSION_26; - } - /** - * Retrieve the Hotrod endpoint address used to connect to the Infinispan instance inside the container. + * Overload this method to use a custom {@link TransportFactory}. * - * @return A String of the format [ipaddress]:[port] + * @return */ - public String getHotrodEndpointConnectionString() { - return getContainerIpAddress() + ":" + getMappedPort(InfinispanEndpoints.HOTROD.protocolPort); + protected Optional> getTransportFactory() { + return Optional.empty(); } - /** - * Retrieve a preconfigured {@link org.infinispan.client.hotrod.RemoteCacheManager}. - * - * @return A cacheManager - */ - public RemoteCacheManager getCacheManager() { - return cacheManager; - } - - private boolean isProtocolConflict() { - return incompatibleProtocolVersions.contains(protocolVersion); - } - - private boolean isMajorVersionConflict() { - return IntStream.range(1, 8) - .anyMatch(majorVersion -> infinispanServerVersion.startsWith(Integer.toString(majorVersion))); - } - - private boolean isMinorVersionConflict() { - boolean minorVersionConflict = false; - if (infinispanServerVersion.startsWith("9.0")) { - minorVersionConflict = true; - } - return minorVersionConflict; - } } diff --git a/src/main/java/de/rpr/testcontainers/infinispan/StandaloneInfinispanContainer.java b/src/main/java/de/rpr/testcontainers/infinispan/StandaloneInfinispanContainer.java new file mode 100644 index 0000000..58f9dc3 --- /dev/null +++ b/src/main/java/de/rpr/testcontainers/infinispan/StandaloneInfinispanContainer.java @@ -0,0 +1,96 @@ +package de.rpr.testcontainers.infinispan; + +import com.github.dockerjava.api.command.InspectContainerResponse; +import org.infinispan.client.hotrod.ProtocolVersion; +import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; +import org.infinispan.client.hotrod.exceptions.HotRodClientException; +import org.testcontainers.containers.BindMode; + +import java.util.*; +import java.util.stream.IntStream; + +/** + * An implementation of the {@link org.testcontainers.containers.GenericContainer} class that can be + * used to easily instantiate an Infinispan server for integration tests. + * + * @param + */ +@SuppressWarnings("ALL") +public class StandaloneInfinispanContainer extends InfinispanContainer { + + private static final String IMAGE_NAME = "jboss/infinispan-server"; + private static final String STANDALONE_MODE_CMD = "standalone"; + + /** + * Construct an instance using the latest Infinispan image version. + */ + public StandaloneInfinispanContainer() { + this(IMAGE_NAME + ":latest"); + } + + /** + * Construct n instance using the specifice image name. + * + * @param imageName The image name, must contain a version reference + */ + public StandaloneInfinispanContainer(final String imageName) { + super(imageName); + + this.withCommand(STANDALONE_MODE_CMD); + } + + /** + * Overloading, because we want to make sure that the "standalone" command is always present. + *

+ * The {@link org.testcontainers.containers.GenericContainer#setCommand} method splits on empty string. + * In order to avoid dependency of that behaviour, we set the cmd first, then getting the commandParts + * and ensuring that it contains the "standalone" command. + *

+ * + * @param cmd The command(s) to set. {@link org.testcontainers.containers.GenericContainer#setCommand} + * @return The container instance + */ + @Override + public StandaloneInfinispanContainer withCommand(String cmd) { + super.setCommand(cmd); + this.withCommand(ensureStandaloneCommand(getCommandParts())); + return this; + } + + /** + * Overloading, because we want to make sure that the "standalone" command is always present. + * + * @param cmd + * @return + */ + @Override + public StandaloneInfinispanContainer withCommand(String... commandParts) { + this.setCommand(ensureStandaloneCommand(commandParts)); + return this; + } + + private String[] ensureStandaloneCommand(final String[] commandParts) { + List commands = Arrays.asList(commandParts); + if (commands.contains(STANDALONE_MODE_CMD)) { + return commands.toArray(new String[0]); + } else { + commands.add(STANDALONE_MODE_CMD); + return commands.toArray(new String[0]); + } + } + + /** + * Links a configuration file for a standalone Infinispan server into the container. The configuration file format needs to match the server version. + * + * @param filenameFromClasspath The filename containing the standalone configuration. + * @return The container itself + */ + public StandaloneInfinispanContainer withStandaloneConfiguration(final String filenameFromClasspath) { + return (StandaloneInfinispanContainer) withClasspathResourceMapping( + filenameFromClasspath, + "/opt/jboss/infinispan-server/standalone/configuration/standalone.xml", + BindMode.READ_ONLY); + } + +} diff --git a/src/main/java/de/rpr/testcontainers/infinispan/transport/NoTopologyStateTransferTransportFactory.java b/src/main/java/de/rpr/testcontainers/infinispan/transport/NoTopologyStateTransferTransportFactory.java new file mode 100644 index 0000000..22affdb --- /dev/null +++ b/src/main/java/de/rpr/testcontainers/infinispan/transport/NoTopologyStateTransferTransportFactory.java @@ -0,0 +1,18 @@ +package de.rpr.testcontainers.infinispan.transport; + +import org.infinispan.client.hotrod.impl.transport.tcp.TcpTransportFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.SocketAddress; +import java.util.Collection; + +public class NoTopologyStateTransferTransportFactory extends TcpTransportFactory { + + private static final Logger LOG = LoggerFactory.getLogger(NoTopologyStateTransferTransportFactory.class); + + @Override + public void updateServers(final Collection newServers, final byte[] cacheName, final boolean quiet) { + LOG.info("Receiving new Servers: {}. Ignoring...", newServers); + } +} diff --git a/src/test/java/GenericInfinispanContainerIntegrationTest.java b/src/test/java/GenericInfinispanContainerIntegrationTest.java index 024e51d..056d802 100644 --- a/src/test/java/GenericInfinispanContainerIntegrationTest.java +++ b/src/test/java/GenericInfinispanContainerIntegrationTest.java @@ -10,7 +10,10 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.LogMessageWaitStrategy; import java.time.Duration; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertNotNull; diff --git a/src/test/java/Infinispan90xContainerIntegrationTest.java b/src/test/java/Infinispan90xContainerIntegrationTest.java index 20560c3..6e0c440 100644 --- a/src/test/java/Infinispan90xContainerIntegrationTest.java +++ b/src/test/java/Infinispan90xContainerIntegrationTest.java @@ -1,4 +1,5 @@ import de.rpr.testcontainers.infinispan.InfinispanContainer; +import de.rpr.testcontainers.infinispan.StandaloneInfinispanContainer; import org.infinispan.client.hotrod.ProtocolVersion; import org.junit.ClassRule; import org.junit.Test; @@ -8,9 +9,9 @@ import static org.junit.Assert.assertNotNull; public class Infinispan90xContainerIntegrationTest { @ClassRule - public static InfinispanContainer infinispan = new InfinispanContainer("jboss/infinispan-server:9.0.3.Final") - .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_26) - .withStandaloneConfiguration("infinispan-90x-standalone.xml"); + public static InfinispanContainer infinispan = new StandaloneInfinispanContainer("jboss/infinispan-server:9.0.3.Final") + .withStandaloneConfiguration("infinispan-90x-standalone.xml") + .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_26); @Test public void rule_should_have_mapped_hotrod_port() { diff --git a/src/test/java/Infinispan91xClusteredContainerIntegrationTest.java b/src/test/java/Infinispan91xClusteredContainerIntegrationTest.java new file mode 100644 index 0000000..574255c --- /dev/null +++ b/src/test/java/Infinispan91xClusteredContainerIntegrationTest.java @@ -0,0 +1,26 @@ +import de.rpr.testcontainers.infinispan.ClusteredInfinispanContainer; +import de.rpr.testcontainers.infinispan.InfinispanContainer; +import org.infinispan.client.hotrod.ProtocolVersion; +import org.junit.ClassRule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class Infinispan91xClusteredContainerIntegrationTest { + + @ClassRule + public static InfinispanContainer infinispan = + new ClusteredInfinispanContainer("jboss/infinispan-server:9.1.4.Final") + .withCaches("testCache") + .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_20); + + @Test + public void rule_should_have_mapped_hotrod_port() { + assertNotNull(infinispan.getMappedPort(11222)); + } + + @Test + public void should_get_existing_cache() { + assertNotNull(infinispan.getCacheManager().getCache("testCache")); + } +} diff --git a/src/test/java/Infinispan91xClusteredContainerXmlConfigIntegrationTest.java b/src/test/java/Infinispan91xClusteredContainerXmlConfigIntegrationTest.java new file mode 100644 index 0000000..32cdba7 --- /dev/null +++ b/src/test/java/Infinispan91xClusteredContainerXmlConfigIntegrationTest.java @@ -0,0 +1,26 @@ +import de.rpr.testcontainers.infinispan.ClusteredInfinispanContainer; +import de.rpr.testcontainers.infinispan.InfinispanContainer; +import org.infinispan.client.hotrod.ProtocolVersion; +import org.junit.ClassRule; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class Infinispan91xClusteredContainerXmlConfigIntegrationTest { + + @ClassRule + public static InfinispanContainer infinispan = + new ClusteredInfinispanContainer("jboss/infinispan-server:9.1.4.Final") + .withClusteredConfiguration("infinispan-91x-clustered.xml") + .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_20); + + @Test + public void rule_should_have_mapped_hotrod_port() { + assertNotNull(infinispan.getMappedPort(11222)); + } + + @Test + public void should_get_existing_cache() { + assertNotNull(infinispan.getCacheManager().getCache("testCache")); + } +} diff --git a/src/test/java/Infinispan91xContainerIntegrationTest.java b/src/test/java/Infinispan91xContainerIntegrationTest.java index 284804d..b3048ec 100644 --- a/src/test/java/Infinispan91xContainerIntegrationTest.java +++ b/src/test/java/Infinispan91xContainerIntegrationTest.java @@ -1,4 +1,5 @@ import de.rpr.testcontainers.infinispan.InfinispanContainer; +import de.rpr.testcontainers.infinispan.StandaloneInfinispanContainer; import org.infinispan.client.hotrod.ProtocolVersion; import org.junit.ClassRule; import org.junit.Test; @@ -8,9 +9,10 @@ import static org.junit.Assert.assertNotNull; public class Infinispan91xContainerIntegrationTest { @ClassRule - public static InfinispanContainer infinispan = new InfinispanContainer("jboss/infinispan-server:9.1.3.Final") - .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_20) - .withCaches("testCache"); + public static InfinispanContainer infinispan = + new StandaloneInfinispanContainer("jboss/infinispan-server:9.1.4.Final") + .withCaches("testCache") + .withProtocolVersion(ProtocolVersion.PROTOCOL_VERSION_20); @Test public void rule_should_have_mapped_hotrod_port() { diff --git a/src/test/resources/infinispan-91x-clustered.xml b/src/test/resources/infinispan-91x-clustered.xml new file mode 100644 index 0000000..0a8be33 --- /dev/null +++ b/src/test/resources/infinispan-91x-clustered.xml @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + h2 + + sa + sa + + + + + org.h2.jdbcx.JdbcDataSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + ${jgroups.gossip.initial_hosts:} + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file