From e85cb2c4c5cc30a540d646fae2ca6c3800b59128 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 3 May 2016 16:44:02 +0200 Subject: [PATCH 1/2] Add schemaAction property to CassandraProperties Set schemaAction property in CassandraSessionFactoryBean. Use relaxed property resolver for enum lookup of the configured schemaAction. See gh-5855 --- .travis.yml | 1 + .../cassandra/CassandraProperties.java | 14 +++ .../CassandraDataAutoConfiguration.java | 14 ++- ...DataAutoConfigurationIntegrationTests.java | 8 ++ .../CassandraDataAutoConfigurationTests.java | 85 +++++++++++++++++++ ...draRepositoriesAutoConfigurationTests.java | 26 ++++++ .../data/cassandra/CassandraTestServer.java | 8 ++ .../data/cassandra/city/City.java | 4 + .../appendix-application-properties.adoc | 1 + 9 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java diff --git a/.travis.yml b/.travis.yml index 68e0c01fc74..4abb8a807d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ jdk: services: - mongodb - redis + - cassandra sudo: false install: true before_script: diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java index a2d04ec74bf..eeab6ec4898 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java @@ -32,6 +32,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * * @author Julien Dubois * @author Phillip Webb + * @author Mark Paluch * @since 1.3.0 */ @ConfigurationProperties(prefix = "spring.data.cassandra") @@ -112,6 +113,11 @@ public class CassandraProperties { */ private int readTimeoutMillis = SocketOptions.DEFAULT_READ_TIMEOUT_MILLIS; + /** + * Action to take at startup. + */ + private String schemaAction = "none"; + /** * Enable SSL support. */ @@ -247,4 +253,12 @@ public class CassandraProperties { this.ssl = ssl; } + public String getSchemaAction() { + return this.schemaAction; + } + + public void setSchemaAction(String schemaAction) { + this.schemaAction = schemaAction; + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java index a086da5a020..280c8a7ffb1 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java @@ -30,9 +30,12 @@ import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.domain.EntityScanPackages; +import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; 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.CassandraSessionFactoryBean; import org.springframework.data.cassandra.config.SchemaAction; @@ -48,6 +51,7 @@ import org.springframework.data.cassandra.mapping.CassandraMappingContext; * * @author Julien Dubois * @author Eddú Meléndez + * @author Mark Paluch * @since 1.3.0 */ @Configuration @@ -62,11 +66,14 @@ public class CassandraDataAutoConfiguration { private final Cluster cluster; + private final PropertyResolver propertyResolver; + public CassandraDataAutoConfiguration(BeanFactory beanFactory, - CassandraProperties properties, Cluster cluster) { + CassandraProperties properties, Cluster cluster, Environment environment) { this.beanFactory = beanFactory; this.properties = properties; this.cluster = cluster; + this.propertyResolver = new RelaxedPropertyResolver(environment, "spring.data.cassandra."); } @Bean @@ -94,11 +101,14 @@ public class CassandraDataAutoConfiguration { @ConditionalOnMissingBean(Session.class) public CassandraSessionFactoryBean session(CassandraConverter converter) throws Exception { + CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); session.setCluster(this.cluster); session.setConverter(converter); session.setKeyspaceName(this.properties.getKeyspaceName()); - session.setSchemaAction(SchemaAction.NONE); + + SchemaAction schemaAction = propertyResolver.getProperty("schemaAction", SchemaAction.class, SchemaAction.NONE); + session.setSchemaAction(schemaAction); return session; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java new file mode 100644 index 00000000000..3b3b493de52 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java @@ -0,0 +1,8 @@ +package org.springframework.boot.autoconfigure.data.cassandra; + +/** + * + * @author Stephane Nicoll + */ +public class CassandraDataAutoConfigurationIntegrationTests { +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java index bd9b15fd7b6..5a274f773fe 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java @@ -18,19 +18,26 @@ package org.springframework.boot.autoconfigure.data.cassandra; import java.util.Set; +import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.DriverException; + import org.junit.After; import org.junit.Test; +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.city.City; 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.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; 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.mapping.CassandraMappingContext; import org.springframework.test.util.ReflectionTestUtils; @@ -42,6 +49,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link CassandraDataAutoConfiguration} * * @author Eddú Meléndez + * @author Mark Paluch */ public class CassandraDataAutoConfigurationTests { @@ -65,6 +73,47 @@ public class CassandraDataAutoConfigurationTests { .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 @SuppressWarnings("unchecked") public void entityScanShouldSetInitialEntitySet() throws Exception { @@ -80,6 +129,42 @@ public class CassandraDataAutoConfigurationTests { 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 @ComponentScan(excludeFilters = @ComponentScan.Filter(classes = { Session.class }, type = FilterType.ASSIGNABLE_TYPE)) diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java index 8f21ed54211..ea005538688 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.data.cassandra; +import java.util.Set; + import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; import org.junit.After; @@ -34,7 +36,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; +import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext; 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.mockito.Mockito.mock; @@ -43,6 +47,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link CassandraRepositoriesAutoConfiguration}. * * @author Eddú Meléndez + * @author Mark Paluch */ public class CassandraRepositoriesAutoConfigurationTests { @@ -63,18 +68,39 @@ public class CassandraRepositoriesAutoConfigurationTests { addConfigurations(TestConfiguration.class); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); assertThat(this.context.getBean(Cluster.class)).isNotNull(); + + BasicCassandraMappingContext mappingContext = this.context + .getBean(BasicCassandraMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1); } @Test public void testNoRepositoryConfiguration() { addConfigurations(TestExcludeConfiguration.class, EmptyConfiguration.class); assertThat(this.context.getBean(Cluster.class)).isNotNull(); + + BasicCassandraMappingContext mappingContext = this.context + .getBean(BasicCassandraMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1).containsOnly(City.class); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { addConfigurations(TestExcludeConfiguration.class, CustomizedConfiguration.class); assertThat(this.context.getBean(CityCassandraRepository.class)).isNotNull(); + + BasicCassandraMappingContext mappingContext = this.context + .getBean(BasicCassandraMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1).containsOnly(City.class); } private void addConfigurations(Class... configurations) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java new file mode 100644 index 00000000000..1762f4a6a2e --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java @@ -0,0 +1,8 @@ +package org.springframework.boot.autoconfigure.data.cassandra; + +/** + * + * @author Stephane Nicoll + */ +public class CassandraTestServer { +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java index b785a4ec012..fd9436bedb6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java @@ -16,6 +16,9 @@ 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.PrimaryKey; import org.springframework.data.cassandra.mapping.Table; @@ -24,6 +27,7 @@ import org.springframework.data.cassandra.mapping.Table; public class City { @PrimaryKey + @CassandraType(type = Name.BIGINT) private Long id; @Column diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 3341165842d..9cb3bc07a13 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -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.retry-policy= # Class name of the retry policy. 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.username= # Login user of the server. From 397c64d2565ee44ebcf76ecf9f26aca69524ad06 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 28 Jun 2016 10:21:48 +0200 Subject: [PATCH 2/2] Polish "Add schemaAction property" Closes gh-5855 --- .../cassandra/CassandraProperties.java | 4 +- .../CassandraDataAutoConfiguration.java | 5 +- ...itional-spring-configuration-metadata.json | 11 +++ ...DataAutoConfigurationIntegrationTests.java | 90 ++++++++++++++++++ .../CassandraDataAutoConfigurationTests.java | 84 ----------------- ...draRepositoriesAutoConfigurationTests.java | 25 ++--- .../data/cassandra/CassandraTestServer.java | 93 ++++++++++++++++++- .../data/cassandra/city/City.java | 2 +- .../appendix-application-properties.adoc | 2 +- 9 files changed, 207 insertions(+), 109 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java index eeab6ec4898..ddcab2bc74e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,7 +114,7 @@ public class CassandraProperties { private int readTimeoutMillis = SocketOptions.DEFAULT_READ_TIMEOUT_MILLIS; /** - * Action to take at startup. + * Schema action to take at startup. */ private String schemaAction = "none"; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java index 280c8a7ffb1..72772348ccf 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java @@ -101,13 +101,12 @@ public class CassandraDataAutoConfiguration { @ConditionalOnMissingBean(Session.class) public CassandraSessionFactoryBean session(CassandraConverter converter) throws Exception { - CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); session.setCluster(this.cluster); session.setConverter(converter); session.setKeyspaceName(this.properties.getKeyspaceName()); - - SchemaAction schemaAction = propertyResolver.getProperty("schemaAction", SchemaAction.class, SchemaAction.NONE); + SchemaAction schemaAction = this.propertyResolver + .getProperty("schemaAction", SchemaAction.class, SchemaAction.NONE); session.setSchemaAction(schemaAction); return session; } diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 549db95ebc1..808b1358306 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -325,6 +325,17 @@ } ] }, + { + "name": "spring.data.cassandra.schema-action", + "providers": [ + { + "name": "handle-as", + "parameters": { + "target": "org.springframework.data.cassandra.config.SchemaAction" + } + } + ] + }, { "name": "spring.data.mongodb.field-naming-strategy", "providers": [ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java index 3b3b493de52..2c98e6f6f1e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationIntegrationTests.java @@ -1,8 +1,98 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.boot.autoconfigure.data.cassandra; +import com.datastax.driver.core.Session; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.data.cassandra.city.City; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; +import org.springframework.data.cassandra.config.SchemaAction; + +import static org.assertj.core.api.Assertions.assertThat; + /** + * Tests for {@link CassandraDataAutoConfiguration} that require a Cassandra instance. * + * @author Mark Paluch * @author Stephane Nicoll */ public class CassandraDataAutoConfigurationIntegrationTests { + + @Rule + public final CassandraTestServer cassandra = new CassandraTestServer(); + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void hasDefaultSchemaActionSet() { + 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() { + 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); + } + + private void createTestKeyspaceIfNotExists() { + Session session = this.cassandra.getCluster().connect(); + try { + session.execute("CREATE KEYSPACE IF NOT EXISTS boot_test" + + " WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"); + } + finally { + session.close(); + } + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java index 5a274f773fe..faf46b1815f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java @@ -18,26 +18,20 @@ package org.springframework.boot.autoconfigure.data.cassandra; import java.util.Set; -import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.DriverException; import org.junit.After; import org.junit.Test; -import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.data.cassandra.city.City; 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.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; 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.mapping.CassandraMappingContext; import org.springframework.test.util.ReflectionTestUtils; @@ -49,7 +43,6 @@ import static org.mockito.Mockito.mock; * Tests for {@link CassandraDataAutoConfiguration} * * @author Eddú Meléndez - * @author Mark Paluch */ public class CassandraDataAutoConfigurationTests { @@ -73,47 +66,6 @@ public class CassandraDataAutoConfigurationTests { .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 @SuppressWarnings("unchecked") public void entityScanShouldSetInitialEntitySet() throws Exception { @@ -129,42 +81,6 @@ public class CassandraDataAutoConfigurationTests { 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 @ComponentScan(excludeFilters = @ComponentScan.Filter(classes = { Session.class }, type = FilterType.ASSIGNABLE_TYPE)) diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java index ea005538688..9db24e6b188 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraRepositoriesAutoConfigurationTests.java @@ -48,6 +48,7 @@ import static org.mockito.Mockito.mock; * * @author Eddú Meléndez * @author Mark Paluch + * @author Stephane Nicoll */ public class CassandraRepositoriesAutoConfigurationTests { @@ -68,39 +69,29 @@ public class CassandraRepositoriesAutoConfigurationTests { addConfigurations(TestConfiguration.class); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); assertThat(this.context.getBean(Cluster.class)).isNotNull(); - - BasicCassandraMappingContext mappingContext = this.context - .getBean(BasicCassandraMappingContext.class); - @SuppressWarnings("unchecked") - Set> entities = (Set>) ReflectionTestUtils - .getField(mappingContext, "initialEntitySet"); - assertThat(entities).hasSize(1); + assertThat(getInitialEntitySet()).hasSize(1); } @Test public void testNoRepositoryConfiguration() { addConfigurations(TestExcludeConfiguration.class, EmptyConfiguration.class); assertThat(this.context.getBean(Cluster.class)).isNotNull(); - - BasicCassandraMappingContext mappingContext = this.context - .getBean(BasicCassandraMappingContext.class); - @SuppressWarnings("unchecked") - Set> entities = (Set>) ReflectionTestUtils - .getField(mappingContext, "initialEntitySet"); - assertThat(entities).hasSize(1).containsOnly(City.class); + assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); } @Test public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { addConfigurations(TestExcludeConfiguration.class, CustomizedConfiguration.class); assertThat(this.context.getBean(CityCassandraRepository.class)).isNotNull(); + assertThat(getInitialEntitySet()).hasSize(1).containsOnly(City.class); + } + @SuppressWarnings("unchecked") + private Set> getInitialEntitySet() { BasicCassandraMappingContext mappingContext = this.context .getBean(BasicCassandraMappingContext.class); - @SuppressWarnings("unchecked") - Set> entities = (Set>) ReflectionTestUtils + return (Set>) ReflectionTestUtils .getField(mappingContext, "initialEntitySet"); - assertThat(entities).hasSize(1).containsOnly(City.class); } private void addConfigurations(Class... configurations) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java index 1762f4a6a2e..5fcb6a0b331 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraTestServer.java @@ -1,8 +1,99 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.boot.autoconfigure.data.cassandra; +import com.datastax.driver.core.Cluster; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + /** + * {@link TestRule} for working with an optional Cassandra server. * * @author Stephane Nicoll */ -public class CassandraTestServer { +public class CassandraTestServer implements TestRule { + + private static final Log logger = LogFactory.getLog(CassandraTestServer.class); + + private Cluster cluster; + + @Override + public Statement apply(Statement base, Description description) { + try { + this.cluster = newCluster(); + return new CassandraStatement(base, this.cluster); + } + catch (Exception ex) { + logger.error("No Cassandra server available", ex); + return new SkipStatement(); + } + } + + private Cluster newCluster() { + Cluster cluster = Cluster.builder().addContactPoint("localhost").build(); + testCluster(cluster); + return cluster; + } + + private void testCluster(Cluster cluster) { + cluster.connect().close(); + } + + /** + * @return the cluster if any + */ + public Cluster getCluster() { + return this.cluster; + } + + private static class CassandraStatement extends Statement { + + private final Statement base; + + private final Cluster cluster; + + CassandraStatement(Statement base, Cluster cluster) { + this.base = base; + this.cluster = cluster; + } + + @Override + public void evaluate() throws Throwable { + try { + this.base.evaluate(); + } + finally { + this.cluster.closeAsync(); + } + } + + } + + private static class SkipStatement extends Statement { + + @Override + public void evaluate() throws Throwable { + Assume.assumeTrue("Skipping test due to Cassandra not being available", false); + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java index fd9436bedb6..2ddbe1c7b6b 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/city/City.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 9cb3bc07a13..b1f34a86b0a 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -548,7 +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.retry-policy= # Class name of the retry policy. 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.schema-action= # Schema action to take at startup. spring.data.cassandra.ssl=false # Enable SSL support. spring.data.cassandra.username= # Login user of the server.