Skip to content

Commit a410e25

Browse files
committed
Fix failing Inline Caching with Apache Cassandra Integration Tests.
1 parent 195d746 commit a410e25

11 files changed

+254
-68
lines changed

spring-geode-project/spring-geode/spring-geode.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
testImplementation "org.springframework.boot:spring-boot-starter-data-cassandra"
3333
testImplementation "org.springframework.data:spring-data-geode-test"
3434
testImplementation "org.testcontainers:testcontainers"
35+
testImplementation "org.testcontainers:cassandra"
3536

3637
testRuntimeOnly "org.hsqldb:hsqldb"
3738

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

Lines changed: 152 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,96 +15,204 @@
1515
*/
1616
package example.app.crm.config;
1717

18+
import static org.assertj.core.api.Assertions.assertThat;
1819
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
1920

2021
import java.io.BufferedReader;
2122
import java.io.IOException;
2223
import java.io.InputStreamReader;
2324
import java.util.ArrayList;
2425
import java.util.List;
26+
import java.util.Optional;
27+
import java.util.function.Consumer;
28+
import java.util.function.Predicate;
2529
import java.util.stream.Collectors;
2630

31+
import com.datastax.oss.driver.api.core.CqlIdentifier;
32+
import com.datastax.oss.driver.api.core.session.Session;
33+
34+
import org.springframework.beans.BeansException;
35+
import org.springframework.beans.factory.config.BeanPostProcessor;
36+
import org.springframework.context.annotation.Bean;
2737
import org.springframework.core.io.ClassPathResource;
2838
import org.springframework.core.io.Resource;
29-
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
39+
import org.springframework.data.cassandra.SessionFactory;
3040
import org.springframework.data.cassandra.config.CqlSessionFactoryBean;
31-
import org.springframework.data.gemfire.tests.util.IOUtils;
41+
import org.springframework.data.cassandra.core.CassandraTemplate;
42+
import org.springframework.data.cassandra.core.cql.CqlTemplate;
43+
import org.springframework.data.cassandra.core.cql.RowMapper;
44+
import org.springframework.data.cassandra.core.cql.session.init.KeyspacePopulator;
45+
import org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer;
3246
import org.springframework.lang.NonNull;
3347
import org.springframework.lang.Nullable;
3448
import org.springframework.util.StringUtils;
3549

50+
import example.app.crm.model.Customer;
51+
3652
/**
3753
* Base test configuration used to configure and bootstrap an Apache Cassandra database with a schema and data.
3854
*
3955
* @author John Blum
56+
* @see com.datastax.oss.driver.api.core.session.Session
57+
* @see org.springframework.beans.factory.config.BeanPostProcessor
58+
* @see org.springframework.context.annotation.Bean
4059
* @see org.springframework.core.io.Resource
41-
* @see org.springframework.data.cassandra.config.AbstractCassandraConfiguration
60+
* @see org.springframework.data.cassandra.SessionFactory
4261
* @see org.springframework.data.cassandra.config.CqlSessionFactoryBean
62+
* @see org.springframework.data.cassandra.core.CassandraTemplate
63+
* @see org.springframework.data.cassandra.core.cql.CqlTemplate
64+
* @see org.springframework.data.cassandra.core.cql.session.init.KeyspacePopulator
65+
* @see org.springframework.data.cassandra.core.cql.session.init.SessionFactoryInitializer
4366
* @since 1.1.0
4467
*/
45-
public abstract class TestCassandraConfiguration extends AbstractCassandraConfiguration {
68+
@SuppressWarnings("unused")
69+
public abstract class TestCassandraConfiguration {
4670

4771
protected static final int CASSANDRA_DEFAULT_PORT = CqlSessionFactoryBean.DEFAULT_PORT;
4872

49-
private static final String CASSANDRA_DATA_CQL = "cassandra-data.cql";
50-
private static final String CASSANDRA_SCHEMA_CQL = "cassandra-schema.cql";
51-
private static final String LOCAL_DATA_CENTER = "datacenter1";
52-
private static final String KEYSPACE_NAME = "CustomerService";
53-
private static final String SESSION_NAME = "CustomerServiceCluster";
73+
protected static final String CASSANDRA_DATA_CQL = "cassandra-data.cql";
74+
protected static final String CASSANDRA_SCHEMA_CQL = "cassandra-schema.cql";
5475

55-
@NonNull
56-
@Override
57-
protected String getKeyspaceName() {
58-
return KEYSPACE_NAME;
59-
}
76+
private static final String COMMENT_LINE_PREFIX = "--";
6077

61-
@Override
62-
protected String getLocalDataCenter() {
63-
return LOCAL_DATA_CENTER;
64-
}
78+
protected static final String LOCAL_DATA_CENTER = "datacenter1";
79+
protected static final String KEYSPACE_NAME = "CustomerService";
6580

66-
@Nullable
67-
@Override
68-
protected String getSessionName() {
69-
return SESSION_NAME;
70-
}
81+
@Bean
82+
SessionFactoryInitializer sessionFactoryInitializer(SessionFactory sessionFactory) {
83+
84+
SessionFactoryInitializer sessionFactoryInitializer = new SessionFactoryInitializer();
85+
86+
KeyspacePopulator keyspacePopulator =
87+
// cqlSession -> loadCassandraCqlScripts().forEach(cqlSession::execute);
88+
cqlSession -> loadCassandraDataCqlScript().forEach(cqlSession::execute);
7189

72-
/*
73-
@Nullable @Override
74-
protected KeyspacePopulator keyspacePopulator() {
75-
return cqlSession -> loadCassandraCqlScripts().forEach(cqlSession::execute);
90+
sessionFactoryInitializer.setKeyspacePopulator(keyspacePopulator);
91+
sessionFactoryInitializer.setSessionFactory(sessionFactory);
92+
93+
return sessionFactoryInitializer;
7694
}
77-
*/
7895

79-
// TODO: Remove use of deprecation after Spring Data for Apache Cassandra issues are resolved!
80-
@Override
81-
protected List<String> getStartupScripts() {
96+
protected List<String> loadCassandraCqlScripts() {
8297

83-
List<String> startupScripts = new ArrayList<>(super.getStartupScripts());
98+
List<String> cassandraCqlStatements = new ArrayList<>();
8499

85-
startupScripts.addAll(readLines(new ClassPathResource(CASSANDRA_SCHEMA_CQL)));
86-
startupScripts.addAll(readLines(new ClassPathResource(CASSANDRA_DATA_CQL)));
100+
cassandraCqlStatements.addAll(loadCassandraSchemaCqlScript());
101+
cassandraCqlStatements.addAll(loadCassandraDataCqlScript());
87102

88-
return startupScripts;
103+
return cassandraCqlStatements;
89104
}
90105

91-
private @NonNull List<String> readLines(@NonNull Resource resource) {
92-
93-
BufferedReader resourceReader = null;
106+
protected List<String> loadCassandraDataCqlScript() {
107+
return readLines(new ClassPathResource(CASSANDRA_DATA_CQL));
108+
}
94109

95-
try {
110+
protected List<String> loadCassandraSchemaCqlScript() {
111+
return readLines(new ClassPathResource(CASSANDRA_SCHEMA_CQL));
112+
}
96113

97-
resourceReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
114+
private @NonNull List<String> readLines(@NonNull Resource resource) {
98115

116+
try (BufferedReader resourceReader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
99117
return resourceReader.lines()
100-
.filter(StringUtils::hasText)
118+
.filter(cqlPredicate())
101119
.collect(Collectors.toList());
102120
}
103121
catch (IOException cause) {
104122
throw newRuntimeException(cause, "Failed to read from Resource [%s]", resource);
105123
}
106-
finally {
107-
IOUtils.close(resourceReader);
108-
}
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;
133+
}
134+
135+
private boolean isCommentLine(@Nullable String line) {
136+
return String.valueOf(line).trim().startsWith(COMMENT_LINE_PREFIX);
137+
}
138+
139+
private boolean isNotCommentLine(@Nullable String line) {
140+
return !isCommentLine(line);
141+
}
142+
143+
@Bean
144+
BeanPostProcessor cassandraTemplatePostProcessor() {
145+
146+
return new BeanPostProcessor() {
147+
148+
@org.jetbrains.annotations.Nullable @Override
149+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
150+
151+
if (bean instanceof CassandraTemplate cassandraTemplate) {
152+
153+
Consumer<CassandraTemplate> cassandraTemplateConsumer = noopCassandraTemplateConsumer()
154+
.andThen(entityObjectInsertingCassandraTemplateConsumer())
155+
.andThen(entityObjectAssertingCassandraTemplateConsumer());
156+
//.andThen(entityTableNameAssertingCassandraTemplateConsumer())
157+
//.andThen(keyspaceNameAssertingCassandraTemplateConsumer());
158+
159+
cassandraTemplateConsumer.accept(cassandraTemplate);
160+
}
161+
162+
return bean;
163+
}
164+
};
165+
}
166+
167+
private Consumer<CassandraTemplate> entityCountAssertingCassandraTemplateConsumer() {
168+
return cassandraTemplate -> assertThat(cassandraTemplate.count(Customer.class)).isOne();
169+
}
170+
171+
private Consumer<CassandraTemplate> entityObjectAssertingCassandraTemplateConsumer() {
172+
return cassandraTemplate -> {
173+
174+
String cql = "SELECT id, name FROM Customers";
175+
176+
RowMapper<Customer> customerRowMapper = (row, rowNumber) ->
177+
Customer.newCustomer(row.getLong("id"), row.getString("name"));
178+
179+
Customer actualCustomer = cassandraTemplate.getCqlOperations().queryForObject(cql, customerRowMapper);
180+
Customer expectedCustomer = Customer.newCustomer(16L, "Pie Doe");
181+
182+
assertThat(actualCustomer).isEqualTo(expectedCustomer);
183+
};
184+
}
185+
186+
// TODO: Why does this work and the CQL data script not work!
187+
private Consumer<CassandraTemplate> entityObjectInsertingCassandraTemplateConsumer() {
188+
return cassandraTemplate -> cassandraTemplate.insert(Customer.newCustomer(16L, "Pie Doe"));
189+
}
190+
191+
private Consumer<CassandraTemplate> entityTableNameAssertingCassandraTemplateConsumer() {
192+
193+
return cassandraTemplate ->
194+
assertThat(cassandraTemplate.getTableName(Customer.class).toString()).endsWithIgnoringCase("Customers");
195+
}
196+
197+
private Consumer<CassandraTemplate> keyspaceNameAssertingCassandraTemplateConsumer() {
198+
199+
return cassandraTemplate -> {
200+
201+
String resolvedKeyspaceName = Optional.of(cassandraTemplate)
202+
.map(CassandraTemplate::getCqlOperations)
203+
.filter(CqlTemplate.class::isInstance)
204+
.map(CqlTemplate.class::cast)
205+
.map(CqlTemplate::getSessionFactory)
206+
.map(SessionFactory::getSession)
207+
.flatMap(Session::getKeyspace)
208+
.map(CqlIdentifier::toString)
209+
.orElse(null);
210+
211+
assertThat(resolvedKeyspaceName).isEqualToIgnoringCase(KEYSPACE_NAME);
212+
};
213+
}
214+
215+
private Consumer<CassandraTemplate> noopCassandraTemplateConsumer() {
216+
return cassandraTemplate -> {};
109217
}
110218
}

0 commit comments

Comments
 (0)