initial commit

This commit is contained in:
Alex Shvid
2013-11-10 19:51:25 -08:00
parent 276b809dc5
commit 7bf6e948a8
89 changed files with 6442 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
/*
* 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.data.cassandra.config;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.Keyspace;
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.mapping.Table;
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;
/**
* Base class for Spring Data Cassandra configuration using JavaConfig.
*
* @author Alex Shvid
*/
@Configuration
public abstract class AbstractCassandraConfiguration {
/**
* Return the name of the keyspace to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getKeyspaceName();
/**
* Return the {@link Cluster} instance to connect to.
*
* @return
* @throws Exception
*/
@Bean
public abstract Cluster cluster() throws Exception;
/**
* Creates a {@link Session} to be used by the {@link Keyspace}. Will use the {@link Cluster} instance
* configured in {@link #cluster()}.
*
* @see #cluster()
* @see #Keyspace()
* @return
* @throws Exception
*/
@Bean
public Session session() throws Exception {
String keyspace = getKeyspaceName();
if (StringUtils.hasText(keyspace)) {
return cluster().connect(keyspace);
}
else {
return cluster().connect();
}
}
/**
* Creates a {@link Keyspace} to be used by the {@link CassandraTemplate}. Will use the {@link Session} instance
* configured in {@link #session()} and {@link CassandraConverter} configured in {@link #converter()}.
*
* @see #cluster()
* @see #Keyspace()
* @return
* @throws Exception
*/
@Bean
public Keyspace keyspace() throws Exception {
return new Keyspace(getKeyspaceName(), session(), converter());
}
/**
* Return the base package to scan for mapped {@link Table}s. Will return the package name of the configuration
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
* {@link AbstractCassandraConfiguration} the base package will be considered {@code com.acme} unless the method is
* overriden to implement alternate behaviour.
*
* @return the base package to scan for mapped {@link Table} classes or {@literal null} to not enable scanning for
* entities.
*/
protected String getMappingBasePackage() {
return getClass().getPackage().getName();
}
/**
* Creates a {@link CassandraTemplate}.
*
* @return
* @throws Exception
*/
@Bean
public CassandraTemplate cassandraTemplate() throws Exception {
return new CassandraTemplate(keyspace());
}
/**
* Return the {@link MappingContext} instance to map Entities to properties.
*
* @return
* @throws Exception
*/
@Bean
public MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext() {
return new CassandraMappingContext();
}
/**
* Return the {@link CassandraConverter} instance to convert Rows to Objects.
*
* @return
* @throws Exception
*/
@Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
/**
* Scans the mapping base package for classes annotated with {@link Table}.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Table.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
AbstractCassandraConfiguration.class.getClassLoader()));
}
}
return initialEntitySet;
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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.data.cassandra.config;
/**
* @author Alex Shvid
*/
public class BeanNames {
static final String CASSANDRA_CLUSTER = "cassandra-cluster";
static final String CASSANDRA_KEYSPACE = "cassandra-keyspace";
}

View File

@@ -0,0 +1,121 @@
/*
* 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.data.cassandra.config;
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.data.cassandra.core.CassandraClusterFactoryBean;
import org.springframework.data.config.ParsingUtils;
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
*/
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 defBuilder = BeanDefinitionBuilder.genericBeanDefinition(PoolingOptionsConfig.class);
ParsingUtils.setPropertyValue(defBuilder, element, "min-simultaneous-requests", "minSimultaneousRequests");
ParsingUtils.setPropertyValue(defBuilder, element, "max-simultaneous-requests", "maxSimultaneousRequests");
ParsingUtils.setPropertyValue(defBuilder, element, "core-connections", "coreConnections");
ParsingUtils.setPropertyValue(defBuilder, element, "max-connections", "maxConnections");
return defBuilder.getBeanDefinition();
}
private BeanDefinition parseSocketOptions(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsConfig.class);
ParsingUtils.setPropertyValue(defBuilder, element, "connect-timeout-mls", "connectTimeoutMls");
ParsingUtils.setPropertyValue(defBuilder, element, "keep-alive", "keepAlive");
ParsingUtils.setPropertyValue(defBuilder, element, "reuse-address", "reuseAddress");
ParsingUtils.setPropertyValue(defBuilder, element, "so-linger", "soLinger");
ParsingUtils.setPropertyValue(defBuilder, element, "tcp-no-delay", "tcpNoDelay");
ParsingUtils.setPropertyValue(defBuilder, element, "receive-buffer-size", "receiveBufferSize");
ParsingUtils.setPropertyValue(defBuilder, element, "send-buffer-size", "sendBufferSize");
return defBuilder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.data.cassandra.config;
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.data.cassandra.core.CassandraKeyspaceFactoryBean;
import org.springframework.data.config.ParsingUtils;
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 CassandraKeyspaceFactoryBean.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);
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 defBuilder = BeanDefinitionBuilder.genericBeanDefinition(TableAttributes.class);
ParsingUtils.setPropertyValue(defBuilder, element, "entity", "entity");
ParsingUtils.setPropertyValue(defBuilder, element, "name", "name");
return defBuilder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 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());
}
}

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.data.cassandra.config;
/**
* Simple enumeration for the various compression types.
*
* @author Alex Shvid
*/
public enum CompressionType {
none, snappy;
}

View File

@@ -0,0 +1,108 @@
/*
* 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.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.data.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.data.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.data.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

@@ -0,0 +1,67 @@
/*
* 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.convert;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.convert.EntityInstantiators;
/**
* Base class for {@link CassandraConverter} implementations. Sets up a {@link GenericConversionService} and populates basic
* converters.
*
* @author Alex Shvid
*/
public abstract class AbstractCassandraConverter implements CassandraConverter, InitializingBean {
protected final GenericConversionService conversionService;
protected EntityInstantiators instantiators = new EntityInstantiators();
/**
* Creates a new {@link AbstractMongoConverter} using the given {@link GenericConversionService}.
*
* @param conversionService
*/
public AbstractCassandraConverter(GenericConversionService conversionService) {
this.conversionService = conversionService == null ? new DefaultConversionService() : conversionService;
}
/**
* Registers {@link EntityInstantiators} to customize entity instantiation.
*
* @param instantiators
*/
public void setInstantiators(EntityInstantiators instantiators) {
this.instantiators = instantiators == null ? new EntityInstantiators() : instantiators;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.core.convert.MongoConverter#getConversionService()
*/
public ConversionService getConversionService() {
return conversionService;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2010-2011 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.convert;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.convert.EntityConverter;
import com.datastax.driver.core.Row;
/**
* Central Cassandra specific converter interface from Object to Row.
*
* @author Alex Shvid
*/
public interface CassandraConverter extends EntityConverter<CassandraPersistentEntity<?>, CassandraPersistentProperty, Object, Row> {
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 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.data.cassandra.convert;
import java.nio.ByteBuffer;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.util.Assert;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
/**
* {@link PropertyValueProvider} to read property values from a {@link Row}.
*
* @author Alex Shvid
*/
public class CassandraPropertyValueProvider implements PropertyValueProvider<CassandraPersistentProperty> {
private final Row source;
private final SpELExpressionEvaluator evaluator;
/**
* Creates a new {@link CassandraPropertyValueProvider} with the given {@link Row} and {@link DefaultSpELExpressionEvaluator}.
*
* @param source must not be {@literal null}.
* @param evaluator must not be {@literal null}.
*/
public CassandraPropertyValueProvider(Row source, DefaultSpELExpressionEvaluator evaluator) {
Assert.notNull(source);
Assert.notNull(evaluator);
this.source = source;
this.evaluator = evaluator;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty)
*/
@SuppressWarnings("unchecked")
public <T> T getPropertyValue(CassandraPersistentProperty property) {
String expression = property.getSpelExpression();
if (expression != null) {
return evaluator.evaluate(expression);
}
String columnName = property.getColumnName();
if (source.isNull(property.getColumnName())) {
return null;
}
DataType columnType = source.getColumnDefinitions().getType(columnName);
ByteBuffer bytes = source.getBytes(columnName);
return (T) columnType.deserialize(bytes);
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2011-2013 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.data.cassandra.convert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.Row;
/**
* {@link CassandraConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to
* {@link Row}.
*
* @author Alex Shvid
*/
public class MappingCassandraConverter extends AbstractCassandraConverter implements ApplicationContextAware {
protected static final Logger log = LoggerFactory.getLogger(MappingCassandraConverter.class);
protected final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
protected ApplicationContext applicationContext;
private SpELContext spELContext;
private boolean useFieldAccessOnly = true;
/**
* Creates a new {@link MappingCassandraConverter} given the new {@link MappingContext}.
*
* @param mappingContext must not be {@literal null}.
*/
public MappingCassandraConverter(MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext) {
super(new DefaultConversionService());
this.mappingContext = mappingContext;
this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
}
@SuppressWarnings("unchecked")
public <R> R read(Class<R> clazz, Row row) {
TypeInformation<? extends R> type = ClassTypeInformation.from(clazz);
//TypeInformation<? extends R> typeToUse = typeMapper.readType(row, type);
TypeInformation<? extends R> typeToUse = type;
Class<? extends R> rawType = typeToUse.getType();
if (Row.class.isAssignableFrom(rawType)) {
return (R) row;
}
CassandraPersistentEntity<R> persistentEntity = (CassandraPersistentEntity<R>) mappingContext.getPersistentEntity(typeToUse);
if (persistentEntity == null) {
throw new MappingException("No mapping metadata found for " + rawType.getName());
}
return read(persistentEntity, row);
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.EntityConverter#getMappingContext()
*/
public MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> getMappingContext() {
return mappingContext;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.spELContext = new SpELContext(this.spELContext, applicationContext);
}
private <S extends Object> S read(final CassandraPersistentEntity<S> entity, final Row row) {
final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(row, spELContext);
final PropertyValueProvider<CassandraPersistentProperty> propertyProvider = new CassandraPropertyValueProvider(row, evaluator);
PersistentEntityParameterValueProvider<CassandraPersistentProperty> parameterProvider = new PersistentEntityParameterValueProvider<CassandraPersistentProperty>(
entity, propertyProvider, null);
EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
S instance = instantiator.createInstance(entity, parameterProvider);
final BeanWrapper<CassandraPersistentEntity<S>, S> wrapper = BeanWrapper.create(instance, conversionService);
final S result = wrapper.getBean();
// Set properties not already set in the constructor
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
boolean isConstructorProperty = entity.isConstructorArgument(prop);
boolean hasValueForProperty = row.getColumnDefinitions().contains(prop.getColumnName());
if (!hasValueForProperty || isConstructorProperty) {
return;
}
Object obj = propertyProvider.getPropertyValue(prop);
wrapper.setProperty(prop, obj, useFieldAccessOnly);
}
});
return result;
}
public void write(Object source, Row sink) {
// TODO Auto-generated method stub
}
public void setUseFieldAccessOnly(boolean useFieldAccessOnly) {
this.useFieldAccessOnly = useFieldAccessOnly;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 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.data.cassandra.convert;
import java.nio.ByteBuffer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
/**
* {@link PropertyAccessor} to read values from a {@link Row}.
*
* @author Alex Shvid
*/
enum RowReaderPropertyAccessor implements PropertyAccessor {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.expression.PropertyAccessor#getSpecificTargetClasses()
*/
public Class<?>[] getSpecificTargetClasses() {
return new Class<?>[] { Row.class };
}
/*
* (non-Javadoc)
* @see org.springframework.expression.PropertyAccessor#canRead(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String)
*/
public boolean canRead(EvaluationContext context, Object target, String name) {
return ((Row) target).getColumnDefinitions().contains(name);
}
/*
* (non-Javadoc)
* @see org.springframework.expression.PropertyAccessor#read(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String)
*/
public TypedValue read(EvaluationContext context, Object target, String name) {
Row row = (Row) target;
if (row.isNull(name)) {
return TypedValue.NULL;
}
DataType columnType = row.getColumnDefinitions().getType(name);
ByteBuffer bytes = row.getBytes(name);
Object object = columnType.deserialize(bytes);
return new TypedValue(object);
}
/*
* (non-Javadoc)
* @see org.springframework.expression.PropertyAccessor#canWrite(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String)
*/
public boolean canWrite(EvaluationContext context, Object target, String name) {
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.expression.PropertyAccessor#write(org.springframework.expression.EvaluationContext, java.lang.Object, java.lang.String, java.lang.Object)
*/
public void write(EvaluationContext context, Object target, String name, Object newValue) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,261 @@
/*
* 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.core;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.config.CompressionType;
import org.springframework.data.cassandra.config.PoolingOptionsConfig;
import org.springframework.data.cassandra.config.SocketOptionsConfig;
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
*/
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,35 @@
/*
* 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.core;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* Cassandra connection exception.
*
* @author Alex Shvid
*/
public class CassandraConnectionFailureException extends DataAccessResourceFailureException {
public CassandraConnectionFailureException(String msg) {
super(msg);
}
public CassandraConnectionFailureException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2010-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.core;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import com.datastax.driver.core.exceptions.InvalidQueryException;
/**
* Simple {@link PersistenceExceptionTranslator} for Cassandra. Convert the given runtime exception to an appropriate
* exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
*
* @author Alex Shvid
*/
public class CassandraExceptionTranslator implements PersistenceExceptionTranslator {
/*
* (non-Javadoc)
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
// Check for well-known Cassandra subclasses.
if (ex instanceof InvalidQueryException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
// If we get here, we have an exception that resulted from user code,
// rather than the persistence provider, so we return null to indicate
// that translation should not occur.
return null;
}
}

View File

@@ -0,0 +1,345 @@
/*
* 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.core;
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.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.config.KeyspaceAttributes;
import org.springframework.data.cassandra.config.TableAttributes;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
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<Keyspace>,
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 Keyspace keyspaceBean;
private KeyspaceAttributes keyspaceAttributes;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public Keyspace getObject() throws Exception {
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 Keyspace(keyspace, session, converter);
}
private void createNewTable(Session session, String useTableName,
CassandraPersistentEntity<?> entity)
throws NoHostAvailableException {
String cql = CQLUtils.createTable(useTableName, entity);
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

@@ -0,0 +1,143 @@
/*
* 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.core;
import java.util.List;
import org.springframework.data.cassandra.convert.CassandraConverter;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.Update;
/**
* @author Alex Shvid
*/
public interface CassandraOperations {
/**
* The table name used for the specified class by this template.
*
* @param entityClass must not be {@literal null}.
* @return
*/
String getTableName(Class<?> entityClass);
/**
* Execute query and return Cassandra ResultSet
*
* @param query must not be {@literal null}.
* @return
*/
ResultSet executeQuery(String query);
/**
* Execute query and convert ResultSet to the list of entities
*
* @param query must not be {@literal null}.
* @param selectClass must not be {@literal null}, mapped entity type.
* @return
*/
<T> List<T> select(String query, Class<T> selectClass);
/**
* Execute query and convert ResultSet to the entity
*
* @param query must not be {@literal null}.
* @param selectClass must not be {@literal null}, mapped entity type.
* @return
*/
<T> T selectOne(String query, Class<T> selectClass);
/**
* Insert the given object to the table by id.
*
* @param object
*/
void insert(Object entity);
/**
* Insert the given object to the table by id.
*
* @param object
*/
void insert(Object entity, String tableName);
/**
* Remove the given object from the table by id.
*
* @param object
*/
void remove(Object object);
/**
* Removes the given object from the given table.
*
* @param object
* @param table must not be {@literal null} or empty.
*/
void remove(Object object, String tableName);
/**
* Create a table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void createTable(Class<?> entityClass);
/**
* Create a table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
* @param tableName explicit name of the table
*/
void createTable(Class<?> entityClass, String tableName);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void alterTable(Class<?> entityClass);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
* @param tableName explicit name of the table
*/
void alterTable(Class<?> entityClass, String tableName);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param entityClass class that determines metadata of the table to create/drop.
*/
void dropTable(Class<?> entityClass);
/**
* Alter table with the name and fields indicated by the entity class
*
* @param tableName explicit name of the table.
*/
void dropTable(String tableName);
/**
* Returns the underlying {@link CassandraConverter}.
*
* @return
*/
CassandraConverter getConverter();
}

View File

@@ -0,0 +1,32 @@
/*
* 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.core;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Exception thrown when we can't classify a Cassandra exception into one of Spring generic data access exceptions.
*
* @author Alex Shvid
*/
public class CassandraSystemException extends UncategorizedDataAccessException {
public CassandraSystemException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,229 @@
/*
* 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.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.convert.EntityReader;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
/**
* @author Alex Shvid
*/
public class CassandraTemplate implements CassandraOperations {
private final Session session;
private final CassandraConverter cassandraConverter;
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
/**
* Constructor used for a basic template configuration
*
* @param keyspace must not be {@literal null}.
*/
public CassandraTemplate(Keyspace keyspace) {
this.session = keyspace.getSession();
this.cassandraConverter = keyspace.getCassandraConverter();
this.mappingContext = this.cassandraConverter.getMappingContext();
}
public String getTableName(Class<?> entityClass) {
return determineTableName(entityClass);
}
public ResultSet executeQuery(String query) {
try {
return session.execute(query);
} catch (NoHostAvailableException e) {
throw new CassandraConnectionFailureException("no host available", e);
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e);
}
}
public <T> List<T> select(String query, Class<T> selectClass) {
return selectInternal(query, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
public <T> T selectOne(String query, Class<T> selectClass) {
return selectOneInternal(query, new ReadRowCallback<T>(cassandraConverter, selectClass));
}
public void insert(Object entity) {
// TODO Auto-generated method stub
}
public void insert(Object entity, String tableName) {
// TODO Auto-generated method stub
}
public void remove(Object object) {
// TODO Auto-generated method stub
}
public void remove(Object object, String tableName) {
// TODO Auto-generated method stub
}
public void createTable(Class<?> entityClass) {
// TODO Auto-generated method stub
}
public void createTable(Class<?> entityClass, String tableName) {
// TODO Auto-generated method stub
}
public void alterTable(Class<?> entityClass) {
// TODO Auto-generated method stub
}
public void alterTable(Class<?> entityClass, String tableName) {
// TODO Auto-generated method stub
}
public void dropTable(Class<?> entityClass) {
// TODO Auto-generated method stub
}
public void dropTable(String tableName) {
// TODO Auto-generated method stub
}
public CassandraConverter getConverter() {
return cassandraConverter;
}
/**
* Simple internal callback to allow operations on a {@link Row}.
*
* @author Alex Shvid
*/
private interface RowCallback<T> {
T doWith(Row object);
}
/**
* Simple {@link RowCallback} that will transform {@link Row} into the given target type using the given
* {@link EntityReader}.
*
* @author Alex Shvid
*/
private static class ReadRowCallback<T> implements RowCallback<T> {
private final EntityReader<? super T, Row> reader;
private final Class<T> type;
public ReadRowCallback(EntityReader<? super T, Row> reader, Class<T> type) {
Assert.notNull(reader);
Assert.notNull(type);
this.reader = reader;
this.type = type;
}
public T doWith(Row object) {
T source = reader.read(type, object);
return source;
}
}
<T> List<T> selectInternal(String query, ReadRowCallback<T> readRowCallback) {
try {
ResultSet resultSet = session.execute(query);
List<T> result = new ArrayList<T>();
Iterator<Row> iterator = resultSet.iterator();
while(iterator.hasNext()) {
Row row = iterator.next();
result.add(readRowCallback.doWith(row));
}
return result;
} catch (NoHostAvailableException e) {
throw new CassandraConnectionFailureException("no host available", e);
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e);
}
}
<T> T selectOneInternal(String query, ReadRowCallback<T> readRowCallback) {
try {
ResultSet resultSet = session.execute(query);
Iterator<Row> iterator = resultSet.iterator();
if (iterator.hasNext()) {
Row row = iterator.next();
T result = readRowCallback.doWith(row);
if (iterator.hasNext()) {
throw new DuplicateKeyException("found two or more results in query " + query);
}
return result;
}
return null;
} catch (NoHostAvailableException e) {
throw new CassandraConnectionFailureException("no host available", e);
} catch (RuntimeException e) {
throw potentiallyConvertRuntimeException(e);
}
}
String determineTableName(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.getTable();
}
private RuntimeException potentiallyConvertRuntimeException(
RuntimeException ex) {
RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex);
return resolved == null ? ex : resolved;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.core;
import java.nio.ByteBuffer;
import com.datastax.driver.core.DataType;
/**
* Simple Cassandra value of the ByteBuffer with DataType
*
* @author Alex Shvid
*/
public class CassandraValue {
private final ByteBuffer value;
private final DataType type;
public CassandraValue(ByteBuffer value, DataType type) {
this.value = value;
this.type = type;
}
public ByteBuffer getValue() {
return value;
}
public DataType getType() {
return type;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.core;
import org.springframework.data.cassandra.convert.CassandraConverter;
import com.datastax.driver.core.Session;
/**
* Simple Cassandra Keyspace object
*
* @author Alex Shvid
*/
public class Keyspace {
private final String keyspace;
private final Session session;
private final CassandraConverter cassandraConverter;
/**
* Constructor used for a basic keyspace configuration
*
* @param keyspace, system if {@literal null}.
* @param session must not be {@literal null}.
* @param cassandraConverter must not be {@literal null}.
*/
public Keyspace(String keyspace, Session session, CassandraConverter cassandraConverter) {
this.keyspace = keyspace;
this.session = session;
this.cassandraConverter = cassandraConverter;
}
public String getKeyspace() {
return keyspace;
}
public Session getSession() {
return session;
}
public CassandraConverter getCassandraConverter() {
return cassandraConverter;
}
}

View File

@@ -0,0 +1,9 @@
package org.springframework.data.cassandra.cql;
public abstract class CQLBuilder {
public static CreateTable createTable(String tableName) {
return null;
}
}

View File

@@ -0,0 +1,5 @@
package org.springframework.data.cassandra.cql;
public class CreateTable {
}

View File

@@ -0,0 +1,120 @@
/*
* 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.data.cassandra.mapping;
import java.util.Comparator;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.data.cassandra.util.CassandraNamingUtils;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
/**
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific meta-data such as the
* table name.
*
* @author Alex Shvid
*/
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty> implements
CassandraPersistentEntity<T>, ApplicationContextAware {
private final String table;
private final SpelExpressionParser parser;
private final StandardEvaluationContext context;
/**
* Creates a new {@link BasicCassandraPersistentEntity} with the given {@link TypeInformation}. Will default the
* table name to the entities simple type name.
*
* @param typeInformation
*/
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation, CassandraPersistentPropertyComparator.INSTANCE);
this.parser = new SpelExpressionParser();
this.context = new StandardEvaluationContext();
Class<?> rawType = typeInformation.getType();
String fallback = CassandraNamingUtils.getPreferredTableName(rawType);
if (rawType.isAnnotationPresent(Table.class)) {
Table d = rawType.getAnnotation(Table.class);
this.table = StringUtils.hasText(d.name()) ? d.name() : fallback;
} else {
this.table = fallback;
}
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
context.setRootObject(applicationContext);
}
/**
* Returns the table the entity shall be persisted to.
*
* @return
*/
public String getTable() {
Expression expression = parser.parseExpression(table, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
/**
* {@link Comparator} implementation inspecting the {@link CassandraPersistentProperty}'s order.
*
* @author Alex Shvid
*/
static enum CassandraPersistentPropertyComparator implements Comparator<CassandraPersistentProperty> {
INSTANCE;
/*
* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(CassandraPersistentProperty o1, CassandraPersistentProperty o2) {
if (o1.isColumnId()) {
return 1;
}
if (o2.isColumnId()) {
return -1;
}
return o1.getColumnName().compareTo(o2.getColumnName());
}
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.data.cassandra.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
/**
* Cassandra specific {@link org.springframework.data.mapping.model.AnnotationBasedPersistentProperty} implementation.
*
* @author Alex Shvid
*/
public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentProperty<CassandraPersistentProperty> implements
CassandraPersistentProperty {
/**
* Creates a new {@link BasicCassandraPersistentProperty}.
*
* @param field
* @param propertyDescriptor
* @param owner
* @param simpleTypeHolder
*/
public BasicCassandraPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
CassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
}
/**
* Also considers fields that has a RowId annotation.
*
*/
@Override
public boolean isIdProperty() {
if (super.isIdProperty()) {
return true;
}
return getField().isAnnotationPresent(RowId.class);
}
/**
* For dynamic tables returns true if property value is used as column name.
*
* @return
*/
public boolean isColumnId() {
return getField().isAnnotationPresent(ColumnId.class);
}
/**
* Returns the column name to be used to store the value of the property inside the Cassandra.
*
* @return
*/
public String getColumnName() {
Column annotation = getField().getAnnotation(Column.class);
return annotation != null && StringUtils.hasText(annotation.value()) ? annotation.value() : field.getName();
}
/**
* Returns the data type information if exists.
*
* @return
*/
public DataType getDataType() {
Qualify annotation = getField().getAnnotation(Qualify.class);
if (annotation != null && annotation.type() != null) {
return qualifyAnnotatedType(annotation);
}
if (isMap()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
ensureTypeArguments(args.size(), 2);
return DataType.map(autodetectPrimitiveType(args.get(0).getType()), autodetectPrimitiveType(args.get(1).getType()));
}
if (isCollectionLike()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
ensureTypeArguments(args.size(), 1);
if (Set.class.isAssignableFrom(getType())) {
return DataType.set(autodetectPrimitiveType(args.get(0).getType()));
}
else if (List.class.isAssignableFrom(getType())) {
return DataType.list(autodetectPrimitiveType(args.get(0).getType()));
}
}
DataType dataType = CassandraSimpleTypes.autodetectPrimitive(this.getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("only primitive types and Set,List,Map collections are allowed, unknown type for property '" + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
return dataType;
}
private DataType qualifyAnnotatedType(Qualify annotation) {
DataType.Name type = annotation.type();
if (type.isCollection()) {
switch(type) {
case MAP:
ensureTypeArguments(annotation.typeArguments().length, 2);
return DataType.map(resolvePrimitiveType(annotation.typeArguments()[0]),
resolvePrimitiveType(annotation.typeArguments()[1]));
case LIST:
ensureTypeArguments(annotation.typeArguments().length, 1);
return DataType.list(resolvePrimitiveType(annotation.typeArguments()[0]));
case SET:
ensureTypeArguments(annotation.typeArguments().length, 1);
return DataType.set(resolvePrimitiveType(annotation.typeArguments()[0]));
default:
throw new InvalidDataAccessApiUsageException("unknown collection DataType for property '" + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
}
else {
return CassandraSimpleTypes.resolvePrimitive(type);
}
}
/**
* Returns true if the property has secondary index on this column.
*
* @return
*/
public boolean isIndexed() {
return getField().isAnnotationPresent(Index.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
*/
@Override
protected Association<CassandraPersistentProperty> createAssociation() {
return new Association<CassandraPersistentProperty>(this, null);
}
DataType resolvePrimitiveType(DataType.Name typeName) {
DataType dataType = CassandraSimpleTypes.resolvePrimitive(typeName);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("only primitive types are allowed inside collections for the property '" + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
return dataType;
}
DataType autodetectPrimitiveType(Class<?> javaType) {
DataType dataType = CassandraSimpleTypes.autodetectPrimitive(javaType);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("only primitive types are allowed inside collections for the property '" + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
return dataType;
}
void ensureTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException("expected " + expected + " of typed arguments for the property '" + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.data.cassandra.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* {@link CassandraPersistentProperty} caching access to {@link #isIdProperty()} and {@link #getColumnName()}.
*
* @author Alex Shvid
*/
public class CachingCassandraPersistentProperty extends BasicCassandraPersistentProperty {
private Boolean isIdProperty;
private Boolean isColumnId;
private String columnName;
private Boolean isIndexed;
/**
* Creates a new {@link CachingCassandraPersistentProperty}.
*
* @param field
* @param propertyDescriptor
* @param owner
* @param simpleTypeHolder
*/
public CachingCassandraPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
CassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicCassandraPersistentProperty#isIdProperty()
*/
@Override
public boolean isIdProperty() {
if (this.isIdProperty == null) {
this.isIdProperty = super.isIdProperty();
}
return this.isIdProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicCassandraPersistentProperty#isColumnId()
*/
@Override
public boolean isColumnId() {
if (this.isColumnId == null) {
this.isColumnId = super.isColumnId();
}
return this.isColumnId;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicCassandraPersistentProperty#getFieldName()
*/
@Override
public String getColumnName() {
if (this.columnName == null) {
this.columnName = super.getColumnName();
}
return this.columnName;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.mapping.BasicCassandraPersistentProperty#isIndexed()
*/
@Override
public boolean isIndexed() {
if (this.isIndexed == null) {
this.isIndexed = super.isIndexed();
}
return this.isIndexed;
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.data.cassandra.mapping;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
/**
* Default implementation of a {@link MappingContext} for Cassandra using {@link BasicCassandraPersistentEntity} and
* {@link BasicCassandraPersistentProperty} as primary abstractions.
*
* @author Alex Shvid
*/
public class CassandraMappingContext extends AbstractMappingContext<BasicCassandraPersistentEntity<?>, CassandraPersistentProperty>
implements ApplicationContextAware {
private ApplicationContext context;
/**
* Creates a new {@link CassandraMappingContext}.
*/
public CassandraMappingContext() {
setSimpleTypeHolder(CassandraSimpleTypes.HOLDER);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder)
*/
@Override
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
BasicCassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new CachingCassandraPersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.BasicMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation, org.springframework.data.mapping.model.MappingContext)
*/
@Override
protected <T> BasicCassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicCassandraPersistentEntity<T> entity = new BasicCassandraPersistentEntity<T>(typeInformation);
if (context != null) {
entity.setApplicationContext(context);
}
return entity;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
super.setApplicationContext(applicationContext);
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.data.cassandra.mapping;
import org.springframework.data.mapping.PersistentEntity;
/**
* Cassandra specific {@link PersistentEntity} abstraction.
*
* @author Alex Shvid
*/
public interface CassandraPersistentEntity<T> extends PersistentEntity<T, CassandraPersistentProperty> {
/**
* Returns the table the entity shall be persisted to.
*
* @return
*/
String getTable();
}

View File

@@ -0,0 +1,57 @@
/*
* 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.data.cassandra.mapping;
import org.springframework.data.mapping.PersistentProperty;
import com.datastax.driver.core.DataType;
/**
* Cassandra specific {@link org.springframework.data.mapping.PersistentProperty} extension.
*
* @author Alex Shvid
*/
public interface CassandraPersistentProperty extends PersistentProperty<CassandraPersistentProperty> {
/**
* For dynamic tables returns true if property value is used as column name.
*
* @return
*/
boolean isColumnId();
/**
* Returns the name of the field a property is persisted to.
*
* @return
*/
String getColumnName();
/**
* Returns the data type.
*
* @return
*/
DataType getDataType();
/**
* Returns true if the property has secondary index on this column.
*
* @return
*/
boolean isIndexed();
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2010-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.mapping;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.DataType;
/**
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Cassandra specific simple types.
*
* @author Alex Shvid
*/
public class CassandraSimpleTypes {
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
private static final Map<Class<?>, DataType> javaClassToDataType = new HashMap<Class<?>, DataType>();
private static final Map<DataType.Name, DataType> nameToDataType = new HashMap<DataType.Name, DataType>();
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
Set<Class<?>> simpleTypes = new HashSet<Class<?>>();
for (DataType dataType : DataType.allPrimitiveTypes()) {
simpleTypes.add(dataType.asJavaClass());
Class<?> javaClass = dataType.asJavaClass();
javaClassToDataType.put(javaClass, dataType);
Class<?> primitiveJavaClass = primitiveWrapperTypeMap.get(javaClass);
if (primitiveJavaClass != null) {
javaClassToDataType.put(primitiveJavaClass, dataType);
}
nameToDataType.put(dataType.getName(), dataType);
}
javaClassToDataType.put(String.class, DataType.text());
CASSANDRA_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
private static final Set<Class<?>> CASSANDRA_SIMPLE_TYPES;
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(CASSANDRA_SIMPLE_TYPES, true);
private CassandraSimpleTypes() {
}
public static DataType resolvePrimitive(DataType.Name name) {
return nameToDataType.get(name);
}
public static DataType autodetectPrimitive(Class<?> javaClass) {
return javaClassToDataType.get(javaClass);
}
public static DataType.Name[] convertPrimitiveTypeArguments(List<TypeInformation<?>> arguments) {
DataType.Name[] result = new DataType.Name[arguments.size()];
for (int i = 0; i != result.length; ++i) {
TypeInformation<?> type = arguments.get(i);
DataType dataType = autodetectPrimitive(type.getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("not found appropriate primitive DataType for type = '" + type.getType());
}
result[i] = dataType.getName();
}
return result;
}
}

View File

@@ -0,0 +1,23 @@
package org.springframework.data.cassandra.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Annotation to define custom metadata for document fields.
*
* @author Alex Shvid
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
/**
* The name of the column in the table.
*
* @return
*/
String value() default "";
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2010-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.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Uses in dynamic tables where column names are values of this field.
* Usually it is a Date/Time field or UUIDTime field.
*
* @author Alex Shvid
*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE})
public @interface ColumnId {
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2010-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.mapping;
import com.datastax.driver.core.DataType;
/**
* Uses to transfer DataType and attributes for the property.
*
* @author Alex Shvid
*/
public class DataTypeInformation {
public static DataType.Name[] EMPTY_ATTRIBUTES = {};
private DataType.Name typeName;
private DataType.Name[] typeAttributes;
public DataTypeInformation(DataType.Name typeName) {
this(typeName, EMPTY_ATTRIBUTES);
}
public DataTypeInformation(DataType.Name typeName, DataType.Name[] typeAttributes) {
this.typeName = typeName;
this.typeAttributes = typeAttributes;
}
public DataType.Name getTypeName() {
return typeName;
}
public void setTypeName(DataType.Name typeName) {
this.typeName = typeName;
}
public DataType.Name[] getTypeAttributes() {
return typeAttributes;
}
public void setTypeAttributes(DataType.Name[] typeAttributes) {
this.typeAttributes = typeAttributes;
}
public String toCQL() {
if (typeAttributes.length == 0) {
return typeName.name();
}
else {
StringBuilder str = new StringBuilder();
str.append(typeName.name());
str.append('<');
for (int i = 0; i != typeAttributes.length; ++i) {
if (i != 0) {
str.append(',');
}
str.append(typeAttributes[i].name());
}
str.append('>');
return str.toString();
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2010-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.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Identifies a secondary index in the table. Usually it is a field with common dublicate values
* for the hole table. such as city, place, educationType, state flags ant etc.
*
* Using unique fields is not common and has overhead, such as email, username and etc.
*
* @author Alex Shvid
*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE})
public @interface Index {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2010-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.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.datastax.driver.core.DataType;
/**
* Qualifies data type as Cassandra type.
*
* @author Alex Shvid
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualify {
DataType.Name type();
DataType.Name[] typeArguments() default {};
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2010-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.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Id;
/**
* Identifies row ID in the Cassandra table. Same as @org.springframework.data.annotation.Id
*
* @author Alex Shvid
*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.FIELD,ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Id
public @interface RowId {
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2010-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.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Persistent;
/**
* Identifies a domain object to be persisted to Cassandra as a table.
*
* @author Alex Shvid
*/
@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Table {
String name() default "";
}

View File

@@ -0,0 +1,178 @@
package org.springframework.data.cassandra.util;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TableMetadata;
public abstract class CQLUtils {
public static String createTable(String tableName, final CassandraPersistentEntity<?> entity) {
final StringBuilder str = new StringBuilder();
str.append("CREATE TABLE ");
str.append(tableName);
str.append('(');
final List<String> ids = new ArrayList<String>();
final List<String> idColumns = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
if (str.charAt(str.length()-1) != '(') {
str.append(',');
}
String columnName = prop.getColumnName();
str.append(columnName);
str.append(' ');
DataType dataType = prop.getDataType();
str.append(toCQL(dataType));
if (prop.isIdProperty()) {
ids.add(prop.getColumnName());
}
if (prop.isColumnId()) {
idColumns.add(prop.getColumnName());
}
}
});
if (ids.isEmpty()) {
throw new InvalidDataAccessApiUsageException("not found primary ID in the entity " + entity.getType());
}
str.append(",PRIMARY KEY(");
if (ids.size() > 1) {
str.append('(');
}
for (String id: ids) {
if (str.charAt(str.length()-1) != '(') {
str.append(',');
}
str.append(id);
}
if (ids.size() > 1) {
str.append(')');
}
for (String id: idColumns) {
str.append(',');
str.append(id);
}
str.append("));");
return str.toString();
}
public static List<String> createIndexes(final String tableName, final CassandraPersistentEntity<?> entity) {
final List<String> result = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
if (prop.isIndexed()) {
final StringBuilder str = new StringBuilder();
str.append("CREATE INDEX ON ");
str.append(tableName);
str.append(" (");
str.append(prop.getColumnName());
str.append(");");
result.add(str.toString());
}
}
});
return result;
}
public static List<String> alterTable(final String tableName, final CassandraPersistentEntity<?> entity, final TableMetadata table) {
final List<String> result = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
String columnName = prop.getColumnName();
DataType columnDataType = prop.getDataType();
ColumnMetadata columnMetadata = table.getColumn(columnName.toLowerCase());
if (columnMetadata != null && columnDataType.equals(columnMetadata.getType())) {
return;
}
final StringBuilder str = new StringBuilder();
str.append("ALTER TABLE ");
str.append(tableName);
if (columnMetadata == null) {
str.append(" ADD ");
}
else {
str.append(" ALTER ");
}
str.append(columnName);
str.append(' ');
if (columnMetadata != null) {
str.append("TYPE ");
}
str.append(toCQL(columnDataType));
str.append(';');
result.add(str.toString());
}
});
//System.out.println("CQL=" + table.asCQLQuery());
return result;
}
public static String toCQL(DataType dataType) {
if (dataType.getTypeArguments().isEmpty()) {
return dataType.getName().name();
}
else {
StringBuilder str = new StringBuilder();
str.append(dataType.getName().name());
str.append('<');
for (DataType argDataType : dataType.getTypeArguments()) {
if (str.charAt(str.length()-1) != '<') {
str.append(',');
}
str.append(argDataType.getName().name());
}
str.append('>');
return str.toString();
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2011 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.util;
/**
* Helper class featuring helper methods for working with Cassandra tables.
* Mainly intended for internal use within the framework.
*
* @author Alex Shvid
*/
public abstract class CassandraNamingUtils {
/**
* Private constructor to prevent instantiation.
*/
private CassandraNamingUtils() {
}
/**
* Obtains the table name to use for the provided class
*
* @param entityClass The class to determine the preferred table name for
* @return The preferred collection name
*/
public static String getPreferredTableName(Class<?> entityClass) {
return entityClass.getSimpleName().toLowerCase();
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,404 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/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/data/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 the Spring Data Cassandra support.
]]></xsd:documentation>
</xsd:annotation>
<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">
<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:schema>

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B