diff --git a/modules/cassandra/build.gradle b/modules/cassandra/build.gradle index 79492c044db..519f0c3d7e7 100644 --- a/modules/cassandra/build.gradle +++ b/modules/cassandra/build.gradle @@ -8,7 +8,7 @@ configurations.all { dependencies { api project(":database-commons") - api "com.datastax.cassandra:cassandra-driver-core:3.10.0" + api "com.datastax.oss:java-driver-core:4.17.0" testImplementation 'com.datastax.oss:java-driver-core:4.17.0' testImplementation 'org.assertj:assertj-core:3.25.1' diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java index bb291cd1f80..0cb31baf509 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/CassandraContainer.java @@ -1,6 +1,11 @@ package org.testcontainers.containers; -import com.datastax.driver.core.Cluster; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.CqlSessionBuilder; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.config.DriverConfigLoader; +import com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder; +import com.datastax.oss.driver.internal.core.loadbalancing.DcInferringLoadBalancingPolicy; import com.github.dockerjava.api.command.InspectContainerResponse; import org.apache.commons.io.IOUtils; import org.testcontainers.containers.delegate.CassandraDatabaseDelegate; @@ -14,6 +19,7 @@ import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.Optional; import javax.script.ScriptException; @@ -48,8 +54,6 @@ public class CassandraContainer> extends G private String initScriptPath; - private boolean enableJmxReporting; - /** * @deprecated use {@link #CassandraContainer(DockerImageName)} instead */ @@ -67,7 +71,6 @@ public CassandraContainer(DockerImageName dockerImageName) { dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); addExposedPort(CQL_PORT); - this.enableJmxReporting = false; withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch"); withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0"); @@ -156,14 +159,6 @@ public SELF withInitScript(String initScriptPath) { return self(); } - /** - * Initialize Cassandra client with JMX reporting enabled or disabled - */ - public SELF withJmxReporting(boolean enableJmxReporting) { - this.enableJmxReporting = enableJmxReporting; - return self(); - } - /** * Get username * @@ -189,37 +184,50 @@ public String getPassword() { } /** - * Get configured Cluster - * - * Can be used to obtain connections to Cassandra in the container + * Get a session on the configured cluster. * - * @deprecated For Cassandra driver 3.x, use {@link #getHost()} and {@link #getMappedPort(int)} with - * the driver's {@link Cluster#builder() Cluster.Builder} {@code addContactPoint(String)} and - * {@code withPort(int)} methods to create a Cluster object. For Cassandra driver 4.x, use - * {@link #getContactPoint()} and {@link #getLocalDatacenter()} with the driver's {@code CqlSession.builder()} - * {@code addContactPoint(InetSocketAddress)} and {@code withLocalDatacenter(String)} methods to create - * a Session Object. See https://docs.datastax.com/en/developer/java-driver/ for more on the driver. + * Can be used to obtain connections to Cassandra in the container. */ - @Deprecated - public Cluster getCluster() { - return getCluster(this, enableJmxReporting); - } + public CqlSession getCqlSession() { + return getCqlSession(this); + } + + public static CqlSession getCqlSession(ContainerState containerState) { + final ProgrammaticDriverConfigLoaderBuilder driverConfigLoaderBuilder = DriverConfigLoader.programmaticBuilder(); + boolean dcAvailable = containerState.getClass().isAssignableFrom(CassandraContainer.class); + + // If the ContainerState is not a CassandraContainer instance, use DcInferringLoadBalancingPolicy to not have + // to specify the local datacenter to establish the connection, otherwise we can keep the default load balancing + // policy requiring to explicitly specify the local data center. + // See https://docs.datastax.com/en/developer/java-driver/latest/manual/core/configuration/reference/ for + // further information. + if (!dcAvailable) { + driverConfigLoaderBuilder.withClass( + DefaultDriverOption.LOAD_BALANCING_POLICY_CLASS, + DcInferringLoadBalancingPolicy.class + ); + } - @Deprecated - public static Cluster getCluster(ContainerState containerState, boolean enableJmxReporting) { - final Cluster.Builder builder = Cluster + // Ignore warnings when a CQL script modifies the current keyspace. Typically, this generates unnecessary logs + // when executing an init script using multiple keyspaces. + driverConfigLoaderBuilder.withBoolean(DefaultDriverOption.REQUEST_WARN_IF_SET_KEYSPACE, false); + + // Using Java driver 4.x, a feature called debouncing has been introduced: schema and topology changes received + // from the server could be accumulated before being processed by the driver. For more information, see: + // https://docs.datastax.com/en/developer/java-driver/latest/manual/core/performance/index.html#debouncing + // and https://stackoverflow.com/a/74152732/13292108 + // To maintain good performance, reduce the default values for the schema debouncing properties. + driverConfigLoaderBuilder.withInt(DefaultDriverOption.METADATA_SCHEMA_MAX_EVENTS, 1); + driverConfigLoaderBuilder.withDuration(DefaultDriverOption.METADATA_SCHEMA_WINDOW, Duration.ofMillis(1)); + + final CqlSessionBuilder cqlSessionBuilder = CqlSession .builder() - .addContactPoint(containerState.getHost()) - .withPort(containerState.getMappedPort(CQL_PORT)); - if (!enableJmxReporting) { - builder.withoutJMXReporting(); + .withConfigLoader(driverConfigLoaderBuilder.build()) + .addContactPoint(new InetSocketAddress(containerState.getHost(), containerState.getMappedPort(CQL_PORT))); + if (dcAvailable) { + cqlSessionBuilder.withLocalDatacenter(((CassandraContainer) containerState).getLocalDatacenter()); } - return builder.build(); - } - - @Deprecated - public static Cluster getCluster(ContainerState containerState) { - return getCluster(containerState, false); + return cqlSessionBuilder.build(); } /** @@ -240,7 +248,6 @@ public String getLocalDatacenter() { return getEnvMap().getOrDefault("CASSANDRA_DC", DEFAULT_LOCAL_DATACENTER); } - @Deprecated private DatabaseDelegate getDatabaseDelegate() { return new CassandraDatabaseDelegate(this); } diff --git a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java index 8d604822083..e4d49c93bb7 100644 --- a/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java +++ b/modules/cassandra/src/main/java/org/testcontainers/containers/delegate/CassandraDatabaseDelegate.java @@ -1,8 +1,8 @@ package org.testcontainers.containers.delegate; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.DriverException; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.DriverException; +import com.datastax.oss.driver.api.core.cql.ResultSet; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.CassandraContainer; @@ -16,14 +16,14 @@ */ @Slf4j @RequiredArgsConstructor -public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate { +public class CassandraDatabaseDelegate extends AbstractDatabaseDelegate { private final ContainerState container; @Override - protected Session createNewConnection() { + protected CqlSession createNewConnection() { try { - return CassandraContainer.getCluster(container).newSession(); + return CassandraContainer.getCqlSession(container); } catch (DriverException e) { log.error("Could not obtain cassandra connection"); throw new ConnectionCreationException("Could not obtain cassandra connection", e); @@ -51,9 +51,9 @@ public void execute( } @Override - protected void closeConnectionQuietly(Session session) { + protected void closeConnectionQuietly(CqlSession session) { try { - session.getCluster().close(); + session.close(); } catch (Exception e) { log.error("Could not close cassandra connection", e); } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java index eb3a8b068ea..ef2469d383d 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraContainerTest.java @@ -1,14 +1,15 @@ package org.testcontainers.containers; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.ResultSet; +import com.datastax.oss.driver.api.core.cql.Row; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.testcontainers.containers.wait.CassandraQueryWaitStrategy; import org.testcontainers.utility.DockerImageName; +import java.net.InetSocketAddress; + import static org.assertj.core.api.Assertions.assertThat; @Slf4j @@ -112,7 +113,7 @@ public void testCassandraQueryWaitStrategy() { public void testCassandraGetCluster() { try (CassandraContainer cassandraContainer = new CassandraContainer<>()) { cassandraContainer.start(); - ResultSet resultSet = performQuery(cassandraContainer.getCluster(), BASIC_QUERY); + ResultSet resultSet = performQuery(cassandraContainer.getCqlSession(), BASIC_QUERY); assertThat(resultSet.wasApplied()).as("Query was applied").isTrue(); assertThat(resultSet.one().getString(0)).as("Result set has release_version").isNotNull(); } @@ -127,18 +128,22 @@ private void testInitScript(CassandraContainer cassandraContainer) { } private ResultSet performQuery(CassandraContainer cassandraContainer, String cql) { - Cluster explicitCluster = Cluster + final CqlSession cqlSession = CqlSession .builder() - .addContactPoint(cassandraContainer.getHost()) - .withPort(cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT)) + .addContactPoint( + new InetSocketAddress( + cassandraContainer.getHost(), + cassandraContainer.getMappedPort(CassandraContainer.CQL_PORT) + ) + ) + .withLocalDatacenter(cassandraContainer.getLocalDatacenter()) .build(); - return performQuery(explicitCluster, cql); + return performQuery(cqlSession, cql); } - private ResultSet performQuery(Cluster cluster, String cql) { - try (Cluster closeableCluster = cluster) { - Session session = closeableCluster.newSession(); - return session.execute(cql); - } + private ResultSet performQuery(CqlSession session, String cql) { + final ResultSet rs = session.execute(cql); + session.close(); + return rs; } } diff --git a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraServer5Test.java similarity index 85% rename from modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java rename to modules/cassandra/src/test/java/org/testcontainers/containers/CassandraServer5Test.java index 502f2ca8e67..70dd006e8e9 100644 --- a/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraDriver3Test.java +++ b/modules/cassandra/src/test/java/org/testcontainers/containers/CassandraServer5Test.java @@ -8,21 +8,19 @@ import static org.assertj.core.api.Assertions.assertThat; -public class CassandraDriver3Test { +public class CassandraServer5Test { @Rule - public CassandraContainer cassandra = new CassandraContainer<>("cassandra:3.11.2"); + public CassandraContainer cassandra = new CassandraContainer<>("cassandra:5"); @Test public void testCassandraGetContactPoint() { try ( - // cassandra { CqlSession session = CqlSession .builder() .addContactPoint(this.cassandra.getContactPoint()) .withLocalDatacenter(this.cassandra.getLocalDatacenter()) .build() - // } ) { session.execute( "CREATE KEYSPACE IF NOT EXISTS test WITH replication = \n" + @@ -31,7 +29,7 @@ public void testCassandraGetContactPoint() { KeyspaceMetadata keyspace = session.getMetadata().getKeyspaces().get(CqlIdentifier.fromCql("test")); - assertThat(keyspace).as("keyspace created").isNotNull(); + assertThat(keyspace).as("test keyspace created").isNotNull(); } } }