Commit e85cb2c4 authored by Mark Paluch's avatar Mark Paluch Committed by Stephane Nicoll

Add schemaAction property to CassandraProperties

Set schemaAction property in CassandraSessionFactoryBean. Use relaxed
property resolver for enum lookup of the configured schemaAction.

See gh-5855
parent 43867323
...@@ -4,6 +4,7 @@ jdk: ...@@ -4,6 +4,7 @@ jdk:
services: services:
- mongodb - mongodb
- redis - redis
- cassandra
sudo: false sudo: false
install: true install: true
before_script: before_script:
......
...@@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; ...@@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* *
* @author Julien Dubois * @author Julien Dubois
* @author Phillip Webb * @author Phillip Webb
* @author Mark Paluch
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties(prefix = "spring.data.cassandra") @ConfigurationProperties(prefix = "spring.data.cassandra")
...@@ -112,6 +113,11 @@ public class CassandraProperties { ...@@ -112,6 +113,11 @@ public class CassandraProperties {
*/ */
private int readTimeoutMillis = SocketOptions.DEFAULT_READ_TIMEOUT_MILLIS; private int readTimeoutMillis = SocketOptions.DEFAULT_READ_TIMEOUT_MILLIS;
/**
* Action to take at startup.
*/
private String schemaAction = "none";
/** /**
* Enable SSL support. * Enable SSL support.
*/ */
...@@ -247,4 +253,12 @@ public class CassandraProperties { ...@@ -247,4 +253,12 @@ public class CassandraProperties {
this.ssl = ssl; this.ssl = ssl;
} }
public String getSchemaAction() {
return this.schemaAction;
}
public void setSchemaAction(String schemaAction) {
this.schemaAction = schemaAction;
}
} }
...@@ -30,9 +30,12 @@ import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; ...@@ -30,9 +30,12 @@ import org.springframework.boot.autoconfigure.cassandra.CassandraProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.domain.EntityScanPackages; import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.data.cassandra.config.CassandraEntityClassScanner; import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.config.SchemaAction;
...@@ -48,6 +51,7 @@ import org.springframework.data.cassandra.mapping.CassandraMappingContext; ...@@ -48,6 +51,7 @@ import org.springframework.data.cassandra.mapping.CassandraMappingContext;
* *
* @author Julien Dubois * @author Julien Dubois
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Mark Paluch
* @since 1.3.0 * @since 1.3.0
*/ */
@Configuration @Configuration
...@@ -62,11 +66,14 @@ public class CassandraDataAutoConfiguration { ...@@ -62,11 +66,14 @@ public class CassandraDataAutoConfiguration {
private final Cluster cluster; private final Cluster cluster;
private final PropertyResolver propertyResolver;
public CassandraDataAutoConfiguration(BeanFactory beanFactory, public CassandraDataAutoConfiguration(BeanFactory beanFactory,
CassandraProperties properties, Cluster cluster) { CassandraProperties properties, Cluster cluster, Environment environment) {
this.beanFactory = beanFactory; this.beanFactory = beanFactory;
this.properties = properties; this.properties = properties;
this.cluster = cluster; this.cluster = cluster;
this.propertyResolver = new RelaxedPropertyResolver(environment, "spring.data.cassandra.");
} }
@Bean @Bean
...@@ -94,11 +101,14 @@ public class CassandraDataAutoConfiguration { ...@@ -94,11 +101,14 @@ public class CassandraDataAutoConfiguration {
@ConditionalOnMissingBean(Session.class) @ConditionalOnMissingBean(Session.class)
public CassandraSessionFactoryBean session(CassandraConverter converter) public CassandraSessionFactoryBean session(CassandraConverter converter)
throws Exception { throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(this.cluster); session.setCluster(this.cluster);
session.setConverter(converter); session.setConverter(converter);
session.setKeyspaceName(this.properties.getKeyspaceName()); session.setKeyspaceName(this.properties.getKeyspaceName());
session.setSchemaAction(SchemaAction.NONE);
SchemaAction schemaAction = propertyResolver.getProperty("schemaAction", SchemaAction.class, SchemaAction.NONE);
session.setSchemaAction(schemaAction);
return session; return session;
} }
......
package org.springframework.boot.autoconfigure.data.cassandra;
/**
*
* @author Stephane Nicoll
*/
public class CassandraDataAutoConfigurationIntegrationTests {
}
...@@ -18,19 +18,26 @@ package org.springframework.boot.autoconfigure.data.cassandra; ...@@ -18,19 +18,26 @@ package org.springframework.boot.autoconfigure.data.cassandra;
import java.util.Set; import java.util.Set;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session; import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.cassandra.city.City; import org.springframework.boot.autoconfigure.data.cassandra.city.City;
import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.core.CassandraTemplate; import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -42,6 +49,7 @@ import static org.mockito.Mockito.mock; ...@@ -42,6 +49,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link CassandraDataAutoConfiguration} * Tests for {@link CassandraDataAutoConfiguration}
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Mark Paluch
*/ */
public class CassandraDataAutoConfigurationTests { public class CassandraDataAutoConfigurationTests {
...@@ -65,6 +73,47 @@ public class CassandraDataAutoConfigurationTests { ...@@ -65,6 +73,47 @@ public class CassandraDataAutoConfigurationTests {
.isEqualTo(1); .isEqualTo(1);
} }
@Test
public void hasDefaultSchemaActionSet() {
if (isCassandraAvailable()) {
this.context = new AnnotationConfigApplicationContext();
String cityPackage = City.class.getPackage().getName();
AutoConfigurationPackages.register(this.context, cityPackage);
this.context.register(CassandraAutoConfiguration.class,
CassandraDataAutoConfiguration.class);
this.context.refresh();
CassandraSessionFactoryBean bean = this.context
.getBean(CassandraSessionFactoryBean.class);
assertThat(bean.getSchemaAction()).isEqualTo(SchemaAction.NONE);
}
}
@Test
public void hasRecreateSchemaActionSet() {
if (isCassandraAvailable()) {
createTestKeyspaceIfNotExists();
this.context = new AnnotationConfigApplicationContext();
String cityPackage = City.class.getPackage().getName();
AutoConfigurationPackages.register(this.context, cityPackage);
EnvironmentTestUtils.addEnvironment(this.context,
"spring.data.cassandra.schemaAction:RECREATE_DROP_UNUSED", "spring.data.cassandra.keyspaceName:boot_test");
this.context.register(CassandraAutoConfiguration.class,
CassandraDataAutoConfiguration.class);
this.context.refresh();
CassandraSessionFactoryBean bean = this.context
.getBean(CassandraSessionFactoryBean.class);
assertThat(bean.getSchemaAction()).isEqualTo(SchemaAction.RECREATE_DROP_UNUSED);
}
}
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void entityScanShouldSetInitialEntitySet() throws Exception { public void entityScanShouldSetInitialEntitySet() throws Exception {
...@@ -80,6 +129,42 @@ public class CassandraDataAutoConfigurationTests { ...@@ -80,6 +129,42 @@ public class CassandraDataAutoConfigurationTests {
assertThat(initialEntitySet).containsOnly(City.class); assertThat(initialEntitySet).containsOnly(City.class);
} }
/**
* @return {@literal true} if Cassandra is available
*/
private static boolean isCassandraAvailable() {
Cluster cluster = newCluster();
try {
cluster.connect().close();
return true;
}
catch (DriverException exception) {
return false;
}
finally {
cluster.closeAsync();
}
}
private static void createTestKeyspaceIfNotExists() {
Cluster cluster = newCluster();
try {
Session session = cluster.connect();
session.execute("CREATE KEYSPACE IF NOT EXISTS boot_test"
+ " WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };");
session.close();
}
finally {
cluster.closeAsync();
}
}
private static Cluster newCluster() {
return Cluster.builder().addContactPoint("localhost").build();
}
@Configuration @Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter(classes = { @ComponentScan(excludeFilters = @ComponentScan.Filter(classes = {
Session.class }, type = FilterType.ASSIGNABLE_TYPE)) Session.class }, type = FilterType.ASSIGNABLE_TYPE))
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.data.cassandra; package org.springframework.boot.autoconfigure.data.cassandra;
import java.util.Set;
import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session; import com.datastax.driver.core.Session;
import org.junit.After; import org.junit.After;
...@@ -34,7 +36,9 @@ import org.springframework.context.annotation.Bean; ...@@ -34,7 +36,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories; import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
...@@ -43,6 +47,7 @@ import static org.mockito.Mockito.mock; ...@@ -43,6 +47,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link CassandraRepositoriesAutoConfiguration}. * Tests for {@link CassandraRepositoriesAutoConfiguration}.
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Mark Paluch
*/ */
public class CassandraRepositoriesAutoConfigurationTests { public class CassandraRepositoriesAutoConfigurationTests {
...@@ -63,18 +68,39 @@ public class CassandraRepositoriesAutoConfigurationTests { ...@@ -63,18 +68,39 @@ public class CassandraRepositoriesAutoConfigurationTests {
addConfigurations(TestConfiguration.class); addConfigurations(TestConfiguration.class);
assertThat(this.context.getBean(CityRepository.class)).isNotNull(); assertThat(this.context.getBean(CityRepository.class)).isNotNull();
assertThat(this.context.getBean(Cluster.class)).isNotNull(); assertThat(this.context.getBean(Cluster.class)).isNotNull();
BasicCassandraMappingContext mappingContext = this.context
.getBean(BasicCassandraMappingContext.class);
@SuppressWarnings("unchecked")
Set<? extends Class<?>> entities = (Set<? extends Class<?>>) ReflectionTestUtils
.getField(mappingContext, "initialEntitySet");
assertThat(entities).hasSize(1);
} }
@Test @Test
public void testNoRepositoryConfiguration() { public void testNoRepositoryConfiguration() {
addConfigurations(TestExcludeConfiguration.class, EmptyConfiguration.class); addConfigurations(TestExcludeConfiguration.class, EmptyConfiguration.class);
assertThat(this.context.getBean(Cluster.class)).isNotNull(); assertThat(this.context.getBean(Cluster.class)).isNotNull();
BasicCassandraMappingContext mappingContext = this.context
.getBean(BasicCassandraMappingContext.class);
@SuppressWarnings("unchecked")
Set<? extends Class<?>> entities = (Set<? extends Class<?>>) ReflectionTestUtils
.getField(mappingContext, "initialEntitySet");
assertThat(entities).hasSize(1).containsOnly(City.class);
} }
@Test @Test
public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() {
addConfigurations(TestExcludeConfiguration.class, CustomizedConfiguration.class); addConfigurations(TestExcludeConfiguration.class, CustomizedConfiguration.class);
assertThat(this.context.getBean(CityCassandraRepository.class)).isNotNull(); assertThat(this.context.getBean(CityCassandraRepository.class)).isNotNull();
BasicCassandraMappingContext mappingContext = this.context
.getBean(BasicCassandraMappingContext.class);
@SuppressWarnings("unchecked")
Set<? extends Class<?>> entities = (Set<? extends Class<?>>) ReflectionTestUtils
.getField(mappingContext, "initialEntitySet");
assertThat(entities).hasSize(1).containsOnly(City.class);
} }
private void addConfigurations(Class<?>... configurations) { private void addConfigurations(Class<?>... configurations) {
......
package org.springframework.boot.autoconfigure.data.cassandra;
/**
*
* @author Stephane Nicoll
*/
public class CassandraTestServer {
}
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.data.cassandra.city; package org.springframework.boot.autoconfigure.data.cassandra.city;
import com.datastax.driver.core.DataType.Name;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.mapping.Column; import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKey; import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table; import org.springframework.data.cassandra.mapping.Table;
...@@ -24,6 +27,7 @@ import org.springframework.data.cassandra.mapping.Table; ...@@ -24,6 +27,7 @@ import org.springframework.data.cassandra.mapping.Table;
public class City { public class City {
@PrimaryKey @PrimaryKey
@CassandraType(type = Name.BIGINT)
private Long id; private Long id;
@Column @Column
......
...@@ -548,6 +548,7 @@ content into your application; rather pick only the properties that you need. ...@@ -548,6 +548,7 @@ content into your application; rather pick only the properties that you need.
spring.data.cassandra.reconnection-policy= # Reconnection policy class. spring.data.cassandra.reconnection-policy= # Reconnection policy class.
spring.data.cassandra.retry-policy= # Class name of the retry policy. spring.data.cassandra.retry-policy= # Class name of the retry policy.
spring.data.cassandra.serial-consistency-level= # Queries serial consistency level. spring.data.cassandra.serial-consistency-level= # Queries serial consistency level.
spring.data.cassandra.schema-action= # Action to take at starup. Default: NONE.
spring.data.cassandra.ssl=false # Enable SSL support. spring.data.cassandra.ssl=false # Enable SSL support.
spring.data.cassandra.username= # Login user of the server. spring.data.cassandra.username= # Login user of the server.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment