From 58ed9789defb879970e74e26d98d9cabbd0d2b82 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 25 Jul 2016 00:50:43 -0700 Subject: [PATCH] DATACASS-325 - Add ClusterBuilderConfigurer configuration API. We allow users to provide a ClusterBuilderConfigurer that can be applied to the Cluster Builder. ClusterBuilderConfigurer is a callback interface to handle extended configuration when the DataStax API changes. It allows configuration of options after all provided properties were set. Original pull request: #79. Related pull request: #80. --- .../CassandraCqlClusterFactoryBean.java | 18 +++++++++ .../config/ClusterBuilderConfigurer.java | 40 +++++++++++++++++++ .../java/AbstractClusterConfiguration.java | 13 ++++++ .../config/xml/CassandraCqlClusterParser.java | 1 + .../cassandra/config/spring-cql-1.0.xsd | 18 +++++++++ ...ssandraCqlClusterFactoryBeanUnitTests.java | 13 ++++++ ...AbstractClusterConfigurationUnitTests.java | 24 ++++++++++- .../CassandraCqlClusterParserUnitTests.java | 3 ++ .../config/xml/XmlConfigIntegrationTests.java | 21 ++++++++++ .../xml/XmlConfigIntegrationTests-context.xml | 3 ++ .../cassandra/config/spring-cassandra-1.0.xsd | 18 +++++++++ 11 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 spring-cql/src/main/java/org/springframework/cassandra/config/ClusterBuilderConfigurer.java diff --git a/spring-cql/src/main/java/org/springframework/cassandra/config/CassandraCqlClusterFactoryBean.java b/spring-cql/src/main/java/org/springframework/cassandra/config/CassandraCqlClusterFactoryBean.java index 6b0fe1029..f65b53b71 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/config/CassandraCqlClusterFactoryBean.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/config/CassandraCqlClusterFactoryBean.java @@ -101,6 +101,8 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean, Ini private Cluster cluster; + private ClusterBuilderConfigurer clusterBuilderConfigurer; + private CompressionType compressionType; private Host.StateListener hostStateListener; @@ -237,6 +239,10 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean, Ini clusterBuilder.withTimestampGenerator(timestampGenerator); } + if (clusterBuilderConfigurer != null) { + clusterBuilderConfigurer.configure(clusterBuilder); + } + cluster = clusterBuilder.build(); if (hostStateListener != null) { @@ -607,6 +613,18 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean, Ini this.addressTranslator = addressTranslator; } + /** + * Sets the {@link ClusterBuilderConfigurer} used to apply additional configuration logic + * to the {@link com.datastax.driver.core.Cluster.Builder}. + * + * @param clusterBuilderConfigurer {@link ClusterBuilderConfigurer} used to configure the + * {@link com.datastax.driver.core.Cluster.Builder}. + * @see org.springframework.cassandra.config.ClusterBuilderConfigurer + */ + public void setClusterBuilderConfigurer(ClusterBuilderConfigurer clusterBuilderConfigurer) { + this.clusterBuilderConfigurer = clusterBuilderConfigurer; + } + /** * An optional name for the create cluster. * \ diff --git a/spring-cql/src/main/java/org/springframework/cassandra/config/ClusterBuilderConfigurer.java b/spring-cql/src/main/java/org/springframework/cassandra/config/ClusterBuilderConfigurer.java new file mode 100644 index 000000000..e6d1e94a3 --- /dev/null +++ b/spring-cql/src/main/java/org/springframework/cassandra/config/ClusterBuilderConfigurer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-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.cassandra.config; + +import com.datastax.driver.core.Cluster; + +/** + * Configuration callback class to allow a user to apply additional configuration logic + * to the {@link Cluster.Builder}. + * + * @author John Blum + * @see com.datastax.driver.core.Cluster + * @since 1.0.0 + */ +public interface ClusterBuilderConfigurer { + + /** + * Apply addition configuration to the {@link com.datastax.driver.core.Cluster.Builder}. + * + * @param clusterBuilder {@link Cluster.Builder} to configure. + * @return the argument to the {@code clusterBuilder} parameter. + * @see com.datastax.driver.core.Cluster.Builder + */ + Cluster.Builder configure(Cluster.Builder clusterBuilder); + +} diff --git a/spring-cql/src/main/java/org/springframework/cassandra/config/java/AbstractClusterConfiguration.java b/spring-cql/src/main/java/org/springframework/cassandra/config/java/AbstractClusterConfiguration.java index f7e665930..d084cac8d 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/config/java/AbstractClusterConfiguration.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/config/java/AbstractClusterConfiguration.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.List; import org.springframework.cassandra.config.CassandraCqlClusterFactoryBean; +import org.springframework.cassandra.config.ClusterBuilderConfigurer; import org.springframework.cassandra.config.CompressionType; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; @@ -45,6 +46,7 @@ import com.datastax.driver.core.policies.SpeculativeExecutionPolicy; * @author Matthew T. Adams * @author Jorge Davison * @author Mark Paluch + * @author John Blum */ @Configuration public abstract class AbstractClusterConfiguration { @@ -56,6 +58,7 @@ public abstract class AbstractClusterConfiguration { bean.setAddressTranslator(getAddressTranslator()); bean.setAuthProvider(getAuthProvider()); + bean.setClusterBuilderConfigurer(getClusterBuilderConfigurer()); bean.setClusterName(getClusterName()); bean.setCompressionType(getCompressionType()); bean.setContactPoints(getContactPoints()); @@ -100,6 +103,16 @@ public abstract class AbstractClusterConfiguration { return null; } + /** + * Returns the {@link ClusterBuilderConfigurer}. + * + * @return the {@link ClusterBuilderConfigurer}; may be {@literal null}. + * @since 1.5 + */ + protected ClusterBuilderConfigurer getClusterBuilderConfigurer() { + return null; + } + /** * Returns the cluster name. * diff --git a/spring-cql/src/main/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParser.java b/spring-cql/src/main/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParser.java index 0dca42647..d73373789 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParser.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParser.java @@ -91,6 +91,7 @@ public class CassandraCqlClusterParser extends AbstractBeanDefinitionParser { addOptionalPropertyReference(builder, "addressTranslator", element, "address-translator-ref"); addOptionalPropertyReference(builder, "authProvider", element, "auth-info-provider-ref"); + addOptionalPropertyReference(builder, "clusterBuilderConfigurer", element, "cluster-builder-configurer-ref"); addOptionalPropertyReference(builder, "hostStateListener", element, "host-state-listener-ref"); addOptionalPropertyReference(builder, "latencyTracker", element, "latency-tracker-ref"); addOptionalPropertyReference(builder, "loadBalancingPolicy", element, "load-balancing-policy-ref"); diff --git a/spring-cql/src/main/resources/org/springframework/cassandra/config/spring-cql-1.0.xsd b/spring-cql/src/main/resources/org/springframework/cassandra/config/spring-cql-1.0.xsd index 8ebf228e8..dd33c6cf0 100644 --- a/spring-cql/src/main/resources/org/springframework/cassandra/config/spring-cql-1.0.xsd +++ b/spring-cql/src/main/resources/org/springframework/cassandra/config/spring-cql-1.0.xsd @@ -139,6 +139,24 @@ AuthInfoProvider implementation. + + + + + + + + + + + + + + + DATACASS-316 */ diff --git a/spring-cql/src/test/java/org/springframework/cassandra/config/java/AbstractClusterConfigurationUnitTests.java b/spring-cql/src/test/java/org/springframework/cassandra/config/java/AbstractClusterConfigurationUnitTests.java index f0a48dd8c..4c4c1e612 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/config/java/AbstractClusterConfigurationUnitTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/config/java/AbstractClusterConfigurationUnitTests.java @@ -18,12 +18,15 @@ package org.springframework.cassandra.config.java; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.isA; +import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; import org.junit.Test; import org.springframework.cassandra.config.CassandraCqlClusterFactoryBean; +import org.springframework.cassandra.config.ClusterBuilderConfigurer; import org.springframework.cassandra.config.CompressionType; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; @@ -52,8 +55,9 @@ import com.datastax.driver.core.policies.SpeculativeExecutionPolicy; * Unit tests for {@link AbstractClusterConfiguration}. * * @author Mark Paluch + * @author John Blum * @soundtrack Max Graham Feat Neev Kennedy - So Caught Up (Dns Project Remix) - * @see DATACASS-226 + * @see */ public class AbstractClusterConfigurationUnitTests { @@ -344,6 +348,24 @@ public class AbstractClusterConfigurationUnitTests { is(equalTo(mockAddressTranslator))); } + /** + * DATACASS-325 + */ + @Test + public void shouldSetAndApplyClusterBuilderConfigurer() throws Exception { + final ClusterBuilderConfigurer mockClusterBuilderConfigurer = mock(ClusterBuilderConfigurer.class); + + AbstractClusterConfiguration clusterConfiguration = new AbstractClusterConfiguration() { + @Override protected ClusterBuilderConfigurer getClusterBuilderConfigurer() { + return mockClusterBuilderConfigurer; + } + }; + + assertThat(getCluster(clusterConfiguration), is(notNullValue(Cluster.class))); + + verify(mockClusterBuilderConfigurer, times(1)).configure(isA(Cluster.Builder.class)); + } + /** * DATACASS-120 * DATACASS-317 diff --git a/spring-cql/src/test/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParserUnitTests.java b/spring-cql/src/test/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParserUnitTests.java index 5ddf55429..6b20939ed 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParserUnitTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/config/xml/CassandraCqlClusterParserUnitTests.java @@ -88,6 +88,7 @@ public class CassandraCqlClusterParserUnitTests { when(mockContainingBeanDefinition.getScope()).thenReturn("Singleton"); when(mockElement.getAttribute("address-translator-ref")).thenReturn("testAddressTranslator"); when(mockElement.getAttribute("auth-info-provider-ref")).thenReturn("testAuthInfoProvider"); + when(mockElement.getAttribute("cluster-builder-configurer-ref")).thenReturn("testClusterBuilderConfigurer"); when(mockElement.getAttribute("host-state-listener-ref")).thenReturn("testHostStateListener"); when(mockElement.getAttribute("latency-tracker-ref")).thenReturn("testLatencyTracker"); when(mockElement.getAttribute("load-balancing-policy-ref")).thenReturn("testLoadBalancingPolicy"); @@ -124,6 +125,7 @@ public class CassandraCqlClusterParserUnitTests { assertThat(beanDefinition.isLazyInit(), is(false)); assertThat(getPropertyValueAsString(beanDefinition, "addressTranslator"), is(equalTo("testAddressTranslator"))); assertThat(getPropertyValueAsString(beanDefinition, "authProvider"), is(equalTo("testAuthInfoProvider"))); + assertThat(getPropertyValueAsString(beanDefinition, "clusterBuilderConfigurer"), is(equalTo("testClusterBuilderConfigurer"))); assertThat(getPropertyValueAsString(beanDefinition, "hostStateListener"), is(equalTo("testHostStateListener"))); assertThat(getPropertyValueAsString(beanDefinition, "latencyTracker"), is(equalTo("testLatencyTracker"))); assertThat(getPropertyValueAsString(beanDefinition, "loadBalancingPolicy"), is(equalTo("testLoadBalancingPolicy"))); @@ -146,6 +148,7 @@ public class CassandraCqlClusterParserUnitTests { verify(mockContainingBeanDefinition).getScope(); verify(mockElement).getAttribute(eq("address-translator-ref")); verify(mockElement).getAttribute(eq("auth-info-provider-ref")); + verify(mockElement).getAttribute(eq("cluster-builder-configurer-ref")); verify(mockElement).getAttribute(eq("host-state-listener-ref")); verify(mockElement).getAttribute(eq("latency-tracker-ref")); verify(mockElement).getAttribute(eq("load-balancing-policy-ref")); diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests.java index a3d39a1c9..766c10a1b 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests.java @@ -19,11 +19,13 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.springframework.cassandra.config.ClusterBuilderConfigurer; import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; import org.springframework.cassandra.test.integration.KeyspaceRule; import org.springframework.cassandra.test.integration.config.IntegrationTestUtils; @@ -59,6 +61,7 @@ public class XmlConfigIntegrationTests extends AbstractEmbeddedCassandraIntegrat private AddressTranslator addressTranslator; private Cluster cluster; + private ClusterBuilderConfigurer clusterBuilderConfigurer; private Executor executor; private Session session; private SpeculativeExecutionPolicy speculativeExecutionPolicy; @@ -71,6 +74,7 @@ public class XmlConfigIntegrationTests extends AbstractEmbeddedCassandraIntegrat this.addressTranslator = applicationContext.getBean(AddressTranslator.class); this.cluster = applicationContext.getBean(Cluster.class); + this.clusterBuilderConfigurer = applicationContext.getBean(ClusterBuilderConfigurer.class); this.executor = applicationContext.getBean(Executor.class); this.session = applicationContext.getBean(Session.class); this.speculativeExecutionPolicy = applicationContext.getBean(SpeculativeExecutionPolicy.class); @@ -101,6 +105,12 @@ public class XmlConfigIntegrationTests extends AbstractEmbeddedCassandraIntegrat assertThat(cluster.getConfiguration().getPolicies().getTimestampGenerator(), is(equalTo(timestampGenerator))); } + @Test + public void clusterBuilderConfigurerWasCalled() { + assertThat(clusterBuilderConfigurer, is(instanceOf(TestClusterBuilderConfigurer.class))); + assertThat(((TestClusterBuilderConfigurer) clusterBuilderConfigurer).configureCalled.get(), is(true)); + } + /** * @see DATACASS-298 */ @@ -142,4 +152,15 @@ public class XmlConfigIntegrationTests extends AbstractEmbeddedCassandraIntegrat assertThat(socketOptions.getSoLinger(), is(equalTo(60))); assertThat(socketOptions.getTcpNoDelay(), is(true)); } + + public static class TestClusterBuilderConfigurer implements ClusterBuilderConfigurer { + + AtomicBoolean configureCalled = new AtomicBoolean(false); + + @Override + public Cluster.Builder configure(Cluster.Builder clusterBuilder) { + configureCalled.set(true); + return clusterBuilder; + } + } } diff --git a/spring-cql/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests-context.xml b/spring-cql/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests-context.xml index 1c136145c..47db2c364 100644 --- a/spring-cql/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests-context.xml +++ b/spring-cql/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigIntegrationTests-context.xml @@ -17,6 +17,8 @@ + + @@ -24,6 +26,7 @@ + + + + + + + + + + + + + + +