diff --git a/README.md b/README.md
index d09c3699d..daba9a4f7 100644
--- a/README.md
+++ b/README.md
@@ -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``.
spring-libs-snapshot
http://repo.spring.io/libs-snapshot
+
+ true
+
-Then include the latest and greatest JAR into your project dependencies.
+### CQL Only (spring-cassandra)
-**CQL Only**
+*Maven Coordinates*
org.springframework.data
@@ -19,7 +22,41 @@ Then include the latest and greatest JAR into your project dependencies.
1.0.0.BUILD-SNAPSHOT
-**CQL and Object Mapping**
+*Minimal Spring XML Configuration*
+
+
+
+
+
+*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*
org.springframework.data
@@ -27,10 +64,39 @@ Then include the latest and greatest JAR into your project dependencies.
1.0.0.BUILD-SNAPSHOT
+*Minimal Spring XML Configuration*
+
+
+
+
+
+*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 `` 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 `` __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
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java
index 344308e42..184fdc0d3 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraSessionParser.java
@@ -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) {
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java
index 871bd0bbe..03f370e7a 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraTemplateParser.java
@@ -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) {
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java
index c50dc5316..89b34678b 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/DefaultBeanNames.java
@@ -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";
}
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java
new file mode 100644
index 000000000..9371948ab
--- /dev/null
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/CqlIdentifier.java
@@ -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.
+ *
+ * - If the given identifier is a legal quoted identifier or forceQuote is true, it is set encased in double quotes.
+ *
+ * - If the given identifier is a legal unquoted identifier, it is set unchanged.
+ * - If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.
+ *
+ */
+ 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;
+ }
+}
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java
index 61180cc7f..73d0c70e3 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/CqlStringUtils.java
@@ -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.
- *
- * - If the given identifier is a legal unquoted identifier, it is returned unchanged.
- * - If the given identifier is a legal quoted identifier, it is returned encased in double quotes.
- * - If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.
- *
- */
- 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 null, returns null.
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java
index 6cea473f8..0cdc23ee1 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnChangeSpecification.java
@@ -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();
}
}
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java
index 9c2d0ba03..ca8a2eec3 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/ColumnSpecification.java
@@ -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
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java
index 1f9f89d46..5744e5c1a 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/CreateIndexSpecification.java
@@ -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
/**
* The name of the index.
*/
- private String name;
+ private CqlIdentifier identifier;
/**
* Sets the index name.
@@ -38,17 +37,16 @@ public abstract class IndexNameSpecification
*/
@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();
}
}
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java
index cddc2115d..f8fd83af7 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java
@@ -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> {
/**
- * 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 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();
}
}
diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java
index f99addd15..2553bab61 100644
--- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java
+++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/TableNameSpecification.java
@@ -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
/**
* The name of the table.
*/
- private String name;
+ private CqlIdentifier identifier;
/**
* Sets the table name.
@@ -38,16 +37,15 @@ public abstract class TableNameSpecification
*/
@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();
}
}
diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java
new file mode 100644
index 000000000..8b404ec5e
--- /dev/null
+++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlIdentifierTest.java
@@ -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"));
+ }
+
+}
diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java
index e54d7b29b..9da6874a5 100644
--- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java
+++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/CqlStringUtilsTest.java
@@ -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"));
- }
}
diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java
index e281fdaa7..e1b3fe86e 100644
--- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java
+++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterTableCqlGeneratorTests.java
@@ -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 + " "));
}
diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml
index 906a2a32f..8259eb66c 100644
--- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml
+++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml
@@ -10,69 +10,66 @@
location="classpath:/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties" />
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
+
-
+
+
+
+
-
+
-
+
-
-
-
+ max-simultaneous-requests="${remote.max.requests}" core-connections="${remote.core.connections}"
+ max-connections="${remote.max.connections}" />
+
+
+
-
-
+
+
-
-
+
-
+
diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml
index 7f63c803a..c779fde4b 100644
--- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml
+++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml
@@ -9,8 +9,8 @@
-
+
@@ -18,16 +18,15 @@
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="1" max-connections="2" />
+ 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" />
-
+
-
+
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java
new file mode 100644
index 000000000..fa72ff549
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionBuilderArgument.java
@@ -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:
+ *
+ * {@link BeanDefinitionBuilder#addConstructorArgReference(String)},
+ * {@link BeanDefinitionBuilder#addConstructorArgValue(Object)},
+ * {@link BeanDefinitionBuilder#addPropertyReference(String, String)}, and
+ * {@link BeanDefinitionBuilder#addPropertyValue(String, Object)}.
+ *
+ * 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 true. 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 false. 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;
+ }
+}
\ No newline at end of file
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java
new file mode 100644
index 000000000..666c5beb5
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/BeanDefinitionUtils.java
@@ -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 null, 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
+ * null.
+ * @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
+ * required was false, otherwise throws {@link IllegalArgumentException}.
+ *
+ * @param registry The {@link BeanDefinitionRegistry}, often the very same instance as the factor
+ * parameter.
+ * @param factory The {@link ListableBeanFactory}, often the very same instance as the registry
+ * parameter.
+ * @param type The required {@link BeanDefinition}'s type.
+ * @param includeNonSingletons Whether to include beans with scope other than singleton
+ * @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 required.
+ * @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 factor
+ * parameter.
+ * @param factory The {@link ListableBeanFactory}, often the very same instance as the registry
+ * parameter.
+ * @param type The required {@link BeanDefinition}'s type.
+ * @param includeNonSingletons Whether to include beans with scope other than singleton
+ * @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;
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java
index 436b66b68..69bc1be26 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraDataSessionFactoryBean.java
@@ -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;
- }
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java
new file mode 100644
index 000000000..a32c21055
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraEntityClassScanner.java
@@ -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> scan(String... entityBasePackages) throws ClassNotFoundException {
+ return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses();
+ }
+
+ public static Set> scan(Class>... entityBasePackageClasses) throws ClassNotFoundException {
+ return new CassandraEntityClassScanner(entityBasePackageClasses).scanForEntityClasses();
+ }
+
+ public static Set> scan(Collection entityBasePackages) throws ClassNotFoundException {
+ return new CassandraEntityClassScanner(entityBasePackages).scanForEntityClasses();
+ }
+
+ public static Set> scan(Collection entityBasePackages, Collection> entityBasePackageClasses)
+ throws ClassNotFoundException {
+ return new CassandraEntityClassScanner(entityBasePackages, entityBasePackageClasses).scanForEntityClasses();
+ }
+
+ protected Set entityBasePackages = new HashSet();
+ protected Set> entityBasePackageClasses = new HashSet>();
+ 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 entityBasePackages) {
+ this(entityBasePackages, null);
+ }
+
+ public CassandraEntityClassScanner(Collection entityBasePackages,
+ Collection> entityBasePackageClasses) {
+
+ setEntityBasePackages(entityBasePackages);
+ setEntityBasePackageClasses(entityBasePackageClasses);
+ }
+
+ public Set getEntityBasePackages() {
+ return Collections.unmodifiableSet(entityBasePackages);
+ }
+
+ public void setEntityBasePackages(Collection entityBasePackages) {
+ this.entityBasePackages = entityBasePackages == null ? new HashSet() : new HashSet(
+ entityBasePackages);
+ }
+
+ public Set> getEntityBasePackageClasses() {
+ return Collections.unmodifiableSet(entityBasePackageClasses);
+ }
+
+ public void setEntityBasePackageClasses(Collection> entityBasePackageClasses) {
+ this.entityBasePackageClasses = entityBasePackageClasses == null ? new HashSet>() : new HashSet>(
+ 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 Set<Class<?>> representing the annotated entity classes found.
+ * @throws ClassNotFoundException
+ */
+ public Set> scanForEntityClasses() throws ClassNotFoundException {
+
+ Set> classes = new HashSet>();
+
+ for (String basePackage : getEntityBasePackages()) {
+ classes.addAll(scanBasePackageForEntities(basePackage));
+ }
+
+ for (Class> basePackageClass : getEntityBasePackageClasses()) {
+ classes.addAll(scanBasePackageForEntities(basePackageClass.getPackage().getName()));
+ }
+
+ return classes;
+ }
+
+ protected Set> scanBasePackageForEntities(String basePackage) throws ClassNotFoundException {
+
+ HashSet> classes = new HashSet>();
+
+ 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 };
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java
new file mode 100644
index 000000000..7b12fbb3e
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/CassandraMappingBeanFactoryPostProcessor.java
@@ -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:
+ *
+ * - {@link CassandraOperations} via {@link CassandraTemplate}
+ * - {@link CassandraMappingContext} via {@link DefaultCassandraMappingContext}
+ * - {@link CassandraConverter} via {@link MappingCassandraConverter}
+ *
+ *
+ * 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}.
+ *
+ * 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.
+ *
+ * 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;
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java
index 43e4ded92..959c0623b 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/DefaultDataBeanNames.java
@@ -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";
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java
index 736aa0922..601235749 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/java/AbstractSpringDataCassandraConfiguration.java
@@ -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} (this.getClass().getPackage().getName()).
+ * The base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package
+ * name of {@literal this} (this.getClass().getPackage().getName()). 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 Set<Class<?>> representing the annotated entity classes found.
- * @throws ClassNotFoundException
- */
- protected Set> getInitialEntitySet() throws ClassNotFoundException {
-
- String basePackage = getEntityBasePackage();
- Set> initialEntitySet = new HashSet>();
-
- 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> 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
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java
index e2d3dc43f..856057ed1 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataClusterParser.java
@@ -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 <cluster> 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);
+ }
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java
index fdd7fc80c..d432cd458 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataNamespaceHandler.java
@@ -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());
}
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java
index ea2327801..8e80f39ce 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataSessionParser.java
@@ -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 attributes here, if there ever are any
-
- Set mappings = new HashSet();
-
- 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);
}
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java
index 13251df72..893086ec1 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraDataTemplateParser.java
@@ -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);
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java
new file mode 100644
index 000000000..1c4d482d6
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingContextParser.java
@@ -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 <mapping> 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 mappings = new HashSet();
+
+ 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);
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java
new file mode 100644
index 000000000..5dd2a6c81
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingConverterParser.java
@@ -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 <converter> 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);
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java
new file mode 100644
index 000000000..b7499c34e
--- /dev/null
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/config/xml/CassandraMappingXmlBeanFactoryPostProcessorRegistrar.java
@@ -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));
+ }
+}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java
index 189719802..f000371d7 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java
@@ -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 List selectBySimpleIds(Class type, Iterable> ids) {
- CassandraPersistentEntity> entity = mappingContext.getRequiredPersistentEntity(type);
+ CassandraPersistentEntity> entity = mappingContext.getPersistentEntity(type);
if (entity.getIdProperty().isCompositePrimaryKey()) {
throw new IllegalArgumentException(String.format(
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java
index d468e4b0a..06bf6fe15 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraMappingContext.java
@@ -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);
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java
index e48f7ec7d..ff7747400 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/DefaultCassandraMappingContext.java
@@ -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>> entitySetsByTableName = new HashMap>>();
protected Set> nonPrimaryKeyEntities = new HashSet>();
protected Set> primaryKeyEntities = new HashSet>();
+ protected Map, CassandraPersistentEntity>> entitiesByType = new HashMap, 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> 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);
}
}
diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java
index 91b2fc35a..5821450f0 100644
--- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java
+++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/config/CassandraRepositoryConfigurationExtension.java
@@ -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
diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/ext-spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0-trying-to-use-xml-extension.xsd
similarity index 100%
rename from spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/ext-spring-cassandra-1.0.xsd
rename to spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0-trying-to-use-xml-extension.xsd
diff --git a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd
index bed6fd896..ee2d195ee 100644
--- a/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd
+++ b/spring-data-cassandra/src/main/resources/org/springframework/data/cassandra/config/spring-cassandra-1.0.xsd
@@ -452,8 +452,6 @@ Arbitrary CQL script to be executed against the session's keyspace during bean d
]]>
-
@@ -482,7 +480,7 @@ The name of a Cassandra Keyspace. No default; for the system keyspace, use the
use="optional">
@@ -500,7 +498,7 @@ The schema action to perform; default is NONE.
@@ -515,7 +513,7 @@ The reference to a Cassandra session; default is "cassandra-session".
use="optional">
@@ -629,13 +627,6 @@ The replication factor for the data center.
-
-
-
-
-
-
-
+
@@ -696,4 +686,65 @@ The reference to a cassandraTemplate. Will default to 'cassandra-template'.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java
index 4515b97af..0980a8472 100644
--- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryJavaConfigIntegrationTests.java
@@ -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() };
}
}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java
index 306351a00..a59e06da6 100644
--- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mapping/BasicCassandraPersistentEntityIntegrationTests.java
@@ -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;
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java
new file mode 100644
index 000000000..11ab552b4
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/mappingcontext/MappingContextIntegrationTests.java
@@ -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));
+ }
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java
new file mode 100644
index 000000000..eef313a63
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest.java
@@ -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);
+ }
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java
new file mode 100644
index 000000000..cd49629c3
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMin.java
@@ -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;
+
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java
new file mode 100644
index 000000000..ffcb0fc2f
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/minimal/config/entities/AbsMinRepository.java
@@ -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 {
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java
new file mode 100644
index 000000000..ee5fd8014
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/MultipackageScanningIntegrationTests.java
@@ -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> entities = mapping.getPersistentEntities();
+
+ Collection> types = new HashSet>(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));
+ }
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java
new file mode 100644
index 000000000..2d4bcfea4
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/Top.java
@@ -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;
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java
new file mode 100644
index 000000000..ac937b8ef
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/first/First.java
@@ -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;
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java
new file mode 100644
index 000000000..7e3478ba4
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/second/Second.java
@@ -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;
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java
new file mode 100644
index 000000000..39ccbdda0
--- /dev/null
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/multipackagescanning/third/Third.java
@@ -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;
+}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java
index 9ca1eac3e..76715f958 100644
--- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/repository/UserRepositoryJavaConfigIntegrationTests.java
@@ -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() };
}
}
diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java
index 6ad24eca7..cc40cd6cf 100644
--- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java
+++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/template/CassandraDataOperationsTest.java
@@ -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() };
}
}
diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml
index a181a4745..1177ad40f 100644
--- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml
+++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/composites/CommentRepositoryXmlConfigIntegrationTests-context.xml
@@ -1,33 +1,34 @@
-
+
+
+
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
+
+
-
diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml
index 5709113fc..b434f4221 100644
--- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml
+++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/config/CassandraNamespaceTests-context.xml
@@ -11,9 +11,8 @@
-
+
@@ -27,21 +26,13 @@
durable-writes="true" />
-
-
+
+
+
-
-
-
-
+
-
-
-
-
+
diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml
new file mode 100644
index 000000000..7e2bc7eaa
--- /dev/null
+++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/minimal/config/AbsoluteMinimumXmlConfigIntegrationTest-context.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml
index a80188b22..418d3d343 100644
--- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml
+++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryXmlConfigIntegrationTests-context.xml
@@ -1,7 +1,8 @@
-
+
+
+
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
+
+
-
diff --git a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml
index 7c6868246..b5ac038be 100644
--- a/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml
+++ b/spring-data-cassandra/src/test/resources/spring-data-cassandra-basic.xml
@@ -1,6 +1,6 @@
-
+
-
-
-
-
-
+