This commit is contained in:
David T Webb
2014-02-13 00:13:49 -05:00
55 changed files with 1490 additions and 498 deletions

View File

@@ -2,16 +2,19 @@
## Quick Start
To begin working with ``spring-cassandra`` and ``spring-data-cassandra`` add the Spring Maven Repository to your ``pom.xml``.
To begin working with ``spring-cassandra`` or ``spring-data-cassandra``, add the Spring Maven Snapshot Repository to your ``pom.xml``.
<repository>
<id>spring-libs-snapshot</id>
<url>http://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
Then include the latest and greatest JAR into your project dependencies.
### CQL Only (spring-cassandra)
**CQL Only**
*Maven Coordinates*
<dependency>
<groupId>org.springframework.data</groupId>
@@ -19,7 +22,41 @@ Then include the latest and greatest JAR into your project dependencies.
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
**CQL and Object Mapping**
*Minimal Spring XML Configuration*
<cql:cluster />
<cql:session keyspace-name="sensors" />
<cql:template />
*Minimal Spring JavaConfig*
@Configuration
public class MyConfig extends AbstractSessionConfiguration {
@Override
public String getKeyspaceName() {
return "sensors";
}
@Bean
public CqlOperations cqlTemplate() {
return new CqlTemplate(session.getObject());
}
}
*Application Class*
public class SensorService {
@Autowired
CqlOperations template;
// ...
}
### CQL and Object Mapping (spring-data-cassandra)
*Maven Coordinates*
<dependency>
<groupId>org.springframework.data</groupId>
@@ -27,10 +64,39 @@ Then include the latest and greatest JAR into your project dependencies.
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
*Minimal Spring XML Configuration*
<cassandra:cluster />
<cassandra:session keyspace-name="foobar" />
<cassandra:repositories base-package="org.example.domain" />
*Minimal Spring JavaConfig*
@Configuration
@EnableCassandraRepositories(basePackage = "org.example.domain")
public class MyConfig extends AbstractSpringDataCassandraConfiguration {
@Override
public String getKeyspaceName() {
return "foobar";
}
}
*Application Class*
public class SensorService {
@Autowired
SensorRepository repo;
// ...
}
## Release Preview
The goal of this release preview is to publish the pieces of spring-data-cassandra as they become available
so that user's of the module can start to familiarize themselves with the components, and ultimately to provide
so that users of the module can start to familiarize themselves with the components, and ultimately to provide
the development team feedback. We hope this iterative approach produces the most usable and developer friendly
``spring-data-cassandra`` repository.
@@ -72,9 +138,9 @@ create more than one ``CqlTemplate`` (one per session, one session per keyspace)
Here are some considerations when designing your application for use with ``spring-cassandra``.
* When creating a template, wire in a single ``Session`` per keyspace. _Remember, ``Session`` is threadsafe, so only use one session per keyspace!_
* Cassandra's ``Session`` object is thread-safe, so you only need one per application & keyspace.
* Do not issue ``USE <keyspace>`` commands on your session; instead, _configure_ the keyspace name you intend to use.
* When creating a template, wire in a single ``Session`` per keyspace.
* ``Session`` is threadsafe, so only use one per keyspace per application context!
* __Do not issue__ ``USE <keyspace>`` __commands__ on your session; instead, _configure_ the keyspace name you intend to use.
* The DataStax Java Driver handles all failover and retry logic for you. Become familiar with the [Driver Documentation](http://www.datastax.com/documentation/developer/java-driver/1.0/webhelp/index.html), which will help you configure your ``Cluster``.
* If you are using a Cassandra ``Cluster`` spanning multiple data centers, please be insure to include hosts from all data centers in your contact points.
@@ -84,7 +150,7 @@ We have included a variety of overloaded ``ingest()`` methods in ``CqlTemplate``
### What's Next (early Q1 - 2014): Spring _Data_ Cassandra
The next round of work to do is to complete module ``spring-data-cassandra``, while taking feedback from the community's use of module ``spring-cassandra``.
The next round of work to do is to complete module ``spring-data-cassandra``, while taking feedback from the community's use of module ``spring-cassandra``. We are already well on our way to completion.
#### Cassandra Repository
@@ -101,7 +167,7 @@ This is another Spring template class to help you with all of your keyspace and
#### Official Reference Guide
Once we have all the inner workings of the ``CassandraRepository`` interface completed, we will publish a full Reference Guide on using all of the features in ``spring-data-cassandra``.
Once we have all the inner workings of the ``CassandraRepository`` interface completed, we will publish a full reference guide on using all of the features in ``spring-data-cassandra``.
## CqlTemplate Examples
@@ -163,9 +229,8 @@ following artifacts (or more recent versions thereof):
* Datastax Java Driver 1.x
* JDK 1.6+
The GA release is expected as part of the as-yet unnamed fourth Spring
Data Release Train "D", following Spring Data Release Train
[Codd](https://github.com/spring-projects/spring-data-commons/wiki/Release-Train-Codd).
The GA release is expected as part of the Spring
Data release train [Dijkstra](https://github.com/spring-projects/spring-data-commons/wiki/Release-Train-Dijkstra).
## Cassandra 2.x

View File

@@ -22,7 +22,7 @@ import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredP
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.CassandraSessionFactoryBean;
import org.springframework.util.StringUtils;
@@ -37,7 +37,7 @@ import org.w3c.dom.NamedNodeMap;
* @author David Webb
* @author Matthew T. Adams
*/
public class CassandraSessionParser extends AbstractSimpleBeanDefinitionParser {
public class CassandraSessionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {

View File

@@ -15,12 +15,12 @@
*/
package org.springframework.cassandra.config.xml;
import static org.springframework.cassandra.config.xml.ParsingUtils.*;
import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyReference;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.CassandraTemplateFactoryBean;
import org.springframework.util.StringUtils;
@@ -32,7 +32,7 @@ import org.w3c.dom.Element;
* @author David Webb
* @author Matthew T. Adams
*/
public class CassandraTemplateParser extends AbstractSimpleBeanDefinitionParser {
public class CassandraTemplateParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {

View File

@@ -22,7 +22,7 @@ package org.springframework.cassandra.config.xml;
*/
public interface DefaultBeanNames {
public static final String CLUSTER = "cassandra-cluster";
public static final String SESSION = "cassandra-session";
public static final String TEMPLATE = "cql-template";
public static final String CLUSTER = "cassandraCluster";
public static final String SESSION = "cassandraSession";
public static final String TEMPLATE = "cqlTemplate";
}

View File

@@ -0,0 +1,117 @@
package org.springframework.cassandra.core;
import java.util.regex.Pattern;
import org.springframework.cassandra.core.cql.CqlStringUtils;
/**
* This encapsulates the logic for CQL identifiers.
*
* @author John McPeek
*
*/
public class CqlIdentifier {
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
private String name;
private boolean quoted;
public CqlIdentifier(String identifier) {
this(identifier, false);
}
/**
* Renders the given string as a legal Cassandra identifier.
* <ul>
* <li>If the given identifier is a legal quoted identifier or forceQuote is true, it is set encased in double quotes.
* </li>
* <li>If the given identifier is a legal unquoted identifier, it is set unchanged.</li>
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*/
public CqlIdentifier(String name, boolean forceQuoting) {
if (isUnquotedIdentifier(name) && forceQuoting == false) {
this.name = name;
} else if (isQuotedIdentifier(name)) {
this.name = name;
quoted = true;
} else {
throw new IllegalArgumentException("[" + name + "] is not a valid CQL quoted or unquoted identifier");
}
}
public String toCql() {
String id = quoted ? CqlStringUtils.doubleQuote(name) : name;
return id;
}
public StringBuilder toCql(StringBuilder sb) {
return sb.append(toCql());
}
@Override
public String toString() {
return toCql();
}
public String getName() {
return name;
}
public boolean isQuoted() {
return quoted;
}
public static CqlIdentifier cqlId(String identifier) {
CqlIdentifier id = new CqlIdentifier(identifier);
return id;
}
public static CqlIdentifier quotedCqlId(String identifier) {
CqlIdentifier id = new CqlIdentifier(identifier, true);
return id;
}
public static boolean isIdentifier(CharSequence chars) {
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
}
public static boolean isUnquotedIdentifier(CharSequence chars) {
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static boolean isQuotedIdentifier(CharSequence chars) {
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (quoted ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CqlIdentifier other = (CqlIdentifier) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (quoted != other.quoted)
return false;
return true;
}
}

View File

@@ -23,7 +23,7 @@ public class CqlStringUtils {
protected static final String SINGLE_QUOTE = "\'";
protected static final String DOUBLE_SINGLE_QUOTE = "\'\'";
protected static final String DOUBLE_QUOTE = "\"";
public static final String DOUBLE_QUOTE = "\"";
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
protected static final String EMPTY_STRING = "";
protected static final String TYPE_PARAMETER_PREFIX = "<";
@@ -36,61 +36,6 @@ public class CqlStringUtils {
public static final String UNESCAPED_DOUBLE_QUOTE_REGEX = "TODO";
public static final Pattern UNESCAPED_DOUBLE_QUOTE_PATTERN = Pattern.compile(UNESCAPED_DOUBLE_QUOTE_REGEX);
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
public static boolean isUnquotedIdentifier(CharSequence chars) {
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkUnquotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isUnquotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL identifier");
}
}
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
public static boolean isQuotedIdentifier(CharSequence chars) {
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkQuotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isQuotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted identifier");
}
}
public static boolean isIdentifier(CharSequence chars) {
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
}
public static void checkIdentifier(CharSequence chars) {
if (!CqlStringUtils.isIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted or unquoted identifier");
}
}
/**
* Renders the given string as a legal Cassandra identifier.
* <ul>
* <li>If the given identifier is a legal unquoted identifier, it is returned unchanged.</li>
* <li>If the given identifier is a legal quoted identifier, it is returned encased in double quotes.</li>
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*/
public static String identifize(String candidate) {
checkIdentifier(candidate);
if (isUnquotedIdentifier(candidate)) {
return candidate;
}
// else it must be quoted
return doubleQuote(candidate);
}
/**
* Renders the given string as a legal Cassandra string column or table option value, by escaping single quotes and
* encasing the result in single quotes. Given <code>null</code>, returns <code>null</code>.

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Base class for column change specifications.
@@ -25,22 +24,21 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
*/
public abstract class ColumnChangeSpecification {
private String name;
private CqlIdentifier identifier;
public ColumnChangeSpecification(String name) {
setName(name);
}
private void setName(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
}
public String getName() {
return name;
return identifier.getName();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -15,15 +15,14 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.PrimaryKeyType.PARTITIONED;
import static org.springframework.cassandra.core.PrimaryKeyType.CLUSTERED;
import static org.springframework.cassandra.core.Ordering.ASCENDING;
import static org.springframework.cassandra.core.PrimaryKeyType.CLUSTERED;
import static org.springframework.cassandra.core.PrimaryKeyType.PARTITIONED;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.cassandra.core.CqlIdentifier;
import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import com.datastax.driver.core.DataType;
@@ -45,7 +44,7 @@ public class ColumnSpecification {
*/
public static final Ordering DEFAULT_ORDERING = ASCENDING;
private String name;
private CqlIdentifier identifier;
private DataType type; // TODO: determining if we should be coupling this to Datastax Java Driver type?
private PrimaryKeyType keyType;
private Ordering ordering;
@@ -56,8 +55,7 @@ public class ColumnSpecification {
* @return this
*/
public ColumnSpecification name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return this;
}
@@ -148,11 +146,11 @@ public class ColumnSpecification {
}
public String getName() {
return name;
return identifier.getName();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
public DataType getType() {
@@ -172,7 +170,7 @@ public class ColumnSpecification {
}
public StringBuilder toCql(StringBuilder cql) {
return (cql = noNull(cql)).append(name).append(" ").append(type);
return (cql = noNull(cql)).append(identifier).append(" ").append(type);
}
@Override

View File

@@ -15,9 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
import org.springframework.util.StringUtils;
/**
@@ -31,7 +29,7 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
private boolean ifNotExists = false;
private boolean custom = false;
private String tableName;
private CqlIdentifier identifier;
private String columnName;
private String using;
@@ -89,17 +87,16 @@ public class CreateIndexSpecification extends IndexNameSpecification<CreateIndex
* @return this
*/
public CreateIndexSpecification tableName(String tableName) {
checkIdentifier(tableName);
this.tableName = tableName;
identifier = new CqlIdentifier(tableName);
return this;
}
public String getTableName() {
return tableName;
return identifier.getName();
}
public String getTableNameAsIdentifier() {
return identifize(tableName);
return identifier.toCql();
}
public CreateIndexSpecification columnName(String columnName) {

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of table specifications.
@@ -29,7 +28,7 @@ public abstract class IndexNameSpecification<T extends IndexNameSpecification<T>
/**
* The name of the index.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the index name.
@@ -38,17 +37,16 @@ public abstract class IndexNameSpecification<T extends IndexNameSpecification<T>
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getName();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -1,7 +1,6 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of keyspace specifications.
@@ -13,9 +12,9 @@ import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecification<T>> {
/**
* The name of the table.
* The name of the keyspace.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the keyspace name.
@@ -24,17 +23,16 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getName();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
/**
@@ -43,7 +41,7 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Keyspace Action Specification {name: " + name + ", class: " + this.getClass() + "}");
sb.append("Keyspace Action Specification {name: " + identifier + ", class: " + this.getClass() + "}");
return sb.toString();
}
@@ -65,12 +63,12 @@ public abstract class KeyspaceActionSpecification<T extends KeyspaceActionSpecif
return false;
}
KeyspaceActionSpecification<?> thatSpec = (KeyspaceActionSpecification<?>) that;
return this.name.equals(thatSpec.name) && this.getClass().equals(that.getClass());
return this.identifier.equals(thatSpec.identifier) && this.getClass().equals(that.getClass());
}
@Override
public int hashCode() {
return this.name.hashCode() ^ this.getClass().hashCode();
return this.identifier.hashCode() ^ this.getClass().hashCode();
}
}

View File

@@ -15,8 +15,7 @@
*/
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import org.springframework.cassandra.core.CqlIdentifier;
/**
* Abstract builder class to support the construction of table specifications.
@@ -29,7 +28,7 @@ public abstract class TableNameSpecification<T extends TableNameSpecification<T>
/**
* The name of the table.
*/
private String name;
private CqlIdentifier identifier;
/**
* Sets the table name.
@@ -38,16 +37,15 @@ public abstract class TableNameSpecification<T extends TableNameSpecification<T>
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
identifier = new CqlIdentifier(name);
return (T) this;
}
public String getName() {
return name;
return identifier.getName();
}
public String getNameAsIdentifier() {
return identifize(name);
return identifier.toCql();
}
}

View File

@@ -0,0 +1,20 @@
package org.springframework.cassandra.test.unit.core.cql;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.cassandra.core.CqlIdentifier.isQuotedIdentifier;
import static org.springframework.cassandra.core.CqlIdentifier.isUnquotedIdentifier;
import org.junit.Test;
public class CqlIdentifierTest {
@Test
public void testIsQuotedIdentifier() throws Exception {
assertFalse(isQuotedIdentifier("my\"id"));
assertTrue(isQuotedIdentifier("my\"\"id"));
assertFalse(isUnquotedIdentifier("my\"id"));
assertTrue(isUnquotedIdentifier("myid"));
}
}

View File

@@ -1,18 +1,5 @@
package org.springframework.cassandra.test.unit.core.cql;
import static org.junit.Assert.*;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isQuotedIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isUnquotedIdentifier;
import org.junit.Test;
public class CqlStringUtilsTest {
@Test
public void testIsQuotedIdentifier() throws Exception {
assertFalse(isQuotedIdentifier("my\"id"));
assertTrue(isQuotedIdentifier("my\"\"id"));
assertFalse(isUnquotedIdentifier("my\"id"));
assertTrue(isUnquotedIdentifier("myid"));
}
}

View File

@@ -26,6 +26,7 @@ public class AlterTableCqlGeneratorTests {
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
System.out.println("cql: " + cql);
assertTrue(cql.startsWith("ALTER TABLE " + tableName + " "));
}

View File

@@ -10,69 +10,66 @@
location="classpath:/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties" />
<bean id="authProvider" class="com.datastax.driver.core.sasl.DseAuthProvider" />
<bean id="loadBalancingPolicy" class="com.datastax.driver.core.policies.DCAwareRoundRobinPolicy">
<bean id="loadBalancingPolicy"
class="com.datastax.driver.core.policies.DCAwareRoundRobinPolicy">
<constructor-arg name="localDc" value="${lb.policy.dcAware.localDc}" />
<constructor-arg name="usedHostsPerRemoteDc" value="${lb.policy.dcAware.remoteHosts}" />
<constructor-arg name="usedHostsPerRemoteDc"
value="${lb.policy.dcAware.remoteHosts}" />
</bean>
<bean id="reconnectionPolicy" class="com.datastax.driver.core.policies.ConstantReconnectionPolicy">
<constructor-arg name="constantDelayMs" value="${cluster.reconnection.delayMillis}"/>
<bean id="reconnectionPolicy"
class="com.datastax.driver.core.policies.ConstantReconnectionPolicy">
<constructor-arg name="constantDelayMs"
value="${cluster.reconnection.delayMillis}" />
</bean>
<bean id="retryPolicy" class="com.datastax.driver.core.policies.DowngradingConsistencyRetryPolicy" />
<bean id="hostStateListener" class="org.springframework.cassandra.test.integration.config.xml.TestHostStateListener"/>
<bean id="latencyTracker" class="org.springframework.cassandra.test.integration.config.xml.TestLatencyTracker"/>
<bean id="retryPolicy"
class="com.datastax.driver.core.policies.DowngradingConsistencyRetryPolicy" />
<cassandra:cluster id="cassandra-cluster"
contact-points="${cluster.contactPoints}" port="${cluster.port}"
compression="${cluster.compression}" auth-info-provider-ref="authProvider"
load-balancing-policy-ref="loadBalancingPolicy" username="${auth.username}" password="${auth.password}"
<bean id="hostStateListener"
class="org.springframework.cassandra.test.integration.config.xml.TestHostStateListener" />
<bean id="latencyTracker"
class="org.springframework.cassandra.test.integration.config.xml.TestLatencyTracker" />
<cassandra:cluster contact-points="${cluster.contactPoints}"
port="${cluster.port}" compression="${cluster.compression}"
auth-info-provider-ref="authProvider" load-balancing-policy-ref="loadBalancingPolicy"
username="${auth.username}" password="${auth.password}"
deferred-initialization="${cluster.deferredInit}" metrics-enabled="${cluster.metricsEnabled}"
jmx-reporting-enabled="${cluster.jmxReportingEnabled}"
reconnection-policy-ref="reconnectionPolicy"
retry-policy-ref="retryPolicy"
host-state-listener-ref="hostStateListener"
latency-tracker-ref="latencyTracker">
<cassandra:local-pooling-options
reconnection-policy-ref="reconnectionPolicy" retry-policy-ref="retryPolicy"
host-state-listener-ref="hostStateListener" latency-tracker-ref="latencyTracker">
<cassandra:local-pooling-options
min-simultaneous-requests="${local.min.requests}"
max-simultaneous-requests="${local.max.requests}"
core-connections="${local.core.connections}"
max-connections="${local.max.connections}"
/>
<cassandra:remote-pooling-options
max-simultaneous-requests="${local.max.requests}" core-connections="${local.core.connections}"
max-connections="${local.max.connections}" />
<cassandra:remote-pooling-options
min-simultaneous-requests="${remote.min.requests}"
max-simultaneous-requests="${remote.max.requests}"
core-connections="${remote.core.connections}"
max-connections="${remote.max.connections}"
/>
<cassandra:socket-options
connect-timeout-millis="${socket.connectTimeoutMillis}"
keep-alive="${socket.keepAlive}"
read-timeout-millis="${socket.readTimeoutMillis}"
reuse-address="${socket.reuseAddress}"
so-linger="${socket.soLinger}"
tcp-no-delay="${socket.tcpNoDelay}"
receive-buffer-size="${socket.receiveBufferSize}"
send-buffer-size="${socket.sendBufferSize}"
/>
<cassandra:keyspace name="${keyspace.name}" action="${keyspace.action}"/>
<cassandra:keyspace name="Foo123" action="CREATE_DROP" durable-writes="true">
max-simultaneous-requests="${remote.max.requests}" core-connections="${remote.core.connections}"
max-connections="${remote.max.connections}" />
<cassandra:socket-options
connect-timeout-millis="${socket.connectTimeoutMillis}" keep-alive="${socket.keepAlive}"
read-timeout-millis="${socket.readTimeoutMillis}" reuse-address="${socket.reuseAddress}"
so-linger="${socket.soLinger}" tcp-no-delay="${socket.tcpNoDelay}"
receive-buffer-size="${socket.receiveBufferSize}" send-buffer-size="${socket.sendBufferSize}" />
<cassandra:keyspace name="${keyspace.name}" action="${keyspace.action}" />
<cassandra:keyspace name="Foo123" action="CREATE_DROP"
durable-writes="true">
<cassandra:replication class="NETWORK_TOPOLOGY_STRATEGY">
<cassandra:data-center replication-factor="${dc1.rf}" name="${dc1.name}"/>
<cassandra:data-center replication-factor="${dc1.rf}" name="${dc2.name}"/>
<cassandra:data-center replication-factor="${dc1.rf}"
name="${dc1.name}" />
<cassandra:data-center replication-factor="${dc1.rf}"
name="${dc2.name}" />
</cassandra:replication>
</cassandra:keyspace>
</cassandra:cluster>
<cassandra:session id="cassandra-session"
keyspace-name="system">
</cassandra:session>
<cassandra:session keyspace-name="system" />
<bean id="cassandraTemplate" class="org.springframework.cassandra.core.CqlTemplate">
<constructor-arg ref="cassandra-session" />
<constructor-arg ref="cassandraSession" />
</bean>
</beans>

View File

@@ -9,8 +9,8 @@
<context:property-placeholder
location="classpath:org.springframework.cassandra.test.integration.support.SpringCassandraBuildProperties.properties" />
<cassandra:cluster id="cassandra-cluster"
contact-points="localhost" port="${build.cassandra.native_transport_port}">
<cassandra:cluster contact-points="localhost"
port="${build.cassandra.native_transport_port}">
<cassandra:local-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="2" max-connections="8" />
@@ -18,16 +18,15 @@
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="1" max-connections="2" />
<cassandra:socket-options
connect-timeout-millis="5000" keep-alive="true" read-timeout-millis="60000" reuse-address="true"
so-linger="60" tcp-no-delay="true" receive-buffer-size="65536"
send-buffer-size="65536" />
connect-timeout-millis="5000" keep-alive="true" read-timeout-millis="60000"
reuse-address="true" so-linger="60" tcp-no-delay="true"
receive-buffer-size="65536" send-buffer-size="65536" />
</cassandra:cluster>
<cassandra:session id="cassandra-session"
keyspace-name="xmlconfigtest" />
<cassandra:session keyspace-name="xmlconfigtest" />
<bean id="cassandraTemplate" class="org.springframework.cassandra.core.CqlTemplate">
<constructor-arg ref="cassandra-session" />
<constructor-arg ref="cassandraSession" />
</bean>
</beans>

View File

@@ -0,0 +1,65 @@
package org.springframework.data.cassandra.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.util.StringUtils;
/**
* Parameter used in conjunction with:
* <p/>
* {@link BeanDefinitionBuilder#addConstructorArgReference(String)},
* {@link BeanDefinitionBuilder#addConstructorArgValue(Object)},
* {@link BeanDefinitionBuilder#addPropertyReference(String, String)}, and
* {@link BeanDefinitionBuilder#addPropertyValue(String, Object)}.
* <p/>
* Easy and succinct to create if methods {@link #ref(CharSequence)} or {@link #val(Object)} are used and imported
* statically.
*
* @see BeanDefinitionBuilderArgument#ref(CharSequence)
* @see BeanDefinitionBuilderArgument#val(Object)
*/
public class BeanDefinitionBuilderArgument {
/**
* Returns a {@link BeanDefinitionBuilderArgument} with {@link #reference} equal to <code>true</code>. Convenient if
* imported statically.
*
* @param value The name of the bean reference.
*/
public static BeanDefinitionBuilderArgument ref(CharSequence value) {
return new BeanDefinitionBuilderArgument(true, value);
}
/**
* Returns a {@link BeanDefinitionBuilderArgument} with {@link #reference} equal to <code>false</code>. Convenient if
* imported statically.
*
* @param value The constructor argument's value.
*/
public static BeanDefinitionBuilderArgument val(Object value) {
return new BeanDefinitionBuilderArgument(false, value);
}
protected boolean reference;
protected Object value;
protected BeanDefinitionBuilderArgument(boolean reference, Object value) {
this.reference = reference;
if (this.reference && (value == null || !(value instanceof CharSequence))) {
throw new IllegalArgumentException(String.format(
"reference argument must have value of type CharSequence, not [%s]", value == null ? "null" : value
.getClass().getName()));
}
if (!StringUtils.hasText((CharSequence) value)) {
throw new IllegalArgumentException("given CharSequence has no text");
}
this.value = value;
}
public boolean isReference() {
return reference;
}
public Object getValue() {
return value;
}
}

View File

@@ -0,0 +1,144 @@
package org.springframework.data.cassandra.config;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.util.StringUtils;
public class BeanDefinitionUtils {
/**
* Returns a {@link BeanDefinitionBuilder} iff no {@link BeanDefinition} of the required type is found in the given
* {@link ListableBeanFactory}, otherwise returns <code>null</code>, indicating that at least one existed.
*
* @param factory The {@link ListableBeanFactory} in which to look for the {@link BeanDefinition}, including
* ancestors.
* @param requiredType The {@link BeanDefinition}'s required type.
* @param instantiableType The instantiable type for the {@link BeanDefinitionBuilder}.
* @param constructorArgs Any {@link BeanDefinitionBuilderArgument}s required by the instantiableType's constructor.
* @return A {@link BeanDefinitionBuilder} iff no {@link BeanDefinition} of the required type is found, otherwise
* <code>null</code>.
* @see BeanDefinitionUtils#createBeanDefinitionBuilderIfNoBeanDefinitionOfTypeExists(ListableBeanFactory, Class,
* Class, BeanDefinitionBuilderArgument...)
* @see BeanDefinitionBuilderArgument#ref(Object)
* @see BeanDefinitionBuilderArgument#val(Object)
*/
public static BeanDefinitionBuilder createBeanDefinitionBuilderIfNoBeanDefinitionOfTypeExists(
ListableBeanFactory factory, Class<?> requiredType, Class<?> instantiableType,
BeanDefinitionBuilderArgument... constructorArgs) {
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, requiredType, true, false);
if (names.length > 0) {
return null;
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(instantiableType);
if (constructorArgs == null) {
return builder;
}
for (BeanDefinitionBuilderArgument arg : constructorArgs) {
if (arg.reference) {
builder.addConstructorArgReference(arg.value.toString());
} else {
builder.addConstructorArgValue(arg.value);
}
}
return builder;
}
/**
* Returns the single {@link BeanDefinitionHolder} with the given type, or null of none were found and
* <code>required</code> was <code>false</code>, otherwise throws {@link IllegalArgumentException}.
*
* @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the <code>factor</code>
* parameter.
* @param factory The {@link ListableBeanFactory}, often the very same instance as the <code>registry</code>
* parameter.
* @param type The required {@link BeanDefinition}'s type.
* @param includeNonSingletons Whether to include beans with scope other than <code>singleton</code>
* @param allowEagerInit Whether to allow eager initialization of beans.
* @param required Whether to allow the return of null if none were found.
* @return The {@link BeanDefinitionHolder} or null if none found, depending on the value of <code>required</code>.
* @throws IllegalArgumentException If multiple were found.
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
public static BeanDefinitionHolder getSingleBeanDefinitionOfType(BeanDefinitionRegistry registry,
ListableBeanFactory factory, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit, boolean required) {
BeanDefinitionHolder[] definitions = getBeanDefinitionsOfType(registry, factory, type, includeNonSingletons,
allowEagerInit);
if (definitions.length == 1) {
return definitions[0];
}
if (definitions.length == 0 && !required) {
return null;
}
String[] names = new String[definitions.length];
for (int i = 0; i < names.length; i++) {
names[i] = definitions[i].getBeanName();
}
throw new IllegalStateException(String.format("expected one bean definition of type [%s], but found %d: %s",
type.getName(), definitions.length, StringUtils.arrayToCommaDelimitedString(names)));
}
/**
* Returns all {@link BeanDefinitionHolder}s with the given type.
*
* @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the <code>factor</code>
* parameter.
* @param factory The {@link ListableBeanFactory}, often the very same instance as the <code>registry</code>
* parameter.
* @param type The required {@link BeanDefinition}'s type.
* @param includeNonSingletons Whether to include beans with scope other than <code>singleton</code>
* @param allowEagerInit Whether to allow eager initialization of beans.
* @param required Whether to allow the return of null if none were found.
* @return The {@link BeanDefinitionHolder}s -- never returns null.
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
public static BeanDefinitionHolder[] getBeanDefinitionsOfType(BeanDefinitionRegistry registry,
ListableBeanFactory factory, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, type, includeNonSingletons,
allowEagerInit);
if (names.length == 0) {
return new BeanDefinitionHolder[] {};
}
BeanDefinitionHolder[] array = new BeanDefinitionHolder[names.length];
for (int i = 0; i < names.length; i++) {
String name = names[i];
BeanDefinition beanDefinition = null;
while (beanDefinition == null) {
try {
beanDefinition = registry.getBeanDefinition(name);
} catch (NoSuchBeanDefinitionException x) {
if (FactoryBean.class.isAssignableFrom(type)) { // try unmangled BeanFactory-prefixed name
name = name.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
} else {
throw x;
}
}
}
array[i] = new BeanDefinitionHolder(beanDefinition, name);
}
return array;
}
}

View File

@@ -7,10 +7,7 @@ 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.data.cassandra.mapping.EntityMapping;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Metadata;
@@ -22,8 +19,6 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean
protected CassandraAdminTemplate admin;
protected CassandraConverter converter;
protected CassandraMappingContext mappingContext;
protected Mapping mapping;
protected ClassLoader entityClassLoader = getClass().getClassLoader();
@Override
public void afterPropertiesSet() throws Exception {
@@ -34,42 +29,9 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean
admin = new CassandraAdminTemplate(session, converter);
mapping = mapping == null ? new Mapping() : mapping;
processMappingOverrides();
performSchemaAction();
}
protected void processMappingOverrides() throws ClassNotFoundException {
if (mapping == null) {
return;
}
for (EntityMapping entityMapping : mapping.getEntityMappings()) {
if (entityMapping == null) {
continue;
}
String entityClassName = entityMapping.getEntityClassName();
Class<?> entityClass = Class.forName(entityClassName, false, entityClassLoader);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
if (entity == null) {
throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName));
}
String tableName = entityMapping.getTableName();
if (!StringUtils.hasText(tableName)) {
continue;
}
entity.setTableName(tableName);
}
}
protected void performSchemaAction() {
boolean dropTables = false;
@@ -138,22 +100,4 @@ public class CassandraDataSessionFactoryBean extends CassandraSessionFactoryBean
this.converter = converter;
this.mappingContext = converter.getMappingContext();
}
public Mapping getMapping() {
return mapping;
}
public void setMapping(Mapping mapping) {
Assert.notNull(mapping);
this.mapping = mapping;
}
public ClassLoader getEntityClassLoader() {
return entityClassLoader;
}
public void setEntityClassLoader(ClassLoader entityClassLoader) {
Assert.notNull(entityClassLoader);
this.entityClassLoader = entityClassLoader;
}
}

View File

@@ -0,0 +1,136 @@
package org.springframework.data.cassandra.config;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Scans packages for Cassandra entities.
*
* @author Matthew T. Adams
*/
public class CassandraEntityClassScanner {
public static Set<Class<?>> scan(String... entityBasePackages) throws ClassNotFoundException {
return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses();
}
public static Set<Class<?>> scan(Class<?>... entityBasePackageClasses) throws ClassNotFoundException {
return new CassandraEntityClassScanner(entityBasePackageClasses).scanForEntityClasses();
}
public static Set<Class<?>> scan(Collection<String> entityBasePackages) throws ClassNotFoundException {
return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses();
}
public static Set<Class<?>> scan(Collection<String> entityBasePackages, Collection<Class<?>> entityBasePackageClasses)
throws ClassNotFoundException {
return new CassandraEntityClassScanner(entityBasePackages, entityBasePackageClasses).scanForEntityClasses();
}
protected Set<String> entityBasePackages = new HashSet<String>();
protected Set<Class<?>> entityBasePackageClasses = new HashSet<Class<?>>();
protected ClassLoader beanClassLoader;
public CassandraEntityClassScanner() {
}
public CassandraEntityClassScanner(Class<?>... entityBasePackageClasses) {
this(null, Arrays.asList(entityBasePackageClasses));
}
public CassandraEntityClassScanner(String... entityBasePackages) {
this(Arrays.asList(entityBasePackages));
}
public CassandraEntityClassScanner(Collection<String> entityBasePackages) {
this(entityBasePackages, null);
}
public CassandraEntityClassScanner(Collection<String> entityBasePackages,
Collection<Class<?>> entityBasePackageClasses) {
setEntityBasePackages(entityBasePackages);
setEntityBasePackageClasses(entityBasePackageClasses);
}
public Set<String> getEntityBasePackages() {
return Collections.unmodifiableSet(entityBasePackages);
}
public void setEntityBasePackages(Collection<String> entityBasePackages) {
this.entityBasePackages = entityBasePackages == null ? new HashSet<String>() : new HashSet<String>(
entityBasePackages);
}
public Set<Class<?>> getEntityBasePackageClasses() {
return Collections.unmodifiableSet(entityBasePackageClasses);
}
public void setEntityBasePackageClasses(Collection<Class<?>> entityBasePackageClasses) {
this.entityBasePackageClasses = entityBasePackageClasses == null ? new HashSet<Class<?>>() : new HashSet<Class<?>>(
entityBasePackageClasses);
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Scans the mapping base package for entity classes annotated with {@link Table} or {@link Persistent}.
*
* @see #getEntityBasePackages()
* @return <code>Set&lt;Class&lt;?&gt;&gt;</code> representing the annotated entity classes found.
* @throws ClassNotFoundException
*/
public Set<Class<?>> scanForEntityClasses() throws ClassNotFoundException {
Set<Class<?>> classes = new HashSet<Class<?>>();
for (String basePackage : getEntityBasePackages()) {
classes.addAll(scanBasePackageForEntities(basePackage));
}
for (Class<?> basePackageClass : getEntityBasePackageClasses()) {
classes.addAll(scanBasePackageForEntities(basePackageClass.getPackage().getName()));
}
return classes;
}
protected Set<Class<?>> scanBasePackageForEntities(String basePackage) throws ClassNotFoundException {
HashSet<Class<?>> classes = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
for (Class<? extends Annotation> annoClass : getEntityAnnotations()) {
componentProvider.addIncludeFilter(new AnnotationTypeFilter(annoClass));
}
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
classes.add(ClassUtils.forName(candidate.getBeanClassName(), beanClassLoader));
}
}
return classes;
}
@SuppressWarnings("unchecked")
public Class<? extends Annotation>[] getEntityAnnotations() {
return new Class[] { Table.class, Persistent.class, PrimaryKeyClass.class };
}
}

View File

@@ -0,0 +1,188 @@
package org.springframework.data.cassandra.config;
import static org.springframework.data.cassandra.config.BeanDefinitionUtils.getBeanDefinitionsOfType;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Session;
/**
* {@link BeanDefinitionRegistryPostProcessor} that does its best to register any missing Spring Data Cassandra beans
* that can be defaulted. Specifically, it attempts to create default bean definitions for the following required
* interface types via their default implementation types:
* <ul>
* <li>{@link CassandraOperations} via {@link CassandraTemplate}</li>
* <li>{@link CassandraMappingContext} via {@link DefaultCassandraMappingContext}</li>
* <li> {@link CassandraConverter} via {@link MappingCassandraConverter}</li>
* </ul>
* <p/>
* If there are multiple definitions for any type that another type depends on, an {@link IllegalStateException} is
* thrown. For example, if there are two definitions for type {@link CassandraMappingContext} present and no definition
* for type {@link CassandraConverter}, then it's impossible to know which {@link CassandraMappingContext} is to be used
* when creating a default definition for the {@link CassandraConverter}.
* <p/>
* If a single definition of a required type is present, then it is used. For example, if there is already a
* {@link CassandraMappingContext} definition present, then it will be used in the
* {@link DefaultCassandraMappingContext} bean definition.
* <p/>
* It requires that a single {@link Session} or {@link CassandraDataSessionFactoryBean} definition be present. As
* described above, multiple {@link Session} definitions, multiple {@link CassandraDataSessionFactoryBean} definitions,
* or both a {@link Session} and {@link CassandraDataSessionFactoryBean} will cause an {@link IllegalStateException} to
* be thrown.
*
* @author Matthew T. Adams
*/
public class CassandraMappingBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* Does nothing.
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
/**
* Ensures that {@link BeanDefinition}s for a {@link CassandraMappingContext} and a {@link CassandraConverter} exist.
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (!(registry instanceof ListableBeanFactory)) {
return;
}
ListableBeanFactory factory = (ListableBeanFactory) registry;
registerMissingDefaultableBeanDefinitions(registry, factory);
}
protected void registerMissingDefaultableBeanDefinitions(BeanDefinitionRegistry registry, ListableBeanFactory factory) {
// see if any template definitions exist, which requires a converter, which requires a mapping context
BeanDefinitionHolder[] templateBeans = getBeanDefinitionsOfType(registry, factory, CassandraOperations.class, true,
false);
if (templateBeans.length >= 1) {
return;
}
// need a session & converter for the default template
// see if an actual Session definition exists
String sessionBeanName = findSessionBeanName(registry, factory);
// see if any converter bean definitions exist, which requires a mapping context
BeanDefinitionHolder[] converterBeans = getBeanDefinitionsOfType(registry, factory,
MappingCassandraConverter.class, true, false);
if (converterBeans.length == 1) {
registerDefaultTemplate(registry, sessionBeanName, converterBeans[0].getBeanName());
return;
} else if (converterBeans.length > 1) {
// then throw, because we need to create a default converter, but we wouldn't know which mapping context to use
throw new IllegalStateException(String.format(
"found %d beans of type [%s] - can't disambiguate for creation of [%s]", converterBeans.length,
CassandraConverter.class.getName(), CassandraTemplate.class.getName()));
}
// see if any mapping context bean definitions exist
BeanDefinitionHolder[] contextBeans = getBeanDefinitionsOfType(registry, factory, CassandraMappingContext.class,
true, false);
if (contextBeans.length > 1) {
// then throw, because we need to create a default converter, but we wouldn't know which mapping context to use
throw new IllegalStateException(String.format(
"found %d beans of type [%s] - can't disambiguate for creation of [%s]", contextBeans.length,
CassandraMappingContext.class.getName(), MappingCassandraConverter.class.getName()));
}
// create the mapping context if necessary
BeanDefinitionHolder contextBean = contextBeans.length == 1 ? contextBeans[0] : null;
if (contextBean == null) {
contextBean = regsiterDefaultContext(registry);
}
// create the default converter & template bean definitions
BeanDefinitionHolder converter = registerDefaultConverter(registry, contextBean.getBeanName());
registerDefaultTemplate(registry, sessionBeanName, converter.getBeanName());
}
public String findSessionBeanName(BeanDefinitionRegistry registry, ListableBeanFactory factory) {
// first, search for any session and session factory beans
BeanDefinitionHolder[] sessionBeans = getBeanDefinitionsOfType(registry, factory, Session.class, true, false);
BeanDefinitionHolder[] sessionFactoryBeans = getBeanDefinitionsOfType(registry, factory,
CassandraDataSessionFactoryBean.class, true, false);
int sessionCount = sessionBeans.length;
int sessionFactoryCount = sessionFactoryBeans.length;
int totalCount = sessionCount + sessionFactoryCount;
if (totalCount == 0 || totalCount > 1) { // can't create default template -- none or multiple
throw createSessionException(totalCount, Session.class, CassandraDataSessionFactoryBean.class);
}
if (sessionCount == 1) {
return sessionBeans[0].getBeanName();
}
// else it must be the one session factory bean
return sessionFactoryBeans[0].getBeanName();
}
protected IllegalStateException createSessionException(int beanDefinitionCount, Class<?>... types) {
return new IllegalStateException(String.format("found %d beans of type%s [%s] - %s for creation of default [%s]",
beanDefinitionCount, beanDefinitionCount == 1 ? "" : "s", StringUtils.arrayToCommaDelimitedString(types),
beanDefinitionCount == 0 ? "need exactly one" : "can't disambiguate", CassandraTemplate.class.getName()));
}
protected BeanDefinitionHolder regsiterDefaultContext(BeanDefinitionRegistry registry) {
BeanDefinitionHolder contextBean = new BeanDefinitionHolder(BeanDefinitionBuilder.genericBeanDefinition(
DefaultCassandraMappingContext.class).getBeanDefinition(), DefaultDataBeanNames.CONTEXT);
registry.registerBeanDefinition(contextBean.getBeanName(), contextBean.getBeanDefinition());
return contextBean;
}
public BeanDefinitionHolder registerDefaultConverter(BeanDefinitionRegistry registry, String contextBeanName) {
BeanDefinitionBuilder converterBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
MappingCassandraConverter.class).addConstructorArgReference(contextBeanName);
BeanDefinitionHolder beanDefinition = new BeanDefinitionHolder(converterBeanDefinitionBuilder.getBeanDefinition(),
DefaultDataBeanNames.CONVERTER);
registry.registerBeanDefinition(beanDefinition.getBeanName(), beanDefinition.getBeanDefinition());
return beanDefinition;
}
public BeanDefinitionHolder registerDefaultTemplate(BeanDefinitionRegistry registry, String sessionBeanName,
String converterBeanName) {
BeanDefinitionBuilder templateBeanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(CassandraTemplate.class).addConstructorArgReference(sessionBeanName)
.addConstructorArgReference(converterBeanName);
BeanDefinition beanDefinition = templateBeanDefinitionBuilder.getBeanDefinition();
BeanDefinitionHolder template = new BeanDefinitionHolder(beanDefinition, DefaultDataBeanNames.TEMPLATE);
registry.registerBeanDefinition(template.getBeanName(), template.getBeanDefinition());
return template;
}
}

View File

@@ -4,6 +4,7 @@ import org.springframework.cassandra.config.xml.DefaultBeanNames;
public interface DefaultDataBeanNames extends DefaultBeanNames {
public static final String DATA_TEMPLATE = "cassandra-template";
public static final String CONVERTER = "cassandra-converter";
public static final String DATA_TEMPLATE = "cassandraTemplate";
public static final String CONVERTER = "cassandraConverter";
public static final String CONTEXT = "cassandraMapping";
}

View File

@@ -15,18 +15,12 @@
*/
package org.springframework.data.cassandra.config.java;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cassandra.config.java.AbstractClusterConfiguration;
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.config.CassandraDataSessionFactoryBean;
import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
@@ -34,12 +28,8 @@ 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.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.data.cassandra.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base class for Spring Data Cassandra configuration using JavaConfig.
@@ -54,7 +44,6 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
protected abstract String getKeyspaceName();
protected ClassLoader beanClassLoader;
protected Mapping mapping = new Mapping();
/**
* The {@link SchemaAction} to perform. Defaults to {@link SchemaAction#NONE}.
@@ -64,11 +53,11 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
}
/**
* The base package to scan for entities annotated with {@link Table} annotations. By default, returns the package
* name of {@literal this} (<code>this.getClass().getPackage().getName()</code>).
* The base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package
* name of {@literal this} (<code>this.getClass().getPackage().getName()</code>). This method must never return null.
*/
public String getEntityBasePackage() {
return getClass().getPackage().getName();
public String[] getEntityBasePackages() {
return new String[] { getClass().getPackage().getName() };
}
@Bean
@@ -77,15 +66,12 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
CassandraDataSessionFactoryBean bean = new CassandraDataSessionFactoryBean();
bean.setCluster(cluster().getObject());
bean.setConverter(converter());
bean.setConverter(cassandraConverter());
bean.setSchemaAction(getSchemaAction());
bean.setKeyspaceName(getKeyspaceName());
bean.setStartupScripts(getStartupScripts());
bean.setShutdownScripts(getShutdownScripts());
bean.setEntityClassLoader(beanClassLoader);
bean.setMapping(mapping);
return bean;
}
@@ -96,7 +82,7 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
*/
@Bean
public CassandraAdminOperations cassandraTemplate() throws Exception {
return new CassandraAdminTemplate(session().getObject(), converter());
return new CassandraAdminTemplate(session().getObject(), cassandraConverter());
}
/**
@@ -105,59 +91,21 @@ public abstract class AbstractSpringDataCassandraConfiguration extends AbstractC
* @throws ClassNotFoundException
*/
@Bean
public CassandraMappingContext cassandraMappingContext() throws ClassNotFoundException {
DefaultCassandraMappingContext context = new DefaultCassandraMappingContext();
context.setInitialEntitySet(getInitialEntitySet());
return context;
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
DefaultCassandraMappingContext bean = new DefaultCassandraMappingContext();
bean.setInitialEntitySet(CassandraEntityClassScanner.scan(getEntityBasePackages()));
bean.setBeanClassLoader(beanClassLoader);
return bean;
}
/**
* Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements
*
* @throws ClassNotFoundException
*/
@Bean
public CassandraConverter converter() throws ClassNotFoundException {
MappingCassandraConverter converter = new MappingCassandraConverter(cassandraMappingContext());
converter.setBeanClassLoader(beanClassLoader);
return converter;
}
/**
* Scans the mapping base package for entity classes annotated with {@link Table} or {@link Persistent}.
*
* @see #getEntityBasePackage()
* @return <code>Set&lt;Class&lt;?&gt;&gt;</code> representing the annotated entity classes found.
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getEntityBasePackage();
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));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(PrimaryKeyClass.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
Class<?> clazz = ClassUtils.forName(candidate.getBeanClassName(), beanClassLoader);
initialEntitySet.add(clazz);
}
}
processMappingOverrides(initialEntitySet);
return initialEntitySet;
}
protected void processMappingOverrides(Set<Class<?>> entityTypes) {
// TODO: search for external entity mapping info (xml/properties/yaml/etc) here & update this.mapping
// similar to JPA's or JDO's external metadata search algorithms
public CassandraConverter cassandraConverter() throws Exception {
return new MappingCassandraConverter(cassandraMapping());
}
@Override

View File

@@ -1,6 +1,9 @@
package org.springframework.data.cassandra.config.xml;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.xml.CassandraClusterParser;
import org.w3c.dom.Element;
/**
* Spring Data Cassandra XML namespace parser for the &lt;cluster&gt; element.
@@ -8,4 +11,12 @@ import org.springframework.cassandra.config.xml.CassandraClusterParser;
* @author Matthew T. Adams
*/
public class CassandraDataClusterParser extends CassandraClusterParser {
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
return super.parseInternal(element, parserContext);
}
}

View File

@@ -36,5 +36,7 @@ public class CassandraDataNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("cluster", new CassandraDataClusterParser());
registerBeanDefinitionParser("session", new CassandraDataSessionParser());
registerBeanDefinitionParser("template", new CassandraDataTemplateParser());
registerBeanDefinitionParser("converter", new CassandraMappingConverterParser());
registerBeanDefinitionParser("mapping", new CassandraMappingContextParser());
}
}

View File

@@ -1,20 +1,16 @@
package org.springframework.data.cassandra.config.xml;
import static org.springframework.cassandra.config.xml.ParsingUtils.*;
import java.util.HashSet;
import java.util.Set;
import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyReference;
import static org.springframework.cassandra.config.xml.ParsingUtils.addOptionalPropertyValue;
import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredPropertyReference;
import static org.springframework.cassandra.config.xml.ParsingUtils.addRequiredPropertyValue;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cassandra.config.xml.CassandraSessionParser;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.config.CassandraDataSessionFactoryBean;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.config.SchemaAction;
import org.springframework.data.cassandra.mapping.EntityMapping;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -30,6 +26,14 @@ public class CassandraDataSessionParser extends CassandraSessionParser {
return CassandraDataSessionFactoryBean.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
}
@Override
protected void parseUnhandledSessionElementAttribute(Attr attribute, ParserContext parserContext,
BeanDefinitionBuilder builder) {
@@ -46,55 +50,11 @@ public class CassandraDataSessionParser extends CassandraSessionParser {
}
@Override
protected void parseUnhandledElement(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
protected void setDefaultProperties(BeanDefinitionBuilder builder) {
if ("mapping".equals(element.getLocalName())) {
parseMapping(element, parserContext, builder);
} else {
super.parseUnhandledElement(element, parserContext, builder);
}
}
super.setDefaultProperties(builder);
protected void parseMapping(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
// TODO: parse <mapping> attributes here, if there ever are any
Set<EntityMapping> mappings = new HashSet<EntityMapping>();
for (Element child : DomUtils.getChildElementsByTagName(element, "entity")) {
EntityMapping entityMapping = parseEntity(child);
if (entityMapping != null) {
mappings.add(entityMapping);
}
}
Mapping mapping = new Mapping();
mapping.setEntityMappings(mappings);
builder.addPropertyValue("mapping", mapping);
}
protected EntityMapping parseEntity(Element entity) {
String className = entity.getAttribute("class");
if (!StringUtils.hasText(className)) {
throw new IllegalStateException("class attribute must not be empty");
}
Element table = DomUtils.getChildElementByTagName(entity, "table");
if (table == null) {
return null;
}
String tableName = table.getAttribute("name");
if (!StringUtils.hasText(tableName)) {
tableName = null;
}
// TODO: parse future entity mappings here, like table options
return new EntityMapping(className, tableName);
addRequiredPropertyValue(builder, "schemaAction", SchemaAction.NONE.name());
addRequiredPropertyReference(builder, "converter", DefaultDataBeanNames.CONVERTER);
}
}

View File

@@ -32,6 +32,9 @@ public class CassandraDataTemplateParser extends CassandraTemplateParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
super.doParse(element, parserContext, builder);
parseConverterAttribute(element, parserContext, builder);

View File

@@ -0,0 +1,87 @@
package org.springframework.data.cassandra.config.xml;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.EntityMapping;
import org.springframework.data.cassandra.mapping.Mapping;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Spring Data Cassandra XML namespace parser for the &lt;mapping&gt; element.
*
* @author Matthew T. Adams
*/
public class CassandraMappingContextParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return DefaultCassandraMappingContext.class;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : DefaultDataBeanNames.CONTEXT;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
parseMapping(element, builder);
}
protected void parseMapping(Element element, BeanDefinitionBuilder builder) {
Set<EntityMapping> mappings = new HashSet<EntityMapping>();
for (Element entity : DomUtils.getChildElementsByTagName(element, "entity")) {
EntityMapping entityMapping = parseEntity(entity);
if (entityMapping != null) {
mappings.add(entityMapping);
}
}
Mapping mapping = new Mapping();
mapping.setEntityMappings(mappings);
builder.addPropertyValue("mapping", mapping);
}
protected EntityMapping parseEntity(Element entity) {
String className = entity.getAttribute("class");
if (!StringUtils.hasText(className)) {
throw new IllegalStateException("class attribute must not be empty");
}
Element table = DomUtils.getChildElementByTagName(entity, "table");
if (table == null) {
return null;
}
String tableName = table.getAttribute("name");
if (!StringUtils.hasText(tableName)) {
tableName = null;
}
// TODO: parse future entity mappings here, like table options
return new EntityMapping(className, tableName);
}
}

View File

@@ -0,0 +1,45 @@
package org.springframework.data.cassandra.config.xml;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Spring Data Cassandra XML namespace parser for the &lt;converter&gt; element.
*
* @author Matthew T. Adams
*/
public class CassandraMappingConverterParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return MappingCassandraConverter.class;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : DefaultDataBeanNames.CONVERTER;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
CassandraMappingXmlBeanFactoryPostProcessorRegistrar.ensureRegistration(element, parserContext);
String mappingRef = element.getAttribute("mapping-ref");
if (!StringUtils.hasText(mappingRef)) {
mappingRef = DefaultDataBeanNames.CONTEXT;
}
builder.addConstructorArgReference(mappingRef);
}
}

View File

@@ -0,0 +1,44 @@
package org.springframework.data.cassandra.config.xml;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.cassandra.config.CassandraMappingBeanFactoryPostProcessor;
import org.springframework.data.config.BeanComponentDefinitionBuilder;
import org.w3c.dom.Element;
/**
* Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered.
*
* @author Matthew T. Adams
*/
public class CassandraMappingXmlBeanFactoryPostProcessorRegistrar {
/**
* Ensures that a {@link CassandraMappingBeanFactoryPostProcessor} is registered. This method is a no-op if one is
* already registered.
*/
public static void ensureRegistration(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
if (!(registry instanceof GenericApplicationContext)) {
return;
}
ConfigurableListableBeanFactory factory = ((GenericApplicationContext) registry).getBeanFactory();
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory,
CassandraMappingBeanFactoryPostProcessor.class, true, false);
if (names.length > 0) {
return;
}
BeanComponentDefinitionBuilder componentBuilder = new BeanComponentDefinitionBuilder(element, parserContext);
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(CassandraMappingBeanFactoryPostProcessor.class);
parserContext.registerBeanComponent(componentBuilder.getComponent(definitionBuilder));
}
}

View File

@@ -123,7 +123,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(type);
Assert.notNull(id);
CassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
Select select = QueryBuilder.select().countAll().from(entity.getTableName());
appendIdCriteria(select.where(), entity, id);
@@ -152,9 +152,9 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
Assert.notNull(type);
Assert.notNull(id);
CassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
Delete delete = QueryBuilder.delete().all().from(entity.getTableName());
Delete delete = QueryBuilder.delete().from(entity.getTableName());
appendIdCriteria(delete.where(), entity, id);
execute(delete.getQueryString());
@@ -192,7 +192,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public String getTableName(Class<?> type) {
return mappingContext.getRequiredPersistentEntity(type).getTableName();
return mappingContext.getPersistentEntity(type).getTableName();
}
@Override
@@ -252,7 +252,7 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
@Override
public <T> List<T> selectBySimpleIds(Class<T> type, Iterable<?> ids) {
CassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
CassandraPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
if (entity.getIdProperty().isCompositePrimaryKey()) {
throw new IllegalArgumentException(String.format(

View File

@@ -4,7 +4,6 @@ import java.util.Collection;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.TableMetadata;
@@ -59,22 +58,17 @@ public interface CassandraMappingContext extends
boolean usesTable(TableMetadata table);
/**
* Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws
* {@link IllegalArgumentException}.
* Returns the existing {@link CassandraPersistentEntity} for the given {@link Class}. If it is not yet known to this
* {@link CassandraMappingContext}, an {@link IllegalArgumentException} is thrown.
*
* @param type The Java type of the persistent entity.
* @return The {@link CassandraPersistentEntity} describing the persistent Java type.
* @throws IllegalArgumentException if the persistent entity is unknown
* @param type The class of the existing persistent entity.
* @return The existing persistent entity.
*/
public CassandraPersistentEntity<?> getRequiredPersistentEntity(Class<?> type);
CassandraPersistentEntity<?> getExistingPersistentEntity(Class<?> type);
/**
* Returns the {@link CassandraPersistentEntity} for the given type. If it doesn't exist, this method throws
* {@link IllegalArgumentException}.
*
* @param type The {@link TypeInformation} of the persistent entity.
* @return The {@link CassandraPersistentEntity} describing the persistent Java type.
* @throws IllegalArgumentException if the persistent entity is unknown
* Returns whether this {@link CassandraMappingContext} already contains a {@link CassandraPersistentEntity} for the
* given type.
*/
public CassandraPersistentEntity<?> getRequiredPersistentEntity(TypeInformation<?> type);
boolean contains(Class<?> type);
}

View File

@@ -37,6 +37,8 @@ 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 org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.TableMetadata;
@@ -52,9 +54,14 @@ public class DefaultCassandraMappingContext extends
CassandraMappingContext, ApplicationContextAware {
protected ApplicationContext context;
protected Mapping mapping = new Mapping();
protected ClassLoader beanClassLoader;
// useful caches
protected Map<String, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<String, Set<CassandraPersistentEntity<?>>>();
protected Set<CassandraPersistentEntity<?>> nonPrimaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Set<CassandraPersistentEntity<?>> primaryKeyEntities = new HashSet<CassandraPersistentEntity<?>>();
protected Map<Class<?>, CassandraPersistentEntity<?>> entitiesByType = new HashMap<Class<?>, CassandraPersistentEntity<?>>();
/**
* Creates a new {@link DefaultCassandraMappingContext}.
@@ -63,6 +70,14 @@ public class DefaultCassandraMappingContext extends
setSimpleTypeHolder(new CassandraSimpleTypeHolder());
}
@Override
public void initialize() {
super.initialize();
processMappingOverrides();
}
@Override
public Collection<CassandraPersistentEntity<?>> getPersistentEntities() {
return getPersistentEntities(false);
@@ -123,6 +138,8 @@ public class DefaultCassandraMappingContext extends
nonPrimaryKeyEntities.add(entity);
}
entitiesByType.put(entity.getType(), entity);
return entity;
}
@@ -184,28 +201,65 @@ public class DefaultCassandraMappingContext extends
return spec;
}
@Override
public CassandraPersistentEntity<?> getRequiredPersistentEntity(Class<?> type) {
public void setMapping(Mapping mapping) {
CassandraPersistentEntity<?> entity = getPersistentEntity(type);
Assert.notNull(mapping);
if (entity == null) {
throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]", type.getName()));
this.mapping = mapping;
}
protected void processMappingOverrides() {
if (mapping == null) {
return;
}
return entity;
for (EntityMapping entityMapping : mapping.getEntityMappings()) {
if (entityMapping == null) {
continue;
}
String entityClassName = entityMapping.getEntityClassName();
Class<?> entityClass;
try {
entityClass = ClassUtils.forName(entityClassName, beanClassLoader);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format("unknown persistent entity name [%s]", entityClassName), e);
}
CassandraPersistentEntity<?> entity = getPersistentEntity(entityClass);
if (entity == null) {
throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName));
}
String tableName = entityMapping.getTableName();
if (!StringUtils.hasText(tableName)) {
continue;
}
entity.setTableName(tableName);
}
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
@Override
public CassandraPersistentEntity<?> getRequiredPersistentEntity(TypeInformation<?> type) {
public CassandraPersistentEntity<?> getExistingPersistentEntity(Class<?> type) {
CassandraPersistentEntity<?> entity = getPersistentEntity(type);
if (entity == null) {
throw new IllegalArgumentException(String.format("no persistence metadata found for type [%s]",
type.getActualType()));
CassandraPersistentEntity<?> entity = entitiesByType.get(type);
if (entity != null) {
return entity;
}
return entity;
throw new IllegalArgumentException(String.format("unknown persistent type [%s]", type.getName()));
}
@Override
public boolean contains(Class<?> type) {
return entitiesByType.containsKey(type);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.repository.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.cassandra.config.xml.ParsingUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.data.cassandra.config.DefaultDataBeanNames;
import org.springframework.data.cassandra.repository.support.CassandraRepositoryFactoryBean;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
@@ -52,7 +53,7 @@ public class CassandraRepositoryConfigurationExtension extends RepositoryConfigu
Element element = config.getElement();
ParsingUtils.addOptionalPropertyReference(builder, "cassandraTemplate", element, CASSANDRA_TEMPLATE_REF,
"cassandra-template");
DefaultDataBeanNames.TEMPLATE);
}
@Override

View File

@@ -452,8 +452,6 @@ Arbitrary CQL script to be executed against the session's keyspace during bean d
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="mapping" type="mappingType" minOccurs="0"
maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="optional">
@@ -482,7 +480,7 @@ The name of a Cassandra Keyspace. No default; for the system keyspace, use the
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandra-converter".
The reference to a CassandraConverter; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -500,7 +498,7 @@ The schema action to perform; default is NONE.
<xsd:attribute name="id" type="xsd:ID" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the template; default is "cassandra-template".
The name of the template; default is "cassandraTemplate".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -515,7 +513,7 @@ The reference to a Cassandra session; default is "cassandra-session".
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a CassandraConverter; default is "cassandra-converter".
The reference to a CassandraConverter; default is "cassandraConverter".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -629,13 +627,6 @@ The replication factor for the data center.
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
<xsd:complexType name="mappingType">
<xsd:sequence>
<xsd:element name="entity" type="entityType" minOccurs="0"
maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="entityType">
<xsd:sequence>
<xsd:element name="table" type="tableType" minOccurs="0"
@@ -676,11 +667,10 @@ Table name override.
</xsd:simpleType>
<xsd:attributeGroup name="cassandra-repository-attributes">
<xsd:attribute name="cassandra-template-ref" type="cassandraTemplateRef"
default="cassandra-template">
<xsd:attribute name="cassandra-template-ref" type="cassandraTemplateRef">
<xsd:annotation>
<xsd:documentation><![CDATA[
The reference to a cassandraTemplate. Will default to 'cassandra-template'.
The reference to a cassandraTemplate. Will default to 'cassandraTemplate'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -696,4 +686,65 @@ The reference to a cassandraTemplate. Will default to 'cassandra-template'.
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="mapping">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a CassandraMappingContext for holding rich entity mapping information.
]]></xsd:documentation>
<xsd:appinfo>
<tool:exports
type="org.springframework.data.cassandra.mapping.CassandraMappingContext" />
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="entity" type="entityType" minOccurs="0"
maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="entity-base-packages" type="xsd:string"
use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The comma-delimited base packages in which to scan for entities and their mapping information.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="converter">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines a CassandraConverter for getting rich mapping functionality.
]]></xsd:documentation>
<xsd:appinfo>
<tool:exports
type="org.springframework.data.cassandra.convert.CassandraConverter" />
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="mapping-ref" type="mappingContextRef"
use="optional">
<xsd:annotation>
<xsd:documentation
source="org.springframework.data.cassandra.mapping.CassandraMappingContext"><![CDATA[
The reference to a CassandraMappingContext. Will default to 'cassandraMapping'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:simpleType name="mappingContextRef">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:assignable-to
type="org.springframework.data.cassandra.mapping.CassandraMappingContext" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:union memberTypes="xsd:string" />
</xsd:simpleType>
</xsd:schema>

View File

@@ -69,8 +69,8 @@ public class CommentRepositoryJavaConfigIntegrationTests extends AbstractSpringD
}
@Override
public String getEntityBasePackage() {
return Comment.class.getPackage().getName();
public String[] getEntityBasePackages() {
return new String[] { Comment.class.getPackage().getName() };
}
}

View File

@@ -19,8 +19,6 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;

View File

@@ -0,0 +1,54 @@
package org.springframework.data.cassandra.test.integration.mappingcontext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
public class MappingContextIntegrationTests {
public static class Transient {
}
@Table
public static class X {
@PrimaryKey
String key;
}
@Table
public static class Y {
@PrimaryKey
String key;
}
DefaultCassandraMappingContext ctx = new DefaultCassandraMappingContext();
@Test
// TODO: (expected = MappingException.class)
public void testGetPersistentEntityOfTransientType() {
// TODO: when entity verification is added (DATACASS-85), this should throw a MappingException
CassandraPersistentEntity<?> entity = ctx.getPersistentEntity(Transient.class);
// TODO: remove following lines after DATACASS-85
assertNotNull(entity);
assertEquals(Transient.class.getSimpleName().toLowerCase(), entity.getTableName());
}
@Test
public void testGetExistingPersistentEntityHappyPath() {
ctx.getPersistentEntity(X.class);
assertTrue(ctx.contains(X.class));
assertNotNull(ctx.getExistingPersistentEntity(X.class));
assertFalse(ctx.contains(Y.class));
}
}

View File

@@ -0,0 +1,28 @@
package org.springframework.data.cassandra.test.integration.minimal.config;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.test.integration.minimal.config.entities.AbsMin;
import org.springframework.data.cassandra.test.integration.support.AbstractSpringDataEmbeddedCassandraIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AbsoluteMinimumXmlConfigIntegrationTest extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Autowired
CassandraMappingContext context;
@Test
public void test() {
assertNotNull(context);
context.getPersistentEntity(AbsMin.class);
}
}

View File

@@ -0,0 +1,12 @@
package org.springframework.data.cassandra.test.integration.minimal.config.entities;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class AbsMin {
@PrimaryKey
String key;
}

View File

@@ -0,0 +1,6 @@
package org.springframework.data.cassandra.test.integration.minimal.config.entities;
import org.springframework.data.cassandra.repository.CassandraRepository;
public interface AbsMinRepository extends CassandraRepository<AbsMin, String> {
}

View File

@@ -0,0 +1,47 @@
package org.springframework.data.cassandra.test.integration.multipackagescanning;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.HashSet;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext;
import org.springframework.data.cassandra.test.integration.multipackagescanning.first.First;
import org.springframework.data.cassandra.test.integration.multipackagescanning.second.Second;
import org.springframework.data.cassandra.test.integration.multipackagescanning.third.Third;
public class MultipackageScanningIntegrationTests {
DefaultCassandraMappingContext mapping;
String pkg = getClass().getPackage().getName();
@Before
public void before() throws ClassNotFoundException {
mapping = new DefaultCassandraMappingContext();
mapping.setInitialEntitySet(CassandraEntityClassScanner.scan(pkg + ".first", pkg + ".second"));
mapping.initialize();
}
@Test
public void test() {
Collection<CassandraPersistentEntity<?>> entities = mapping.getPersistentEntities();
Collection<Class<?>> types = new HashSet<Class<?>>(entities.size());
for (CassandraPersistentEntity<?> entity : entities) {
types.add(entity.getType());
}
assertTrue(types.contains(First.class));
assertTrue(types.contains(Second.class));
assertFalse(types.contains(Third.class));
assertFalse(types.contains(Top.class));
}
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.cassandra.test.integration.multipackagescanning;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class Top {
@PrimaryKey
String key;
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.cassandra.test.integration.multipackagescanning.first;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class First {
@PrimaryKey
String key;
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.cassandra.test.integration.multipackagescanning.second;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class Second {
@PrimaryKey
String key;
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.cassandra.test.integration.multipackagescanning.third;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Table;
@Table
public class Third {
@PrimaryKey
String key;
}

View File

@@ -68,8 +68,8 @@ public class UserRepositoryJavaConfigIntegrationTests extends AbstractSpringData
}
@Override
public String getEntityBasePackage() {
return User.class.getPackage().getName();
public String[] getEntityBasePackages() {
return new String[] { User.class.getPackage().getName() };
}
}

View File

@@ -67,8 +67,8 @@ public class CassandraDataOperationsTest extends AbstractSpringDataEmbeddedCassa
}
@Override
public String getEntityBasePackage() {
return Book.class.getPackage().getName();
public String[] getEntityBasePackages() {
return new String[] { Book.class.getPackage().getName() };
}
}

View File

@@ -1,33 +1,34 @@
<?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: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"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util-3.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">
<import resource="classpath:/spring-data-cassandra-basic.xml" />
<cassandra:cluster id="cassandra-cluster"
port="${cassandra.native_transport_port}">
<cass:mapping
entity-base-packages="org.springframework.data.cassandra.test.integration.composites">
<cass:entity
class="org.springframework.data.cassandra.test.integration.composites.Comment">
<cass:table name="comments_x" />
</cass:entity>
</cass:mapping>
<cassandra:keyspace name="CommentRepositoryXmlConfigIntegrationTests"
<cass:cluster port="${cassandra.native_transport_port}">
<cass:keyspace name="CommentRepositoryXmlConfigIntegrationTests"
action="CREATE" durable-writes="true">
</cassandra:keyspace>
</cassandra:cluster>
</cass:keyspace>
</cass:cluster>
<cassandra:session id="cassandra-session"
cluster-ref="cassandra-cluster" keyspace-name="CommentRepositoryXmlConfigIntegrationTests"
cassandra-converter-ref="cassandra-converter" schema-action="CREATE">
<cassandra:mapping>
<cassandra:entity
class="org.springframework.data.cassandra.test.integration.composites.Comment">
<cassandra:table name="comments" />
</cassandra:entity>
</cassandra:mapping>
</cassandra:session>
<cass:session keyspace-name="CommentRepositoryXmlConfigIntegrationTests"
schema-action="CREATE">
</cass:session>
<cassandra:repositories
<cass:repositories
base-package="org.springframework.data.cassandra.test.integration.composites" />
</beans>

View File

@@ -11,9 +11,8 @@
<context:property-placeholder
location="classpath:/org/springframework/data/cassandra/test/integration/config/cassandra.properties" />
<cass:cluster id="cassandra-cluster"
contact-points="${cassandra.contactPoints}" port="${cassandra.native_transport_port}"
compression="SNAPPY">
<cass:cluster contact-points="${cassandra.contactPoints}"
port="${cassandra.native_transport_port}" compression="SNAPPY">
<cass:local-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="2" max-connections="8" />
@@ -27,21 +26,13 @@
durable-writes="true" />
</cass:cluster>
<!-- TODO: not require that this bean be defined -->
<bean id="cassandra-mapping"
class="org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext"/>
<!-- TODO: not require that these beans be defined -->
<cass:mapping />
<cass:converter />
<!-- TODO: not require that this bean be defined -->
<bean id="cassandra-converter"
class="org.springframework.data.cassandra.convert.MappingCassandraConverter">
<constructor-arg ref="cassandra-mapping" />
</bean>
<cass:session keyspace-name="${cassandra.keyspace}"
schema-action="NONE" />
<cass:session id="cassandra-session" keyspace-name="${cassandra.keyspace}"
schema-action="NONE" cluster-ref="cassandra-cluster"
cassandra-converter-ref="cassandra-converter">
</cass:session>
<cass:template session-ref="cassandra-session" />
<cass:template />
</beans>

View File

@@ -0,0 +1,22 @@
<?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:cass="http://www.springframework.org/schema/data/cassandra"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util-3.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">
<!-- this is needed just so it runs correctly as part of the build, so ignore it & the port attribute of the cluster below -->
<context:property-placeholder
location="classpath:/spring-data-cassandra-build.properties" />
<cass:cluster port="${cassandra.native_transport_port}" />
<cass:session keyspace-name="system" />
<cass:repositories
base-package="org.springframework.data.cassandra.test.integration.minimal.config" />
</beans>

View File

@@ -1,7 +1,8 @@
<?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:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
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"
xmlns:util="http://www.springframework.org/schema/util"
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
@@ -11,25 +12,24 @@
<import resource="classpath:/spring-data-cassandra-basic.xml" />
<cassandra:cluster id="cassandra-cluster"
port="${cassandra.native_transport_port}">
<cass:mapping
entity-base-packages="org.springframework.data.cassandra.test.integration.repository">
<cass:entity
class="org.springframework.data.cassandra.test.integration.repository.User">
<cass:table name="users_x" />
</cass:entity>
</cass:mapping>
<cassandra:keyspace name="UserRepositoryXmlConfigIntegrationTests"
<cass:cluster port="${cassandra.native_transport_port}">
<cass:keyspace name="UserRepositoryXmlConfigIntegrationTests"
action="CREATE" durable-writes="true">
</cassandra:keyspace>
</cassandra:cluster>
</cass:keyspace>
</cass:cluster>
<cassandra:session id="cassandra-session"
cluster-ref="cassandra-cluster" keyspace-name="UserRepositoryXmlConfigIntegrationTests"
cassandra-converter-ref="cassandra-converter" schema-action="CREATE">
<cassandra:mapping>
<cassandra:entity
class="org.springframework.data.cassandra.test.integration.repository.User">
<cassandra:table name="users_x" />
</cassandra:entity>
</cassandra:mapping>
</cassandra:session>
<cass:session keyspace-name="UserRepositoryXmlConfigIntegrationTests"
schema-action="CREATE">
</cass:session>
<cassandra:repositories
<cass:repositories
base-package="org.springframework.data.cassandra.test.integration.repository" />
</beans>

View File

@@ -1,6 +1,6 @@
<?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: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
@@ -10,15 +10,8 @@
<context:property-placeholder
location="classpath:/spring-data-cassandra-build.properties" />
<bean id="cassandra-mapping"
class="org.springframework.data.cassandra.mapping.DefaultCassandraMappingContext" />
<cass:converter mapping-ref="cassandraMapping"/>
<bean id="cassandra-converter"
class="org.springframework.data.cassandra.convert.MappingCassandraConverter">
<constructor-arg ref="cassandra-mapping" />
</bean>
<cassandra:template id="cassandra-template"
cassandra-converter-ref="cassandra-converter" session-ref="cassandra-session" />
<cass:template />
</beans>