wip: beginning to move appropriate config to spring-cassandra

This commit is contained in:
Matthew Adams
2013-12-09 14:23:39 -06:00
parent a177562b27
commit c1d30dcc52
21 changed files with 1949 additions and 5 deletions

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2010-2012 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;
/**
* Simple enumeration for the various compression types.
*
* @author Alex Shvid
*/
public enum CompressionType {
NONE, SNAPPY;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2011-2013 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 java.util.Collection;
/**
* Keyspace attributes are used for manipulation around keyspace at the startup. Auto property defines the way how to do
* this. Other attributes used to ensure or update keyspace settings.
*
* @author Alex Shvid
*/
public class KeyspaceAttributes {
public static final String DEFAULT_REPLICATION_STRATEGY = "SimpleStrategy";
public static final int DEFAULT_REPLICATION_FACTOR = 1;
public static final boolean DEFAULT_DURABLE_WRITES = true;
/*
* auto possible values:
* validate: validate the keyspace, makes no changes.
* update: update the keyspace.
* create: creates the keyspace, destroying previous data.
* create-drop: drop the keyspace at the end of the session.
*/
public static final String AUTO_VALIDATE = "validate";
public static final String AUTO_UPDATE = "update";
public static final String AUTO_CREATE = "create";
public static final String AUTO_CREATE_DROP = "create-drop";
private String auto = AUTO_VALIDATE;
private String replicationStrategy = DEFAULT_REPLICATION_STRATEGY;
private int replicationFactor = DEFAULT_REPLICATION_FACTOR;
private boolean durableWrites = DEFAULT_DURABLE_WRITES;
private Collection<TableAttributes> tables;
public String getAuto() {
return auto;
}
public void setAuto(String auto) {
this.auto = auto;
}
public boolean isValidate() {
return AUTO_VALIDATE.equals(auto);
}
public boolean isUpdate() {
return AUTO_UPDATE.equals(auto);
}
public boolean isCreate() {
return AUTO_CREATE.equals(auto);
}
public boolean isCreateDrop() {
return AUTO_CREATE_DROP.equals(auto);
}
public String getReplicationStrategy() {
return replicationStrategy;
}
public void setReplicationStrategy(String replicationStrategy) {
this.replicationStrategy = replicationStrategy;
}
public int getReplicationFactor() {
return replicationFactor;
}
public void setReplicationFactor(int replicationFactor) {
this.replicationFactor = replicationFactor;
}
public boolean isDurableWrites() {
return durableWrites;
}
public void setDurableWrites(boolean durableWrites) {
this.durableWrites = durableWrites;
}
public Collection<TableAttributes> getTables() {
return tables;
}
public void setTables(Collection<TableAttributes> tables) {
this.tables = tables;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2011-2013 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;
/**
* Pooling options POJO. Can be remote or local.
*
* @author Alex Shvid
*/
public class PoolingOptionsConfig {
private Integer minSimultaneousRequests;
private Integer maxSimultaneousRequests;
private Integer coreConnections;
private Integer maxConnections;
public Integer getMinSimultaneousRequests() {
return minSimultaneousRequests;
}
public void setMinSimultaneousRequests(Integer minSimultaneousRequests) {
this.minSimultaneousRequests = minSimultaneousRequests;
}
public Integer getMaxSimultaneousRequests() {
return maxSimultaneousRequests;
}
public void setMaxSimultaneousRequests(Integer maxSimultaneousRequests) {
this.maxSimultaneousRequests = maxSimultaneousRequests;
}
public Integer getCoreConnections() {
return coreConnections;
}
public void setCoreConnections(Integer coreConnections) {
this.coreConnections = coreConnections;
}
public Integer getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(Integer maxConnections) {
this.maxConnections = maxConnections;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2011-2013 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;
/**
* Socket options POJO. Uses to configure Netty.
*
* @author Alex Shvid
*/
public class SocketOptionsConfig {
private Integer connectTimeoutMls;
private Boolean keepAlive;
private Boolean reuseAddress;
private Integer soLinger;
private Boolean tcpNoDelay;
private Integer receiveBufferSize;
private Integer sendBufferSize;
public Integer getConnectTimeoutMls() {
return connectTimeoutMls;
}
public void setConnectTimeoutMls(Integer connectTimeoutMls) {
this.connectTimeoutMls = connectTimeoutMls;
}
public Boolean getKeepAlive() {
return keepAlive;
}
public void setKeepAlive(Boolean keepAlive) {
this.keepAlive = keepAlive;
}
public Boolean getReuseAddress() {
return reuseAddress;
}
public void setReuseAddress(Boolean reuseAddress) {
this.reuseAddress = reuseAddress;
}
public Integer getSoLinger() {
return soLinger;
}
public void setSoLinger(Integer soLinger) {
this.soLinger = soLinger;
}
public Boolean getTcpNoDelay() {
return tcpNoDelay;
}
public void setTcpNoDelay(Boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
public Integer getReceiveBufferSize() {
return receiveBufferSize;
}
public void setReceiveBufferSize(Integer receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
public Integer getSendBufferSize() {
return sendBufferSize;
}
public void setSendBufferSize(Integer sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2011-2013 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;
/**
* Table attributes are used for manipulation around table at the startup (create/update/validate).
*
* @author Alex Shvid
*/
public class TableAttributes {
private String entity;
private String name;
public String getEntity() {
return entity;
}
public void setEntity(String entity) {
this.entity = entity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "TableAttributes [entity=" + entity + "]";
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cassandra.config;
package org.springframework.cassandra.config.java;
import org.springframework.cassandra.core.CassandraOperations;
import org.springframework.cassandra.core.CassandraTemplate;

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.xml;
/**
* @author Alex Shvid
* @author David Webb
*/
public final class BeanNames {
private BeanNames() {
}
public static final String CASSANDRA_CLUSTER = "cassandra-cluster";
public static final String CASSANDRA_KEYSPACE = "cassandra-keyspace";
public static final String CASSANDRA_SESSION = "cassandra-session";
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright 2011-2013 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.xml;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cassandra.config.CompressionType;
import org.springframework.cassandra.config.PoolingOptionsConfig;
import org.springframework.cassandra.config.SocketOptionsConfig;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.AuthProvider;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.SocketOptions;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
/**
* Convenient factory for configuring a Cassandra Cluster.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraClusterFactoryBean implements FactoryBean<Cluster>, InitializingBean, DisposableBean,
PersistenceExceptionTranslator {
private static final int DEFAULT_PORT = 9042;
private Cluster cluster;
private String contactPoints;
private int port = DEFAULT_PORT;
private CompressionType compressionType;
private PoolingOptionsConfig localPoolingOptions;
private PoolingOptionsConfig remotePoolingOptions;
private SocketOptionsConfig socketOptions;
private AuthProvider authProvider;
private LoadBalancingPolicy loadBalancingPolicy;
private ReconnectionPolicy reconnectionPolicy;
private RetryPolicy retryPolicy;
private boolean metricsEnabled = true;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
public Cluster getObject() throws Exception {
return cluster;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<? extends Cluster> getObjectType() {
return Cluster.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (!StringUtils.hasText(contactPoints)) {
throw new IllegalArgumentException("at least one server is required");
}
Cluster.Builder builder = Cluster.builder();
builder.addContactPoints(StringUtils.commaDelimitedListToStringArray(contactPoints)).withPort(port);
if (compressionType != null) {
builder.withCompression(convertCompressionType(compressionType));
}
if (localPoolingOptions != null) {
builder.withPoolingOptions(configPoolingOptions(HostDistance.LOCAL, localPoolingOptions));
}
if (remotePoolingOptions != null) {
builder.withPoolingOptions(configPoolingOptions(HostDistance.REMOTE, remotePoolingOptions));
}
if (socketOptions != null) {
builder.withSocketOptions(configSocketOptions(socketOptions));
}
if (authProvider != null) {
builder.withAuthProvider(authProvider);
}
if (loadBalancingPolicy != null) {
builder.withLoadBalancingPolicy(loadBalancingPolicy);
}
if (reconnectionPolicy != null) {
builder.withReconnectionPolicy(reconnectionPolicy);
}
if (retryPolicy != null) {
builder.withRetryPolicy(retryPolicy);
}
if (!metricsEnabled) {
builder.withoutMetrics();
}
Cluster cluster = builder.build();
// initialize property
this.cluster = cluster;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
this.cluster.shutdown();
}
public void setContactPoints(String contactPoints) {
this.contactPoints = contactPoints;
}
public void setPort(int port) {
this.port = port;
}
public void setCompressionType(CompressionType compressionType) {
this.compressionType = compressionType;
}
public void setLocalPoolingOptions(PoolingOptionsConfig localPoolingOptions) {
this.localPoolingOptions = localPoolingOptions;
}
public void setRemotePoolingOptions(PoolingOptionsConfig remotePoolingOptions) {
this.remotePoolingOptions = remotePoolingOptions;
}
public void setSocketOptions(SocketOptionsConfig socketOptions) {
this.socketOptions = socketOptions;
}
public void setAuthProvider(AuthProvider authProvider) {
this.authProvider = authProvider;
}
public void setLoadBalancingPolicy(LoadBalancingPolicy loadBalancingPolicy) {
this.loadBalancingPolicy = loadBalancingPolicy;
}
public void setReconnectionPolicy(ReconnectionPolicy reconnectionPolicy) {
this.reconnectionPolicy = reconnectionPolicy;
}
public void setRetryPolicy(RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy;
}
public void setMetricsEnabled(boolean metricsEnabled) {
this.metricsEnabled = metricsEnabled;
}
private static Compression convertCompressionType(CompressionType type) {
switch (type) {
case NONE:
return Compression.NONE;
case SNAPPY:
return Compression.SNAPPY;
}
throw new IllegalArgumentException("unknown compression type " + type);
}
private static PoolingOptions configPoolingOptions(HostDistance hostDistance, PoolingOptionsConfig config) {
PoolingOptions poolingOptions = new PoolingOptions();
if (config.getMinSimultaneousRequests() != null) {
poolingOptions
.setMinSimultaneousRequestsPerConnectionThreshold(hostDistance, config.getMinSimultaneousRequests());
}
if (config.getMaxSimultaneousRequests() != null) {
poolingOptions
.setMaxSimultaneousRequestsPerConnectionThreshold(hostDistance, config.getMaxSimultaneousRequests());
}
if (config.getCoreConnections() != null) {
poolingOptions.setCoreConnectionsPerHost(hostDistance, config.getCoreConnections());
}
if (config.getMaxConnections() != null) {
poolingOptions.setMaxConnectionsPerHost(hostDistance, config.getMaxConnections());
}
return poolingOptions;
}
private static SocketOptions configSocketOptions(SocketOptionsConfig config) {
SocketOptions socketOptions = new SocketOptions();
if (config.getConnectTimeoutMls() != null) {
socketOptions.setConnectTimeoutMillis(config.getConnectTimeoutMls());
}
if (config.getKeepAlive() != null) {
socketOptions.setKeepAlive(config.getKeepAlive());
}
if (config.getReuseAddress() != null) {
socketOptions.setReuseAddress(config.getReuseAddress());
}
if (config.getSoLinger() != null) {
socketOptions.setSoLinger(config.getSoLinger());
}
if (config.getTcpNoDelay() != null) {
socketOptions.setTcpNoDelay(config.getTcpNoDelay());
}
if (config.getReceiveBufferSize() != null) {
socketOptions.setReceiveBufferSize(config.getReceiveBufferSize());
}
if (config.getSendBufferSize() != null) {
socketOptions.setSendBufferSize(config.getSendBufferSize());
}
return socketOptions;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2011-2012 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.xml;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.CompressionType;
import org.springframework.cassandra.config.PoolingOptionsConfig;
import org.springframework.cassandra.config.SocketOptionsConfig;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;cluster;gt; definitions.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraClusterFactoryBean.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.CASSANDRA_CLUSTER;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String contactPoints = element.getAttribute("contactPoints");
if (StringUtils.hasText(contactPoints)) {
builder.addPropertyValue("contactPoints", contactPoints);
}
String port = element.getAttribute("port");
if (StringUtils.hasText(port)) {
builder.addPropertyValue("port", port);
}
String compression = element.getAttribute("compression");
if (StringUtils.hasText(compression)) {
builder.addPropertyValue("compressionType", CompressionType.valueOf(compression));
}
postProcess(builder, element);
}
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
List<Element> subElements = DomUtils.getChildElements(element);
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("local-pooling-options".equals(name)) {
builder.addPropertyValue("localPoolingOptions", parsePoolingOptions(subElement));
} else if ("remote-pooling-options".equals(name)) {
builder.addPropertyValue("remotePoolingOptions", parsePoolingOptions(subElement));
} else if ("socket-options".equals(name)) {
builder.addPropertyValue("socketOptions", parseSocketOptions(subElement));
}
}
}
private BeanDefinition parsePoolingOptions(Element element) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PoolingOptionsConfig.class);
// TODO
// ParsingUtils.setPropertyValue(builder, element, "min-simultaneous-requests", "minSimultaneousRequests");
// ParsingUtils.setPropertyValue(builder, element, "max-simultaneous-requests", "maxSimultaneousRequests");
// ParsingUtils.setPropertyValue(builder, element, "core-connections", "coreConnections");
// ParsingUtils.setPropertyValue(builder, element, "max-connections", "maxConnections");
return builder.getBeanDefinition();
}
private BeanDefinition parseSocketOptions(Element element) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsConfig.class);
// TODO
// ParsingUtils.setPropertyValue(builder, element, "connect-timeout-mls", "connectTimeoutMls");
// ParsingUtils.setPropertyValue(builder, element, "keep-alive", "keepAlive");
// ParsingUtils.setPropertyValue(builder, element, "reuse-address", "reuseAddress");
// ParsingUtils.setPropertyValue(builder, element, "so-linger", "soLinger");
// ParsingUtils.setPropertyValue(builder, element, "tcp-no-delay", "tcpNoDelay");
// ParsingUtils.setPropertyValue(builder, element, "receive-buffer-size", "receiveBufferSize");
// ParsingUtils.setPropertyValue(builder, element, "send-buffer-size", "sendBufferSize");
return builder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2011-2012 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.xml;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.KeyspaceAttributes;
import org.springframework.cassandra.config.TableAttributes;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;keyspace;gt; definitions.
*
* @author Alex Shvid
*/
public class CassandraKeyspaceParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraSessionFactoryBean.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.CASSANDRA_KEYSPACE;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
if (StringUtils.hasText(name)) {
builder.addPropertyValue("keyspace", name);
}
String clusterRef = element.getAttribute("cassandra-cluster-ref");
if (!StringUtils.hasText(clusterRef)) {
clusterRef = BeanNames.CASSANDRA_CLUSTER;
}
builder.addPropertyReference("cluster", clusterRef);
String converterRef = element.getAttribute("cassandra-converter-ref");
if (StringUtils.hasText(converterRef)) {
builder.addPropertyReference("converter", converterRef);
}
postProcess(builder, element);
}
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
List<Element> subElements = DomUtils.getChildElements(element);
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("keyspace-attributes".equals(name)) {
builder.addPropertyValue("keyspaceAttributes", parseKeyspaceAttributes(subElement));
}
}
}
private BeanDefinition parseKeyspaceAttributes(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(KeyspaceAttributes.class);
// TODO
// ParsingUtils.setPropertyValue(defBuilder, element, "auto", "auto");
// ParsingUtils.setPropertyValue(defBuilder, element, "replication-strategy", "replicationStrategy");
// ParsingUtils.setPropertyValue(defBuilder, element, "replication-factor", "replicationFactor");
// ParsingUtils.setPropertyValue(defBuilder, element, "durable-writes", "durableWrites");
List<Element> subElements = DomUtils.getChildElements(element);
ManagedList<Object> tables = new ManagedList<Object>(subElements.size());
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("table".equals(name)) {
tables.add(parseTable(subElement));
}
}
if (!tables.isEmpty()) {
defBuilder.addPropertyValue("tables", tables);
}
return defBuilder.getBeanDefinition();
}
private BeanDefinition parseTable(Element element) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TableAttributes.class);
// TODO
// ParsingUtils.setPropertyValue(builder, element, "entity", "entity");
// ParsingUtils.setPropertyValue(builder, element, "name", "name");
return builder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2011-2013 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.xml;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* Namespace handler for &lt;cassandra;gt;.
*
* @author Alex Shvid
*/
public class CassandraNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("cluster", new CassandraClusterParser());
registerBeanDefinitionParser("keyspace", new CassandraKeyspaceParser());
registerBeanDefinitionParser("session", new CassandraSessionParser());
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2011-2013 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.xml;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cassandra.config.KeyspaceAttributes;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Session;
/**
* Convenient factory for configuring a Cassandra Session. Session is a thread safe singleton and created per a
* keyspace. So, it is enough to have one session per application.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraSessionFactoryBean implements FactoryBean<Session>, InitializingBean, DisposableBean,
BeanClassLoaderAware, PersistenceExceptionTranslator {
private static final Logger log = LoggerFactory.getLogger(CassandraSessionFactoryBean.class);
public static final String DEFAULT_REPLICATION_STRATEGY = "SimpleStrategy";
public static final int DEFAULT_REPLICATION_FACTOR = 1;
private ClassLoader beanClassLoader;
private Cluster cluster;
private Session session;
private String keyspace;
private KeyspaceAttributes keyspaceAttributes;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public Session getObject() {
return session;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<? extends Session> getObjectType() {
return Session.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (cluster == null) {
throw new IllegalArgumentException("at least one cluster is required");
}
this.session = StringUtils.hasText(this.keyspace) ? cluster.connect(keyspace) : cluster.connect();
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
this.session.shutdown();
}
public void setKeyspace(String keyspace) {
this.keyspace = keyspace;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public void setKeyspaceAttributes(KeyspaceAttributes keyspaceAttributes) {
this.keyspaceAttributes = keyspaceAttributes;
}
private static String compareKeyspaceAttributes(KeyspaceAttributes keyspaceAttributes,
KeyspaceMetadata keyspaceMetadata) {
if (keyspaceAttributes.isDurableWrites() != keyspaceMetadata.isDurableWrites()) {
return "durableWrites";
}
Map<String, String> replication = keyspaceMetadata.getReplication();
String replicationFactorStr = replication.get("replication_factor");
if (replicationFactorStr == null) {
return "replication_factor";
}
try {
int replicationFactor = Integer.parseInt(replicationFactorStr);
if (keyspaceAttributes.getReplicationFactor() != replicationFactor) {
return "replication_factor";
}
} catch (NumberFormatException e) {
return "replication_factor";
}
String attributesStrategy = keyspaceAttributes.getReplicationStrategy();
if (attributesStrategy.indexOf('.') == -1) {
attributesStrategy = "org.apache.cassandra.locator." + attributesStrategy;
}
String replicationStrategy = replication.get("class");
if (!attributesStrategy.equals(replicationStrategy)) {
return "replication_class";
}
return null;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2011-2012 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.xml;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.core.SessionFactoryBean;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;session;gt; definitions.
*
* @author David Webb
*/
public class CassandraSessionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return SessionFactoryBean.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.CASSANDRA_SESSION;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String keyspaceRef = element.getAttribute("cassandra-keyspace-ref");
if (!StringUtils.hasText(keyspaceRef)) {
keyspaceRef = BeanNames.CASSANDRA_KEYSPACE;
}
builder.addPropertyReference("keyspace", keyspaceRef);
postProcess(builder, element);
}
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
}
}

View File

@@ -0,0 +1 @@
http\://www.springframework.org/schema/cassandra=org.springframework.cassandra.config.xml.CassandraNamespaceHandler

View File

@@ -0,0 +1,2 @@
http\://www.springframework.org/schema/cassandra/spring-cassandra-1.0.xsd=org/springframework/cassandra/config/spring-cassandra-1.0.xsd
http\://www.springframework.org/schema/cassandra/spring-cassandra.xsd=org/springframework/cassandra/config/spring-cassandra-1.0.xsd

View File

@@ -0,0 +1,4 @@
# Tooling related information for the cassandra namespace
http\://www.springframework.org/schema/cassandra@name=Spring Cassandra Namespace
http\://www.springframework.org/schema/cassandra@prefix=cassandra
http\://www.springframework.org/schema/cassandra@icon=org/springframework/data/cassandra/config/spring-cassandra.gif

View File

@@ -0,0 +1,453 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/cassandra"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/cassandra"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/tool"
schemaLocation="http://www.springframework.org/schema/tool/spring-tool.xsd" />
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Cassandra support.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="session" type="sessionType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.cassandra.core.SessionFactoryBean"><![CDATA[
Defines a Cassandra Session instance used for accessing Cassandra Keyspace'.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Session" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="cluster" type="clusterType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.core.CassandraClusterFactoryBean"><![CDATA[
Defines a Cassandra Cluster instance used for accessing Cassandra'.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Cluster" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="clusterType">
<xsd:sequence>
<xsd:element name="local-pooling-options" type="poolingOptionsType"
maxOccurs="1" minOccurs="0">
</xsd:element>
<xsd:element name="remote-pooling-options" type="poolingOptionsType"
maxOccurs="1" minOccurs="0">
</xsd:element>
<xsd:element name="socket-options" type="socketOptionsType"
maxOccurs="1" minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation>
The name of the Cassandra Cluster definition (by
default "cassandra-cluster")
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="contactPoints" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma separated hosts to Cassandra servers. Default is localhost
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The port to connect to Cassandra server as native CQL client. Default is 9042
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="compression" default="NONE" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The protocol options compression. Default is 'none'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="NONE">
<xsd:annotation>
<xsd:documentation><![CDATA[
No compression.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="SNAPPY">
<xsd:annotation>
<xsd:documentation><![CDATA[
Uses SNAPPY compression algorithm.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="auth-info-provider" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
AuthInfoProvider implementation.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.AuthInfoProvider" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="load-balancing-policy" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
LoadBalancingPolicy implementation.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="com.datastax.driver.core.policies.LoadBalancingPolicy" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="reconnection-policy" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
ReconnectionPolicy implementation.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="com.datastax.driver.core.policies.ReconnectionPolicy" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="retry-policy" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
RetryPolicy implementation.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="com.datastax.driver.core.policies.RetryPolicy" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="keyspace" type="keyspaceType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.core.CassandraKeyspaceFactoryBean"><![CDATA[
Defines a Cassandra Session instance used for accessing Cassandra Keyspace'.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Session" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="keyspaceType">
<xsd:sequence>
<xsd:element name="keyspace-attributes" type="keyspaceAttributesType"
maxOccurs="1" minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation>
The name of the Keyspace definition (by default
"cassandra-keyspace")
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The keyspace name of the Cassandra database.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-cluster-ref" type="clusterRef"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra Cluster instance. Will default to 'cassandra-cluster'.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="converterRef"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter instance. Default is null.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="clusterRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.Cluster" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
<xsd:simpleType name="converterRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.convert.CassandraConverter" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
<xsd:complexType name="poolingOptionsType">
<xsd:attribute name="min-simultaneous-requests" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
if the utilisation of opened connections drops below by this configured threshold, then cassandra drops connections till core-connections.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-simultaneous-requests" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
If the utilisation of connections reaches this configurable threshold, then cassandra creates more connections up to max-connections.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="core-connections" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
For each host, the driver keeps a core amount of connections open at all time.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-connections" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
More connections are created up to a configurable maximum number of connections.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="socketOptionsType">
<xsd:attribute name="connect-timeout-mls" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets connection timeout for client socket in milliseconds.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keep-alive" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_KEEPALIVE socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reuse-address" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_REUSEADDR socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="so-linger" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_LINGER socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="tcp-no-delay" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_TCPNODELAY socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="receive-buffer-size" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_RCVBUF socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-buffer-size" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Sets the SO_SNDBUF socket option.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="keyspaceAttributesType">
<xsd:sequence>
<xsd:element name="table" type="tableType" maxOccurs="unbounded"
minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="auto" default="validate">
<xsd:annotation>
<xsd:documentation><![CDATA[
The keyspace manipulation operation on startup. Default value is 'validate'.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="validate">
<xsd:annotation>
<xsd:documentation><![CDATA[
Validate the keyspace, makes no changes.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="update">
<xsd:annotation>
<xsd:documentation><![CDATA[
Update the keyspace.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="create">
<xsd:annotation>
<xsd:documentation><![CDATA[
Creates the keyspace, destroying previous data.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="create-drop">
<xsd:annotation>
<xsd:documentation><![CDATA[
Creates and then drop the keyspace at the end of the session.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="replication-stategy" type="xsd:string"
use="optional" default="SimpleStrategy">
<xsd:annotation>
<xsd:documentation><![CDATA[
Replication strategy of the Cassandra keyspace. Default value is 'SimpleStrategy'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replication-factor" type="xsd:string"
use="optional" default="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Replication factor used by the Cassandra keyspace. Default value is '1'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="durable-writes" type="xsd:string"
use="optional" default="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
Support durable writes in the Cassandra keyspace. Default value is 'true'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="tableType">
<xsd:attribute name="entity" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Entity class name.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Table name override.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="sessionType">
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the Session definition; "cassandra-session" by default.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-keyspace-ref" type="keyspaceRef"
use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra Keyspace instance. Will default to 'cassandra-keyspace'.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:simpleType name="keyspaceRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.Session" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:schema>

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

View File

@@ -20,8 +20,7 @@ import java.util.Set;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cassandra.config.AbstractCassandraConfiguration;
import org.springframework.cassandra.core.CassandraOperations;
import org.springframework.cassandra.config.java.AbstractCassandraConfiguration;
import org.springframework.cassandra.core.CassandraTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
@@ -41,7 +40,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
/**

View File

@@ -0,0 +1,340 @@
/*
* Copyright 2011-2013 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.data.cassandra.config;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.SpringDataKeyspace;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.util.CqlUtils;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
/**
* Convenient factory for configuring a Cassandra Session. Session is a thread safe singleton and created per a
* keyspace. So, it is enough to have one session per application.
*
* @author Alex Shvid
*/
public class CassandraKeyspaceFactoryBean implements FactoryBean<SpringDataKeyspace>, InitializingBean, DisposableBean,
BeanClassLoaderAware, PersistenceExceptionTranslator {
private static final Logger log = LoggerFactory.getLogger(CassandraKeyspaceFactoryBean.class);
public static final String DEFAULT_REPLICATION_STRATEGY = "SimpleStrategy";
public static final int DEFAULT_REPLICATION_FACTOR = 1;
private ClassLoader beanClassLoader;
private Cluster cluster;
private Session session;
private String keyspace;
private CassandraConverter converter;
private MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private SpringDataKeyspace keyspaceBean;
private KeyspaceAttributes keyspaceAttributes;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public SpringDataKeyspace getObject() {
return keyspaceBean;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class<? extends Session> getObjectType() {
return Session.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return exceptionTranslator.translateExceptionIfPossible(ex);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (this.converter == null) {
this.converter = getDefaultCassandraConverter();
}
this.mappingContext = this.converter.getMappingContext();
if (cluster == null) {
throw new IllegalArgumentException("at least one cluster is required");
}
Session session = null;
session = cluster.connect();
if (StringUtils.hasText(keyspace)) {
KeyspaceMetadata keyspaceMetadata = cluster.getMetadata().getKeyspace(keyspace.toLowerCase());
boolean keyspaceExists = keyspaceMetadata != null;
boolean keyspaceCreated = false;
if (keyspaceExists) {
log.info("keyspace exists " + keyspaceMetadata.asCQLQuery());
}
if (keyspaceAttributes == null) {
keyspaceAttributes = new KeyspaceAttributes();
}
// drop the old keyspace if needed
if (keyspaceExists && (keyspaceAttributes.isCreate() || keyspaceAttributes.isCreateDrop())) {
log.info("Drop keyspace " + keyspace + " on afterPropertiesSet");
session.execute("DROP KEYSPACE " + keyspace + ";");
keyspaceExists = false;
}
// create the new keyspace if needed
if (!keyspaceExists
&& (keyspaceAttributes.isCreate() || keyspaceAttributes.isCreateDrop() || keyspaceAttributes.isUpdate())) {
String query = String
.format(
"CREATE KEYSPACE %1$s WITH replication = { 'class' : '%2$s', 'replication_factor' : %3$d } AND DURABLE_WRITES = %4$b",
keyspace, keyspaceAttributes.getReplicationStrategy(), keyspaceAttributes.getReplicationFactor(),
keyspaceAttributes.isDurableWrites());
log.info("Create keyspace " + keyspace + " on afterPropertiesSet " + query);
session.execute(query);
keyspaceCreated = true;
}
// update keyspace if needed
if (keyspaceAttributes.isUpdate() && !keyspaceCreated) {
if (compareKeyspaceAttributes(keyspaceAttributes, keyspaceMetadata) != null) {
String query = String
.format(
"ALTER KEYSPACE %1$s WITH replication = { 'class' : '%2$s', 'replication_factor' : %3$d } AND DURABLE_WRITES = %4$b",
keyspace, keyspaceAttributes.getReplicationStrategy(), keyspaceAttributes.getReplicationFactor(),
keyspaceAttributes.isDurableWrites());
log.info("Update keyspace " + keyspace + " on afterPropertiesSet " + query);
session.execute(query);
}
}
// validate keyspace if needed
if (keyspaceAttributes.isValidate()) {
if (!keyspaceExists) {
throw new InvalidDataAccessApiUsageException("keyspace '" + keyspace + "' not found in the Cassandra");
}
String errorField = compareKeyspaceAttributes(keyspaceAttributes, keyspaceMetadata);
if (errorField != null) {
throw new InvalidDataAccessApiUsageException(errorField + " attribute is not much in the keyspace '"
+ keyspace + "'");
}
}
session.execute("USE " + keyspace);
if (!CollectionUtils.isEmpty(keyspaceAttributes.getTables())) {
for (TableAttributes tableAttributes : keyspaceAttributes.getTables()) {
String entityClassName = tableAttributes.getEntity();
Class<?> entityClass = ClassUtils.forName(entityClassName, this.beanClassLoader);
CassandraPersistentEntity<?> entity = determineEntity(entityClass);
String useTableName = tableAttributes.getName() != null ? tableAttributes.getName() : entity.getTable();
if (keyspaceCreated) {
createNewTable(session, useTableName, entity);
} else if (keyspaceAttributes.isUpdate()) {
TableMetadata table = keyspaceMetadata.getTable(useTableName.toLowerCase());
if (table == null) {
createNewTable(session, useTableName, entity);
} else {
// alter table columns
for (String cql : CqlUtils.alterTable(useTableName, entity, table)) {
log.info("Execute on keyspace " + keyspace + " CQL " + cql);
session.execute(cql);
}
}
} else if (keyspaceAttributes.isValidate()) {
TableMetadata table = keyspaceMetadata.getTable(useTableName.toLowerCase());
if (table == null) {
throw new InvalidDataAccessApiUsageException("not found table " + useTableName + " for entity "
+ entityClassName);
}
// validate columns
List<String> alter = CqlUtils.alterTable(useTableName, entity, table);
if (!alter.isEmpty()) {
throw new InvalidDataAccessApiUsageException("invalid table " + useTableName + " for entity "
+ entityClassName + ". modify it by " + alter);
}
}
// System.out.println("tableAttributes, entityClass=" + entityClass + ", table = " + entity.getTable());
}
}
}
// initialize property
this.session = session;
this.keyspaceBean = new SpringDataKeyspace(keyspace, session, converter);
}
private void createNewTable(Session session, String useTableName, CassandraPersistentEntity<?> entity)
throws NoHostAvailableException {
String cql = CqlUtils.createTable(useTableName, entity, converter);
log.info("Execute on keyspace " + keyspace + " CQL " + cql);
session.execute(cql);
for (String indexCQL : CqlUtils.createIndexes(useTableName, entity)) {
log.info("Execute on keyspace " + keyspace + " CQL " + indexCQL);
session.execute(indexCQL);
}
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if (StringUtils.hasText(keyspace) && keyspaceAttributes != null && keyspaceAttributes.isCreateDrop()) {
log.info("Drop keyspace " + keyspace + " on destroy");
session.execute("USE system");
session.execute("DROP KEYSPACE " + keyspace);
}
this.session.shutdown();
}
public void setKeyspace(String keyspace) {
this.keyspace = keyspace;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public void setKeyspaceAttributes(KeyspaceAttributes keyspaceAttributes) {
this.keyspaceAttributes = keyspaceAttributes;
}
public void setConverter(CassandraConverter converter) {
this.converter = converter;
}
private static String compareKeyspaceAttributes(KeyspaceAttributes keyspaceAttributes,
KeyspaceMetadata keyspaceMetadata) {
if (keyspaceAttributes.isDurableWrites() != keyspaceMetadata.isDurableWrites()) {
return "durableWrites";
}
Map<String, String> replication = keyspaceMetadata.getReplication();
String replicationFactorStr = replication.get("replication_factor");
if (replicationFactorStr == null) {
return "replication_factor";
}
try {
int replicationFactor = Integer.parseInt(replicationFactorStr);
if (keyspaceAttributes.getReplicationFactor() != replicationFactor) {
return "replication_factor";
}
} catch (NumberFormatException e) {
return "replication_factor";
}
String attributesStrategy = keyspaceAttributes.getReplicationStrategy();
if (attributesStrategy.indexOf('.') == -1) {
attributesStrategy = "org.apache.cassandra.locator." + attributesStrategy;
}
String replicationStrategy = replication.get("class");
if (!attributesStrategy.equals(replicationStrategy)) {
return "replication_class";
}
return null;
}
CassandraPersistentEntity<?> determineEntity(Class<?> entityClass) {
if (entityClass == null) {
throw new InvalidDataAccessApiUsageException(
"No class parameter provided, entity table name can't be determined!");
}
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (entity == null) {
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
+ entityClass.getName());
}
return entity;
}
private static final CassandraConverter getDefaultCassandraConverter() {
MappingCassandraConverter converter = new MappingCassandraConverter(new CassandraMappingContext());
converter.afterPropertiesSet();
return converter;
}
}

View File

@@ -1,4 +1,4 @@
# Tooling related information for the cassandra namespace
http\://www.springframework.org/schema/data/cassandra@name=Cassandra Namespace
http\://www.springframework.org/schema/data/cassandra@name=Spring Data Cassandra Namespace
http\://www.springframework.org/schema/data/cassandra@prefix=cassandra
http\://www.springframework.org/schema/data/cassandra@icon=org/springframework/data/cassandra/config/spring-cassandra.gif