DATACASS-219 - On startup CREATE TABLE from entities should only add 'if not exists'.
(cherry picked from commit a1e75b3857)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
47
pom.xml
47
pom.xml
@@ -1,8 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.8.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-cassandra-parent</artifactId>
|
||||
<version>1.4.2.BUILD-SNAPSHOT</version>
|
||||
@@ -12,13 +21,6 @@
|
||||
<description>Spring Data Cassandra</description>
|
||||
<url>http://www.springsource.org/spring-data/cassandra</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>1.8.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-build/parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>spring-cql</module>
|
||||
<module>spring-data-cassandra</module>
|
||||
@@ -75,6 +77,20 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -210,20 +226,6 @@
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.datastax.cassandra</groupId>
|
||||
@@ -353,6 +355,7 @@
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cql</artifactId>
|
||||
|
||||
<name>Spring CQL</name>
|
||||
<description>Raw Cassandra CQL Support for Spring Core</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-cassandra-parent</artifactId>
|
||||
@@ -16,6 +12,12 @@
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cql</artifactId>
|
||||
|
||||
<name>Spring CQL</name>
|
||||
<description>Raw Cassandra CQL Support for Spring Core</description>
|
||||
<url>https://github.com/spring-projects/spring-data-cassandra/tree/master/spring-cql</url>
|
||||
|
||||
<properties>
|
||||
<validation>1.0.0.GA</validation>
|
||||
</properties>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cassandra.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -24,136 +25,210 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cassandra.core.CqlOperations;
|
||||
import org.springframework.cassandra.core.CqlTemplate;
|
||||
import org.springframework.cassandra.support.CassandraExceptionTranslator;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.Session;
|
||||
|
||||
/**
|
||||
* Factory for configuring a Cassandra {@link Session}, which is a thread-safe singleton. As such, it is sufficient to
|
||||
* have one {@link Session} per application and keyspace.
|
||||
* Factory for creating and configuring a Cassandra {@link Session}, which is a thread-safe singleton.
|
||||
* As such, it is sufficient to have one {@link Session} per application and keyspace.
|
||||
*
|
||||
* @author Alex Shvid
|
||||
* @author Matthew T. Adams
|
||||
* @author John Blum
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.FactoryBean
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator
|
||||
* @see org.springframework.cassandra.core.CqlTemplate
|
||||
* @see org.springframework.cassandra.support.CassandraExceptionTranslator
|
||||
* @see com.datastax.driver.core.Cluster
|
||||
* @see com.datastax.driver.core.Session
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CassandraCqlSessionFactoryBean implements FactoryBean<Session>, InitializingBean, DisposableBean,
|
||||
PersistenceExceptionTranslator {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CassandraCqlSessionFactoryBean.class);
|
||||
private Cluster cluster;
|
||||
|
||||
private List<String> startupScripts = Collections.emptyList();
|
||||
private List<String> shutdownScripts = Collections.emptyList();
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected Cluster cluster;
|
||||
protected Session session;
|
||||
protected String keyspaceName;
|
||||
protected List<String> startupScripts = new ArrayList<String>();
|
||||
protected List<String> shutdownScripts = new ArrayList<String>();
|
||||
protected final PersistenceExceptionTranslator exceptionTranslator = new CassandraExceptionTranslator();
|
||||
|
||||
private Session session;
|
||||
|
||||
private String keyspaceName;
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public Session getObject() {
|
||||
return session;
|
||||
return this.session;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public Class<? extends Session> getObjectType() {
|
||||
return Session.class;
|
||||
return (this.session != null ? this.session.getClass() : Session.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return exceptionTranslator.translateExceptionIfPossible(ex);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
Assert.notNull(cluster);
|
||||
|
||||
session = StringUtils.hasText(keyspaceName) ? cluster.connect(keyspaceName) : cluster.connect();
|
||||
executeScripts(startupScripts);
|
||||
this.session = connect(getKeyspaceName());
|
||||
executeScripts(getStartupScripts());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes given scripts. Session must be connected when this method is called.
|
||||
*/
|
||||
protected void executeScripts(List<String> scripts) {
|
||||
|
||||
if (scripts == null || scripts.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
CqlTemplate template = new CqlTemplate(session);
|
||||
|
||||
for (String script : scripts) {
|
||||
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("executing raw CQL [{}]", script);
|
||||
}
|
||||
|
||||
template.execute(script);
|
||||
}
|
||||
/* (non-Javadoc) */
|
||||
Session connect(String keyspaceName) {
|
||||
return (StringUtils.hasText(keyspaceName) ? getCluster().connect(keyspaceName) : getCluster().connect());
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
|
||||
executeScripts(shutdownScripts);
|
||||
session.close();
|
||||
executeScripts(getShutdownScripts());
|
||||
getSession().close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keyspace name to connect to. Using <code>null</code>, empty string, or only whitespace will cause the
|
||||
* system keyspace to be used.
|
||||
* Executes the given Cassandra CQL scripts. The {@link Session} must be connected when this method is called.
|
||||
*/
|
||||
protected void executeScripts(List<String> scripts) {
|
||||
if (!CollectionUtils.isEmpty(scripts)) {
|
||||
CqlOperations template = newCqlOperations(getSession());
|
||||
|
||||
for (String script : scripts) {
|
||||
logger.info("executing raw CQL [{}]", script);
|
||||
template.execute(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
CqlOperations newCqlOperations(Session session) {
|
||||
return new CqlTemplate(session);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException e) {
|
||||
return this.exceptionTranslator.translateExceptionIfPossible(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Null-safe operation to determine whether the Cassandra {@link Session} is connected or not.
|
||||
*
|
||||
* @return a boolean value indicating whether the Cassandra {@link Session} is connected.
|
||||
* @see com.datastax.driver.core.Session#isClosed()
|
||||
* @see #getObject()
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
Session session = getObject();
|
||||
return !(session == null || session.isClosed());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a reference to the Cassandra {@link Cluster} to use.
|
||||
*
|
||||
* @param cluster a reference to the Cassandra {@link Cluster} used by this application.
|
||||
* @throws IllegalArgumentException if the {@link Cluster} reference is null.
|
||||
* @see com.datastax.driver.core.Cluster
|
||||
* @see #getCluster()
|
||||
*/
|
||||
public void setCluster(Cluster cluster) {
|
||||
Assert.notNull(cluster, "Cluster must not be null");
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the configured Cassandra {@link Cluster} used by this application.
|
||||
*
|
||||
* @return a reference to the configured Cassandra {@link Cluster}.
|
||||
* @throws IllegalStateException if the reference to the {@link Cluster} was not properly initialized.
|
||||
* @see com.datastax.driver.core.Cluster
|
||||
* @see #setCluster(Cluster)
|
||||
*/
|
||||
protected Cluster getCluster() {
|
||||
Assert.state(this.cluster != null, "Cluster was not properly initialized");
|
||||
return this.cluster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the Cassandra Keyspace to connect to. Passing <code>null</code>, an empty String,
|
||||
* or whitespace will cause the Cassandra System Keyspace to be used.
|
||||
*
|
||||
* @param keyspaceName a String indicating the name of the Keyspace in which to connect.
|
||||
* @see #getKeyspaceName()
|
||||
*/
|
||||
public void setKeyspaceName(String keyspaceName) {
|
||||
this.keyspaceName = keyspaceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cluster to use. Must not be null.
|
||||
* Gets the name of the Cassandra Keyspace to connect to.
|
||||
*
|
||||
* @return the name of the Cassandra Keyspace to connect to as a String.
|
||||
* @see #setKeyspaceName(String)
|
||||
*/
|
||||
public void setCluster(Cluster cluster) {
|
||||
if (cluster == null) {
|
||||
throw new IllegalArgumentException("cluster must not be null");
|
||||
}
|
||||
this.cluster = cluster;
|
||||
protected String getKeyspaceName() {
|
||||
return this.keyspaceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the connected Cassandra {@link Session}.
|
||||
*
|
||||
* @return a reference to the connected Cassandra {@link Session}.
|
||||
* @throws IllegalStateException if the Cassandra {@link Session} was not properly initialized.
|
||||
* @see com.datastax.driver.core.Session
|
||||
*/
|
||||
protected Session getSession() {
|
||||
Session session = getObject();
|
||||
Assert.state(session != null, "Session was not properly initialized");
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets CQL scripts to be executed immediately after the session is connected.
|
||||
*/
|
||||
public void setStartupScripts(List<String> scripts) {
|
||||
this.startupScripts = scripts == null ? new ArrayList<String>() : new ArrayList<String>(scripts);
|
||||
this.startupScripts = (scripts != null ? new ArrayList<String>(scripts) : Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of startup scripts.
|
||||
*/
|
||||
public List<String> getStartupScripts() {
|
||||
return Collections.unmodifiableList(startupScripts);
|
||||
return Collections.unmodifiableList(this.startupScripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets CQL scripts to be executed immediately before the session is shutdown.
|
||||
*/
|
||||
public void setShutdownScripts(List<String> scripts) {
|
||||
this.shutdownScripts = scripts == null ? new ArrayList<String>() : new ArrayList<String>(scripts);
|
||||
this.shutdownScripts = (scripts != null ? new ArrayList<String>(scripts) : Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of shutdown scripts.
|
||||
*/
|
||||
public List<String> getShutdownScripts() {
|
||||
return Collections.unmodifiableList(shutdownScripts);
|
||||
return Collections.unmodifiableList(this.shutdownScripts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,28 +19,32 @@ import org.springframework.cassandra.config.CassandraCqlSessionFactoryBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
|
||||
/**
|
||||
* Base class for Spring Cassandra configuration that can handle creating namespaces, execute arbitrary CQL on startup &
|
||||
* shutdown, and optionally drop namespaces.
|
||||
*
|
||||
* Spring {@link @Configuration} class used to configure a Cassandra client application
|
||||
* {@link com.datastax.driver.core.Session} connected to a Cassandra {@link com.datastax.driver.core.Cluster}.
|
||||
*
|
||||
* Enables a Cassandra Keyspace to be specified along with the ability to execute arbitrary CQL on startup
|
||||
* as well as shutdown.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @author John Blum
|
||||
* @see org.springframework.cassandra.config.java.AbstractClusterConfiguration
|
||||
* @see org.springframework.context.annotation.Configuration
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractSessionConfiguration extends AbstractClusterConfiguration {
|
||||
|
||||
protected abstract String getKeyspaceName();
|
||||
|
||||
@Bean
|
||||
public CassandraCqlSessionFactoryBean session() throws Exception {
|
||||
|
||||
Cluster cluster = cluster().getObject();
|
||||
|
||||
CassandraCqlSessionFactoryBean bean = new CassandraCqlSessionFactoryBean();
|
||||
bean.setCluster(cluster);
|
||||
|
||||
bean.setCluster(cluster().getObject());
|
||||
bean.setKeyspaceName(getKeyspaceName());
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
protected abstract String getKeyspaceName();
|
||||
|
||||
}
|
||||
|
||||
@@ -13,64 +13,44 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cassandra.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class CollectionUtils {
|
||||
public abstract class CollectionUtils extends org.springframework.util.CollectionUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] toArray(Iterable<T> i) {
|
||||
return (T[]) toList(i).toArray();
|
||||
public static Object[] toArray(Iterable<?> iterable) {
|
||||
return toList(iterable).toArray();
|
||||
}
|
||||
|
||||
public static <T> List<T> toList(Iterable<T> i) {
|
||||
public static <T> List<T> toList(T... elements) {
|
||||
List<T> list = Collections.emptyList();
|
||||
|
||||
List<T> list = null;
|
||||
if (i instanceof List) {
|
||||
list = (List<T>) i;
|
||||
} else {
|
||||
list = new ArrayList<T>();
|
||||
for (T t : i) {
|
||||
list.add(t);
|
||||
}
|
||||
if (elements != null) {
|
||||
list = new ArrayList<T>(elements.length);
|
||||
Collections.addAll(list, elements);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<?> toList(Object thing) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(thing);
|
||||
return list;
|
||||
}
|
||||
public static <T> List<T> toList(Iterable<T> iterable) {
|
||||
if (!(iterable instanceof List)) {
|
||||
List<T> list = new ArrayList<T>();
|
||||
|
||||
public static List<?> toList(Object thing1, Object thing2) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(thing1);
|
||||
list.add(thing2);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<?> toList(Object thing1, Object thing2, Object thing3) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(thing1);
|
||||
list.add(thing2);
|
||||
list.add(thing3);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<?> toList(Object thing1, Object thing2, Object thing3, Object... rest) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(thing1);
|
||||
list.add(thing2);
|
||||
list.add(thing3);
|
||||
if (rest != null) {
|
||||
for (Object thing : rest) {
|
||||
list.add(thing);
|
||||
if (iterable != null) {
|
||||
for (T element : iterable) {
|
||||
list.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
iterable = list;
|
||||
}
|
||||
return list;
|
||||
|
||||
return (List<T>) iterable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cassandra.config;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cassandra.core.CqlOperations;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.Session;
|
||||
|
||||
/**
|
||||
* The CassandraCqlSessionFactoryBeanUnitTests class is a test suite of test cases testing the contract
|
||||
* and functionality of the {@link CassandraCqlSessionFactoryBean} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Rule
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.rules.ExpectedException
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.runners.MockitoJUnitRunner
|
||||
* @see org.springframework.cassandra.config.CassandraCqlSessionFactoryBean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CassandraCqlSessionFactoryBeanUnitTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
private Cluster mockCluster;
|
||||
|
||||
@Mock
|
||||
private Session mockSession;
|
||||
|
||||
private CassandraCqlSessionFactoryBean factoryBean;
|
||||
|
||||
protected <T> List<T> asList(T... array) {
|
||||
return new ArrayList<T>(Arrays.asList(array));
|
||||
}
|
||||
|
||||
protected void assertNonNullEmptyCollection(Collection<?> collection) {
|
||||
assertThat(collection, is(notNullValue()));
|
||||
assertThat(collection.isEmpty(), is(true));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
factoryBean = spy(new CassandraCqlSessionFactoryBean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cassandraCqlSessionFactoryBeanIsSingleton() {
|
||||
assertThat(factoryBean.isSingleton(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void objectTypeWhenSessionHasNotBeenInitializedIsSessionClass() {
|
||||
assertThat(factoryBean.getObject(), is(nullValue()));
|
||||
assertEquals(Session.class, factoryBean.<Session>getObjectType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSetInitializesSessionWithKeyspaceAndExecutesStartupScripts() throws Exception {
|
||||
List<String> expectedStartupScripts = asList("/path/to/schema.cql", "/path/to/data.cql");
|
||||
|
||||
CqlOperations mockCqlOperations = mock(CqlOperations.class);
|
||||
|
||||
doReturn(mockSession).when(factoryBean).connect(eq("TestKeyspace"));
|
||||
doReturn(mockCqlOperations).when(factoryBean).newCqlOperations(eq(mockSession));
|
||||
|
||||
factoryBean.setKeyspaceName("TestKeyspace");
|
||||
factoryBean.setStartupScripts(expectedStartupScripts);
|
||||
|
||||
assertThat(factoryBean.getKeyspaceName(), is(equalTo("TestKeyspace")));
|
||||
assertThat(factoryBean.getStartupScripts(), is(equalTo(expectedStartupScripts)));
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
assertEquals(mockSession.getClass(), factoryBean.getObjectType());
|
||||
assertThat(factoryBean.getObject(), is(equalTo(mockSession)));
|
||||
assertThat(factoryBean.getSession(), is(equalTo(mockSession)));
|
||||
|
||||
InOrder inOrder = inOrder(factoryBean);
|
||||
|
||||
inOrder.verify(factoryBean, times(1)).connect(eq("TestKeyspace"));
|
||||
inOrder.verify(factoryBean, times(1)).executeScripts(eq(expectedStartupScripts));
|
||||
inOrder.verify(factoryBean, times(1)).newCqlOperations(eq(mockSession));
|
||||
verify(mockCqlOperations, times(1)).execute(eq(expectedStartupScripts.get(0)));
|
||||
verify(mockCqlOperations, times(1)).execute(eq(expectedStartupScripts.get(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectToSystemKeyspace() {
|
||||
when(mockCluster.connect()).thenReturn(mockSession);
|
||||
|
||||
factoryBean.setCluster(mockCluster);
|
||||
|
||||
assertThat(factoryBean.connect(null), is(equalTo(mockSession)));
|
||||
|
||||
verify(mockCluster, times(1)).connect();
|
||||
verify(mockCluster, never()).connect(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectToTargetKeyspace() {
|
||||
when(mockCluster.connect(eq("TestKeyspace"))).thenReturn(mockSession);
|
||||
|
||||
factoryBean.setCluster(mockCluster);
|
||||
|
||||
assertThat(factoryBean.connect("TestKeyspace"), is(equalTo(mockSession)));
|
||||
|
||||
verify(mockCluster, never()).connect();
|
||||
verify(mockCluster, times(1)).connect(eq("TestKeyspace"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void destroySessionAndExecutesShutdownScripts() throws Exception {
|
||||
List<String> expectedShutdownScripts = asList("/path/to/shutdown.cql");
|
||||
|
||||
CqlOperations mockCqlOperations = mock(CqlOperations.class);
|
||||
|
||||
doReturn(mockSession).when(factoryBean).getSession();
|
||||
doReturn(mockCqlOperations).when(factoryBean).newCqlOperations(eq(mockSession));
|
||||
|
||||
factoryBean.setShutdownScripts(expectedShutdownScripts);
|
||||
factoryBean.destroy();
|
||||
|
||||
InOrder inOrder = inOrder(factoryBean, mockSession);
|
||||
|
||||
inOrder.verify(factoryBean, times(1)).executeScripts(eq(expectedShutdownScripts));
|
||||
verify(mockCqlOperations, times(1)).execute(eq(expectedShutdownScripts.get(0)));
|
||||
inOrder.verify(mockSession, times(1)).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isConnectedWithNullSessionIsFalse() {
|
||||
assertThat(factoryBean.getObject(), is(nullValue()));
|
||||
assertThat(factoryBean.isConnected(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isConnectedWithClosedSessionIsFalse() {
|
||||
doReturn(mockSession).when(factoryBean).getObject();
|
||||
when(mockSession.isClosed()).thenReturn(true);
|
||||
assertThat(factoryBean.isConnected(), is(false));
|
||||
verify(mockSession, times(1)).isClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isConnectedWithOpenSessionIsTrue() {
|
||||
doReturn(mockSession).when(factoryBean).getObject();
|
||||
when(mockSession.isClosed()).thenReturn(false);
|
||||
assertThat(factoryBean.isConnected(), is(true));
|
||||
verify(mockSession, times(1)).isClosed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetCluster() {
|
||||
factoryBean.setCluster(mockCluster);
|
||||
assertThat(factoryBean.getCluster(), is(equalTo(mockCluster)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setClusterToNullThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("Cluster must not be null");
|
||||
|
||||
factoryBean.setCluster(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClusterWhenUninitializedThrowsIllegalStateException() {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("Cluster was not properly initialized");
|
||||
|
||||
factoryBean.getCluster();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetKeyspaceName() {
|
||||
assertThat(factoryBean.getKeyspaceName(), is(nullValue()));
|
||||
|
||||
factoryBean.setKeyspaceName("TEST");
|
||||
|
||||
assertThat(factoryBean.getKeyspaceName(), is(equalTo("TEST")));
|
||||
|
||||
factoryBean.setKeyspaceName(null);
|
||||
|
||||
assertThat(factoryBean.getKeyspaceName(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSessionWhenUninitializedThrowsIllegalStateException() {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage(is(equalTo("Session was not properly initialized")));
|
||||
|
||||
assertThat(factoryBean.getObject(), is(nullValue()));
|
||||
|
||||
factoryBean.getSession();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetStartupScripts() {
|
||||
assertNonNullEmptyCollection(factoryBean.getStartupScripts());
|
||||
|
||||
List<String> expectedStartupScripts = asList("/path/to/schema.cql", "/path/to/data.cql");
|
||||
|
||||
factoryBean.setStartupScripts(expectedStartupScripts);
|
||||
|
||||
List<String> actualStartupScripts = factoryBean.getStartupScripts();
|
||||
|
||||
assertThat(actualStartupScripts, is(not(sameInstance(expectedStartupScripts))));
|
||||
assertThat(actualStartupScripts, is(equalTo(expectedStartupScripts)));
|
||||
|
||||
factoryBean.setStartupScripts(null);
|
||||
|
||||
assertNonNullEmptyCollection(factoryBean.getStartupScripts());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupScriptsAreImmutable() {
|
||||
List<String> startupScripts = asList("/path/to/startup.cql");
|
||||
|
||||
factoryBean.setStartupScripts(startupScripts);
|
||||
|
||||
List<String> actualStartupScripts = factoryBean.getStartupScripts();
|
||||
|
||||
assertThat(actualStartupScripts, is(notNullValue()));
|
||||
assertThat(actualStartupScripts, is(not(sameInstance(startupScripts))));
|
||||
assertThat(actualStartupScripts, is(equalTo(startupScripts)));
|
||||
|
||||
startupScripts.add("/path/to/another.cql");
|
||||
|
||||
actualStartupScripts = factoryBean.getStartupScripts();
|
||||
|
||||
assertThat(actualStartupScripts, is(not(equalTo(startupScripts))));
|
||||
assertThat(actualStartupScripts.size(), is(equalTo(1)));
|
||||
assertThat(actualStartupScripts.get(0), is(equalTo(startupScripts.get(0))));
|
||||
|
||||
try {
|
||||
exception.expect(UnsupportedOperationException.class);
|
||||
actualStartupScripts.add("/path/to/yetAnother.cql");
|
||||
}
|
||||
finally {
|
||||
assertThat(actualStartupScripts.size(), is(equalTo(1)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetShutdownScripts() {
|
||||
assertNonNullEmptyCollection(factoryBean.getShutdownScripts());
|
||||
|
||||
List<String> expectedShutdownScripts = asList("/path/to/backup.cql", "/path/to/dropTables.cql");
|
||||
|
||||
factoryBean.setShutdownScripts(expectedShutdownScripts);
|
||||
|
||||
List<String> actualShutdownScripts = factoryBean.getShutdownScripts();
|
||||
|
||||
assertThat(actualShutdownScripts, is(not(sameInstance(expectedShutdownScripts))));
|
||||
assertThat(actualShutdownScripts, is(equalTo(expectedShutdownScripts)));
|
||||
|
||||
factoryBean.setShutdownScripts(null);
|
||||
|
||||
assertNonNullEmptyCollection(factoryBean.getShutdownScripts());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownScriptsAreImmutable() {
|
||||
List<String> shutdownScripts = asList("/path/to/shutdown.cql");
|
||||
|
||||
factoryBean.setShutdownScripts(shutdownScripts);
|
||||
|
||||
List<String> actualShutdownScripts = factoryBean.getShutdownScripts();
|
||||
|
||||
assertThat(actualShutdownScripts, is(notNullValue()));
|
||||
assertThat(actualShutdownScripts, is(not(sameInstance(shutdownScripts))));
|
||||
assertThat(actualShutdownScripts, is(equalTo(shutdownScripts)));
|
||||
|
||||
shutdownScripts.add("/path/to/corruptSession.cql");
|
||||
|
||||
actualShutdownScripts = factoryBean.getShutdownScripts();
|
||||
|
||||
assertThat(actualShutdownScripts, is(not(sameInstance(shutdownScripts))));
|
||||
assertThat(actualShutdownScripts, is(not(equalTo(shutdownScripts))));
|
||||
assertThat(actualShutdownScripts.size(), is(equalTo(1)));
|
||||
|
||||
try {
|
||||
exception.expect(UnsupportedOperationException.class);
|
||||
actualShutdownScripts.add("/path/to/blowUpCluster.cql");
|
||||
}
|
||||
finally {
|
||||
assertThat(actualShutdownScripts.size(), is(equalTo(1)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cassandra.core.util;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* The CollectionUtilsUnitTests class is a test suite of test cases testing the contract and functionality
|
||||
* of the {@link CollectionUtils} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.springframework.cassandra.core.util.CollectionUtils
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class CollectionUtilsUnitTests {
|
||||
|
||||
<T> List<T> asList(T... array) {
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
void assertNonNullEmptyArray(Object[] array) {
|
||||
assertThat(array, is(notNullValue()));
|
||||
assertThat(array.length, is(equalTo(0)));
|
||||
}
|
||||
|
||||
void assertNonNullEmptyCollection(Collection<?> collection) {
|
||||
assertThat(collection, is(notNullValue()));
|
||||
assertThat(collection.isEmpty(), is(true));
|
||||
}
|
||||
|
||||
<T> Iterable<T> newIterable(final T... elements) {
|
||||
return new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
int index = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (index < elements.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return elements[index++];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toArrayWithIterable() {
|
||||
Object[] array = CollectionUtils.toArray(newIterable(1, 2, 3));
|
||||
|
||||
assertThat(array, is(notNullValue()));
|
||||
assertThat(array.length, is(equalTo(3)));
|
||||
|
||||
for (int index = 0; index < array.length; index++) {
|
||||
Object valueAtIndex = (index + 1);
|
||||
assertThat(array[index], is(equalTo(valueAtIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toArrayWithEmptyIterable() {
|
||||
assertNonNullEmptyArray(CollectionUtils.toArray(newIterable()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toArrayWithNull() {
|
||||
assertNonNullEmptyArray(CollectionUtils.toArray(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toListWithArray() {
|
||||
List<String> list = CollectionUtils.toList("test", "testing", "tested");
|
||||
|
||||
assertThat(list, is(notNullValue()));
|
||||
assertThat(list.size(), is(equalTo(3)));
|
||||
assertThat(list.containsAll(asList("test", "testing", "tested")), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toListWithEmptyArray() {
|
||||
assertNonNullEmptyCollection(CollectionUtils.toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toListWithNullArray() {
|
||||
assertNonNullEmptyCollection(CollectionUtils.toList((Object[]) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void toListWithIterable() {
|
||||
List<Integer> list = CollectionUtils.toList(newIterable(1, 2, 3));
|
||||
|
||||
assertThat(list, is(notNullValue()));
|
||||
assertThat(list.size(), is(equalTo(3)));
|
||||
assertThat(list.containsAll(asList(1, 2, 3)), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toListWithList() {
|
||||
List<Integer> expected = asList(1, 2, 3);
|
||||
List<Integer> actual = CollectionUtils.toList(expected);
|
||||
|
||||
assertThat(actual, is(sameInstance(expected)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toListWithNullIterable() {
|
||||
assertNonNullEmptyCollection(CollectionUtils.toList((Iterable<?>) null));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-cassandra</artifactId>
|
||||
|
||||
<name>Spring Data Cassandra - Core</name>
|
||||
<description>Cassandra Support for Spring Data</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-cassandra-parent</artifactId>
|
||||
@@ -16,6 +12,12 @@
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-data-cassandra</artifactId>
|
||||
|
||||
<name>Spring Data Cassandra Core</name>
|
||||
<description>Cassandra support for Spring Data</description>
|
||||
<url>https://github.com/spring-projects/spring-data-cassandra/tree/master/spring-data-cassandra</url>
|
||||
|
||||
<properties>
|
||||
<validation>1.0.0.GA</validation>
|
||||
</properties>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors
|
||||
* Copyright 2013-2016 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,12 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.cassandra.config;
|
||||
|
||||
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cassandra.config.CassandraCqlSessionFactoryBean;
|
||||
import org.springframework.data.cassandra.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminOperations;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
|
||||
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
|
||||
@@ -26,96 +30,136 @@ import org.springframework.util.Assert;
|
||||
|
||||
import com.datastax.driver.core.KeyspaceMetadata;
|
||||
import com.datastax.driver.core.Metadata;
|
||||
import com.datastax.driver.core.Session;
|
||||
import com.datastax.driver.core.TableMetadata;
|
||||
|
||||
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
|
||||
|
||||
/**
|
||||
* Factory to create and configure a Cassandra {@link com.datastax.driver.core.Session} with support
|
||||
* for executing CQL and initializing the database schema (a.k.a. keyspace).
|
||||
*
|
||||
* @author Mathew Adams
|
||||
* @author David Webb
|
||||
* @author John Blum
|
||||
* @see com.datastax.driver.core.KeyspaceMetadata
|
||||
* @see com.datastax.driver.core.TableMetadata
|
||||
*/
|
||||
public class CassandraSessionFactoryBean extends CassandraCqlSessionFactoryBean {
|
||||
|
||||
protected SchemaAction schemaAction = SchemaAction.NONE;
|
||||
protected CassandraAdminTemplate admin;
|
||||
protected CassandraConverter converter;
|
||||
protected CassandraMappingContext mappingContext;
|
||||
protected static final boolean DEFAULT_CREATE_IF_NOT_EXISTS = false;
|
||||
protected static final boolean DEFAULT_DROP_TABLES = false;
|
||||
protected static final boolean DEFAULT_DROP_UNUSED_TABLES = false;
|
||||
|
||||
private CassandraAdminOperations admin;
|
||||
|
||||
private CassandraConverter converter;
|
||||
|
||||
private SchemaAction schemaAction = SchemaAction.NONE;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
|
||||
Assert.notNull(converter);
|
||||
Assert.state(converter != null, "Converter must not be null");
|
||||
|
||||
admin = new CassandraAdminTemplate(session, converter);
|
||||
admin = newCassandraAdminOperations(getObject(), converter);
|
||||
|
||||
performSchemaAction();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
CassandraAdminOperations newCassandraAdminOperations(Session session, CassandraConverter converter) {
|
||||
return new CassandraAdminTemplate(session, converter);
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected void performSchemaAction() {
|
||||
|
||||
boolean dropTables = false;
|
||||
boolean dropUnused = false;
|
||||
boolean dropTables = DEFAULT_DROP_TABLES;
|
||||
boolean dropUnused = DEFAULT_DROP_UNUSED_TABLES;
|
||||
boolean ifNotExists = DEFAULT_CREATE_IF_NOT_EXISTS;
|
||||
|
||||
switch (schemaAction) {
|
||||
|
||||
case NONE:
|
||||
return;
|
||||
|
||||
case RECREATE_DROP_UNUSED:
|
||||
dropUnused = true;
|
||||
// don't break!
|
||||
case RECREATE:
|
||||
dropTables = true;
|
||||
// don't break!
|
||||
case CREATE_IF_NOT_EXISTS:
|
||||
ifNotExists = SchemaAction.CREATE_IF_NOT_EXISTS.equals(schemaAction);
|
||||
case CREATE:
|
||||
createTables(dropTables, dropUnused);
|
||||
createTables(dropTables, dropUnused, ifNotExists);
|
||||
case NONE:
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
protected void createTables(boolean dropTables, boolean dropUnused) {
|
||||
/* (non-Javadoc) */
|
||||
protected void createTables(boolean dropTables, boolean dropUnused, boolean ifNotExists) {
|
||||
|
||||
Metadata md = session.getCluster().getMetadata();
|
||||
KeyspaceMetadata kmd = md.getKeyspace(keyspaceName);
|
||||
|
||||
// TODO: fix this with KeyspaceIdentifier
|
||||
if (kmd == null) { // try lower-cased keyspace name
|
||||
kmd = md.getKeyspace(keyspaceName.toLowerCase());
|
||||
if (dropTables) {
|
||||
dropTables(dropUnused);
|
||||
}
|
||||
|
||||
if (kmd == null) {
|
||||
throw new IllegalStateException(String.format("keyspace [%s] does not exist", keyspaceName));
|
||||
}
|
||||
|
||||
for (TableMetadata table : kmd.getTables()) {
|
||||
if (dropTables) {
|
||||
if (dropUnused || mappingContext.usesTable(table)) {
|
||||
admin.dropTable(cqlId(table.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<? extends CassandraPersistentEntity<?>> entities = converter.getMappingContext()
|
||||
.getNonPrimaryKeyEntities();
|
||||
Collection<? extends CassandraPersistentEntity<?>> entities =
|
||||
getConverter().getMappingContext().getNonPrimaryKeyEntities();
|
||||
|
||||
for (CassandraPersistentEntity<?> entity : entities) {
|
||||
admin.createTable(false, entity.getTableName(), entity.getType(), null); // TODO: allow spec of table options
|
||||
// TODO: pass specification of user configurable table options
|
||||
getCassandraAdminOperations().createTable(ifNotExists, entity.getTableName(), entity.getType(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public SchemaAction getSchemaAction() {
|
||||
return schemaAction;
|
||||
/* (non-Javadoc) */
|
||||
@SuppressWarnings("all")
|
||||
protected void dropTables(boolean dropUnused) {
|
||||
|
||||
String keyspaceName = getKeyspaceName();
|
||||
|
||||
Metadata clusterMetadata = getSession().getCluster().getMetadata();
|
||||
KeyspaceMetadata keyspaceMetadata = clusterMetadata.getKeyspace(keyspaceName);
|
||||
|
||||
// TODO: fix this with KeyspaceIdentifier
|
||||
keyspaceMetadata = (keyspaceMetadata != null ? keyspaceMetadata
|
||||
: clusterMetadata.getKeyspace(keyspaceName.toLowerCase()));
|
||||
|
||||
Assert.state(keyspaceMetadata != null, String.format("keyspace [%s] does not exist", keyspaceName));
|
||||
|
||||
for (TableMetadata table : keyspaceMetadata.getTables()) {
|
||||
if (dropUnused || getMappingContext().usesTable(table)) {
|
||||
getCassandraAdminOperations().dropTable(cqlId(table.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected CassandraAdminOperations getCassandraAdminOperations() {
|
||||
return this.admin;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void setConverter(CassandraConverter converter) {
|
||||
Assert.notNull(converter, "CassandraConverter must not be null");
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public CassandraConverter getConverter() {
|
||||
return this.converter;
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
protected CassandraMappingContext getMappingContext() {
|
||||
return getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
/* (non-Javadoc) */
|
||||
public void setSchemaAction(SchemaAction schemaAction) {
|
||||
Assert.notNull(schemaAction);
|
||||
Assert.notNull(schemaAction, "SchemaAction must not be null");
|
||||
this.schemaAction = schemaAction;
|
||||
}
|
||||
|
||||
public CassandraConverter getConverter() {
|
||||
return converter;
|
||||
}
|
||||
|
||||
public void setConverter(CassandraConverter converter) {
|
||||
Assert.notNull(converter);
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
/* (non-Javadoc) */
|
||||
public SchemaAction getSchemaAction() {
|
||||
return schemaAction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.cassandra.config;
|
||||
|
||||
/**
|
||||
* Enum identifying any schema actions to take at startup.
|
||||
*
|
||||
* @author Matthew T. Adams
|
||||
* @author John Blum
|
||||
*/
|
||||
public enum SchemaAction {
|
||||
|
||||
@@ -32,6 +34,11 @@ public enum SchemaAction {
|
||||
*/
|
||||
CREATE,
|
||||
|
||||
/**
|
||||
* Create each table as necessary. Avoid table creation if the table already exists.
|
||||
*/
|
||||
CREATE_IF_NOT_EXISTS,
|
||||
|
||||
/**
|
||||
* Create each table as necessary, dropping the table first if it exists.
|
||||
*/
|
||||
@@ -44,11 +51,6 @@ public enum SchemaAction {
|
||||
|
||||
// TODO:
|
||||
// /**
|
||||
// * Validate that each required table and column exists. Fail if any required table or column does not exists.
|
||||
// */
|
||||
// VALIDATE,
|
||||
//
|
||||
// /**
|
||||
// * Alter or create each table and column as necessary, leaving unused tables and columns untouched.
|
||||
// */
|
||||
// UPDATE,
|
||||
@@ -56,5 +58,11 @@ public enum SchemaAction {
|
||||
// /**
|
||||
// * Alter or create each table and column as necessary, removing unused tables and columns.
|
||||
// */
|
||||
// UPDATE_DROP_UNUNSED;
|
||||
// UPDATE_DROP_UNUNSED,
|
||||
//
|
||||
// /**
|
||||
// * Validate that each required table and column exists. Fail if any required table or column does not exists.
|
||||
// */
|
||||
// VALIDATE
|
||||
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.cassandra.config.java.AbstractClusterConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
|
||||
import org.springframework.data.cassandra.config.CassandraEntityClassScanner;
|
||||
import org.springframework.data.cassandra.config.CassandraSessionFactoryBean;
|
||||
import org.springframework.data.cassandra.config.SchemaAction;
|
||||
import org.springframework.data.cassandra.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminOperations;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
|
||||
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
|
||||
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.mapping.Table;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
|
||||
@@ -36,68 +36,43 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
*
|
||||
* @author Alex Shvid
|
||||
* @author Matthew T. Adams
|
||||
* @author John Blum
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractCassandraConfiguration extends AbstractClusterConfiguration implements
|
||||
BeanClassLoaderAware {
|
||||
|
||||
protected abstract String getKeyspaceName();
|
||||
public abstract class AbstractCassandraConfiguration extends AbstractClusterConfiguration
|
||||
implements BeanClassLoaderAware {
|
||||
|
||||
protected ClassLoader beanClassLoader;
|
||||
|
||||
/**
|
||||
* The {@link SchemaAction} to perform. Defaults to {@link SchemaAction#NONE}.
|
||||
*/
|
||||
public SchemaAction getSchemaAction() {
|
||||
return SchemaAction.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package
|
||||
* name of {@literal this} (<code>this.getClass().getPackage().getName()</code>). This method must never return null.
|
||||
*/
|
||||
public String[] getEntityBasePackages() {
|
||||
return new String[] { getClass().getPackage().getName() };
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CassandraSessionFactoryBean session() throws Exception {
|
||||
|
||||
CassandraSessionFactoryBean bean = new CassandraSessionFactoryBean();
|
||||
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
|
||||
|
||||
bean.setCluster(cluster().getObject());
|
||||
bean.setConverter(cassandraConverter());
|
||||
bean.setSchemaAction(getSchemaAction());
|
||||
bean.setKeyspaceName(getKeyspaceName());
|
||||
bean.setStartupScripts(getStartupScripts());
|
||||
bean.setShutdownScripts(getShutdownScripts());
|
||||
session.setCluster(cluster().getObject());
|
||||
session.setConverter(cassandraConverter());
|
||||
session.setKeyspaceName(getKeyspaceName());
|
||||
session.setSchemaAction(getSchemaAction());
|
||||
session.setStartupScripts(getStartupScripts());
|
||||
session.setShutdownScripts(getShutdownScripts());
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CassandraAdminTemplate}.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean
|
||||
public CassandraAdminOperations cassandraTemplate() throws Exception {
|
||||
return new CassandraAdminTemplate(session().getObject(), cassandraConverter());
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link MappingContext} instance to map Entities to properties.
|
||||
*
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@Bean
|
||||
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
|
||||
|
||||
BasicCassandraMappingContext bean = new BasicCassandraMappingContext();
|
||||
bean.setInitialEntitySet(CassandraEntityClassScanner.scan(getEntityBasePackages()));
|
||||
bean.setBeanClassLoader(beanClassLoader);
|
||||
BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
|
||||
|
||||
return bean;
|
||||
mappingContext.setBeanClassLoader(beanClassLoader);
|
||||
mappingContext.setInitialEntitySet(CassandraEntityClassScanner.scan(getEntityBasePackages()));
|
||||
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,8 +83,37 @@ public abstract class AbstractCassandraConfiguration extends AbstractClusterConf
|
||||
return new MappingCassandraConverter(cassandraMapping());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CassandraAdminTemplate}.
|
||||
*
|
||||
* @throws Exception if the {@link com.datastax.driver.core.Session} could not be obtained.
|
||||
*/
|
||||
@Bean
|
||||
public CassandraAdminOperations cassandraTemplate() throws Exception {
|
||||
return new CassandraAdminTemplate(session().getObject(), cassandraConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base packages to scan for entities annotated with {@link Table} annotations. By default, returns the package
|
||||
* name of {@literal this} (<code>this.getClass().getPackage().getName()</code>).
|
||||
*
|
||||
* This method must never return null.
|
||||
*/
|
||||
public String[] getEntityBasePackages() {
|
||||
return new String[] { getClass().getPackage().getName() };
|
||||
}
|
||||
|
||||
protected abstract String getKeyspaceName();
|
||||
|
||||
/**
|
||||
* The {@link SchemaAction} to perform at startup. Defaults to {@link SchemaAction#NONE}.
|
||||
*/
|
||||
public SchemaAction getSchemaAction() {
|
||||
return SchemaAction.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,12 @@ public class CassandraNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
|
||||
registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(
|
||||
new CassandraRepositoryConfigurationExtension()));
|
||||
|
||||
registerBeanDefinitionParser("cluster", new CassandraClusterParser());
|
||||
registerBeanDefinitionParser("session", new CassandraSessionParser());
|
||||
registerBeanDefinitionParser("template", new CassandraTemplateParser());
|
||||
registerBeanDefinitionParser("converter", new CassandraMappingConverterParser());
|
||||
registerBeanDefinitionParser("mapping", new CassandraMappingContextParser());
|
||||
registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(
|
||||
new CassandraRepositoryConfigurationExtension()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,12 +94,25 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
setConverter(converter);
|
||||
}
|
||||
|
||||
private static CassandraConverter getDefaultCassandraConverter() {
|
||||
|
||||
MappingCassandraConverter mappingCassandraConverter = new MappingCassandraConverter();
|
||||
mappingCassandraConverter.afterPropertiesSet();
|
||||
return mappingCassandraConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CassandraConverter} used by this template to perform conversions.
|
||||
*
|
||||
* @param cassandraConverter Converter used to perform conversion of Cassandra data types to entity types.
|
||||
* Must not be {@literal null}.
|
||||
*/
|
||||
public void setConverter(CassandraConverter cassandraConverter) {
|
||||
|
||||
Assert.notNull(cassandraConverter);
|
||||
Assert.notNull(cassandraConverter, "CassandraConverter must not be null");
|
||||
|
||||
this.cassandraConverter = cassandraConverter;
|
||||
mappingContext = cassandraConverter.getMappingContext();
|
||||
this.mappingContext = cassandraConverter.getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -838,10 +851,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
/**
|
||||
* Generates a Query Object for an insert
|
||||
*
|
||||
* @param tableName
|
||||
* @param objectToSave
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return The Query object to run with session.execute();
|
||||
*/
|
||||
public static Insert createInsertQuery(String tableName, Object objectToSave, WriteOptions options,
|
||||
@@ -866,10 +875,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
/**
|
||||
* Generates a Query Object for an Update
|
||||
*
|
||||
* @param tableName
|
||||
* @param objectToSave
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return The Query object to run with session.execute();
|
||||
*/
|
||||
public static Update createUpdateQuery(String tableName, Object objectToSave, WriteOptions options,
|
||||
@@ -894,10 +899,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
/**
|
||||
* Generates a Batch Object for multiple Updates
|
||||
*
|
||||
* @param tableName
|
||||
* @param objectsToSave
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return The Query object to run with session.execute();
|
||||
*/
|
||||
public static <T> Batch createUpdateBatchQuery(String tableName, List<T> objectsToSave, WriteOptions options,
|
||||
@@ -917,10 +918,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
/**
|
||||
* Generates a Batch Object for multiple inserts
|
||||
*
|
||||
* @param tableName
|
||||
* @param entities
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return The Query object to run with session.execute();
|
||||
*/
|
||||
public static <T> Batch createInsertBatchQuery(String tableName, List<T> entities, WriteOptions options,
|
||||
@@ -939,12 +936,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
|
||||
/**
|
||||
* Create a Delete Query Object from an annotated POJO
|
||||
*
|
||||
* @param tableName
|
||||
* @param object
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return
|
||||
*/
|
||||
public static Delete createDeleteQuery(String tableName, Object object, QueryOptions options,
|
||||
EntityWriter<Object, Object> entityWriter) {
|
||||
@@ -959,12 +950,6 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation
|
||||
|
||||
/**
|
||||
* Create a Batch Query object for multiple deletes.
|
||||
*
|
||||
* @param tableName
|
||||
* @param entities
|
||||
* @param entity
|
||||
* @param optionsByName
|
||||
* @return
|
||||
*/
|
||||
public static <T> Batch createDeleteBatchQuery(String tableName, List<T> entities, QueryOptions options,
|
||||
EntityWriter<Object, Object> entityWriter) {
|
||||
|
||||
@@ -153,7 +153,7 @@ public class BasicCassandraMappingContext extends
|
||||
|
||||
@Override
|
||||
public boolean usesTable(TableMetadata table) {
|
||||
return entitySetsByTableName.containsKey(table.getName());
|
||||
return entitySetsByTableName.containsKey(cqlId(table.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.cassandra.config;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.data.cassandra.config.CassandraSessionFactoryBean.DEFAULT_CREATE_IF_NOT_EXISTS;
|
||||
import static org.springframework.data.cassandra.config.CassandraSessionFactoryBean.DEFAULT_DROP_TABLES;
|
||||
import static org.springframework.data.cassandra.config.CassandraSessionFactoryBean.DEFAULT_DROP_UNUSED_TABLES;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.cassandra.core.cql.CqlIdentifier;
|
||||
import org.springframework.data.cassandra.convert.CassandraConverter;
|
||||
import org.springframework.data.cassandra.core.CassandraAdminOperations;
|
||||
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.KeyspaceMetadata;
|
||||
import com.datastax.driver.core.Metadata;
|
||||
import com.datastax.driver.core.Session;
|
||||
import com.datastax.driver.core.TableMetadata;
|
||||
|
||||
/**
|
||||
* The CassandraSessionFactoryBeanUnitTests class is a test suite of test cases testing the contract and functionality
|
||||
* of the {@link CassandraSessionFactoryBean} class.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Rule
|
||||
* @see org.junit.Test
|
||||
* @see org.junit.rules.ExpectedException
|
||||
* @see org.junit.runner.RunWith
|
||||
* @see org.mockito.Mock
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.mockito.runners.MockitoJUnitRunner
|
||||
* @see org.springframework.data.cassandra.config.CassandraSessionFactoryBean
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CassandraSessionFactoryBeanUnitTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
private CassandraConverter mockConverter;
|
||||
|
||||
@Mock
|
||||
private Cluster mockCluster;
|
||||
|
||||
@Mock
|
||||
private Session mockSession;
|
||||
|
||||
private CassandraSessionFactoryBean factoryBean;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
when(mockCluster.connect()).thenReturn(mockSession);
|
||||
when(mockSession.getCluster()).thenReturn(mockCluster);
|
||||
|
||||
factoryBean = spy(new CassandraSessionFactoryBean());
|
||||
factoryBean.setCluster(mockCluster);
|
||||
}
|
||||
|
||||
protected CqlIdentifier newCqlIdentifier(String id) {
|
||||
return new CqlIdentifier(id, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetPerformsSchemaAction() throws Exception {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.RECREATE)));
|
||||
return null;
|
||||
}
|
||||
}).when(factoryBean).performSchemaAction();
|
||||
|
||||
factoryBean.setConverter(mockConverter);
|
||||
factoryBean.setSchemaAction(SchemaAction.RECREATE);
|
||||
|
||||
assertThat(factoryBean.getConverter(), is(equalTo(mockConverter)));
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.RECREATE)));
|
||||
|
||||
factoryBean.afterPropertiesSet();
|
||||
|
||||
assertThat(factoryBean.getCassandraAdminOperations(), is(notNullValue(CassandraAdminOperations.class)));
|
||||
assertThat(factoryBean.getObject(), is(equalTo(mockSession)));
|
||||
|
||||
verify(factoryBean, times(1)).performSchemaAction();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetThrowsIllegalStateExceptionWhenConverterIsNull() throws Exception {
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("Converter must not be null");
|
||||
|
||||
factoryBean.setCluster(mockCluster);
|
||||
factoryBean.afterPropertiesSet();
|
||||
}
|
||||
|
||||
protected void performSchemaActionCallsCreateTableWithArgumentsMatchingTheSchemaAction(SchemaAction schemaAction,
|
||||
final boolean dropTables, final boolean dropUnused, final boolean ifNotExists) {
|
||||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
assertThat(invocationOnMock.getArgumentAt(0, Boolean.class), is(equalTo(dropTables)));
|
||||
assertThat(invocationOnMock.getArgumentAt(1, Boolean.class), is(equalTo(dropUnused)));
|
||||
assertThat(invocationOnMock.getArgumentAt(2, Boolean.class), is(equalTo(ifNotExists)));
|
||||
return null;
|
||||
}
|
||||
}).when(factoryBean).createTables(anyBoolean(), anyBoolean(), anyBoolean());
|
||||
|
||||
factoryBean.setSchemaAction(schemaAction);
|
||||
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(schemaAction)));
|
||||
|
||||
factoryBean.performSchemaAction();
|
||||
|
||||
verify(factoryBean, times(1)).createTables(eq(dropTables), eq(dropUnused), eq(ifNotExists));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performsSchemaActionCreatesTablesWithDefaults() {
|
||||
performSchemaActionCallsCreateTableWithArgumentsMatchingTheSchemaAction(SchemaAction.CREATE,
|
||||
DEFAULT_DROP_TABLES, DEFAULT_DROP_UNUSED_TABLES, DEFAULT_CREATE_IF_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performsSchemaActionCreatesTablesIfNotExists() {
|
||||
performSchemaActionCallsCreateTableWithArgumentsMatchingTheSchemaAction(SchemaAction.CREATE_IF_NOT_EXISTS,
|
||||
DEFAULT_DROP_TABLES, DEFAULT_DROP_UNUSED_TABLES, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performsSchemaActionRecreatesTables() {
|
||||
performSchemaActionCallsCreateTableWithArgumentsMatchingTheSchemaAction(SchemaAction.RECREATE, true,
|
||||
DEFAULT_DROP_UNUSED_TABLES, DEFAULT_CREATE_IF_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performsSchemaActionRecreatesAndDropsUnusedTables() {
|
||||
performSchemaActionCallsCreateTableWithArgumentsMatchingTheSchemaAction(SchemaAction.RECREATE_DROP_UNUSED,
|
||||
true, true, DEFAULT_CREATE_IF_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performsSchemaActionDoesNotCallCreateTablesWhenSchemaActionIsNone() {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
fail("'createTables(..)' should not have been called");
|
||||
return null;
|
||||
}
|
||||
}).when(factoryBean).createTables(anyBoolean(), anyBoolean(), anyBoolean());
|
||||
|
||||
factoryBean.setSchemaAction(SchemaAction.NONE);
|
||||
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.NONE)));
|
||||
|
||||
factoryBean.performSchemaAction();
|
||||
|
||||
verify(factoryBean, never()).createTables(anyBoolean(), anyBoolean(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createsTableForEntity() throws Exception {
|
||||
Metadata mockMetadata = mock(Metadata.class);
|
||||
KeyspaceMetadata mockKeyspaceMetadata = mock(KeyspaceMetadata.class);
|
||||
CassandraMappingContext mockMappingContext = mock(CassandraMappingContext.class);
|
||||
CassandraPersistentEntity<Person> mockPersistentEntity = mock(CassandraPersistentEntity.class);
|
||||
CassandraAdminOperations mockCassandraAdminOperations = mock(CassandraAdminOperations.class);
|
||||
|
||||
doReturn(mockCassandraAdminOperations).when(factoryBean).getCassandraAdminOperations();
|
||||
doReturn(mockSession).when(factoryBean).getObject();
|
||||
when(mockCluster.getMetadata()).thenReturn(mockMetadata);
|
||||
when(mockMetadata.getKeyspace(eq("TestKeyspace"))).thenReturn(mockKeyspaceMetadata);
|
||||
when(mockKeyspaceMetadata.getTables()).thenReturn(Collections.<TableMetadata>emptyList());
|
||||
when(mockConverter.getMappingContext()).thenReturn(mockMappingContext);
|
||||
when(mockMappingContext.getNonPrimaryKeyEntities()).thenReturn(
|
||||
Collections.<CassandraPersistentEntity<?>>singletonList(mockPersistentEntity));
|
||||
when(mockPersistentEntity.getTableName()).thenReturn(newCqlIdentifier("TestTable"));
|
||||
when(mockPersistentEntity.getType()).thenReturn(Person.class);
|
||||
|
||||
factoryBean.setConverter(mockConverter);
|
||||
factoryBean.setKeyspaceName("TestKeyspace");
|
||||
|
||||
assertThat(factoryBean.getConverter(), is(equalTo(mockConverter)));
|
||||
|
||||
factoryBean.createTables(true, false, false);
|
||||
|
||||
verify(mockSession, times(1)).getCluster();
|
||||
verify(mockCluster, times(1)).getMetadata();
|
||||
verify(mockMetadata, times(1)).getKeyspace(eq("TestKeyspace"));
|
||||
verify(mockKeyspaceMetadata, times(1)).getTables();
|
||||
verify(mockConverter, times(1)).getMappingContext();
|
||||
verify(mockMappingContext, times(1)).getNonPrimaryKeyEntities();
|
||||
verify(mockPersistentEntity, times(1)).getTableName();
|
||||
verify(mockPersistentEntity, times(1)).getType();
|
||||
verify(mockCassandraAdminOperations, times(1)).createTable(eq(false), eq(newCqlIdentifier("TestTable")),
|
||||
eq(Person.class), isNull(Map.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createTableForEntityIfNotExists() {
|
||||
CassandraMappingContext mockMappingContext = mock(CassandraMappingContext.class);
|
||||
CassandraPersistentEntity<Person> mockPersistentEntity = mock(CassandraPersistentEntity.class);
|
||||
CassandraAdminOperations mockCassandraAdminOperations = mock(CassandraAdminOperations.class);
|
||||
|
||||
doReturn(mockCassandraAdminOperations).when(factoryBean).getCassandraAdminOperations();
|
||||
doReturn(mockSession).when(factoryBean).getObject();
|
||||
when(mockConverter.getMappingContext()).thenReturn(mockMappingContext);
|
||||
when(mockMappingContext.getNonPrimaryKeyEntities()).thenReturn(
|
||||
Collections.<CassandraPersistentEntity<?>>singletonList(mockPersistentEntity));
|
||||
when(mockPersistentEntity.getTableName()).thenReturn(newCqlIdentifier("TestTable"));
|
||||
when(mockPersistentEntity.getType()).thenReturn(Person.class);
|
||||
|
||||
factoryBean.setConverter(mockConverter);
|
||||
factoryBean.setKeyspaceName("TestKeyspace");
|
||||
|
||||
assertThat(factoryBean.getConverter(), is(equalTo(mockConverter)));
|
||||
|
||||
factoryBean.createTables(false, false, true);
|
||||
|
||||
verify(mockSession, never()).getCluster();
|
||||
verify(mockCluster, never()).getMetadata();
|
||||
verify(mockConverter, times(1)).getMappingContext();
|
||||
verify(mockMappingContext, times(1)).getNonPrimaryKeyEntities();
|
||||
verify(mockPersistentEntity, times(1)).getTableName();
|
||||
verify(mockPersistentEntity, times(1)).getType();
|
||||
verify(mockCassandraAdminOperations, times(1)).createTable(eq(true), eq(newCqlIdentifier("TestTable")),
|
||||
eq(Person.class), isNull(Map.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTableThrowsIllegalStateExceptionWhenKeyspaceNotFound() {
|
||||
Metadata mockMetadata = mock(Metadata.class);
|
||||
|
||||
doReturn(mockSession).when(factoryBean).getObject();
|
||||
when(mockCluster.getMetadata()).thenReturn(mockMetadata);
|
||||
when(mockMetadata.getKeyspace(anyString())).thenReturn(null);
|
||||
|
||||
exception.expect(IllegalStateException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("keyspace [TestKeyspace] does not exist");
|
||||
|
||||
factoryBean.setKeyspaceName("TestKeyspace");
|
||||
factoryBean.createTables(true, false, true);
|
||||
|
||||
verify(mockSession, times(1)).getCluster();
|
||||
verify(mockCluster, times(1)).getMetadata();
|
||||
verify(mockMetadata, times(1)).getKeyspace(eq("TestKeyspace"));
|
||||
verify(mockMetadata, times(1)).getKeyspace(eq("testkeyspace"));
|
||||
}
|
||||
|
||||
// TODO: add more createTable tests covering drop tables, etc
|
||||
|
||||
@Test
|
||||
public void setAndGetConverter() {
|
||||
assertThat(factoryBean.getConverter(), is(nullValue()));
|
||||
factoryBean.setConverter(mockConverter);
|
||||
assertThat(factoryBean.getConverter(), is(equalTo(mockConverter)));
|
||||
verifyZeroInteractions(mockConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setConverterToNull() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("CassandraConverter must not be null");
|
||||
|
||||
factoryBean.setConverter(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAndGetSchemaAction() {
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.NONE)));
|
||||
factoryBean.setSchemaAction(SchemaAction.CREATE);
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.CREATE)));
|
||||
factoryBean.setSchemaAction(SchemaAction.NONE);
|
||||
assertThat(factoryBean.getSchemaAction(), is(equalTo(SchemaAction.NONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSchemaActionToNullThrowsIllegalArgumentException() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectCause(is(nullValue(Throwable.class)));
|
||||
exception.expectMessage("SchemaAction must not be null");
|
||||
|
||||
factoryBean.setSchemaAction(null);
|
||||
}
|
||||
|
||||
static class Person {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cass="http://www.springframework.org/schema/data/cassandra"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
xmlns:cass="http://www.springframework.org/schema/data/cassandra"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
">
|
||||
|
||||
<bean name="randomKeyspaceName" class="java.lang.String">
|
||||
<constructor-arg value="#{'ks' + T(java.util.UUID).randomUUID().toString().replaceAll('-', '')}"/>
|
||||
@@ -16,6 +18,6 @@
|
||||
|
||||
<cass:converter mapping-ref="cassandraMapping"/>
|
||||
|
||||
<cass:template />
|
||||
<cass:template/>
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user