Skip to content

Commit 5a3c580

Browse files
committed
Polish Inline Caching with Apache Cassandra Integration Tests configuration.
1 parent a410e25 commit 5a3c580

File tree

5 files changed

+90
-79
lines changed

5 files changed

+90
-79
lines changed

spring-geode-project/spring-geode/src/test/java/example/app/crm/config/TestCassandraConfiguration.java

Lines changed: 58 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,18 @@
1616
package example.app.crm.config;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19-
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
2019

21-
import java.io.BufferedReader;
22-
import java.io.IOException;
23-
import java.io.InputStreamReader;
24-
import java.util.ArrayList;
25-
import java.util.List;
2620
import java.util.Optional;
2721
import java.util.function.Consumer;
28-
import java.util.function.Predicate;
29-
import java.util.stream.Collectors;
3022

3123
import com.datastax.oss.driver.api.core.CqlIdentifier;
3224
import com.datastax.oss.driver.api.core.session.Session;
3325

3426
import org.springframework.beans.BeansException;
3527
import org.springframework.beans.factory.config.BeanPostProcessor;
28+
import org.springframework.context.ApplicationListener;
3629
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.event.ContextRefreshedEvent;
3731
import org.springframework.core.io.ClassPathResource;
3832
import org.springframework.core.io.Resource;
3933
import org.springframework.data.cassandra.SessionFactory;
@@ -42,12 +36,11 @@
4236
import org.springframework.data.cassandra.core.cql.CqlTemplate;
4337
import org.springframework.data.cassandra.core.cql.RowMapper;
4438
import org.springframework.data.cassandra.core.cql.session.init.KeyspacePopulator;
39+
import org.springframework.data.cassandra.core.cql.session.init.ResourceKeyspacePopulator;
4540
import org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer;
46-
import org.springframework.lang.NonNull;
47-
import org.springframework.lang.Nullable;
48-
import org.springframework.util.StringUtils;
4941

5042
import example.app.crm.model.Customer;
43+
import example.app.crm.repo.CustomerRepository;
5144

5245
/**
5346
* Base test configuration used to configure and bootstrap an Apache Cassandra database with a schema and data.
@@ -68,13 +61,16 @@
6861
@SuppressWarnings("unused")
6962
public abstract class TestCassandraConfiguration {
7063

64+
private static final boolean CONTINUE_ON_ERROR = false;
65+
private static final boolean IGNORE_FAILED_DROPS = true;
66+
67+
private static final String CQL_SCRIPT_ENCODING = null;
68+
7169
protected static final int CASSANDRA_DEFAULT_PORT = CqlSessionFactoryBean.DEFAULT_PORT;
7270

7371
protected static final String CASSANDRA_DATA_CQL = "cassandra-data.cql";
72+
protected static final String CASSANDRA_INIT_CQL = "cassandra-init.cql";
7473
protected static final String CASSANDRA_SCHEMA_CQL = "cassandra-schema.cql";
75-
76-
private static final String COMMENT_LINE_PREFIX = "--";
77-
7874
protected static final String LOCAL_DATA_CENTER = "datacenter1";
7975
protected static final String KEYSPACE_NAME = "CustomerService";
8076

@@ -83,78 +79,46 @@ SessionFactoryInitializer sessionFactoryInitializer(SessionFactory sessionFactor
8379

8480
SessionFactoryInitializer sessionFactoryInitializer = new SessionFactoryInitializer();
8581

86-
KeyspacePopulator keyspacePopulator =
87-
// cqlSession -> loadCassandraCqlScripts().forEach(cqlSession::execute);
88-
cqlSession -> loadCassandraDataCqlScript().forEach(cqlSession::execute);
82+
KeyspacePopulator keyspacePopulator = newKeyspacePopulator(newCassandraDataCqlScriptResource());
8983

9084
sessionFactoryInitializer.setKeyspacePopulator(keyspacePopulator);
9185
sessionFactoryInitializer.setSessionFactory(sessionFactory);
9286

9387
return sessionFactoryInitializer;
9488
}
9589

96-
protected List<String> loadCassandraCqlScripts() {
97-
98-
List<String> cassandraCqlStatements = new ArrayList<>();
99-
100-
cassandraCqlStatements.addAll(loadCassandraSchemaCqlScript());
101-
cassandraCqlStatements.addAll(loadCassandraDataCqlScript());
102-
103-
return cassandraCqlStatements;
104-
}
105-
106-
protected List<String> loadCassandraDataCqlScript() {
107-
return readLines(new ClassPathResource(CASSANDRA_DATA_CQL));
108-
}
109-
110-
protected List<String> loadCassandraSchemaCqlScript() {
111-
return readLines(new ClassPathResource(CASSANDRA_SCHEMA_CQL));
90+
protected Resource newCassandraDataCqlScriptResource() {
91+
return new ClassPathResource(CASSANDRA_DATA_CQL);
11292
}
11393

114-
private @NonNull List<String> readLines(@NonNull Resource resource) {
115-
116-
try (BufferedReader resourceReader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
117-
return resourceReader.lines()
118-
.filter(cqlPredicate())
119-
.collect(Collectors.toList());
120-
}
121-
catch (IOException cause) {
122-
throw newRuntimeException(cause, "Failed to read from Resource [%s]", resource);
123-
}
124-
}
125-
126-
private @NonNull Predicate<String> cqlPredicate() {
127-
128-
Predicate<String> cqlPredicate = StringUtils::hasText;
129-
130-
cqlPredicate.and(this::isNotCommentLine);
131-
132-
return cqlPredicate;
94+
protected Resource newCassandraInitCqlScriptResource() {
95+
return new ClassPathResource(CASSANDRA_INIT_CQL);
13396
}
13497

135-
private boolean isCommentLine(@Nullable String line) {
136-
return String.valueOf(line).trim().startsWith(COMMENT_LINE_PREFIX);
98+
protected Resource newCassandraSchemaCqlScriptResource() {
99+
return new ClassPathResource(CASSANDRA_SCHEMA_CQL);
137100
}
138101

139-
private boolean isNotCommentLine(@Nullable String line) {
140-
return !isCommentLine(line);
102+
protected KeyspacePopulator newKeyspacePopulator(Resource... cqlScripts) {
103+
return new ResourceKeyspacePopulator(CONTINUE_ON_ERROR, IGNORE_FAILED_DROPS, CQL_SCRIPT_ENCODING, cqlScripts);
104+
//return cqlScript -> {};
141105
}
142106

143107
@Bean
144108
BeanPostProcessor cassandraTemplatePostProcessor() {
145109

146110
return new BeanPostProcessor() {
147111

148-
@org.jetbrains.annotations.Nullable @Override
112+
@Override
149113
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
150114

151115
if (bean instanceof CassandraTemplate cassandraTemplate) {
152116

153-
Consumer<CassandraTemplate> cassandraTemplateConsumer = noopCassandraTemplateConsumer()
154-
.andThen(entityObjectInsertingCassandraTemplateConsumer())
155-
.andThen(entityObjectAssertingCassandraTemplateConsumer());
156-
//.andThen(entityTableNameAssertingCassandraTemplateConsumer())
157-
//.andThen(keyspaceNameAssertingCassandraTemplateConsumer());
117+
Consumer<CassandraTemplate> cassandraTemplateConsumer = noopCassandraTemplateConsumer();
118+
//.andThen(entityObjectInsertingCassandraTemplateConsumer())
119+
//.andThen(entityObjectAssertingCassandraTemplateConsumer());
120+
//.andThen(keyspaceNameAssertingCassandraTemplateConsumer())
121+
//.andThen(tableNameAssertingCassandraTemplateConsumer());
158122

159123
cassandraTemplateConsumer.accept(cassandraTemplate);
160124
}
@@ -164,11 +128,16 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
164128
};
165129
}
166130

131+
private Consumer<CassandraTemplate> noopCassandraTemplateConsumer() {
132+
return cassandraTemplate -> {};
133+
}
134+
167135
private Consumer<CassandraTemplate> entityCountAssertingCassandraTemplateConsumer() {
168136
return cassandraTemplate -> assertThat(cassandraTemplate.count(Customer.class)).isOne();
169137
}
170138

171139
private Consumer<CassandraTemplate> entityObjectAssertingCassandraTemplateConsumer() {
140+
172141
return cassandraTemplate -> {
173142

174143
String cql = "SELECT id, name FROM Customers";
@@ -180,6 +149,9 @@ private Consumer<CassandraTemplate> entityObjectAssertingCassandraTemplateConsum
180149
Customer expectedCustomer = Customer.newCustomer(16L, "Pie Doe");
181150

182151
assertThat(actualCustomer).isEqualTo(expectedCustomer);
152+
//assertThat(cassandraTemplate.selectOneById(16L, Customer.class)).isEqualTo(expectedCustomer);
153+
//assertThat(cassandraTemplate.query(Customer.class).stream().findFirst().orElse(null))
154+
// .isEqualTo(expectedCustomer);
183155
};
184156
}
185157

@@ -188,18 +160,11 @@ private Consumer<CassandraTemplate> entityObjectInsertingCassandraTemplateConsum
188160
return cassandraTemplate -> cassandraTemplate.insert(Customer.newCustomer(16L, "Pie Doe"));
189161
}
190162

191-
private Consumer<CassandraTemplate> entityTableNameAssertingCassandraTemplateConsumer() {
192-
193-
return cassandraTemplate ->
194-
assertThat(cassandraTemplate.getTableName(Customer.class).toString()).endsWithIgnoringCase("Customers");
195-
}
196-
197163
private Consumer<CassandraTemplate> keyspaceNameAssertingCassandraTemplateConsumer() {
198164

199165
return cassandraTemplate -> {
200166

201-
String resolvedKeyspaceName = Optional.of(cassandraTemplate)
202-
.map(CassandraTemplate::getCqlOperations)
167+
String resolvedKeyspaceName = Optional.ofNullable(cassandraTemplate.getCqlOperations())
203168
.filter(CqlTemplate.class::isInstance)
204169
.map(CqlTemplate.class::cast)
205170
.map(CqlTemplate::getSessionFactory)
@@ -212,7 +177,28 @@ private Consumer<CassandraTemplate> keyspaceNameAssertingCassandraTemplateConsum
212177
};
213178
}
214179

215-
private Consumer<CassandraTemplate> noopCassandraTemplateConsumer() {
216-
return cassandraTemplate -> {};
180+
private Consumer<CassandraTemplate> tableNameAssertingCassandraTemplateConsumer() {
181+
182+
return cassandraTemplate -> {
183+
184+
String entityTableName = cassandraTemplate.getTableName(Customer.class).toString();
185+
186+
assertThat(entityTableName).endsWithIgnoringCase("Customers");
187+
188+
Optional.ofNullable(cassandraTemplate.getCqlOperations())
189+
.filter(CqlTemplate.class::isInstance)
190+
.map(CqlTemplate.class::cast)
191+
.map(CqlTemplate::getSessionFactory)
192+
.map(SessionFactory::getSession)
193+
.map(Session::getMetadata)
194+
.flatMap(metadata -> metadata.getKeyspace(KEYSPACE_NAME))
195+
.map(keyspaceMetadata -> keyspaceMetadata.getTable(entityTableName))
196+
.orElseThrow(() -> new IllegalStateException(String.format("Table [%s] not found", entityTableName)));
197+
};
198+
}
199+
200+
@Bean
201+
ApplicationListener<ContextRefreshedEvent> populateCassandraDatabaseUsingRepository(CustomerRepository customerRepository) {
202+
return event -> customerRepository.save(Customer.newCustomer(16L, "Pie Doe"));
217203
}
218204
}

spring-geode-project/spring-geode/src/test/java/example/app/crm/config/TestcontainersCassandraConfiguration.java

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.context.annotation.Bean;
2828
import org.springframework.context.annotation.Configuration;
2929
import org.springframework.context.annotation.Profile;
30+
import org.springframework.data.cassandra.core.CassandraTemplate;
3031
import org.springframework.lang.NonNull;
3132

3233
import org.testcontainers.containers.CassandraContainer;
@@ -38,7 +39,10 @@
3839
* Spring {@link @Configuration} for Apache Cassandra using Testcontainers.
3940
*
4041
* @author John Blum
42+
* @see java.net.InetSocketAddress
4143
* @see com.datastax.oss.driver.api.core.CqlSession
44+
* @see org.springframework.boot.autoconfigure.cassandra.CqlSessionBuilderCustomizer
45+
* @see org.springframework.boot.autoconfigure.domain.EntityScan
4246
* @see org.springframework.context.annotation.Bean
4347
* @see org.springframework.context.annotation.Configuration
4448
* @see org.springframework.context.annotation.Profile
@@ -58,7 +62,7 @@ public class TestcontainersCassandraConfiguration extends TestCassandraConfigura
5862
@SuppressWarnings("rawtypes")
5963
GenericContainer cassandraContainer() {
6064

61-
GenericContainer cassandraContainer = newCustomCassandraContainer();
65+
GenericContainer cassandraContainer = newEnvironmentTunedCassandraContainer();
6266

6367
cassandraContainer.start();
6468

@@ -69,12 +73,13 @@ GenericContainer cassandraContainer() {
6973
private @NonNull GenericContainer newCassandraContainer() {
7074
return new CassandraContainer(CASSANDRA_DOCKER_IMAGE_NAME)
7175
.withInitScript(CASSANDRA_SCHEMA_CQL)
76+
//.withInitScript(CASSANDRA_INIT_CQL)
7277
.withExposedPorts(CASSANDRA_DEFAULT_PORT)
7378
.withReuse(true);
7479
}
7580

7681
@SuppressWarnings("rawtypes")
77-
private @NonNull GenericContainer newCustomCassandraContainer() {
82+
private @NonNull GenericContainer newEnvironmentTunedCassandraContainer() {
7883

7984
return newCassandraContainer()
8085
.withEnv("CASSANDRA_SNITCH", "GossipingPropertyFileSnitch")
@@ -83,6 +88,10 @@ GenericContainer cassandraContainer() {
8388
.withEnv("JVM_OPTS", "-Dcassandra.skip_wait_for_gossip_to_settle=0 -Dcassandra.initial_token=0");
8489
}
8590

91+
private @NonNull CassandraTemplate newCassandraTemplate(@NonNull CqlSession session) {
92+
return new CassandraTemplate(session);
93+
}
94+
8695
private @NonNull CqlSession newCqlSession(@NonNull GenericContainer<?> cassandraContainer) {
8796

8897
return CqlSession.builder()
@@ -93,17 +102,16 @@ GenericContainer cassandraContainer() {
93102

94103
private @NonNull GenericContainer<?> withCassandraServer(@NonNull GenericContainer<?> cassandraContainer) {
95104

96-
cassandraContainer = initializeCassandraServer(cassandraContainer);
97-
//cassandraContainer = assertCassandraServerSetup(cassandraContainer);
105+
//cassandraContainer = initializeCassandraServer(cassandraContainer);
106+
cassandraContainer = assertCassandraServerSetup(cassandraContainer);
98107

99108
return cassandraContainer;
100109
}
101110

102111
private GenericContainer<?> initializeCassandraServer(GenericContainer<?> cassandraContainer) {
103112

104113
try (CqlSession session = newCqlSession(cassandraContainer)) {
105-
//loadCassandraCqlScripts().forEach(session::execute);
106-
loadCassandraSchemaCqlScript().forEach(session::execute);
114+
newKeyspacePopulator(newCassandraSchemaCqlScriptResource()).populate(session);
107115
}
108116

109117
return cassandraContainer;
@@ -120,8 +128,11 @@ private GenericContainer<?> assertCassandraServerSetup(GenericContainer<?> cassa
120128

121129
keyspaceMetadata.getTable("Customers")
122130
.map(tableMetadata -> {
131+
123132
assertThat(tableMetadata.getName().toString()).isEqualToIgnoringCase("Customers");
124133
assertThat(tableMetadata.getKeyspace().toString()).isEqualToIgnoringCase(KEYSPACE_NAME);
134+
//assertCustomersTableHasSizeOne(session);
135+
125136
return tableMetadata;
126137
})
127138
.orElseThrow(() -> new IllegalStateException("Table [Customers] not found"));
@@ -134,6 +145,15 @@ private GenericContainer<?> assertCassandraServerSetup(GenericContainer<?> cassa
134145
return cassandraContainer;
135146
}
136147

148+
private void assertCustomersTableHasSizeOne(CqlSession session) {
149+
150+
CassandraTemplate template = newCassandraTemplate(session);
151+
152+
assertThat(template.getCqlOperations().execute(String.format("USE %s;", KEYSPACE_NAME))).isTrue();
153+
assertThat(template.getCqlOperations().queryForObject("SELECT count(*) FROM Customers", Long.class)).isOne();
154+
//assertThat(template.count(Customer.class)).isOne(); // Table Customers not found; needs to use the Keyspace
155+
}
156+
137157
@Bean
138158
CqlSessionBuilderCustomizer cqlSessionBuilderCustomizer(
139159
@Qualifier("CassandraContainer") GenericContainer<?> cassandraContainer) {

spring-geode-project/spring-geode/src/test/java/org/springframework/geode/cache/inline/cassandra/InlineCachingWithCassandraIntegrationTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
* @see org.springframework.geode.cache.inline.AbstractInlineCachingWithExternalDataSourceIntegrationTests
6060
* @see org.springframework.test.context.ActiveProfiles
6161
* @see org.springframework.test.context.junit4.SpringRunner
62+
* @see example.app.crm.config.TestcontainersCassandraConfiguration
6263
* @since 1.1.0
6364
*/
6465
@SpringBootTest
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CREATE KEYSPACE IF NOT EXISTS CustomerService WITH replication = { 'class':'SimpleStrategy', 'replication_factor':1 };
2+
CREATE TABLE IF NOT EXISTS CustomerService.Customers (id BIGINT PRIMARY KEY, name TEXT);
3+
CREATE INDEX IF NOT EXISTS CustomerNameIdx ON CustomerService.Customers(name);
4+
INSERT INTO CustomerService.Customers (id, name) VALUES (16, 'Pie Doe');
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
CREATE KEYSPACE IF NOT EXISTS CustomerService WITH replication = { 'class':'SimpleStrategy', 'replication_factor':1 };
22
USE CustomerService;
33
CREATE TABLE IF NOT EXISTS Customers (id BIGINT PRIMARY KEY, name TEXT);
4-
CREATE INDEX IF NOT EXISTS CustomerNameIdx ON customers(name);
4+
CREATE INDEX IF NOT EXISTS CustomerNameIdx ON Customers(name);

0 commit comments

Comments
 (0)