DATACASS-1 - WIP before merging from master

This commit is contained in:
Matthew Adams
2014-01-20 11:42:16 -06:00
parent 5b9f2850fa
commit a3390c3bf4
29 changed files with 1337 additions and 330 deletions

View File

@@ -27,7 +27,6 @@ import org.springframework.cassandra.core.CassandraTemplate;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
@@ -46,12 +45,12 @@ public class CassandraSessionFactoryBean implements FactoryBean<Session>, Initia
private static final Logger log = LoggerFactory.getLogger(CassandraSessionFactoryBean.class);
private Cluster cluster;
private Session session;
private String keyspaceName;
private List<String> startupScripts = new ArrayList<String>();
private List<String> shutdownScripts = new ArrayList<String>();
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
protected Cluster cluster;
protected Session session;
protected String keyspaceName;
protected List<String> startupScripts = new ArrayList<String>();
protected List<String> shutdownScripts = new ArrayList<String>();
protected final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
@Override
public Session getObject() {

View File

@@ -25,6 +25,7 @@ import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -53,39 +54,45 @@ public class CassandraSessionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String keyspaceName = element.getAttribute("keyspace-name");
if (!StringUtils.hasText(keyspaceName)) {
keyspaceName = null;
}
builder.addPropertyValue("keyspaceName", keyspaceName);
parseKeyspaceName(element, builder);
parseClusterRef(element, builder);
parseScripts(element, builder, "startup-cql", "startupScript");
parseScripts(element, builder, "shutdown-cql", "shutdownScript");
}
protected void parseScripts(Element element, BeanDefinitionBuilder builder, String elementName, String propertyName) {
List<String> scripts = parseScripts(element, elementName);
builder.addPropertyValue(propertyName, scripts);
}
protected void parseClusterRef(Element element, BeanDefinitionBuilder builder) {
String clusterRef = element.getAttribute("cluster-ref");
if (!StringUtils.hasText(clusterRef)) {
clusterRef = BeanNames.CASSANDRA_CLUSTER;
}
builder.addPropertyReference("cluster", clusterRef);
parseChildElements(element, builder);
}
protected void parseChildElements(Element element, BeanDefinitionBuilder builder) {
protected void parseKeyspaceName(Element element, BeanDefinitionBuilder builder) {
List<String> scripts = parseScripts(element, "startup-cql");
builder.addPropertyValue("startupScripts", scripts);
scripts = parseScripts(element, "shutdown-cql");
builder.addPropertyValue("shutdownScripts", scripts);
String keyspaceName = element.getAttribute("keyspace-name");
if (!StringUtils.hasText(keyspaceName)) {
keyspaceName = null;
}
builder.addPropertyValue("keyspaceName", keyspaceName);
}
protected List<String> parseScripts(Element element, String elementName) {
NodeList nodes = element.getElementsByTagName("startup-cql");
NodeList nodes = element.getElementsByTagName(elementName);
int length = nodes.getLength();
List<String> scripts = new ArrayList<String>(length);
for (int i = 0; i < length; i++) {
Element script = (Element) nodes.item(i);
scripts.add(script.getTextContent());
scripts.add(DomUtils.getTextValue(script));
}
return scripts;

View File

@@ -357,7 +357,7 @@ Arbitrary CQL script to be executed against the session's keyspace during bean d
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -0,0 +1,87 @@
package org.springframework.data.cassandra.config;
import java.util.Collection;
import org.springframework.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.util.Assert;
import com.datastax.driver.core.TableMetadata;
public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean {
protected SchemaAction schemaAction;
protected CassandraAdminTemplate admin;
protected CassandraConverter converter;
protected CassandraMappingContext mappingContext;
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
admin = new CassandraAdminTemplate(session);
admin.setCassandraConverter(converter);
performSchemaAction();
}
protected void performSchemaAction() {
boolean dropTables = false;
boolean dropUnused = false;
switch (schemaAction) {
case NONE:
return;
case RECREATE_DROP_UNUSED:
dropUnused = true;
// don't break!
case RECREATE:
dropTables = true;
// don't break!
case CREATE:
createTables(dropTables, dropUnused);
}
}
protected void createTables(boolean dropTables, boolean dropUnused) {
for (TableMetadata table : session.getCluster().getMetadata().getKeyspace(keyspaceName).getTables()) {
if (dropTables) {
if (dropUnused || mappingContext.usesTable(table)) {
admin.dropTable(table.getName());
}
}
}
Collection<? extends CassandraPersistentEntity<?>> entities = converter.getMappingContext().getPersistentEntities();
for (CassandraPersistentEntity<?> entity : entities) {
admin.createTable(false, entity.getTableName(), entity.getType(), null /* TODO */);
}
}
public SchemaAction getSchemaAction() {
return schemaAction;
}
public void setSchemaAction(SchemaAction schemaAction) {
Assert.notNull(schemaAction);
this.schemaAction = schemaAction;
}
public CassandraConverter getConverter() {
return converter;
}
public void setConverter(CassandraConverter converter) {
Assert.notNull(converter);
this.converter = converter;
this.mappingContext = converter.getCassandraMappingContext();
}
}

View File

@@ -17,54 +17,10 @@ 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 extends org.springframework.cassandra.config.KeyspaceAttributes {
/*
* 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 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 Collection<TableAttributes> getTables() {
return tables;
}
@@ -72,5 +28,4 @@ public class KeyspaceAttributes extends org.springframework.cassandra.config.Key
public void setTables(Collection<TableAttributes> tables) {
this.tables = tables;
}
}

View File

@@ -0,0 +1,45 @@
package org.springframework.data.cassandra.config;
/**
* Enum identifying any schema actions to take at startup.
*
* @author Matthew T. Adams
*/
public enum SchemaAction {
/**
* Take no schema actions.
*/
NONE,
/**
* Create each table as necessary. Fail if a table already exists.
*/
CREATE,
/**
* Create each table as necessary, dropping the table first if it exists.
*/
RECREATE,
/**
* Drop <em>all</em> tables in the keyspace, then create each table as necessary.
*/
RECREATE_DROP_UNUSED;
// TODO:
// /**
// * Validate that each required table and column exists. Fail if any required table or column does not exists.
// */
// VALIDATE("VALIDATE"),
//
// /**
// * Alter or create each table and column as necessary, leaving unused tables and columns untouched.
// */
// UPDATE("UPDATE"),
//
// /**
// * Alter or create each table and column as necessary, removing unused tables and columns.
// */
// UPDATE_DROP_UNUNSED("UPDATE_DROP_UNUSED");
}

View File

@@ -31,8 +31,7 @@ import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraAdminOperations;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
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.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
@@ -74,9 +73,8 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
* @throws ClassNotFoundException
*/
@Bean
public MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> cassandraMappingContext()
throws ClassNotFoundException {
CassandraMappingContext context = new CassandraMappingContext();
public CassandraMappingContext cassandraMappingContext() throws ClassNotFoundException {
DefaultCassandraMappingContext context = new DefaultCassandraMappingContext();
context.setInitialEntitySet(getInitialEntitySet());
return context;
}

View File

@@ -13,9 +13,7 @@
* 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;
package org.springframework.data.cassandra.config.xml;
/**
* Namespace handler for &lt;cassandra&gt;.
@@ -23,10 +21,10 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
* @author Alex Shvid
*/
public class CassandraNamespaceHandler extends NamespaceHandlerSupport {
public class CassandraNamespaceHandler extends org.springframework.cassandra.config.xml.CassandraNamespaceHandler {
@Override
public void init() {
// registerBeanDefinitionParser("keyspace", new CassandraKeyspaceParser());
super.init();
}
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.cassandra.convert;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
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.convert.EntityConverter;
@@ -24,10 +24,10 @@ import org.springframework.data.convert.EntityConverter;
* Central Cassandra specific converter interface from Object to Row.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface CassandraConverter extends
EntityConverter<CassandraPersistentEntity<?>, CassandraPersistentProperty, Object, Object> {
// TODO: move this method to a more appropriate location
CreateTableSpecification getCreateTableSpecification(CassandraPersistentEntity<?> entity);
CassandraMappingContext getCassandraMappingContext();
}

View File

@@ -23,6 +23,7 @@ import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.support.DefaultConversionService;
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.convert.EntityInstantiator;
@@ -49,13 +50,14 @@ import com.datastax.driver.core.querybuilder.Update;
* {@link Row}.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class MappingCassandraConverter extends AbstractCassandraConverter implements CassandraConverter,
ApplicationContextAware, BeanClassLoaderAware {
protected static final Logger log = LoggerFactory.getLogger(MappingCassandraConverter.class);
protected final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
protected final CassandraMappingContext mappingContext;
protected ApplicationContext applicationContext;
private SpELContext spELContext;
private boolean useFieldAccessOnly = true;
@@ -63,12 +65,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
private ClassLoader beanClassLoader;
/**
* Creates a new {@link MappingCassandraConverter} given the new {@link MappingContext}.
* Creates a new {@link MappingCassandraConverter} with the given {@link CassandraMappingContext}.
*
* @param mappingContext must not be {@literal null}.
*/
public MappingCassandraConverter(
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext) {
public MappingCassandraConverter(CassandraMappingContext mappingContext) {
super(new DefaultConversionService());
this.mappingContext = mappingContext;
this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
@@ -80,7 +81,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
Class<R> beanClassLoaderClass = transformClassToBeanClassLoaderClass(clazz);
TypeInformation<? extends R> type = ClassTypeInformation.from(beanClassLoaderClass);
// TypeInformation<? extends R> typeToUse = typeMapper.readType(row, type);
TypeInformation<? extends R> typeToUse = type;
Class<? extends R> rawType = typeToUse.getType();
@@ -108,12 +108,13 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
this.spELContext = new SpELContext(this.spELContext, applicationContext);
}
protected <S extends Object> S readEntityFromRow(final CassandraPersistentEntity<S> entity, final Row row) {
protected <S> S readEntityFromRow(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);
@@ -144,7 +145,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
if (prop.isCompositePrimaryKey()) {
// TODO: handle composite primary key properties via recursion into this method
// handle composite primary key properties via recursion into this method
throw new UnsupportedOperationException("composite primary keys are TODO");
}
boolean hasValueForProperty = row.getColumnDefinitions().contains(prop.getColumnName());
@@ -268,55 +270,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
@Override
public CreateTableSpecification getCreateTableSpecification(CassandraPersistentEntity<?> entity) {
final CreateTableSpecification spec = new CreateTableSpecification();
spec.name(entity.getTableName());
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
if (prop.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> pkEntity = mappingContext.getPersistentEntity(prop.getRawType());
pkEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty pkProp) {
if (pkProp.isPartitionKeyColumn()) {
spec.partitionKeyColumn(pkProp.getColumnName(), pkProp.getDataType());
} else {
spec.clusteredKeyColumn(pkProp.getColumnName(), pkProp.getDataType(), pkProp.getPrimaryKeyOrdering());
}
}
});
} else {
if (prop.isIdProperty()) {
spec.partitionKeyColumn(prop.getColumnName(), prop.getDataType());
} else {
spec.column(prop.getColumnName(), prop.getDataType());
}
}
}
});
if (spec.getPartitionKeyColumns().isEmpty()) {
throw new MappingException("not found partition key in the entity " + entity.getType());
}
return spec;
}
@SuppressWarnings("unchecked")
private <T> Class<T> transformClassToBeanClassLoaderClass(Class<T> entity) {
try {
@@ -334,4 +287,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
}
@Override
public CassandraMappingContext getCassandraMappingContext() {
return mappingContext;
}
}

View File

@@ -6,6 +6,8 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cassandra.core.SessionCallback;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.support.CassandraAccessor;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.cassandra.support.exception.CassandraTableExistsException;
@@ -13,10 +15,9 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.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.Assert;
import com.datastax.driver.core.ResultSet;
@@ -32,7 +33,7 @@ public class CassandraAdminTemplate extends CassandraAccessor implements Cassand
private Session session;
private CassandraConverter converter;
private MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private CassandraMappingContext mappingContext;
private final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
@@ -45,34 +46,32 @@ public class CassandraAdminTemplate extends CassandraAccessor implements Cassand
setSession(session);
}
protected CassandraAdminTemplate setCassandraConverter(CassandraConverter converter) {
public void setCassandraConverter(CassandraConverter converter) {
Assert.notNull(converter);
this.converter = converter;
return setMappingContext(converter.getMappingContext());
setMappingContext(converter.getCassandraMappingContext());
}
protected CassandraAdminTemplate setMappingContext(
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext) {
protected void setMappingContext(CassandraMappingContext mappingContext) {
Assert.notNull(mappingContext);
return this;
this.mappingContext = mappingContext;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraAdminOperations#createTable(boolean, java.lang.String, java.lang.Class, java.util.Map)
*/
@Override
public boolean createTable(boolean ifNotExists, final String tableName, Class<?> entityClass,
Map<String, Object> optionsByName) {
try {
final CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
execute(new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
String cql = CqlUtils.createTable(tableName, entity, converter);
String cql = new CreateTableCqlGenerator(mappingContext.getCreateTableSpecificationFor(entity)).toCql();
log.info("CREATE TABLE CQL -> " + cql);
s.execute(cql);
return null;
}
@@ -120,6 +119,7 @@ public class CassandraAdminTemplate extends CassandraAccessor implements Cassand
execute(new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
for (String q : queryList) {
@@ -179,6 +179,7 @@ public class CassandraAdminTemplate extends CassandraAccessor implements Cassand
return execute(new SessionCallback<TableMetadata>() {
@Override
public TableMetadata doInSession(Session s) throws DataAccessException {
return s.getCluster().getMetadata().getKeyspace(keyspace).getTable(tableName);

View File

@@ -39,9 +39,10 @@ import org.springframework.util.StringUtils;
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty> implements
CassandraPersistentEntity<T>, ApplicationContextAware {
private final String table;
private final SpelExpressionParser parser;
private final StandardEvaluationContext context;
private String table;
private final SpelExpressionParser spelParser;
private final StandardEvaluationContext spelContext;
private final Class<T> type;
/**
* Creates a new {@link BasicCassandraPersistentEntity} with the given {@link TypeInformation}. Will default the table
@@ -53,25 +54,32 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
super(typeInformation, DefaultCassandraPersistentPropertyColumnComparator.IT);
this.parser = new SpelExpressionParser();
this.context = new StandardEvaluationContext();
this.spelParser = new SpelExpressionParser();
this.spelContext = new StandardEvaluationContext();
Class<?> rawType = typeInformation.getType();
Table anno = rawType.getAnnotation(Table.class);
this.type = typeInformation.getType();
determineTableName();
}
protected void determineTableName() {
Table anno = type.getAnnotation(Table.class);
this.table = anno != null && StringUtils.hasText(anno.value()) ? anno.value() : CassandraNamingUtils
.getPreferredTableName(rawType);
.getPreferredTableName(type);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context.addPropertyAccessor(new BeanFactoryAccessor());
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
context.setRootObject(applicationContext);
spelContext.addPropertyAccessor(new BeanFactoryAccessor());
spelContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
spelContext.setRootObject(applicationContext);
}
@Override
public String getTableName() {
Expression expression = parser.parseExpression(table, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
Expression expression = spelParser.parseExpression(table, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(spelContext, String.class);
}
}

View File

@@ -52,16 +52,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
super(field, propertyDescriptor, owner, simpleTypeHolder);
}
@Override
public boolean isIdProperty() {
if (super.isIdProperty()) {
return true;
}
return isAnnotationPresent(PrimaryKey.class);
}
@Override
public boolean isCompositePrimaryKey() {
return getField().getType().isAnnotationPresent(PrimaryKeyClass.class);

View File

@@ -1,76 +1,29 @@
/*
* 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.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.TableMetadata;
/**
* Default implementation of a {@link MappingContext} for Cassandra using {@link CassandraPersistentEntity} and
* {@link CassandraPersistentProperty} as primary abstractions.
* A {@link MappingContext} for Cassandra.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraMappingContext extends
AbstractMappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty> implements
ApplicationContextAware {
private ApplicationContext context;
public interface CassandraMappingContext extends
MappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty> {
/**
* Creates a new {@link CassandraMappingContext}.
* Returns a {@link CreateTableSpecification} for the given entity, including all mapping information.
*
* @param The entity. May not be null.
*/
public CassandraMappingContext() {
setSimpleTypeHolder(new CassandraSimpleTypeHolder());
}
CreateTableSpecification getCreateTableSpecificationFor(CassandraPersistentEntity<?> entity);
@Override
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
CassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return createPersistentProperty(field, descriptor, owner, (CassandraSimpleTypeHolder) simpleTypeHolder);
}
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
CassandraPersistentEntity<?> owner, CassandraSimpleTypeHolder simpleTypeHolder) {
return new CachingCassandraPersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
@Override
protected <T> CassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicCassandraPersistentEntity<T> entity = new BasicCassandraPersistentEntity<T>(typeInformation);
if (context != null) {
entity.setApplicationContext(context);
}
return entity;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
/**
* Returns whether this mapping context has any entities mapped to the given table.
*
* @param table May not be null.
*/
boolean usesTable(TableMetadata table);
}

View File

@@ -0,0 +1,151 @@
/*
* 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.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.datastax.driver.core.TableMetadata;
/**
* Default implementation of a {@link MappingContext} for Cassandra using {@link CassandraPersistentEntity} and
* {@link CassandraPersistentProperty} as primary abstractions.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class DefaultCassandraMappingContext extends
AbstractMappingContext<CassandraPersistentEntity<?>, CassandraPersistentProperty> implements
CassandraMappingContext, ApplicationContextAware {
protected ApplicationContext context;
protected Map<String, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<String, Set<CassandraPersistentEntity<?>>>();
/**
* Creates a new {@link DefaultCassandraMappingContext}.
*/
public DefaultCassandraMappingContext() {
setSimpleTypeHolder(new CassandraSimpleTypeHolder());
}
@Override
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
CassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return createPersistentProperty(field, descriptor, owner, (CassandraSimpleTypeHolder) simpleTypeHolder);
}
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
CassandraPersistentEntity<?> owner, CassandraSimpleTypeHolder simpleTypeHolder) {
return new CachingCassandraPersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
@Override
protected <T> CassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicCassandraPersistentEntity<T> entity = new BasicCassandraPersistentEntity<T>(typeInformation);
if (context != null) {
entity.setApplicationContext(context);
}
Set<CassandraPersistentEntity<?>> entities = entitySetsByTableName.get(entity.getTableName());
if (entities == null) {
entities = new HashSet<CassandraPersistentEntity<?>>();
}
entities.add(entity);
entitySetsByTableName.put(entity.getTableName(), entities);
return entity;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@Override
public boolean usesTable(TableMetadata table) {
return entitySetsByTableName.containsKey(table.getName());
}
@Override
public CreateTableSpecification getCreateTableSpecificationFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity);
final CreateTableSpecification spec = new CreateTableSpecification();
spec.name(entity.getTableName());
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
if (prop.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> pkEntity = getPersistentEntity(prop.getRawType());
pkEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty pkProp) {
if (pkProp.isPartitionKeyColumn()) {
spec.partitionKeyColumn(pkProp.getColumnName(), pkProp.getDataType());
} else {
spec.clusteredKeyColumn(pkProp.getColumnName(), pkProp.getDataType(), pkProp.getPrimaryKeyOrdering());
}
}
});
} else {
if (prop.isIdProperty()) {
spec.partitionKeyColumn(prop.getColumnName(), prop.getDataType());
} else {
spec.column(prop.getColumnName(), prop.getDataType());
}
}
}
});
if (spec.getPartitionKeyColumns().isEmpty()) {
throw new MappingException("no partition key columns found in the entity " + entity.getType());
}
return spec;
}
}

View File

@@ -0,0 +1,51 @@
package org.springframework.data.cassandra.mapping;
import java.util.Set;
import javax.validation.constraints.NotNull;
/**
* Interface that stores information about the mapping between a table and the entity or the entities that are mapped to
* it.
*
* @author Matthew T. Adams
*/
public interface TableMapping {
/**
* Convenience method to return the only member of the set of entity classes. This method can be used when the caller
* knows that there is only one entity class in this mapping. If there are multiple and this method is called, an
* {@link IllegalStateException} is thrown.
*/
@NotNull
Class<?> getEntityClass();
/**
* Convenience method to set this mapping to use a single entity class.
*
* @param entityClass The class; may not be null.
*/
void setEntityClass(@NotNull Class<?> entityClass);
/**
* Sets the set of entity classes of this mapping.
*/
void setEntityClasses(Set<Class<?>> entityClasses);
/**
* Returns the set of entity classes of this mapping. Never returns null.
*/
@NotNull
Set<Class<?>> getEntityClasses();
/**
* Returns the name of this mapping. Never returns null.
*/
@NotNull
String getTableName();
/**
* Sets the table name of this mapping.
*/
void setTableName(@NotNull String tableName);
}

View File

@@ -0,0 +1,97 @@
package org.springframework.data.cassandra.mapping;
import java.util.HashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Default implementation of {@link TableMapping}.
*
* @author Matthew T. Adams
*/
public class TableMappingImpl implements TableMapping {
private Set<Class<?>> entityClasses = new HashSet<Class<?>>();
private String tableName;
public TableMappingImpl(Class<?> entityClass, String tableName) {
this(tableName);
setEntityClass(entityClass);
}
public TableMappingImpl(Set<Class<?>> entityClasses, String tableName) {
this(tableName);
setEntityClasses(entityClasses);
}
protected TableMappingImpl(String tableName) {
setTableName(tableName);
}
@Override
public Class<?> getEntityClass() {
if (entityClasses.size() != 1) {
throw new IllegalStateException("more than one entity class exists in this TableMapping");
}
return entityClasses.iterator().next();
}
@Override
public Set<Class<?>> getEntityClasses() {
return entityClasses;
}
@Override
public void setEntityClasses(Set<Class<?>> entityClasses) {
this.entityClasses = entityClasses == null ? new HashSet<Class<?>>() : new HashSet<Class<?>>(entityClasses);
}
@Override
public String getTableName() {
return tableName;
}
@Override
public void setTableName(String tableName) {
Assert.notNull(tableName);
this.tableName = tableName;
}
@Override
public void setEntityClass(Class<?> entityClass) {
if (entityClass == null) {
throw new IllegalArgumentException("entity class required");
}
entityClasses.clear();
entityClasses.add(entityClass);
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (!(that instanceof TableMapping)) {
return false;
}
TableMapping thatMapping = (TableMapping) that;
if (!this.tableName.equals(thatMapping.getTableName())) {
return false;
}
return this.entityClasses.equals(thatMapping.getEntityClasses());
}
@Override
public int hashCode() {
return tableName.hashCode() ^ entityClasses.hashCode();
}
}

View File

@@ -0,0 +1,8 @@
package org.springframework.data.cassandra.mapping;
public interface TableMappings {
TableMapping getTableMappingByClass(Class<?> entityClass);
TableMapping getTableMappingByTableName(String tableName);
}

View File

@@ -0,0 +1,48 @@
package org.springframework.data.cassandra.mapping;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TableMappingsImpl implements TableMappings {
private Map<Class<?>, TableMapping> mappingsByClass = new HashMap<Class<?>, TableMapping>();
private Map<String, TableMapping> mappingsByTable = new HashMap<String, TableMapping>();
public TableMappingsImpl(Set<TableMapping> mappings) {
setMappings(mappings);
}
public void setMappings(Set<TableMapping> mappings) {
mappingsByClass.clear();
mappingsByTable.clear();
if (mappings == null || mappings.size() == 0) {
return;
}
for (TableMapping mapping : mappings) {
if (mapping == null) {
continue;
}
mappingsByTable.put(mapping.getTableName(), mapping);
for (Class<?> entityClass : mapping.getEntityClasses()) {
mappingsByClass.put(entityClass, mapping);
}
}
}
@Override
public TableMapping getTableMappingByClass(Class<?> entityClass) {
return mappingsByClass.get(entityClass);
}
@Override
public TableMapping getTableMappingByTableName(String tableName) {
return mappingsByTable.get(tableName);
}
}

View File

@@ -39,24 +39,6 @@ public abstract class CqlUtils {
private static Logger log = LoggerFactory.getLogger(CqlUtils.class);
/**
* Generates the CQL String to create a table in Cassandra
*
* @param tableName
* @param entity
* @return The CQL that can be passed to session.execute()
*/
public static String createTable(String tableName, final CassandraPersistentEntity<?> entity,
CassandraConverter cassandraConverter) {
CreateTableSpecification spec = cassandraConverter.getCreateTableSpecification(entity);
spec.name(tableName);
CreateTableCqlGenerator generator = new CreateTableCqlGenerator(spec);
return generator.toCql();
}
/**
* Create the List of CQL for the indexes required for Cassandra mapped Table.
*
@@ -68,6 +50,7 @@ public abstract class CqlUtils {
final List<String> result = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
if (prop.isIndexed()) {
@@ -101,6 +84,7 @@ public abstract class CqlUtils {
final List<String> result = new ArrayList<String>();
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty prop) {
String columnName = prop.getColumnName();

View File

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

View File

@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/cassandra"
xmlns:base="http://www.springframework.org/schema/cassandra" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/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:import namespace="http://www.springframework.org/schema/cassandra"
schemaLocation="http://www.springframework.org/schema/cassandra/spring-cassandra.xsd" />
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Data Cassandra support.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="session" type="sessionType" />
<xsd:element name="cluster" type="clusterType"/>
<xsd:element name="keyspace" type="keyspaceType" />
<xsd:complexType name="clusterType">
<xsd:complexContent>
<xsd:extension base="base:clusterType">
<xsd:sequence>
<xsd:element name="tables" minOccurs="0" maxOccurs="unbounded" type="tablesType"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="datacenterType">
<xsd:complexContent>
<xsd:extension base="base:datacenterType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="keyspaceType">
<xsd:complexContent>
<xsd:extension base="base:keyspaceType">
<xsd:sequence>
<xsd:element name="table" type="tableType" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRefType" use="optional"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:simpleType name="cassandraConverterRefType" final="union">
<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:complexContent>
<xsd:extension base="base:poolingOptionsType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="socketOptionsType">
<xsd:complexContent>
<xsd:extension base="base:socketOptionsType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="replicationType">
<xsd:complexContent>
<xsd:extension base="base:replicationType"/>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="sessionType">
<xsd:complexContent>
<xsd:extension base="base:sessionType"/>
</xsd:complexContent>
</xsd:complexType>
<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:element name="data-template" type="templateType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.cassandra.config.xml.TemplateFactoryBean"><![CDATA[
Defines a CassandraTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.data.cassandra.CassandraDataTemplate" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="dataTemplateType">
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the template; default is "cassandra-data-template".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-ref" type="base:sessionRef" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra session; default is "cassandra-session".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
-->
<xsd:complexType name="tablesType">
<xsd:sequence>
<xsd:element name="table" type="tableType" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
<xsd:attribute name="keyspace-name" type="xsd:string" use="optional"></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="table-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Table name override.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!--
<xsd:simpleType name="clusterRef" final="union">
<xsd:union memberTypes="base:clusterRef" />
</xsd:simpleType>
<xsd:simpleType name="sessionRef" final="union">
<xsd:union memberTypes="base:sessionRef" />
</xsd:simpleType>
-->
</xsd:schema>

View File

@@ -1,38 +1,259 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/data/cassandra"
xmlns:base="http://www.springframework.org/schema/cassandra" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tool="http://www.springframework.org/schema/tool"
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:import namespace="http://www.springframework.org/schema/cassandra"
schemaLocation="http://www.springframework.org/schema/cassandra/spring-cassandra.xsd" />
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for Spring Data Cassandra support.
]]></xsd:documentation>
Defines the configuration elements for Spring Cassandra support.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="session" type="base:sessionType" />
<xsd:element name="session" type="sessionType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.cassandra.config.xml.SessionFactoryBean"><![CDATA[
Defines a Cassandra session.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Session" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="cluster" type="base:clusterType" />
<xsd:element name="template" type="templateType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.cassandra.config.xml.TemplateFactoryBean"><![CDATA[
Defines a CassandraTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.cassandra.CassandraTemplate" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="keyspace" type="base:keyspaceType" />
<xsd:element name="cluster" type="clusterType">
<xsd:annotation>
<xsd:documentation
source="org.springframework.cassandra.config.xml.CassandraClusterFactoryBean"><![CDATA[
Defines a Cassandra cluster.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="com.datastax.driver.core.Cluster" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="local-pooling-options" type="base:poolingOptionsType" />
<xsd:complexType name="clusterType">
<xsd:sequence>
<xsd:element name="local-pooling-options" type="poolingOptionsType"
maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Local pooling options.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="remote-pooling-options" type="poolingOptionsType"
maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Remote pooling options.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="socket-options" type="socketOptionsType"
maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Socket options.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="keyspace" type="keyspaceType"
minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to define a keyspace.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="startup-cql" type="xsd:string"
minOccurs="0" maxOccurs="unbounded">
<!-- TODO: cql could come from a resource via a resource attribute... -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the system keyspace during bean initialization. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="shutdown-cql" type="xsd:string"
minOccurs="0" maxOccurs="unbounded">
<!-- TODO: cql could come from a resource via a resource attribute... -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the system keyspace during bean destruction. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the Cassandra Cluster definition; default is "cassandra-cluster".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="contactPoints" type="xsd:string"
use="optional" default="localhost">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma separated list of Cassandra servers. Default is "localhost".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="port" type="xsd:string" use="optional"
default="9042">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native CQL port to connect to. Default is 9042.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="compression" default="NONE" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The protocol compression option. 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[
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="remote-pooling-options" type="base:poolingOptionsType" />
<xsd:simpleType name="clusterRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.Cluster" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
<xsd:element name="socket-options" type="base:socketOptionsType" />
<xsd:simpleType name="sessionRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to type="com.datastax.driver.core.Session" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
<xsd:element name="keyspace-attributes" type="base:keyspaceAttributesType" />
<xsd:simpleType name="converterRef">
<xsd:simpleType name="cassandraConverterRef" final="union">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -44,21 +265,319 @@ Defines the configuration elements for Spring Data Cassandra support.
<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="sessionType">
<xsd:sequence>
<!-- TODO: support custom table mappings
<xsd:element name="mapping" minOccurs="0" maxOccurs="1" type="mappingType"/>
-->
<xsd:element name="startup-cql" type="xsd:string"
minOccurs="0" maxOccurs="unbounded">
<!-- TODO: cql could come from a resource via a resource attribute... -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the session's keyspace during bean initialization. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="shutdown-cql" type="xsd:string"
minOccurs="0" maxOccurs="unbounded">
<!-- TODO: cql could come from a resource via a resource attribute... -->
<xsd:annotation>
<xsd:documentation><![CDATA[
Arbitrary CQL script to be executed against the session's keyspace during bean destruction. Multiple elements will be executed in document order.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the session definition; default is "cassandra-session".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cluster-ref" type="clusterRef" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra cluster; default is "cassandra-cluster".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="keyspace-name" type="xsd:string"
use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of a Cassandra Keyspace. No default; for the system keyspace, use the empty string.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="schema-actions" type="xsd:string"
use="optional" default="NONE">
<xsd:annotation>
<xsd:documentation><![CDATA[
The schema action to take on the Cassandra Keyspace. See the SchemaAction enum.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRef"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandra-converter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="templateType">
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the template; default is "cassandra-template".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="session-ref" type="sessionRef" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a Cassandra session; default is "cassandra-session".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cassandra-converter-ref" type="cassandraConverterRef"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandra-converter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="keyspaceType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to define keyspaces.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="replication" type="replicationType"
minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to configure the keyspace's replication settings.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of this keyspace. Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="action" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The keyspace action to take at startup and possibly shutdown.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="CREATE">
<xsd:annotation>
<xsd:documentation><![CDATA[
Action value that causes keyspace creation during bean initialization.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="CREATE-DROP">
<xsd:annotation>
<xsd:documentation><![CDATA[
Action value that causes keyspace creation during bean initialization and keyspace dropping during bean destruction.
]]></xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="durable-writes" type="xsd:boolean"
use="optional" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether or not the keyspace supports durable writes.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="replicationType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to configure the keyspace's replication settings.
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="data-center" type="datacenterType"
minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to specify replication factors by data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string" use="optional"
default="SimpleStrategy">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the replication class; default is "SimpleStrategy".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replication-factor" type="xsd:integer"
use="optional" default="1">
<xsd:annotation>
<xsd:documentation><![CDATA[
The replication factor; default is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="datacenterType">
<xsd:annotation>
<xsd:documentation><![CDATA[
Provides the ability to specify replication factors by data center.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="replication-factor" type="xsd:integer"
use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The replication factor for the data center.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<!-- TODO: support custom table mappings
<xsd:complexType name="mappingType">
<xsd:sequence>
<xsd:element name="table" type="tableType" minOccurs="0"
maxOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="tableType">
<xsd:attribute name="entity" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Entity class name.
]]></xsd:documentation>
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="optional">
<xsd:attribute name="table-name" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
Table name override.
]]></xsd:documentation>
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
-->
</xsd:schema>

View File

@@ -6,13 +6,17 @@ import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
// @RunWith(SpringJUnit4ClassRunner.class)
// @ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CassandraNamespaceTests {
@Autowired
@@ -29,9 +33,8 @@ public class CassandraNamespaceTests {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
@AfterClass
public static void stopCassandra() {
EmbeddedCassandraServerHelper.stopEmbeddedCassandra();
@Test
public void test() {
Assert.notNull(ctx);
}
}

View File

@@ -6,7 +6,6 @@ import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -42,9 +41,4 @@ public class DriverTests {
public void clearCassandra() {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
@AfterClass
public static void stopCassandra() {
EmbeddedCassandraServerHelper.stopEmbeddedCassandra();
}
}

View File

@@ -7,7 +7,7 @@ import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraDataOperations;
import org.springframework.data.cassandra.core.CassandraDataTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
/**
* Setup any spring configuration for unit tests
@@ -27,7 +27,7 @@ public class TestConfig extends AbstractSpringDataCassandraConfiguration {
@Bean
public CassandraConverter cassandraConverter() {
return new MappingCassandraConverter(new CassandraMappingContext());
return new MappingCassandraConverter(new DefaultCassandraMappingContext());
}
@Bean

View File

@@ -1,54 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xmlns:cassandra-base="http://www.springframework.org/schema/cassandra"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cass="http://www.springframework.org/schema/data/cassandra"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
xsi:schemaLocation="
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
http://www.springframework.org/schema/cassandra http://www.springframework.org/schema/cassandra/spring-cassandra-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder
location="classpath:/org/springframework/data/cassandra/test/integration/config/cassandra.properties" />
<cassandra:cluster id="cassandra-cluster"
contactPoints="${cassandra.contactPoints}" port="${cassandra.port}"
compression="SNAPPY">
<cassandra-base:local-pooling-options
<cass:cluster id="cassandra-cluster" contactPoints="${cassandra.contactPoints}"
port="${cassandra.port}">
<cass:local-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="2" max-connections="8" />
<cassandra-base:remote-pooling-options
<cass:remote-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="1" max-connections="2" />
<cassandra-base:socket-options
connect-timeout-mls="5000" keep-alive="true" reuse-address="true"
so-linger="60" tcp-no-delay="true" receive-buffer-size="65536"
send-buffer-size="65536" />
</cassandra:cluster>
<cass:socket-options connect-timeout-mls="5000"
keep-alive="true" reuse-address="true" so-linger="60" tcp-no-delay="true"
receive-buffer-size="65536" send-buffer-size="65536" />
<cass:keyspace name="TestKS123" action="CREATE"
durable-writes="true" />
</cass:cluster>
<!-- TODO: not require that this bean be defined -->
<bean id="cassandra-mapping"
class=" org.springframework.data.cassandra.mapping.CassandraMappingContext" />
class="org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext"/>
<!-- TODO: not require that this bean be defined -->
<bean id="cassandra-converter"
class=" org.springframework.data.cassandra.convert.MappingCassandraConverter">
class="org.springframework.data.cassandra.convert.MappingCassandraConverter">
<constructor-arg ref="cassandra-mapping" />
</bean>
<cassandra:keyspace id="cassandra-keyspace" name="${cassandra.keyspace}"
cassandra-cluster-ref="cassandra-cluster" cassandra-converter-ref="cassandra-converter">
<cassandra-base:keyspace-attributes auto="update"
replication-stategy="SimpleStrategy" replication-factor="1"
durable-writes="true">
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Comment" />
<cassandra:table
entity="org.springframework.data.cassandra.test.integration.table.Notification" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Post" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Timeline" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.User" />
</cassandra-base:keyspace-attributes>
</cassandra:keyspace>
<cassandra:session id="cassandra-session" keyspace-name="${cassandra.keyspace}"/>
<cassandra:template session-ref="cassandra-session"/>
<cass:session id="cassandra-session" keyspace-name="TestKS123"
schema-actions="NONE" cluster-ref="cassandra-cluster"
cassandra-converter-ref="cassandra-converter">
</cass:session>
<cass:template session-ref="cassandra-session" />
</beans>

View File

@@ -1,7 +1,3 @@
cassandra.contactPoints=localhost
cassandra.port=9042
cassandra.keyspace=TestKS123

View File

@@ -28,7 +28,7 @@
</cassandra:cluster>
<bean id="cassandra-mapping"
class=" org.springframework.data.cassandra.mapping.CassandraMappingContext" />
class=" org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext" />
<bean id="cassandra-converter"
class=" org.springframework.data.cassandra.convert.MappingCassandraConverter">