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.
This commit is contained in:
John Blum
2016-07-25 00:50:43 -07:00
committed by Mark Paluch
parent dfc7e3a716
commit 58ed9789de
11 changed files with 171 additions and 1 deletions

View File

@@ -101,6 +101,8 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean<Cluster>, Ini
private Cluster cluster;
private ClusterBuilderConfigurer clusterBuilderConfigurer;
private CompressionType compressionType;
private Host.StateListener hostStateListener;
@@ -237,6 +239,10 @@ public class CassandraCqlClusterFactoryBean implements FactoryBean<Cluster>, 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<Cluster>, 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.
* \

View File

@@ -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);
}

View File

@@ -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.
*

View File

@@ -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");

View File

@@ -139,6 +139,24 @@ AuthInfoProvider implementation.
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="cluster-builder-configurer-ref" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the ClusterBuilderConfigurer used to apply additional configuration logic
to the com.datastax.driver.core.Cluster.Builder.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.cassandra.config.ClusterBuilderConfigurer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="cluster-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -19,6 +19,7 @@ package org.springframework.cassandra.config;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.isA;
import org.junit.Test;
import org.mockito.Matchers;
@@ -309,6 +310,18 @@ public class CassandraCqlClusterFactoryBeanUnitTests {
assertThat(getConfiguration(bean).getMetricsOptions().isJMXReportingEnabled(), is(false));
}
@Test
public void shouldCallClusterBuilderConfigurer() throws Exception {
ClusterBuilderConfigurer mockClusterBuilderConfigurer = mock(ClusterBuilderConfigurer.class);
CassandraCqlClusterFactoryBean bean = new CassandraCqlClusterFactoryBean();
bean.setClusterBuilderConfigurer(mockClusterBuilderConfigurer);
bean.afterPropertiesSet();
verify(mockClusterBuilderConfigurer, times(1)).configure(isA(Cluster.Builder.class));
}
/**
* @see <a href="https://jira.spring.io/browse/DATACASS-316">DATACASS-316</a>
*/

View File

@@ -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 <a href="https://jira.spring.io/browse/DATACASS-226"></a>
*/
public class AbstractClusterConfigurationUnitTests {
@@ -344,6 +348,24 @@ public class AbstractClusterConfigurationUnitTests {
is(equalTo(mockAddressTranslator)));
}
/**
* <a href="https://jira.spring.io/browse/DATACASS-325">DATACASS-325</a>
*/
@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));
}
/**
* <a href="https://jira.spring.io/browse/DATACASS-120">DATACASS-120</a>
* <a href="https://jira.spring.io/browse/DATACASS-317">DATACASS-317</a>

View File

@@ -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"));

View File

@@ -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 <a href="https://jira.spring.io/browse/DATACASS-298">DATACASS-298</a>
*/
@@ -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;
}
}
}

View File

@@ -17,6 +17,8 @@
<bean name="testAddressTranslator" class="com.datastax.driver.core.policies.IdentityTranslator"/>
<bean name="testClusterBuilderConfigurer" class="org.springframework.cassandra.test.integration.config.xml.XmlConfigIntegrationTests.TestClusterBuilderConfigurer"/>
<bean name="testSpeculativeExecutionPolicy" class="com.datastax.driver.core.policies.NoSpeculativeExecutionPolicy"/>
<bean name="testTimestampGenerator" class="com.datastax.driver.core.AtomicMonotonicTimestampGenerator"/>
@@ -24,6 +26,7 @@
<cassandra:cluster
cluster-name="skynet" contact-points="localhost" port="${build.cassandra.native_transport_port}"
address-translator-ref="testAddressTranslator"
cluster-builder-configurer-ref="testClusterBuilderConfigurer"
heartbeat-interval-seconds="60"
initialization-executor-ref="testExecutor"
idle-timeout-seconds="300"

View File

@@ -148,6 +148,24 @@ AuthInfoProvider implementation.
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="cluster-builder-configurer-ref" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the ClusterBuilderConfigurer used to apply additional configuration logic
to the com.datastax.driver.core.Cluster.Builder.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="org.springframework.cassandra.config.ClusterBuilderConfigurer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="cluster-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[