org.zalando.boot.cassandra.autoconfig.CassandraAutoConfiguration Maven / Gradle / Ivy
package org.zalando.boot.cassandra.autoconfig;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.zalando.boot.cassandra.CassandraClusterFactoryBean;
import org.zalando.boot.cassandra.CassandraMappingManagerFactoryBean;
import org.zalando.boot.cassandra.CassandraSessionFactoryBean;
import org.zalando.boot.etcd.EtcdClient;
import org.zalando.boot.etcd.EtcdException;
import org.zalando.boot.etcd.EtcdNode;
import org.zalando.boot.etcd.EtcdResponse;
import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.SSLOptions;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TimestampGenerator;
import com.datastax.driver.core.policies.AddressTranslator;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.policies.SpeculativeExecutionPolicy;
import com.datastax.driver.mapping.MappingManager;
/**
*
*/
@Configuration
@EnableConfigurationProperties(CassandraProperties.class)
@ConditionalOnMissingBean({ Cluster.class, Session.class })
@ConditionalOnProperty(prefix = "zalando.cassandra", name = "enabled", matchIfMissing = true)
public class CassandraAutoConfiguration {
/**
* Resolves the contact points using the given etcd client and etcd key. The
* key is a directory node under which the seed nodes are listed.
*
* @param etcdClient
* the etcd client
* @param keyName
* the name of the etcd directory node having the cassandra seeds
* as child nodes
* @return the list of contact points
* @throws EtcdException
* in case an error occured during communication with etcd
*/
private static List resolveContactPoints(EtcdClient etcdClient, String keyName) throws EtcdException {
List contactPoints = new ArrayList<>();
EtcdResponse response = etcdClient.get(keyName, true);
for (EtcdNode node : response.getNode().getNodes()) {
String addressString = node.getValue();
contactPoints.add(addressString);
}
return contactPoints;
}
/**
* properties
*/
@Autowired
private CassandraProperties properties;
/**
* conversion service used to convert between
*/
private ConversionService conversionService = new DefaultConversionService();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
/**
* Creates a new factory bean for a cassandra cluster.
*
* @return the cassandra cluster factory bean
* @throws Exception
* in case cluster creation fails
*/
@Bean
@ConditionalOnMissingBean(CassandraClusterFactoryBean.class)
@ConditionalOnProperty(prefix = "zalando.cassandra", value = "contact-points", matchIfMissing = true)
public CassandraClusterFactoryBean cassandraClusterFactoryBean() throws Exception {
return createClusterFactoryBean(properties.getContactPoints());
}
@Bean
@ConditionalOnMissingBean(CassandraClusterFactoryBean.class)
@ConditionalOnProperty(prefix = "zalando.cassandra", value = "etcd-key-name")
public CassandraClusterFactoryBean cassandraClusterFactoryBean(EtcdClient etcdClient) throws Exception {
// resolve and set contact points
String contactPoints = StringUtils
.collectionToCommaDelimitedString(resolveContactPoints(etcdClient, properties.getEtcdKeyName()));
properties.setContactPoints(contactPoints);
return createClusterFactoryBean(contactPoints);
}
private CassandraClusterFactoryBean createClusterFactoryBean(String contactPoints) throws Exception {
CassandraClusterFactoryBean bean = new CassandraClusterFactoryBean();
bean.setClusterName(properties.getClusterName());
bean.setContactPoints(contactPoints);
bean.setPort(properties.getPort());
bean.setProtocolVersion(properties.getProtocolVersion());
bean.setCompression(properties.getCompression());
// policies
if (StringUtils.hasText(properties.getLoadBalancingPolicy())) {
LoadBalancingPolicy policy = instantiate(LoadBalancingPolicy.class,
StringUtils.commaDelimitedListToStringArray(properties.getLoadBalancingPolicy()));
bean.setLoadBalancingPolicy(policy);
}
if (StringUtils.hasText(properties.getReconnectionPolicy())) {
ReconnectionPolicy policy = instantiate(ReconnectionPolicy.class,
StringUtils.commaDelimitedListToStringArray(properties.getReconnectionPolicy()));
bean.setReconnectionPolicy(policy);
}
if (StringUtils.hasText(properties.getSpeculativeExecutionPolicy())) {
SpeculativeExecutionPolicy policy = instantiate(SpeculativeExecutionPolicy.class,
StringUtils.commaDelimitedListToStringArray(properties.getSpeculativeExecutionPolicy()));
bean.setSpeculativeExecutionPolicy(policy);
}
if (StringUtils.hasText(properties.getRetryPolicy())) {
RetryPolicy policy = instantiate(RetryPolicy.class,
StringUtils.commaDelimitedListToStringArray(properties.getRetryPolicy()));
bean.setRetryPolicy(policy);
}
if (StringUtils.hasText(properties.getAddressTranslator())) {
AddressTranslator translator = instantiate(AddressTranslator.class,
StringUtils.commaDelimitedListToStringArray(properties.getAddressTranslator()));
bean.setAddressTranslator(translator);
}
// options
if (properties.getQueryOptions() != null) {
bean.setQueryOptions(properties.getQueryOptions());
}
if (properties.getPoolingOptions() != null) {
bean.setPoolingOptions(properties.getPoolingOptions());
}
if (properties.getSocketOptions() != null) {
bean.setSocketOptions(properties.getSocketOptions());
}
if (properties.getNettyOptions() != null) {
bean.setNettyOptions(properties.getNettyOptions());
}
if (StringUtils.hasText(properties.getTimestampGenerator())) {
TimestampGenerator generator = instantiate(TimestampGenerator.class,
StringUtils.commaDelimitedListToStringArray(properties.getTimestampGenerator()));
bean.setTimestampGenerator(generator);
}
if (StringUtils.hasText(properties.getAuthProvider())) {
AuthProvider provider = instantiate(AuthProvider.class,
StringUtils.commaDelimitedListToStringArray(properties.getAuthProvider()));
bean.setAuthProvider(provider);
}
bean.setUsername(properties.getUsername());
bean.setPassword(properties.getPassword());
bean.setMetricsEnabled(properties.isMetricsEnabled());
bean.setJmxEnabled(properties.isJmxEnabled());
if (properties.isSslEnabled()) {
bean.setSslEnabled(properties.isSslEnabled());
if (StringUtils.hasText(properties.getSslOptions())) {
SSLOptions options = instantiate(SSLOptions.class,
StringUtils.commaDelimitedListToStringArray(properties.getSslOptions()));
bean.setSslOptions(options);
}
}
return bean;
}
@SuppressWarnings("unchecked")
private T instantiate(Class type, String[] nameAndParams) throws ReflectiveOperationException {
Class clazz = beanClassLoader.loadClass(nameAndParams[0]);
Field singleton = null;
try {
singleton = clazz.getField("INSTANCE");
} catch (NoSuchFieldException e) {
// can be ignored
}
if (singleton != null) {
return (T) singleton.get(null);
} else {
// identify constructor and convert parameters
Constructor constructor = null;
int paramCount = nameAndParams.length - 1;
Object[] paramValues = new Object[paramCount];
for (Constructor c : clazz.getConstructors()) {
if (c.getParameterCount() == paramCount) {
constructor = c;
Parameter[] params = c.getParameters();
for (int i = 0; i < paramCount; i++) {
Parameter param = params[i];
paramValues[i] = conversionService.convert(nameAndParams[i + 1], param.getType());
}
break;
}
}
return (T) BeanUtils.instantiateClass(constructor, paramValues);
}
}
/**
* Creates a new factory bean for a cassandra session.
*
* @param cluster
* the cluster to use
* @return cassandra the session factory bean
*/
@Bean
@ConditionalOnMissingBean(CassandraSessionFactoryBean.class)
public CassandraSessionFactoryBean cassandraSessionFactoryBean(Cluster cluster) {
CassandraSessionFactoryBean bean = new CassandraSessionFactoryBean(cluster);
if (StringUtils.hasText(properties.getKeyspace())) {
bean.setKeyspace(properties.getKeyspace());
}
return bean;
}
/**
* Creates a new factory bean for a cassandra mapping manager.
*
* @param session
* the cassandra session
* @return the cassandra mapping manager
*/
@Bean
@ConditionalOnMissingBean({ MappingManager.class, CassandraMappingManagerFactoryBean.class })
public CassandraMappingManagerFactoryBean cassandraMappingManagerFactoryBean(Session session) {
CassandraMappingManagerFactoryBean bean = new CassandraMappingManagerFactoryBean(session);
return bean;
}
}