Skip to content

Commit 8d44702

Browse files
committed
Remove dependency on Spring Data Cassandra from Actuator components
Motivation: It is possible to use CassandraAutoConfiguration to create CqlSession beans in an application with just a dependency on the DataStax Java driver, without the requirement to also depend on Spring Data Cassandra. However, it is currently not possible to do so if the application also needs to create Actuator components. Indeed, both CassandraHealthIndicator and CassandraReactiveHealthContributorAutoConfiguration have a hard dependency on Spring Data Cassandra and its CassandraOperations bean. Modifications: This commit removes the hard dependency on Spring Data Cassandra from CassandraHealthIndicator and CassandraReactiveHealthContributorAutoConfiguration, and replaces all usages of CassandraOperations with direct access to the underlying CqlSession bean. Result: Users are now able to create Actuator health components in their applications, even if they use just Spring Boot and the DataStax Java driver, without Spring Data Cassandra.
1 parent 1c70b67 commit 8d44702

File tree

11 files changed

+107
-93
lines changed

11 files changed

+107
-93
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232

3333
optional(platform(project(":spring-boot-project:spring-boot-dependencies")))
3434
optional("ch.qos.logback:logback-classic")
35+
optional("com.datastax.oss:java-driver-core")
3536
optional("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
3637
optional("com.github.ben-manes.caffeine:caffeine")
3738
optional("com.hazelcast:hazelcast")
@@ -93,7 +94,6 @@ dependencies {
9394
optional("org.springframework:spring-webflux")
9495
optional("org.springframework:spring-webmvc")
9596
optional("org.springframework.amqp:spring-rabbit")
96-
optional("org.springframework.data:spring-data-cassandra")
9797
optional("org.springframework.data:spring-data-couchbase")
9898
optional("org.springframework.data:spring-data-ldap")
9999
optional("org.springframework.data:spring-data-mongodb")

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraHealthContributorAutoConfiguration.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,30 @@
3030
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3131
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
33-
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
3433
import org.springframework.context.annotation.Bean;
3534
import org.springframework.context.annotation.Configuration;
36-
import org.springframework.data.cassandra.core.CassandraOperations;
3735

3836
/**
3937
* {@link EnableAutoConfiguration Auto-configuration} for
4038
* {@link CassandraHealthIndicator}.
4139
*
4240
* @author Julien Dubois
4341
* @author Stephane Nicoll
42+
* @author Alexandre Dutra
4443
* @since 2.1.0
4544
*/
4645
@Configuration(proxyBeanMethods = false)
47-
@ConditionalOnClass({ CqlSession.class, CassandraOperations.class })
48-
@ConditionalOnBean(CassandraOperations.class)
46+
@ConditionalOnClass(CqlSession.class)
47+
@ConditionalOnBean(CqlSession.class)
4948
@ConditionalOnEnabledHealthIndicator("cassandra")
50-
@AutoConfigureAfter({ CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
51-
CassandraReactiveHealthContributorAutoConfiguration.class })
49+
@AutoConfigureAfter({ CassandraAutoConfiguration.class, CassandraReactiveHealthContributorAutoConfiguration.class })
5250
public class CassandraHealthContributorAutoConfiguration
53-
extends CompositeHealthContributorConfiguration<CassandraHealthIndicator, CassandraOperations> {
51+
extends CompositeHealthContributorConfiguration<CassandraHealthIndicator, CqlSession> {
5452

5553
@Bean
5654
@ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" })
57-
public HealthContributor cassandraHealthContributor(Map<String, CassandraOperations> cassandraOperations) {
58-
return createContributor(cassandraOperations);
55+
public HealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) {
56+
return createContributor(sessions);
5957
}
6058

6159
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraReactiveHealthContributorAutoConfiguration.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,34 @@
2626
import org.springframework.boot.actuate.health.ReactiveHealthContributor;
2727
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2828
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
29+
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
2930
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3031
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3132
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
32-
import org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration;
3333
import org.springframework.context.annotation.Bean;
3434
import org.springframework.context.annotation.Configuration;
35-
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
3635

3736
/**
3837
* {@link EnableAutoConfiguration Auto-configuration} for
3938
* {@link CassandraReactiveHealthIndicator}.
4039
*
4140
* @author Artsiom Yudovin
4241
* @author Stephane Nicoll
42+
* @author Alexandre Dutra
4343
* @since 2.1.0
4444
*/
4545
@Configuration(proxyBeanMethods = false)
46-
@ConditionalOnClass({ CqlSession.class, ReactiveCassandraOperations.class, Flux.class })
47-
@ConditionalOnBean(ReactiveCassandraOperations.class)
46+
@ConditionalOnClass({ CqlSession.class, Flux.class })
47+
@ConditionalOnBean(CqlSession.class)
4848
@ConditionalOnEnabledHealthIndicator("cassandra")
49-
@AutoConfigureAfter(CassandraReactiveDataAutoConfiguration.class)
50-
public class CassandraReactiveHealthContributorAutoConfiguration extends
51-
CompositeReactiveHealthContributorConfiguration<CassandraReactiveHealthIndicator, ReactiveCassandraOperations> {
49+
@AutoConfigureAfter(CassandraAutoConfiguration.class)
50+
public class CassandraReactiveHealthContributorAutoConfiguration
51+
extends CompositeReactiveHealthContributorConfiguration<CassandraReactiveHealthIndicator, CqlSession> {
5252

5353
@Bean
5454
@ConditionalOnMissingBean(name = { "cassandraHealthIndicator", "cassandraHealthContributor" })
55-
public ReactiveHealthContributor cassandraHealthContributor(
56-
Map<String, ReactiveCassandraOperations> reactiveCassandraOperations) {
57-
return createContributor(reactiveCassandraOperations);
55+
public ReactiveHealthContributor cassandraHealthContributor(Map<String, CqlSession> sessions) {
56+
return createContributor(sessions);
5857
}
5958

6059
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraHealthContributorAutoConfigurationTests.java

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.cassandra;
1818

19+
import com.datastax.oss.driver.api.core.CqlSession;
1920
import org.junit.jupiter.api.Test;
2021

2122
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
2223
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
2324
import org.springframework.boot.autoconfigure.AutoConfigurations;
24-
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2525
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
26-
import org.springframework.context.annotation.Bean;
27-
import org.springframework.context.annotation.Configuration;
28-
import org.springframework.data.cassandra.core.CassandraOperations;
2926

3027
import static org.assertj.core.api.Assertions.assertThat;
3128
import static org.mockito.Mockito.mock;
@@ -38,8 +35,8 @@
3835
class CassandraHealthContributorAutoConfigurationTests {
3936

4037
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
41-
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class,
42-
CassandraHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class));
38+
.withBean(CqlSession.class, () -> mock(CqlSession.class)).withConfiguration(AutoConfigurations
39+
.of(CassandraHealthContributorAutoConfiguration.class, HealthContributorAutoConfiguration.class));
4340

4441
@Test
4542
void runShouldCreateIndicator() {
@@ -52,15 +49,4 @@ void runWhenDisabledShouldNotCreateIndicator() {
5249
.run((context) -> assertThat(context).doesNotHaveBean(CassandraHealthIndicator.class));
5350
}
5451

55-
@Configuration(proxyBeanMethods = false)
56-
@AutoConfigureBefore(CassandraHealthContributorAutoConfiguration.class)
57-
static class CassandraConfiguration {
58-
59-
@Bean
60-
CassandraOperations cassandraOperations() {
61-
return mock(CassandraOperations.class);
62-
}
63-
64-
}
65-
6652
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cassandra/CassandraReactiveHealthContributorAutoConfigurationTests.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.cassandra;
1818

19+
import com.datastax.oss.driver.api.core.CqlSession;
1920
import org.junit.jupiter.api.Test;
2021

21-
import org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthContributorAutoConfigurationTests.CassandraConfiguration;
2222
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
2323
import org.springframework.boot.actuate.cassandra.CassandraHealthIndicator;
2424
import org.springframework.boot.actuate.cassandra.CassandraReactiveHealthIndicator;
2525
import org.springframework.boot.autoconfigure.AutoConfigurations;
2626
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
27-
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
2827

2928
import static org.assertj.core.api.Assertions.assertThat;
3029
import static org.mockito.Mockito.mock;
@@ -38,7 +37,7 @@
3837
class CassandraReactiveHealthContributorAutoConfigurationTests {
3938

4039
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
41-
.withBean(ReactiveCassandraOperations.class, () -> mock(ReactiveCassandraOperations.class))
40+
.withBean(CqlSession.class, () -> mock(CqlSession.class))
4241
.withConfiguration(AutoConfigurations.of(CassandraReactiveHealthContributorAutoConfiguration.class,
4342
HealthContributorAutoConfiguration.class));
4443

@@ -50,9 +49,7 @@ void runShouldCreateIndicator() {
5049

5150
@Test
5251
void runWithRegularIndicatorShouldOnlyCreateReactiveIndicator() {
53-
this.contextRunner
54-
.withConfiguration(AutoConfigurations.of(CassandraConfiguration.class,
55-
CassandraHealthContributorAutoConfiguration.class))
52+
this.contextRunner.withConfiguration(AutoConfigurations.of(CassandraHealthContributorAutoConfiguration.class))
5653
.run((context) -> assertThat(context).hasSingleBean(CassandraReactiveHealthIndicator.class)
5754
.hasBean("cassandraHealthContributor").doesNotHaveBean(CassandraHealthIndicator.class));
5855
}

spring-boot-project/spring-boot-actuator/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies {
1414
implementation(project(":spring-boot-project:spring-boot"))
1515

1616
optional(platform(project(":spring-boot-project:spring-boot-dependencies")))
17+
optional("com.datastax.oss:java-driver-core")
1718
optional("com.fasterxml.jackson.core:jackson-databind")
1819
optional("com.github.ben-manes.caffeine:caffeine")
1920
optional("com.hazelcast:hazelcast")
@@ -53,7 +54,6 @@ dependencies {
5354
optional("org.springframework:spring-web")
5455
optional("org.springframework:spring-webmvc")
5556
optional("org.springframework.amqp:spring-rabbit")
56-
optional("org.springframework.data:spring-data-cassandra")
5757
optional("org.springframework.data:spring-data-couchbase")
5858
optional("org.springframework.data:spring-data-ldap")
5959
optional("org.springframework.data:spring-data-mongodb")

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraHealthIndicator.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
package org.springframework.boot.actuate.cassandra;
1818

1919
import com.datastax.oss.driver.api.core.ConsistencyLevel;
20+
import com.datastax.oss.driver.api.core.CqlSession;
21+
import com.datastax.oss.driver.api.core.cql.Row;
2022
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2123

2224
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
2325
import org.springframework.boot.actuate.health.Health;
2426
import org.springframework.boot.actuate.health.HealthIndicator;
25-
import org.springframework.data.cassandra.core.CassandraOperations;
2627
import org.springframework.util.Assert;
2728

2829
/**
@@ -38,25 +39,24 @@ public class CassandraHealthIndicator extends AbstractHealthIndicator {
3839
private static final SimpleStatement SELECT = SimpleStatement
3940
.newInstance("SELECT release_version FROM system.local").setConsistencyLevel(ConsistencyLevel.LOCAL_ONE);
4041

41-
private CassandraOperations cassandraOperations;
42-
43-
public CassandraHealthIndicator() {
44-
super("Cassandra health check failed");
45-
}
42+
private final CqlSession session;
4643

4744
/**
4845
* Create a new {@link CassandraHealthIndicator} instance.
49-
* @param cassandraOperations the Cassandra operations
46+
* @param session the {@link CqlSession}.
5047
*/
51-
public CassandraHealthIndicator(CassandraOperations cassandraOperations) {
48+
public CassandraHealthIndicator(CqlSession session) {
5249
super("Cassandra health check failed");
53-
Assert.notNull(cassandraOperations, "CassandraOperations must not be null");
54-
this.cassandraOperations = cassandraOperations;
50+
Assert.notNull(session, "session must not be null");
51+
this.session = session;
5552
}
5653

5754
@Override
5855
protected void doHealthCheck(Health.Builder builder) throws Exception {
59-
String version = this.cassandraOperations.getCqlOperations().queryForObject(SELECT, String.class);
56+
Row row = this.session.execute(SELECT).one();
57+
Assert.notNull(row, "system.local should always return one row");
58+
String version = row.getString(0);
59+
Assert.notNull(version, "release_version should never be null");
6060
builder.up().withDetail("version", version);
6161
}
6262

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraReactiveHealthIndicator.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,47 @@
1515
*/
1616
package org.springframework.boot.actuate.cassandra;
1717

18+
import java.util.Objects;
19+
1820
import com.datastax.oss.driver.api.core.ConsistencyLevel;
21+
import com.datastax.oss.driver.api.core.CqlSession;
1922
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2023
import reactor.core.publisher.Mono;
2124

2225
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
2326
import org.springframework.boot.actuate.health.Health;
2427
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
25-
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
2628
import org.springframework.util.Assert;
2729

2830
/**
2931
* A {@link ReactiveHealthIndicator} for Cassandra.
3032
*
3133
* @author Artsiom Yudovin
34+
* @author Alexandre Dutra
3235
* @since 2.1.0
3336
*/
3437
public class CassandraReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
3538

3639
private static final SimpleStatement SELECT = SimpleStatement
3740
.newInstance("SELECT release_version FROM system.local").setConsistencyLevel(ConsistencyLevel.LOCAL_ONE);
3841

39-
private final ReactiveCassandraOperations reactiveCassandraOperations;
42+
private final CqlSession session;
4043

4144
/**
4245
* Create a new {@link CassandraHealthIndicator} instance.
43-
* @param reactiveCassandraOperations the Cassandra operations
46+
* @param session the {@link CqlSession}.
4447
*/
45-
public CassandraReactiveHealthIndicator(ReactiveCassandraOperations reactiveCassandraOperations) {
48+
public CassandraReactiveHealthIndicator(CqlSession session) {
4649
super("Cassandra health check failed");
47-
Assert.notNull(reactiveCassandraOperations, "ReactiveCassandraOperations must not be null");
48-
this.reactiveCassandraOperations = reactiveCassandraOperations;
50+
Assert.notNull(session, "session must not be null");
51+
this.session = session;
4952
}
5053

5154
@Override
5255
protected Mono<Health> doHealthCheck(Health.Builder builder) {
53-
return this.reactiveCassandraOperations.getReactiveCqlOperations().queryForObject(SELECT, String.class)
54-
.map((version) -> builder.up().withDetail("version", version).build()).single();
56+
return Mono.from(this.session.executeReactive(SELECT))
57+
.map((row) -> Objects.requireNonNull(row.getString(0), "release_version should never be null"))
58+
.map((version) -> builder.up().withDetail("version", version).build());
5559
}
5660

5761
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraHealthIndicatorTests.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,27 @@
1616

1717
package org.springframework.boot.actuate.cassandra;
1818

19+
import com.datastax.oss.driver.api.core.CqlSession;
20+
import com.datastax.oss.driver.api.core.DriverTimeoutException;
21+
import com.datastax.oss.driver.api.core.cql.ResultSet;
22+
import com.datastax.oss.driver.api.core.cql.Row;
1923
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2024
import org.junit.jupiter.api.Test;
2125

2226
import org.springframework.boot.actuate.health.Health;
2327
import org.springframework.boot.actuate.health.Status;
24-
import org.springframework.data.cassandra.CassandraInternalException;
25-
import org.springframework.data.cassandra.core.CassandraOperations;
26-
import org.springframework.data.cassandra.core.cql.CqlOperations;
2728

2829
import static org.assertj.core.api.Assertions.assertThat;
2930
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3031
import static org.mockito.ArgumentMatchers.any;
31-
import static org.mockito.ArgumentMatchers.eq;
3232
import static org.mockito.BDDMockito.given;
3333
import static org.mockito.Mockito.mock;
3434

3535
/**
3636
* Tests for {@link CassandraHealthIndicator}.
3737
*
3838
* @author Oleksii Bondar
39-
* @author Stephane Nicoll
39+
* @author Alexandre Dutra
4040
*/
4141
class CassandraHealthIndicatorTests {
4242

@@ -47,25 +47,28 @@ void createWhenCassandraOperationsIsNullShouldThrowException() {
4747

4848
@Test
4949
void healthWithCassandraUp() {
50-
CassandraOperations cassandraOperations = mock(CassandraOperations.class);
51-
CqlOperations cqlOperations = mock(CqlOperations.class);
52-
CassandraHealthIndicator healthIndicator = new CassandraHealthIndicator(cassandraOperations);
53-
given(cassandraOperations.getCqlOperations()).willReturn(cqlOperations);
54-
given(cqlOperations.queryForObject(any(SimpleStatement.class), eq(String.class))).willReturn("1.0.0");
50+
CqlSession session = mock(CqlSession.class);
51+
ResultSet resultSet = mock(ResultSet.class);
52+
Row row = mock(Row.class);
53+
given(session.execute(any(SimpleStatement.class))).willReturn(resultSet);
54+
given(resultSet.one()).willReturn(row);
55+
given(row.getString(0)).willReturn("1.0.0");
56+
CassandraHealthIndicator healthIndicator = new CassandraHealthIndicator(session);
5557
Health health = healthIndicator.health();
5658
assertThat(health.getStatus()).isEqualTo(Status.UP);
5759
assertThat(health.getDetails().get("version")).isEqualTo("1.0.0");
5860
}
5961

6062
@Test
6163
void healthWithCassandraDown() {
62-
CassandraOperations cassandraOperations = mock(CassandraOperations.class);
63-
given(cassandraOperations.getCqlOperations()).willThrow(new CassandraInternalException("Connection failed"));
64-
CassandraHealthIndicator healthIndicator = new CassandraHealthIndicator(cassandraOperations);
64+
CqlSession session = mock(CqlSession.class);
65+
given(session.execute(any(SimpleStatement.class)))
66+
.willThrow(new DriverTimeoutException("Connection timed out (not really)"));
67+
CassandraHealthIndicator healthIndicator = new CassandraHealthIndicator(session);
6568
Health health = healthIndicator.health();
6669
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
6770
assertThat(health.getDetails().get("error"))
68-
.isEqualTo(CassandraInternalException.class.getName() + ": Connection failed");
71+
.isEqualTo(DriverTimeoutException.class.getName() + ": Connection timed out (not really)");
6972
}
7073

7174
}

0 commit comments

Comments
 (0)