From 4832ff9fe21efe40b9853eee09621b7ca0272ffe Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 20 Jan 2014 14:15:09 -0500 Subject: [PATCH 1/8] DATACASS-71 - XML parsing fails on property placeholders Added BeanFeinitions in Parser to PPH works. --- spring-cassandra/pom.xml | 5 + .../config/CassandraClusterFactoryBean.java | 66 +++++- .../cassandra/config/KeyspaceAction.java | 25 ++ ...eyspaceActionSpecificationFactoryBean.java | 185 +++++++++++++++ .../MultiLevelListFlattenerFactoryBean.java | 71 ++++++ .../MultiLevelSetFlattenerFactoryBean.java | 77 ++++++ .../java/AbstractCassandraConfiguration.java | 2 - .../config/xml/CassandraClusterParser.java | 221 +++++++++++------- .../generator/KeyspaceNameCqlGenerator.java | 10 +- .../keyspace/DropKeyspaceSpecification.java | 56 ++--- .../keyspace/KeyspaceActionSpecification.java | 76 ++++++ .../keyspace/KeyspaceNameSpecification.java | 39 ---- .../core/keyspace/KeyspaceOption.java | 158 ++++++------- .../KeyspaceOptionsSpecification.java | 188 +++++++-------- .../cassandra/config/spring-cassandra-1.0.xsd | 48 +--- ...eholderNamespaceCreatingXmlConfigTest.java | 43 ++++ .../KeyspaceOperationCqlGeneratorTest.java | 4 +- .../src/test/resources/logback-test.xml | 4 +- ...NamespaceCreatingXmlConfigTest-context.xml | 34 +++ .../integration/config/xml/ppncxct.properties | 9 + 20 files changed, 936 insertions(+), 385 deletions(-) create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceAction.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelListFlattenerFactoryBean.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelSetFlattenerFactoryBean.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java delete mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceNameSpecification.java create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest.java create mode 100644 spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml create mode 100644 spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties diff --git a/spring-cassandra/pom.xml b/spring-cassandra/pom.xml index b3afd4fd1..666c35b88 100644 --- a/spring-cassandra/pom.xml +++ b/spring-cassandra/pom.xml @@ -41,6 +41,11 @@ org.springframework spring-tx + + ${project.groupId} + spring-data-commons + ${springdata.commons} + com.datastax.cassandra cassandra-driver-core diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java index cdd63f6c4..5c7610159 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java @@ -16,8 +16,10 @@ package org.springframework.cassandra.config; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +31,7 @@ import org.springframework.cassandra.core.cql.generator.CreateKeyspaceCqlGenerat import org.springframework.cassandra.core.cql.generator.DropKeyspaceCqlGenerator; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; -import org.springframework.cassandra.core.keyspace.KeyspaceNameSpecification; +import org.springframework.cassandra.core.keyspace.KeyspaceActionSpecification; import org.springframework.cassandra.support.CassandraExceptionTranslator; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; @@ -51,6 +53,7 @@ import com.datastax.driver.core.policies.RetryPolicy; * * @author Alex Shvid * @author Matthew T. Adams + * @author David Webb */ public class CassandraClusterFactoryBean implements FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator { @@ -62,9 +65,10 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia protected static final Logger log = LoggerFactory.getLogger(CassandraClusterFactoryBean.class); private Cluster cluster; + private boolean accumulating = true; - /** - * Comma-delimited string of servers. + /* + * Attributes needed for cluster builder */ private String contactPoints = DEFAULT_CONTACT_POINTS; private int port = CassandraClusterFactoryBean.DEFAULT_PORT; @@ -77,6 +81,7 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia private ReconnectionPolicy reconnectionPolicy; private RetryPolicy retryPolicy; private boolean metricsEnabled = DEFAULT_METRICS_ENABLED; + private Set> keyspaceSpecifications = new HashSet>(); private List keyspaceCreations = new ArrayList(); private List keyspaceDrops = new ArrayList(); private List startupScripts = new ArrayList(); @@ -152,9 +157,31 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia } cluster = builder.build(); + + generateSpecificationsFromFactoryBeans(); + executeSpecsAndScripts(keyspaceCreations, startupScripts); } + /** + * Examines the contents of all the KeyspaceSpecificationFactoryBeans and generates the proper KeyspaceSpecification + * from them. + */ + private void generateSpecificationsFromFactoryBeans() { + + for (KeyspaceActionSpecification spec : keyspaceSpecifications) { + + if (spec instanceof CreateKeyspaceSpecification) { + keyspaceCreations.add((CreateKeyspaceSpecification) spec); + } + if (spec instanceof DropKeyspaceSpecification) { + keyspaceDrops.add((DropKeyspaceSpecification) spec); + } + + } + + } + protected void executeSpecsAndScripts(@SuppressWarnings("rawtypes") List specs, List scripts) { Session system = null; @@ -167,7 +194,7 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia Iterator i = specs.iterator(); while (i.hasNext()) { - KeyspaceNameSpecification spec = (KeyspaceNameSpecification) i.next(); + KeyspaceActionSpecification spec = (KeyspaceActionSpecification) i.next(); String cql = (spec instanceof CreateKeyspaceSpecification) ? new CreateKeyspaceCqlGenerator( (CreateKeyspaceSpecification) spec).toCql() : new DropKeyspaceCqlGenerator( (DropKeyspaceSpecification) spec).toCql(); @@ -343,4 +370,35 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia return socketOptions; } + + /** + * @return Returns the keyspaceSpecifications. + */ + public Set> getKeyspaceSpecifications() { + return keyspaceSpecifications; + } + + /** + * If accumlating is true, we append to the list, otherwise we replace the list. + * + * @param keyspaceSpecifications The keyspaceSpecifications to set. + */ + public void setKeyspaceSpecifications(Set> keyspaceSpecifications) { + log.info("Setter Called"); + this.keyspaceSpecifications = keyspaceSpecifications; + } + + /** + * @return Returns the accumulating. + */ + public boolean isAccumulating() { + return accumulating; + } + + /** + * @param accumulating The accumulating to set. + */ + public void setAccumulating(boolean accumulating) { + this.accumulating = accumulating; + } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceAction.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceAction.java new file mode 100644 index 000000000..51f6e93c6 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceAction.java @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cassandra.config; + +/** + * Available actions for Keyspace Specifications + * + * @author David Webb + */ +public enum KeyspaceAction { + CREATE, CREATE_DROP, ALTER; +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java new file mode 100644 index 000000000..5fb25d3fe --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java @@ -0,0 +1,185 @@ +/* + * Copyright 2011-2014 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 java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +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.keyspace.CreateKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.DefaultOption; +import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; +import org.springframework.cassandra.core.keyspace.KeyspaceActionSpecification; +import org.springframework.cassandra.core.keyspace.KeyspaceOption; +import org.springframework.cassandra.core.keyspace.Option; +import org.springframework.util.Assert; + +/** + * @author David Webb (dwebb@brightmove.com) + * + */ +public class KeyspaceActionSpecificationFactoryBean implements FactoryBean>>, + InitializingBean, DisposableBean { + + private final static Logger log = LoggerFactory.getLogger(KeyspaceActionSpecificationFactoryBean.class); + + private KeyspaceAction action; + private String name; + private Map replicationOptions = new LinkedHashMap(); + private boolean durableWrites = false; + private boolean ifNotExists = false; + + private Set> specs = new HashSet>(); + + @Override + public void destroy() throws Exception { + name = null; + replicationOptions = null; + specs = null; + } + + @Override + public void afterPropertiesSet() throws Exception { + + Assert.hasText(name, "Keyspace Name is required for a Keyspace Action"); + Assert.notNull(action, "Keyspace Action is required for a Keyspace Action"); + + switch (action) { + case CREATE_DROP: + specs.add(generateDropKeyspaceSpecification()); + case CREATE: + specs.add(generateCreateKeyspaceSpecification()); + break; + case ALTER: + break; + } + + } + + private CreateKeyspaceSpecification generateCreateKeyspaceSpecification() { + CreateKeyspaceSpecification create = new CreateKeyspaceSpecification(); + create.name(name).ifNotExists(ifNotExists).with(KeyspaceOption.DURABLE_WRITES, durableWrites); + if (replicationOptions != null && replicationOptions.size() > 0) { + create.with(KeyspaceOption.REPLICATION, replicationOptions); + } else { + Map defaultReplicationStrategyMap = new HashMap(); + defaultReplicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true), + KeyspaceOption.ReplicationStrategy.SIMPLE_STRATEGY); + defaultReplicationStrategyMap.put(new DefaultOption("replication_factor", String.class, true, false, false), "1"); + create.with(KeyspaceOption.REPLICATION, defaultReplicationStrategyMap); + } + return create; + } + + private DropKeyspaceSpecification generateDropKeyspaceSpecification() { + DropKeyspaceSpecification drop = new DropKeyspaceSpecification(); + drop.name(getName()); + return drop; + } + + @Override + public Set> getObject() throws Exception { + return specs; + } + + @Override + public Class getObjectType() { + return Set.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + /** + * @return Returns the name. + */ + public String getName() { + return name; + } + + /** + * @param name The name to set. + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return Returns the ifNotExists. + */ + public boolean isIfNotExists() { + return ifNotExists; + } + + /** + * @param ifNotExists The ifNotExists to set. + */ + public void setIfNotExists(boolean ifNotExists) { + this.ifNotExists = ifNotExists; + } + + /** + * @return Returns the action. + */ + public KeyspaceAction getAction() { + return action; + } + + /** + * @param action The action to set. + */ + public void setAction(KeyspaceAction action) { + this.action = action; + } + + /** + * @return Returns the replicationOptions. + */ + public Map getReplicationOptions() { + return replicationOptions; + } + + /** + * @param replicationOptions The replicationOptions to set. + */ + public void setReplicationOptions(Map replicationOptions) { + this.replicationOptions = replicationOptions; + } + + /** + * @return Returns the durableWrites. + */ + public boolean isDurableWrites() { + return durableWrites; + } + + /** + * @param durableWrites The durableWrites to set. + */ + public void setDurableWrites(boolean durableWrites) { + this.durableWrites = durableWrites; + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelListFlattenerFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelListFlattenerFactoryBean.java new file mode 100644 index 000000000..baf973256 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelListFlattenerFactoryBean.java @@ -0,0 +1,71 @@ +/* + * Copyright 2011-2014 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 java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.FactoryBean; + +/** + * Given List of Lists where all child Lists contain the same class, then a single level List of is generated. + * + * @author David Webb + * @param + * + */ +public class MultiLevelListFlattenerFactoryBean implements FactoryBean> { + + private List> multiLevelList; + + @Override + public List getObject() throws Exception { + List list = new ArrayList(); + + for (List topList : multiLevelList) { + for (T t : topList) { + list.add(t); + } + } + + return list; + } + + @Override + public Class getObjectType() { + return List.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + /** + * @return Returns the multiLevelList. + */ + public List> getMultiLevelList() { + return multiLevelList; + } + + /** + * @param multiLevelList The multiLevelList to set. + */ + public void setMultiLevelList(List> multiLevelList) { + this.multiLevelList = multiLevelList; + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelSetFlattenerFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelSetFlattenerFactoryBean.java new file mode 100644 index 000000000..7692c7023 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/MultiLevelSetFlattenerFactoryBean.java @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2014 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 java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.FactoryBean; + +/** + * Given Set of Sets where all child Sets contain the same class, then a single level Set of is generated. + * + * @author David Webb + * @param + * + */ +public class MultiLevelSetFlattenerFactoryBean implements FactoryBean> { + + private final static Logger log = LoggerFactory.getLogger(MultiLevelSetFlattenerFactoryBean.class); + + private Set> multiLevelSet; + + @Override + public Set getObject() throws Exception { + Set set = new HashSet(); + + for (Set topSet : multiLevelSet) { + for (T t : topSet) { + log.info(t.toString()); + log.info("Set contains -> " + set.contains(t)); + set.add(t); + } + } + + return set; + } + + @Override + public Class getObjectType() { + return Set.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + /** + * @return Returns the multiLevelSet. + */ + public Set> getMultiLevelSet() { + return multiLevelSet; + } + + /** + * @param multiLevelSet The multiLevelSet to set. + */ + public void setMultiLevelSet(Set> multiLevelSet) { + this.multiLevelSet = multiLevelSet; + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java index b9f3bb682..9ec7b70e1 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java @@ -8,8 +8,6 @@ import org.springframework.cassandra.config.CassandraSessionFactoryBean; import org.springframework.cassandra.config.CompressionType; import org.springframework.cassandra.config.PoolingOptionsConfig; import org.springframework.cassandra.config.SocketOptionsConfig; -import org.springframework.cassandra.core.CassandraOperations; -import org.springframework.cassandra.core.CassandraTemplate; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; import org.springframework.context.annotation.Bean; diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java index b931fb325..231aa1e3c 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java @@ -15,44 +15,50 @@ */ package org.springframework.cassandra.config.xml; +import static org.springframework.data.config.ParsingUtils.getSourceBeanDefinition; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; +import org.springframework.beans.factory.support.ManagedSet; +import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cassandra.config.CassandraClusterFactoryBean; -import org.springframework.cassandra.config.CompressionType; +import org.springframework.cassandra.config.KeyspaceActionSpecificationFactoryBean; import org.springframework.cassandra.config.KeyspaceAttributes; +import org.springframework.cassandra.config.MultiLevelSetFlattenerFactoryBean; import org.springframework.cassandra.config.PoolingOptionsConfig; import org.springframework.cassandra.config.SocketOptionsConfig; -import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DefaultOption; -import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; -import org.springframework.cassandra.core.keyspace.KeyspaceOption; +import org.springframework.cassandra.core.keyspace.KeyspaceOption.ReplicationStrategy; import org.springframework.cassandra.core.keyspace.Option; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -import org.w3c.dom.NodeList; + +import com.datastax.driver.core.AuthProvider; /** - * Parser for <cluster;gt; definitions. - * * @author Alex Shvid * @author Matthew T. Adams + * @author David Webb */ -public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { +public class CassandraClusterParser extends AbstractBeanDefinitionParser { - @Override - protected Class getBeanClass(Element element) { - return CassandraClusterFactoryBean.class; - } + private final static Logger log = LoggerFactory.getLogger(CassandraClusterParser.class); + + // @Override + // protected Class getBeanClass(Element element) { + // return CassandraClusterFactoryBean.class; + // } @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) @@ -63,7 +69,27 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { } @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CassandraClusterFactoryBean.class); + builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); + builder.getRawBeanDefinition().setDestroyMethodName("destroy"); + if (parserContext.isNested()) { + // Inner bean definition must receive same scope as containing bean. + builder.setScope(parserContext.getContainingBeanDefinition().getScope()); + } + + if (parserContext.isDefaultLazyInit()) { + // Default-lazy-init applies to custom bean definitions as well. + builder.setLazyInit(true); + } + + doParse(element, parserContext, builder); + + return builder.getBeanDefinition(); + } + + protected void doParse(Element element, ParserContext context, BeanDefinitionBuilder builder) { String contactPoints = element.getAttribute("contactPoints"); if (StringUtils.hasText(contactPoints)) { @@ -77,23 +103,30 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { String compression = element.getAttribute("compression"); if (StringUtils.hasText(compression)) { - builder.addPropertyValue("compressionType", CompressionType.valueOf(compression)); + builder.addPropertyValue("compressionType", compression); } - parseChildElements(builder, element); + String authProvider = element.getAttribute("auth-info-provider-ref"); + if (StringUtils.hasText(authProvider)) { + log.info(authProvider); + builder.addPropertyReference("authProvider", authProvider); + } + + parseChildElements(element, context, builder); } - protected void parseChildElements(BeanDefinitionBuilder builder, Element element) { + protected void parseChildElements(Element element, ParserContext context, BeanDefinitionBuilder builder) { - List creates = new ArrayList(); - List drops = new ArrayList(); + ManagedSet keyspaceActionSpecificationBeanDefinitions = new ManagedSet(); List startupScripts = new ArrayList(); List shutdownScripts = new ArrayList(); List elements = DomUtils.getChildElements(element); + BeanDefinition keyspaceActionSpecificationBeanDefinition = null; // parse nested elements for (Element subElement : elements) { + String name = subElement.getLocalName(); if ("local-pooling-options".equals(name)) { @@ -104,14 +137,9 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { builder.addPropertyValue("socketOptions", parseSocketOptions(subElement)); } else if ("keyspace".equals(name)) { - KeyspaceSpecifications specifications = parseKeyspace(subElement); + keyspaceActionSpecificationBeanDefinition = getKeyspaceSpecificationBeanDefinition(subElement, context); + keyspaceActionSpecificationBeanDefinitions.add(keyspaceActionSpecificationBeanDefinition); - if (specifications.create != null) { - creates.add(specifications.create); - } - if (specifications.drop != null) { - drops.add(specifications.drop); - } } else if ("startup-cql".equals(name)) { startupScripts.add(parseScript(subElement)); } else if ("shutdown-cql".equals(name)) { @@ -119,83 +147,80 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { } } - builder.addPropertyValue("keyspaceCreations", creates); - builder.addPropertyValue("keyspaceDrops", drops); + builder.addPropertyValue("keyspaceSpecifications", + getKeyspaceSetFlattenerBeanDefinition(element, context, keyspaceActionSpecificationBeanDefinitions)); builder.addPropertyValue("startupScripts", startupScripts); builder.addPropertyValue("shutdownScripts", startupScripts); } - protected KeyspaceSpecifications parseKeyspace(Element element) { + /** + * Create the Single Factory Bean that will flatten all List> + * + * @param element + * @param context + * @param keyspaceActionSpecificationBeanDefinitions + * @return + */ + private Object getKeyspaceSetFlattenerBeanDefinition(Element element, ParserContext context, + ManagedSet keyspaceActionSpecificationBeanDefinitions) { - CreateKeyspaceSpecification create = null; - DropKeyspaceSpecification drop = null; + BeanDefinitionBuilder flat = BeanDefinitionBuilder.genericBeanDefinition(MultiLevelSetFlattenerFactoryBean.class); + flat.addPropertyValue("multiLevelSet", keyspaceActionSpecificationBeanDefinitions); + return getSourceBeanDefinition(flat, context, element); - String name = element.getAttribute("name"); - if (name == null || name.trim().length() == 0) { - name = BeanNames.CASSANDRA_KEYSPACE; - } - - boolean durableWrites = Boolean.valueOf(element.getAttribute("durable-writes")); - - String action = element.getAttribute("action"); - if (action == null || action.trim().length() == 0) { - throw new IllegalArgumentException("attribute action must be given"); - } - - if (action.startsWith("CREATE")) { - - create = CreateKeyspaceSpecification.createKeyspace().name(name) - .with(KeyspaceOption.DURABLE_WRITES, durableWrites); - - NodeList nodes = element.getElementsByTagName("replication"); - create = parseReplication((Element) (nodes.getLength() == 1 ? nodes.item(0) : null), create); - } - - if (action.equals("CREATE-DROP")) { - drop = DropKeyspaceSpecification.dropKeyspace().name(create.getName()); - } - - return new KeyspaceSpecifications(create, drop); } - protected CreateKeyspaceSpecification parseReplication(Element element, CreateKeyspaceSpecification create) { + /** + * Parses the keyspace replication options and adds them to the supplied BeanDefinitionBuilder. + * + * @param element + * @param builder + */ + /** + * @param element + * @param builder + */ + protected void parseReplication(Element element, BeanDefinitionBuilder builder) { - String strategyClass = null; - if (element != null) { - strategyClass = element.getAttribute("class"); + if (element == null) { + return; } - if (strategyClass == null || (strategyClass = strategyClass.trim()).length() == 0) { + + String strategyClass = element.getAttribute("class"); + if (!StringUtils.hasText(strategyClass)) { strategyClass = KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY; } - Long replicationFactor = null; - if (element != null) { - String s = element.getAttribute("replication-factor"); - replicationFactor = (s == null || s.trim().length() == 0) ? null : Long.parseLong(s); - } - if (replicationFactor == null) { - replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR; + String replicationFactor = null; + if (strategyClass.equals(ReplicationStrategy.SIMPLE_STRATEGY.getValue())) { + + replicationFactor = element.getAttribute("replication-factor"); + + if (replicationFactor == null) { + replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR + ""; + } + } Map replicationMap = new HashMap(); replicationMap.put(new DefaultOption("class", String.class, false, false, true), strategyClass); - replicationMap.put(new DefaultOption("replication_factor", Long.class, true, false, false), replicationFactor); + if (replicationFactor != null) { + replicationMap.put(new DefaultOption("replication_factor", Long.class, true, false, false), replicationFactor); + } - if (element != null) { - - NodeList dataCenters = element.getElementsByTagName("data-center"); - - int length = dataCenters.getLength(); - for (int i = 0; i < length; i++) { - - Element dataCenter = (Element) dataCenters.item(i); - - replicationMap.put(new DefaultOption(dataCenter.getAttribute("name"), Long.class, false, false, true), + /* + * DataCenters only apply to NetworkTolopogyStrategy + */ + if (strategyClass.equals(ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY.getValue())) { + List dcElements = DomUtils.getChildElementsByTagName(element, "data-center"); + for (Element dataCenter : dcElements) { + replicationMap.put(new DefaultOption(dataCenter.getAttribute("name"), Long.class, true, false, true), dataCenter.getAttribute("replication-factor")); } } - return create.with(KeyspaceOption.REPLICATION, replicationMap); + builder.addPropertyValue("replicationOptions", replicationMap); + } protected String parseScript(Element element) { @@ -227,15 +252,35 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { return builder.getBeanDefinition(); } - protected static class KeyspaceSpecifications { + /** + * Returns a {@link BeanDefinition} for a {@link AuthProvider} object. + * + * @param element + * @param context + * @return the {@link BeanDefinition} or {@literal null} if auth-info-provider is not given. + */ + private BeanDefinition getKeyspaceSpecificationBeanDefinition(Element element, ParserContext context) { - public KeyspaceSpecifications(CreateKeyspaceSpecification create, DropKeyspaceSpecification drop) { - this.create = create; - this.drop = drop; + String name = element.getAttribute("name"); + String action = element.getAttribute("action"); + String durableWrites = element.getAttribute("durable-writes"); + + if (!StringUtils.hasText(action)) { + return null; } - public CreateKeyspaceSpecification create; - public DropKeyspaceSpecification drop; - // TODO: public AlterKeyspaceSpecification alter; + BeanDefinitionBuilder keyspaceBuilder = BeanDefinitionBuilder + .genericBeanDefinition(KeyspaceActionSpecificationFactoryBean.class); + keyspaceBuilder.addPropertyValue("name", name); + keyspaceBuilder.addPropertyValue("action", action); + keyspaceBuilder.addPropertyValue("durableWrites", durableWrites); + + Element replicationElement = DomUtils.getChildElementByTagName(element, "replication"); + if (replicationElement != null) { + parseReplication(replicationElement, keyspaceBuilder); + } + + return getSourceBeanDefinition(keyspaceBuilder, context, element); } + } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java index fbaa6b7dc..66092d235 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/cql/generator/KeyspaceNameCqlGenerator.java @@ -15,20 +15,20 @@ */ package org.springframework.cassandra.core.cql.generator; -import org.springframework.cassandra.core.keyspace.KeyspaceNameSpecification; +import org.springframework.cassandra.core.keyspace.KeyspaceActionSpecification; import org.springframework.util.Assert; -public abstract class KeyspaceNameCqlGenerator> { +public abstract class KeyspaceNameCqlGenerator> { public abstract StringBuilder toCql(StringBuilder cql); - private KeyspaceNameSpecification specification; + private KeyspaceActionSpecification specification; - public KeyspaceNameCqlGenerator(KeyspaceNameSpecification specification) { + public KeyspaceNameCqlGenerator(KeyspaceActionSpecification specification) { setSpecification(specification); } - protected void setSpecification(KeyspaceNameSpecification specification) { + protected void setSpecification(KeyspaceActionSpecification specification) { Assert.notNull(specification); this.specification = specification; } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/DropKeyspaceSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/DropKeyspaceSpecification.java index 839e793c5..5e7511f8d 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/DropKeyspaceSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/DropKeyspaceSpecification.java @@ -1,28 +1,28 @@ -package org.springframework.cassandra.core.keyspace; - -public class DropKeyspaceSpecification extends KeyspaceNameSpecification { - - private boolean ifExists; - - public DropKeyspaceSpecification ifExists() { - return ifExists(true); - } - - public DropKeyspaceSpecification ifExists(boolean ifExists) { - this.ifExists = ifExists; - return this; - } - - public boolean getIfExists() { - return ifExists; - } - - /** - * Entry point into the {@link DropKeyspaceSpecification}'s fluent API to drop a keyspace. Convenient if imported - * statically. - */ - public static DropKeyspaceSpecification dropKeyspace() { - return new DropKeyspaceSpecification(); - } - -} +package org.springframework.cassandra.core.keyspace; + +public class DropKeyspaceSpecification extends KeyspaceActionSpecification { + + private boolean ifExists; + + public DropKeyspaceSpecification ifExists() { + return ifExists(true); + } + + public DropKeyspaceSpecification ifExists(boolean ifExists) { + this.ifExists = ifExists; + return this; + } + + public boolean getIfExists() { + return ifExists; + } + + /** + * Entry point into the {@link DropKeyspaceSpecification}'s fluent API to drop a keyspace. Convenient if imported + * statically. + */ + public static DropKeyspaceSpecification dropKeyspace() { + return new DropKeyspaceSpecification(); + } + +} 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 new file mode 100644 index 000000000..cddc2115d --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceActionSpecification.java @@ -0,0 +1,76 @@ +package org.springframework.cassandra.core.keyspace; + +import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier; +import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize; + +/** + * Abstract builder class to support the construction of keyspace specifications. + * + * @author John McPeek + * @author David Webb + * @param The subtype of the {@link KeyspaceActionSpecification} + */ +public abstract class KeyspaceActionSpecification> { + + /** + * The name of the table. + */ + private String name; + + /** + * Sets the keyspace name. + * + * @return this + */ + @SuppressWarnings("unchecked") + public T name(String name) { + checkIdentifier(name); + this.name = name; + return (T) this; + } + + public String getName() { + return name; + } + + public String getNameAsIdentifier() { + return identifize(name); + } + + /** + * For debugging KeyspaceActionSprcifications + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Keyspace Action Specification {name: " + name + ", class: " + this.getClass() + "}"); + return sb.toString(); + } + + /** + * Determine the KeyspaceActionSpecifications are the same if they have the same "name" and same class. + * + * @param that The object to compare this to. + * @return Are this and that the same? + */ + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null) { + return false; + } + if (!(that instanceof KeyspaceActionSpecification)) { + return false; + } + KeyspaceActionSpecification thatSpec = (KeyspaceActionSpecification) that; + return this.name.equals(thatSpec.name) && this.getClass().equals(that.getClass()); + } + + @Override + public int hashCode() { + return this.name.hashCode() ^ this.getClass().hashCode(); + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceNameSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceNameSpecification.java deleted file mode 100644 index 0bec92437..000000000 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceNameSpecification.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.springframework.cassandra.core.keyspace; - -import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier; -import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize; - -/** - * Abstract builder class to support the construction of keyspace specifications. - * - * @author John McPeek - * @param The subtype of the {@link KeyspaceNameSpecification} - */ -public abstract class KeyspaceNameSpecification> { - - /** - * The name of the table. - */ - private String name; - - /** - * Sets the keyspace name. - * - * @return this - */ - @SuppressWarnings( "unchecked" ) - public T name(String name) { - checkIdentifier(name); - this.name = name; - return (T) this; - } - - public String getName() { - return name; - } - - public String getNameAsIdentifier() { - return identifize(name); - } - -} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOption.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOption.java index 3e5905f64..0d02f15c6 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOption.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOption.java @@ -1,79 +1,79 @@ -package org.springframework.cassandra.core.keyspace; - -import java.util.Map; - -public enum KeyspaceOption implements Option { - REPLICATION("replication", Map.class, true, false, false), - - DURABLE_WRITES("durable_writes", Boolean.class, false, false, false); - - private Option delegate; - - private KeyspaceOption(String name, Class type, boolean requiresValue, boolean escapesValue, boolean quotesValue) { - this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue); - } - - public Class getType() { - return delegate.getType(); - } - - public boolean takesValue() { - return delegate.takesValue(); - } - - public String getName() { - return delegate.getName(); - } - - public boolean escapesValue() { - return delegate.escapesValue(); - } - - public boolean quotesValue() { - return delegate.quotesValue(); - } - - public boolean requiresValue() { - return delegate.requiresValue(); - } - - public void checkValue(Object value) { - delegate.checkValue(value); - } - - public boolean isCoerceable(Object value) { - return delegate.isCoerceable(value); - } - - public String toString() { - return delegate.toString(); - } - - public String toString(Object value) { - return delegate.toString(value); - } - - /** - * Known Replication Strategy options. - * - * @author John McPeek - * - */ - public enum ReplicationStrategy { - SIMPLE_STRATEGY("SimpleStrategy"), NETWORK_TOPOLOGY_STRATEGY("NetworkTopologyStrategy"); - - private String value; - - private ReplicationStrategy(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public String toString() { - return getValue(); - } - } -} +package org.springframework.cassandra.core.keyspace; + +import java.util.Map; + +public enum KeyspaceOption implements Option { + REPLICATION("replication", Map.class, true, false, false), + + DURABLE_WRITES("durable_writes", Boolean.class, false, false, false); + + private Option delegate; + + private KeyspaceOption(String name, Class type, boolean requiresValue, boolean escapesValue, boolean quotesValue) { + this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue); + } + + public Class getType() { + return delegate.getType(); + } + + public boolean takesValue() { + return delegate.takesValue(); + } + + public String getName() { + return delegate.getName(); + } + + public boolean escapesValue() { + return delegate.escapesValue(); + } + + public boolean quotesValue() { + return delegate.quotesValue(); + } + + public boolean requiresValue() { + return delegate.requiresValue(); + } + + public void checkValue(Object value) { + delegate.checkValue(value); + } + + public boolean isCoerceable(Object value) { + return delegate.isCoerceable(value); + } + + public String toString() { + return delegate.toString(); + } + + public String toString(Object value) { + return delegate.toString(value); + } + + /** + * Known Replication Strategy options. + * + * @author John McPeek + * + */ + public enum ReplicationStrategy { + SIMPLE_STRATEGY("SimpleStrategy"), NETWORK_TOPOLOGY_STRATEGY("NetworkTopologyStrategy"); + + private String value; + + private ReplicationStrategy(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public String toString() { + return getValue(); + } + } +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOptionsSpecification.java b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOptionsSpecification.java index 574e166a6..23cf20698 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOptionsSpecification.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/core/keyspace/KeyspaceOptionsSpecification.java @@ -1,94 +1,94 @@ -package org.springframework.cassandra.core.keyspace; - -import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle; -import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.cassandra.core.cql.CqlStringUtils; - -/** - * Abstract builder class to support the construction of table specifications that have table options, that is, those - * options normally specified by WITH ... AND .... - *

- * It is important to note that although this class depends on {@link KeyspaceOption} for convenient and typesafe use, it - * ultimately stores its options in a Map for flexibility. This means that - * {@link #with(KeyspaceOption)} and {@link #with(KeyspaceOption, Object)} delegate to - * {@link #with(String, Object, boolean, boolean)}. This design allows the API to support new Cassandra options as they - * are introduced without having to update the code immediately. - * - * @author John McPeek - * @param The subtype of the {@link KeyspaceOptionsSpecification}. - */ -public abstract class KeyspaceOptionsSpecification> extends - KeyspaceNameSpecification> { - - protected Map options = new LinkedHashMap(); - - @SuppressWarnings( "unchecked" ) - public T name(String name) { - return (T) super.name(name); - } - - /** - * Convenience method that calls with(option, null). - * - * @return this - */ - public T with(KeyspaceOption option) { - return with(option, null); - } - - /** - * Sets the given table option. This is a convenience method that calls - * {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link KeyspaceOption} and value for that - * option. - * - * @param option The option to set. - * @param value The value of the option. Must be type-compatible with the {@link KeyspaceOption}. - * @return this - * @see #with(String, Object, boolean, boolean) - */ - public T with(KeyspaceOption option, Object value) { - option.checkValue(value); - return (T) with(option.getName(), value, option.escapesValue(), option.quotesValue()); - } - - /** - * Adds the given option by name to this keyspaces's options. - *

- * Options that have null values are considered single string options where the name of the option is the - * string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option - * with the given name. The value, after conversion to string, may have embedded single quotes escaped according to - * parameter escape and may be single-quoted according to parameter quote. - * - * @param name The name of the option - * @param value The value of the option. If null, the value is ignored and the option is considered to be - * composed of only the name, otherwise the value's {@link Object#toString()} value is used. - * @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value - * is an instance of a {@link Map}. - * @param quote Whether to quote the value via {@link CqlStringUtils#singleQuote(Object)}. Ignored if given value is - * an instance of a {@link Map}. - * @return this - */ - @SuppressWarnings("unchecked") - public T with(String name, Object value, boolean escape, boolean quote) { - if (!(value instanceof Map)) { - if (escape) { - value = escapeSingle(value); - } - if (quote) { - value = singleQuote(value); - } - } - options.put(name, value); - return (T) this; - } - - public Map getOptions() { - return Collections.unmodifiableMap(options); - } - -} +package org.springframework.cassandra.core.keyspace; + +import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle; +import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.cassandra.core.cql.CqlStringUtils; + +/** + * Abstract builder class to support the construction of table specifications that have table options, that is, those + * options normally specified by WITH ... AND .... + *

+ * It is important to note that although this class depends on {@link KeyspaceOption} for convenient and typesafe use, it + * ultimately stores its options in a Map for flexibility. This means that + * {@link #with(KeyspaceOption)} and {@link #with(KeyspaceOption, Object)} delegate to + * {@link #with(String, Object, boolean, boolean)}. This design allows the API to support new Cassandra options as they + * are introduced without having to update the code immediately. + * + * @author John McPeek + * @param The subtype of the {@link KeyspaceOptionsSpecification}. + */ +public abstract class KeyspaceOptionsSpecification> extends + KeyspaceActionSpecification> { + + protected Map options = new LinkedHashMap(); + + @SuppressWarnings( "unchecked" ) + public T name(String name) { + return (T) super.name(name); + } + + /** + * Convenience method that calls with(option, null). + * + * @return this + */ + public T with(KeyspaceOption option) { + return with(option, null); + } + + /** + * Sets the given table option. This is a convenience method that calls + * {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link KeyspaceOption} and value for that + * option. + * + * @param option The option to set. + * @param value The value of the option. Must be type-compatible with the {@link KeyspaceOption}. + * @return this + * @see #with(String, Object, boolean, boolean) + */ + public T with(KeyspaceOption option, Object value) { + option.checkValue(value); + return (T) with(option.getName(), value, option.escapesValue(), option.quotesValue()); + } + + /** + * Adds the given option by name to this keyspaces's options. + *

+ * Options that have null values are considered single string options where the name of the option is the + * string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option + * with the given name. The value, after conversion to string, may have embedded single quotes escaped according to + * parameter escape and may be single-quoted according to parameter quote. + * + * @param name The name of the option + * @param value The value of the option. If null, the value is ignored and the option is considered to be + * composed of only the name, otherwise the value's {@link Object#toString()} value is used. + * @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value + * is an instance of a {@link Map}. + * @param quote Whether to quote the value via {@link CqlStringUtils#singleQuote(Object)}. Ignored if given value is + * an instance of a {@link Map}. + * @return this + */ + @SuppressWarnings("unchecked") + public T with(String name, Object value, boolean escape, boolean quote) { + if (!(value instanceof Map)) { + if (escape) { + value = escapeSingle(value); + } + if (quote) { + value = singleQuote(value); + } + } + options.put(name, value); + return (T) this; + } + + public Map getOptions() { + return Collections.unmodifiableMap(options); + } + +} diff --git a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd index c0b1ab354..03fdb1ac6 100644 --- a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd @@ -133,32 +133,14 @@ The native CQL port to connect to. Default is 9042. ]]> - + - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - The type of the {@link TableNameSpecification} * @param The type of the {@link TableNameCqlGenerator} */ -public abstract class KeyspaceOperationCqlGeneratorTest, G extends KeyspaceNameCqlGenerator> { +public abstract class KeyspaceOperationCqlGeneratorTest, G extends KeyspaceNameCqlGenerator> { public abstract S specification(); diff --git a/spring-cassandra/src/test/resources/logback-test.xml b/spring-cassandra/src/test/resources/logback-test.xml index 7db55523f..a410f7fb5 100644 --- a/spring-cassandra/src/test/resources/logback-test.xml +++ b/spring-cassandra/src/test/resources/logback-test.xml @@ -10,8 +10,8 @@ - - + + 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 new file mode 100644 index 000000000..2c7f28a31 --- /dev/null +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties new file mode 100644 index 000000000..b89cbde8e --- /dev/null +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties @@ -0,0 +1,9 @@ +cluster.contactPoints=localhost +cluster.port=9042 +cluster.compression=SNAPPY +keyspace.name=ppncxct +keyspace.action=CREATE +dc1.name=DCJAX +dc1.rf=2 +dc2.name=DCCTL +dc2.rf=3 From a07438c3d797f0e506c2ed0a03e165e565d75c63 Mon Sep 17 00:00:00 2001 From: David Webb Date: Mon, 20 Jan 2014 15:12:42 -0500 Subject: [PATCH 2/8] DATACASS-70 - Reverted static Session for Abstract Unit Test until further redesign --- .../integration/AbstractEmbeddedCassandraIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java index e4e6fade6..a9a7022e0 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java @@ -63,7 +63,7 @@ public abstract class AbstractEmbeddedCassandraIntegrationTest { /** * The {@link Session} for the {@link #keyspace} from the {@link #cluster}. */ - protected static Session session; + protected Session session; protected String keyspace() { return keyspace; From ad40ac1c08be6f2c2d0a6724d131d7f63d016375 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Mon, 20 Jan 2014 22:32:51 -0600 Subject: [PATCH 3/8] fixed integration test classes to work with a single forked jvm --- pom.xml | 3 +- ...tractEmbeddedCassandraIntegrationTest.java | 123 ++++++------------ ...stractKeyspaceCreatingIntegrationTest.java | 109 ++++++++++++++++ .../java/KeyspaceCreatingJavaConfigTest.java | 15 --- ...pecifiedKeyspaceCreatingXmlConfigTest.java | 5 - .../MinimalKeyspaceCreatingXmlConfigTest.java | 5 - .../config/xml/MinimalXmlConfigTest.java | 14 +- .../integration/config/xml/XmlConfigTest.java | 14 +- ...eateIndexCqlGeneratorIntegrationTests.java | 8 +- ...eKeyspaceCqlGeneratorIntegrationTests.java | 6 +- ...eateTableCqlGeneratorIntegrationTests.java | 8 +- ...LifecycleCqlGeneratorIntegrationTests.java | 16 +-- .../TableLifecycleIntegrationTest.java | 28 ++-- .../TableOptionsIntegrationTest.java | 7 +- .../template/CassandraOperationsTest.java | 14 +- .../AlterKeyspaceCqlGeneratorTests.java | 45 +++---- .../CreateKeyspaceCqlGeneratorTests.java | 5 +- .../DropKeyspaceCqlGeneratorTests.java | 5 +- .../cassandra/test/unit/support/Utils.java | 11 ++ .../config/xml/XmlConfigTest-context.xml | 7 +- .../config/xml/xmlconfigtest.properties | 3 - 21 files changed, 253 insertions(+), 198 deletions(-) create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractKeyspaceCreatingIntegrationTest.java create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/support/Utils.java delete mode 100644 spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/xmlconfigtest.properties diff --git a/pom.xml b/pom.xml index b6ad3dc88..1dad257bd 100644 --- a/pom.xml +++ b/pom.xml @@ -260,7 +260,8 @@ maven-failsafe-plugin ${failsafe.version} - always + 1 + true -Xmx2048m -XX:MaxPermSize=512m false diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java index a9a7022e0..3315c021a 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java @@ -1,117 +1,72 @@ package org.springframework.cassandra.test.integration; import java.io.IOException; -import java.util.UUID; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.thrift.transport.TTransportException; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; -import org.junit.After; import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cassandra.test.unit.support.Utils; import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.Session; -public abstract class AbstractEmbeddedCassandraIntegrationTest { +/** + * Abstract base integration test class that starts an embedded Cassandra instance. + * + * @author Matthew T. Adams + */ +public class AbstractEmbeddedCassandraIntegrationTest { - private static Logger log = LoggerFactory.getLogger(AbstractEmbeddedCassandraIntegrationTest.class); + static Logger log = LoggerFactory.getLogger(AbstractEmbeddedCassandraIntegrationTest.class); - protected final static String CASSANDRA_CONFIG = "spring-cassandra.yaml"; - protected final static String CASSANDRA_HOST = "localhost"; - protected final static int CASSANDRA_NATIVE_PORT = 9042; + protected static final String CASSANDRA_CONFIG = "spring-cassandra.yaml"; + protected static final String CASSANDRA_HOST = "localhost"; + protected static final int CASSANDRA_NATIVE_PORT = 9042; + + /** + * The session connected to the system keyspace. + */ + protected static Session SYSTEM; + /** + * The {@link Cluster} that's connected to Cassandra. + */ + protected static Cluster CLUSTER; + + public static String randomKeyspaceName() { + return Utils.randomKeyspaceName(); + } @BeforeClass public static void startCassandra() throws ConfigurationException, TTransportException, IOException, InterruptedException { - log.info("Starting Cassandra Embedded Server"); + EmbeddedCassandraServerHelper.startEmbeddedCassandra(CASSANDRA_CONFIG); } - public AbstractEmbeddedCassandraIntegrationTest() { - if (session == null) { - connect(); - } - } - - public AbstractEmbeddedCassandraIntegrationTest(String keyspace) { - this.keyspace = keyspace; - if (session == null) { - connect(); - } - } - - /** - * Whether to clear the cluster before the next test. - */ - protected boolean clear = false; - /** - * Whether to connect to Cassandra. - */ - protected boolean connect = true; - /** - * The {@link Cluster} that's connected to Cassandra. - */ - protected Cluster cluster; - /** - * If not null, get a {@link Session} for the from the {@link #cluster}. - */ - protected String keyspace = "ks" + UUID.randomUUID().toString().replace("-", ""); - - /** - * The {@link Session} for the {@link #keyspace} from the {@link #cluster}. - */ - protected Session session; - - protected String keyspace() { - return keyspace; - } - - /** - * Returns whether we're currently connected to the cluster. - */ - public boolean connected() { - return session != null; - } - - public Cluster cluster() { + public static Cluster cluster() { return Cluster.builder().addContactPoint(CASSANDRA_HOST).withPort(CASSANDRA_NATIVE_PORT).build(); } - public void connect() { + /** + * Ensures that the cluster is created and that the session {@link #SYSTEM} is connected to it. + */ + public static void ensureClusterConnection() { - if (connect && !connected()) { + // check cluster + if (CLUSTER == null) { + CLUSTER = cluster(); + } - log.info("Connecting to Cassandra"); - - cluster = cluster(); - - if (keyspace() == null) { - session = cluster.connect(); - } else { - - KeyspaceMetadata kmd = cluster.getMetadata().getKeyspace(keyspace()); - if (kmd == null) { // then create keyspace - session = cluster.connect(); - session.execute("CREATE KEYSPACE " + keyspace() - + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};"); - session.execute("USE " + keyspace() + ";"); - } else {// else keyspace already exists - session = cluster.connect(keyspace()); - } - } + // check system session connected + if (SYSTEM == null) { + SYSTEM = CLUSTER.connect(); } } - @After - public void after() { - log.info("After: clear -> " + clear + ", connected -> " + connected()); - if (clear && connected()) { - log.info("Cleaning Cassandra"); - EmbeddedCassandraServerHelper.cleanEmbeddedCassandra(); - } + public AbstractEmbeddedCassandraIntegrationTest() { + ensureClusterConnection(); } - } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractKeyspaceCreatingIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractKeyspaceCreatingIntegrationTest.java new file mode 100644 index 000000000..b9e04f827 --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractKeyspaceCreatingIntegrationTest.java @@ -0,0 +1,109 @@ +package org.springframework.cassandra.test.integration; + +import org.junit.After; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.Session; + +/** + * Abstract base integration test class that creates a keyspace + * + * @author Matthew T. Adams + */ +public abstract class AbstractKeyspaceCreatingIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest { + + static Logger log = LoggerFactory.getLogger(AbstractKeyspaceCreatingIntegrationTest.class); + + /** + * The session that's connected to the keyspace used in the current instance's test. + */ + protected static Session SESSION; + + /** + * The name of the keyspace to use for this test instance. + */ + protected String keyspace; + + public AbstractKeyspaceCreatingIntegrationTest() { + this(randomKeyspaceName()); + } + + public AbstractKeyspaceCreatingIntegrationTest(String keyspace) { + + this.keyspace = keyspace; + ensureKeyspaceAndSession(); + } + + /** + * Returns whether we're currently connected to the keyspace. + */ + public static boolean connected() { + return SESSION != null; + } + + /** + * Whether to drop the keyspace that was created after the test has completed. Subclasses should override and return + * true, since this default implementation returns false. + */ + public boolean dropKeyspaceAfterTest() { + return false; + } + + public void ensureKeyspaceAndSession() { + + // ensure that test keyspace exists + + if (!StringUtils.hasText(keyspace)) { + keyspace = null; + } + + if (keyspace != null) { + // see if we need to create the keyspace + KeyspaceMetadata kmd = CLUSTER.getMetadata().getKeyspace(keyspace); + if (kmd == null) { // then create keyspace + + String cql = "CREATE KEYSPACE " + keyspace + + " WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};"; + log.info("creating keyspace {} via CQL [{}]", keyspace, cql); + + SYSTEM.execute(cql); + } + } + + // keyspace now exists; ensure the session is using it + if (SESSION == null) { + + log.info("connecting to keyspace {}", keyspace == null ? "system" : keyspace + "..."); + + SESSION = keyspace == null ? CLUSTER.connect() : CLUSTER.connect(keyspace); + + log.info("connected to keyspace {}", keyspace == null ? "system" : keyspace); + + } else { + + log.info("session already connected to a keyspace; attempting to change to use {}", keyspace); + + String cql = "USE " + (keyspace == null ? "system" : keyspace) + ";"; + SESSION.execute(cql); + + log.info("now using keyspace " + keyspace); + } + } + + @After + public void after() { + if (dropKeyspaceAfterTest() && keyspace != null) { + + SESSION.execute("USE system"); + + log.info("dropping keyspace {} ...", keyspace); + + SYSTEM.execute("DROP KEYSPACE " + keyspace); + + log.info("dropped keyspace {}", keyspace); + } + } +} diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfigTest.java index 0f639ec57..fe827695c 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfigTest.java @@ -1,28 +1,13 @@ package org.springframework.cassandra.test.integration.config.java; -import javax.inject.Inject; - import org.junit.Assert; import org.junit.Test; -import org.junit.runner.RunWith; import org.springframework.cassandra.test.integration.config.IntegrationTestUtils; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.datastax.driver.core.Session; - -@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = KeyspaceCreatingJavaConfig.class) public class KeyspaceCreatingJavaConfigTest extends AbstractIntegrationTest { - @Inject - protected Session session; - - @Override - protected String keyspace() { - return null; - } - @Test public void test() { Assert.assertNotNull(session); diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest.java index 44ac32069..228a3c70d 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest.java @@ -15,11 +15,6 @@ import com.datastax.driver.core.Session; @ContextConfiguration public class FullySpecifiedKeyspaceCreatingXmlConfigTest extends AbstractEmbeddedCassandraIntegrationTest { - @Override - protected String keyspace() { - return null; - } - @Inject Session s; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest.java index 492605316..6759daa2c 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest.java @@ -15,11 +15,6 @@ import com.datastax.driver.core.Session; @ContextConfiguration public class MinimalKeyspaceCreatingXmlConfigTest extends AbstractEmbeddedCassandraIntegrationTest { - @Override - protected String keyspace() { - return null; - } - @Inject Session s; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest.java index 760c00192..045d9788e 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest.java @@ -1,13 +1,13 @@ package org.springframework.cassandra.test.integration.config.xml; -import static org.junit.Assert.*; +import static org.junit.Assert.assertNotNull; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.cassandra.core.CassandraOperations; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.integration.config.IntegrationTestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -16,10 +16,12 @@ import com.datastax.driver.core.Session; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class MinimalXmlConfigTest extends AbstractEmbeddedCassandraIntegrationTest { +public class MinimalXmlConfigTest extends AbstractKeyspaceCreatingIntegrationTest { - protected String keyspace() { - return "minimalxmlconfigtest"; + public static final String KEYSPACE = "minimalxmlconfigtest"; + + public MinimalXmlConfigTest() { + super(KEYSPACE); } @Inject @@ -31,7 +33,7 @@ public class MinimalXmlConfigTest extends AbstractEmbeddedCassandraIntegrationTe @Test public void test() { IntegrationTestUtils.assertSession(s); - IntegrationTestUtils.assertKeyspaceExists(keyspace(), s); + IntegrationTestUtils.assertKeyspaceExists(KEYSPACE, s); assertNotNull(ops); } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java index b8eb27ea8..28300bb12 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java @@ -4,7 +4,7 @@ import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.integration.config.IntegrationTestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -13,18 +13,20 @@ import com.datastax.driver.core.Session; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class XmlConfigTest extends AbstractEmbeddedCassandraIntegrationTest { +public class XmlConfigTest extends AbstractKeyspaceCreatingIntegrationTest { - protected String keyspace() { - return "xmlconfigtest"; - } + public static final String KEYSPACE = "xmlconfigtest"; @Inject Session s; + public XmlConfigTest() { + super(KEYSPACE); + } + @Test public void test() { IntegrationTestUtils.assertSession(s); - IntegrationTestUtils.assertKeyspaceExists(keyspace(), s); + IntegrationTestUtils.assertKeyspaceExists(KEYSPACE, s); } } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateIndexCqlGeneratorIntegrationTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateIndexCqlGeneratorIntegrationTests.java index 5c0add73b..d9c043916 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateIndexCqlGeneratorIntegrationTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateIndexCqlGeneratorIntegrationTests.java @@ -6,7 +6,7 @@ import org.cassandraunit.CassandraCQLUnit; import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; import org.junit.Rule; import org.junit.Test; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateIndexCqlGeneratorTests.BasicTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateIndexCqlGeneratorTests.CreateIndexTest; @@ -24,7 +24,7 @@ public class CreateIndexCqlGeneratorIntegrationTests { * * @param The concrete unit test class to which this integration test corresponds. */ - public static abstract class Base extends AbstractEmbeddedCassandraIntegrationTest { + public static abstract class Base extends AbstractKeyspaceCreatingIntegrationTest { T unit; public abstract T unit(); @@ -34,9 +34,9 @@ public class CreateIndexCqlGeneratorIntegrationTests { unit = unit(); unit.prepare(); - session.execute(unit.cql); + SESSION.execute(unit.cql); - assertIndex(unit.specification, keyspace, session); + assertIndex(unit.specification, keyspace, SESSION); } } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateKeyspaceCqlGeneratorIntegrationTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateKeyspaceCqlGeneratorIntegrationTests.java index 2524e0205..d71e79f14 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateKeyspaceCqlGeneratorIntegrationTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateKeyspaceCqlGeneratorIntegrationTests.java @@ -30,9 +30,9 @@ public class CreateKeyspaceCqlGeneratorIntegrationTests { unit = unit(); unit.prepare(); - session.execute(unit.cql); + SYSTEM.execute(unit.cql); - assertKeyspace(unit.specification, unit.keyspace, session); + assertKeyspace(unit.specification, unit.keyspace, SYSTEM); } } @@ -50,5 +50,5 @@ public class CreateKeyspaceCqlGeneratorIntegrationTests { public NetworkTopologyTest unit() { return new NetworkTopologyTest(); } - } + } } \ No newline at end of file diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java index 63806cbc7..fc4de3fbf 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/CreateTableCqlGeneratorIntegrationTests.java @@ -5,7 +5,7 @@ import static org.springframework.cassandra.test.integration.core.cql.generator. import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.BasicTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CompositePartitionKeyTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CreateTableTest; @@ -26,7 +26,7 @@ public class CreateTableCqlGeneratorIntegrationTests { * * @param The concrete unit test class to which this integration test corresponds. */ - public static abstract class Base extends AbstractEmbeddedCassandraIntegrationTest { + public static abstract class Base extends AbstractKeyspaceCreatingIntegrationTest { T unit; public abstract T unit(); @@ -36,9 +36,9 @@ public class CreateTableCqlGeneratorIntegrationTests { unit = unit(); unit.prepare(); - session.execute(unit.cql); + SESSION.execute(unit.cql); - assertTable(unit.specification, keyspace, session); + assertTable(unit.specification, keyspace, SESSION); } } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/IndexLifecycleCqlGeneratorIntegrationTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/IndexLifecycleCqlGeneratorIntegrationTests.java index 0c01fdbd0..801655677 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/IndexLifecycleCqlGeneratorIntegrationTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/IndexLifecycleCqlGeneratorIntegrationTests.java @@ -9,7 +9,7 @@ import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateIndexCqlGeneratorTests; import org.springframework.cassandra.test.unit.core.cql.generator.DropIndexCqlGeneratorTests; @@ -18,7 +18,7 @@ import org.springframework.cassandra.test.unit.core.cql.generator.DropIndexCqlGe * * @author Matthew T. Adams */ -public class IndexLifecycleCqlGeneratorIntegrationTests extends AbstractEmbeddedCassandraIntegrationTest { +public class IndexLifecycleCqlGeneratorIntegrationTests extends AbstractKeyspaceCreatingIntegrationTest { Logger log = LoggerFactory.getLogger(IndexLifecycleCqlGeneratorIntegrationTests.class); @@ -42,19 +42,19 @@ public class IndexLifecycleCqlGeneratorIntegrationTests extends AbstractEmbedded dropIfExists.prepare(); log.info(createTest.cql); - session.execute(createTest.cql); + SESSION.execute(createTest.cql); - assertIndex(createTest.specification, keyspace, session); + assertIndex(createTest.specification, keyspace, SESSION); log.info(dropTest.cql); - session.execute(dropTest.cql); + SESSION.execute(dropTest.cql); - assertNoIndex(createTest.specification, keyspace, session); + assertNoIndex(createTest.specification, keyspace, SESSION); // log.info(dropIfExists.cql); - // session.execute(dropIfExists.cql); + // SESSION.execute(dropIfExists.cql); // - // assertNoIndex(createTest.specification, keyspace, session); + // assertNoIndex(createTest.specification, keyspace, SESSION); } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableLifecycleIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableLifecycleIntegrationTest.java index 689895a81..ca836b2af 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableLifecycleIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableLifecycleIntegrationTest.java @@ -26,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator; import org.springframework.cassandra.core.keyspace.DropTableSpecification; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.AlterTableCqlGeneratorTests; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests; import org.springframework.cassandra.test.unit.core.cql.generator.DropTableCqlGeneratorTests; @@ -36,7 +36,7 @@ import org.springframework.cassandra.test.unit.core.cql.generator.DropTableCqlGe * * @author David Webb */ -public class TableLifecycleIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest { +public class TableLifecycleIntegrationTest extends AbstractKeyspaceCreatingIntegrationTest { private final static Logger log = LoggerFactory.getLogger(TableLifecycleIntegrationTest.class); @@ -44,10 +44,14 @@ public class TableLifecycleIntegrationTest extends AbstractEmbeddedCassandraInte public TableLifecycleIntegrationTest() { super("tlit"); - clear = true; } - // This only ensures the keyspace exists before each test, while using a static session from the parent object. + @Override + public boolean dropKeyspaceAfterTest() { + return true; + } + + // This only ensures the keyspace exists before each test, while using a static SESSION from the parent object. // TODO - DW Make this better. @Rule public CassandraCQLUnit cassandraCQLUnit = new CassandraCQLUnit(new ClassPathCQLDataSet( @@ -61,18 +65,18 @@ public class TableLifecycleIntegrationTest extends AbstractEmbeddedCassandraInte log.info(createTableTest.cql); - session.execute(createTableTest.cql); + SESSION.execute(createTableTest.cql); - assertTable(createTableTest.specification, keyspace, session); + assertTable(createTableTest.specification, keyspace, SESSION); DropTableTest dropTest = new DropTableTest(); dropTest.prepare(); log.info(dropTest.cql); - session.execute(dropTest.cql); + SESSION.execute(dropTest.cql); - assertNoTable(dropTest.specification, keyspace, session); + assertNoTable(dropTest.specification, keyspace, SESSION); } @Test @@ -82,18 +86,18 @@ public class TableLifecycleIntegrationTest extends AbstractEmbeddedCassandraInte log.info(createTableTest.cql); - session.execute(createTableTest.cql); + SESSION.execute(createTableTest.cql); - assertTable(createTableTest.specification, keyspace, session); + assertTable(createTableTest.specification, keyspace, SESSION); AlterTableCqlGeneratorTests.MultipleOptionsTest alterTest = new AlterTableCqlGeneratorTests.MultipleOptionsTest(); alterTest.prepare(); log.info(alterTest.cql); - session.execute(alterTest.cql); + SESSION.execute(alterTest.cql); - // assertTable(alterTest.specification, keyspace, session); + // assertTable(alterTest.specification, keyspace, SESSION); } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableOptionsIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableOptionsIntegrationTest.java index b0d0bd92b..5c2e3ae25 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableOptionsIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/cql/generator/TableOptionsIntegrationTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests; /** @@ -28,7 +29,7 @@ import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCql * * @author David Webb */ -public class TableOptionsIntegrationTest extends AbstractEmbeddedCassandraIntegrationTest { +public class TableOptionsIntegrationTest extends AbstractKeyspaceCreatingIntegrationTest { private final static Logger log = LoggerFactory.getLogger(TableOptionsIntegrationTest.class); @@ -41,8 +42,8 @@ public class TableOptionsIntegrationTest extends AbstractEmbeddedCassandraIntegr log.info(optionsTest.cql); - session.execute(optionsTest.cql); + SESSION.execute(optionsTest.cql); - assertTable(optionsTest.specification, keyspace, session); + assertTable(optionsTest.specification, keyspace, SESSION); } } \ No newline at end of file diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java index da5a4f636..3b31ecffe 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/core/template/CassandraOperationsTest.java @@ -46,7 +46,7 @@ import org.springframework.cassandra.core.RowCallbackHandler; import org.springframework.cassandra.core.RowIterator; import org.springframework.cassandra.core.RowMapper; import org.springframework.cassandra.core.SessionCallback; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.dao.DataAccessException; import org.springframework.util.CollectionUtils; @@ -65,7 +65,7 @@ import com.datastax.driver.core.exceptions.DriverException; * @author David Webb * */ -public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegrationTest { +public class CassandraOperationsTest extends AbstractKeyspaceCreatingIntegrationTest { private static CassandraOperations cassandraTemplate; @@ -89,8 +89,8 @@ public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegratio CASSANDRA_NATIVE_PORT); public CassandraOperationsTest() { - super("sdctest"); - clear = true; + super(); + // TODO clear = true; } @Before @@ -106,7 +106,7 @@ public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegratio // "cassandraOperationsTest-cql-dataload.cql", keyspace), CASSANDRA_CONFIG, CASSANDRA_HOST, // CASSANDRA_NATIVE_PORT); - cassandraTemplate = new CassandraTemplate(session); + cassandraTemplate = new CassandraTemplate(SESSION); } } @@ -160,8 +160,6 @@ public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegratio @SuppressWarnings("unchecked") public void ingestionTestListOfList() { - log.info("Keyspace => " + keyspace); - String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; List> values = new LinkedList>(); @@ -185,8 +183,6 @@ public class CassandraOperationsTest extends AbstractEmbeddedCassandraIntegratio @Test public void ingestionTestObjectArray() { - log.info("Keyspace => " + keyspace); - String cql = "insert into book (isbn, title, author, pages) values (?, ?, ?, ?)"; Object[][] values = new Object[3][]; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterKeyspaceCqlGeneratorTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterKeyspaceCqlGeneratorTests.java index f54ef316e..04e8eac5e 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterKeyspaceCqlGeneratorTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/AlterKeyspaceCqlGeneratorTests.java @@ -11,6 +11,7 @@ import org.springframework.cassandra.core.keyspace.AlterKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DefaultOption; import org.springframework.cassandra.core.keyspace.KeyspaceOption; import org.springframework.cassandra.core.keyspace.Option; +import org.springframework.cassandra.test.unit.support.Utils; public class AlterKeyspaceCqlGeneratorTests { @@ -23,8 +24,8 @@ public class AlterKeyspaceCqlGeneratorTests { private static void assertReplicationMap(Map replicationMap, String cql) { assertTrue(cql.contains(" WITH replication = { ")); - - for (Map.Entry entry : replicationMap.entrySet() ) { + + for (Map.Entry entry : replicationMap.entrySet()) { String keyValuePair = "'" + entry.getKey().getName() + "' : '" + entry.getValue().toString() + "'"; assertTrue(cql.contains(keyValuePair)); } @@ -38,28 +39,28 @@ public class AlterKeyspaceCqlGeneratorTests { * Convenient base class that other test classes can use so as not to repeat the generics declarations. */ public static abstract class AlterKeyspaceTest extends - KeyspaceOperationCqlGeneratorTest { + KeyspaceOperationCqlGeneratorTest { } public static class CompleteTest extends AlterKeyspaceTest { - public String name = "mykeyspace"; + public String name = Utils.randomKeyspaceName(); public Boolean durableWrites = true; - + public Map replicationMap = new HashMap(); + @Override public AlterKeyspaceSpecification specification() { - replicationMap.put( new DefaultOption( "class", String.class, false, false, true ), "SimpleStrategy" ); - replicationMap.put( new DefaultOption( "replication_factor", Long.class, false, false, true ), 1 ); - replicationMap.put( new DefaultOption( "dc1", Long.class, false, false, true ), 2 ); - replicationMap.put( new DefaultOption( "dc2", Long.class, false, false, true ), 3 ); - - return AlterKeyspaceSpecification.alterKeyspace() - .name(name) - .with(KeyspaceOption.REPLICATION, replicationMap) + replicationMap.put(new DefaultOption("class", String.class, false, false, true), "SimpleStrategy"); + replicationMap.put(new DefaultOption("replication_factor", Long.class, false, false, true), 1); + replicationMap.put(new DefaultOption("dc1", Long.class, false, false, true), 2); + replicationMap.put(new DefaultOption("dc2", Long.class, false, false, true), 3); + + return AlterKeyspaceSpecification.alterKeyspace().name(name).with(KeyspaceOption.REPLICATION, replicationMap) .with(KeyspaceOption.DURABLE_WRITES, durableWrites); } + @Override public AlterKeyspaceCqlGenerator generator() { return new AlterKeyspaceCqlGenerator(specification); } @@ -78,20 +79,20 @@ public class AlterKeyspaceCqlGeneratorTests { public String name = "mytable"; public Boolean durableWrites = true; - + public Map replicationMap = new HashMap(); + @Override public AlterKeyspaceSpecification specification() { - replicationMap.put( new DefaultOption( "class", String.class, false, false, true ), "SimpleStrategy" ); - replicationMap.put( new DefaultOption( "replication_factor", Long.class, false, false, true ), 1 ); - replicationMap.put( new DefaultOption( "dc1", Long.class, false, false, true ), 2 ); - replicationMap.put( new DefaultOption( "dc2", Long.class, false, false, true ), 3 ); - - return AlterKeyspaceSpecification.alterKeyspace() - .name(name) - .with(KeyspaceOption.REPLICATION, replicationMap); + replicationMap.put(new DefaultOption("class", String.class, false, false, true), "SimpleStrategy"); + replicationMap.put(new DefaultOption("replication_factor", Long.class, false, false, true), 1); + replicationMap.put(new DefaultOption("dc1", Long.class, false, false, true), 2); + replicationMap.put(new DefaultOption("dc2", Long.class, false, false, true), 3); + + return AlterKeyspaceSpecification.alterKeyspace().name(name).with(KeyspaceOption.REPLICATION, replicationMap); } + @Override public AlterKeyspaceCqlGenerator generator() { return new AlterKeyspaceCqlGenerator(specification); } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/CreateKeyspaceCqlGeneratorTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/CreateKeyspaceCqlGeneratorTests.java index 6de52762b..4613542d8 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/CreateKeyspaceCqlGeneratorTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/CreateKeyspaceCqlGeneratorTests.java @@ -12,6 +12,7 @@ import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DefaultOption; import org.springframework.cassandra.core.keyspace.KeyspaceOption; import org.springframework.cassandra.core.keyspace.Option; +import org.springframework.cassandra.test.unit.support.Utils; public class CreateKeyspaceCqlGeneratorTests { @@ -51,7 +52,7 @@ public class CreateKeyspaceCqlGeneratorTests { public static class BasicTest extends CreateKeyspaceTest { - public String name = "mykeyspace"; + public String name = Utils.randomKeyspaceName(); public Boolean durableWrites = true; public Map replicationMap = KeyspaceAttributes.newSimpleReplication(); @@ -76,7 +77,7 @@ public class CreateKeyspaceCqlGeneratorTests { public static class NetworkTopologyTest extends CreateKeyspaceTest { - public String name = "mykeyspace"; + public String name = Utils.randomKeyspaceName(); public Boolean durableWrites = false; public Map replicationMap = new HashMap(); diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/DropKeyspaceCqlGeneratorTests.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/DropKeyspaceCqlGeneratorTests.java index 08ae48df2..daa302090 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/DropKeyspaceCqlGeneratorTests.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/core/cql/generator/DropKeyspaceCqlGeneratorTests.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.springframework.cassandra.core.cql.generator.DropKeyspaceCqlGenerator; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; +import org.springframework.cassandra.test.unit.support.Utils; public class DropKeyspaceCqlGeneratorTests { @@ -24,12 +25,14 @@ public class DropKeyspaceCqlGeneratorTests { public static class BasicTest extends DropTableTest { - public String name = "mykeyspace"; + public String name = Utils.randomKeyspaceName(); + @Override public DropKeyspaceSpecification specification() { return DropKeyspaceSpecification.dropKeyspace().name(name); } + @Override public DropKeyspaceCqlGenerator generator() { return new DropKeyspaceCqlGenerator(specification); } diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/support/Utils.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/support/Utils.java new file mode 100644 index 000000000..29b136cdc --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/unit/support/Utils.java @@ -0,0 +1,11 @@ +package org.springframework.cassandra.test.unit.support; + +import java.util.UUID; + +public class Utils { + + public static String randomKeyspaceName() { + return "ks" + UUID.randomUUID().toString().replace("-", ""); + } + +} 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 39e2ff612..1a31974f8 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 @@ -6,11 +6,8 @@ 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"> - - + contactPoints="localhost" port="9042"> @@ -24,7 +21,7 @@ + keyspace-name="xmlconfigtest" /> diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/xmlconfigtest.properties b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/xmlconfigtest.properties deleted file mode 100644 index 2d7a6eb68..000000000 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/xmlconfigtest.properties +++ /dev/null @@ -1,3 +0,0 @@ -cassandra.contactPoints=localhost -cassandra.port=9042 -cassandra.keyspace=xmlconfigtest From 7f640c20de99e6e44a299a20fbcb3e28d34afe31 Mon Sep 17 00:00:00 2001 From: David Webb Date: Tue, 21 Jan 2014 15:32:21 -0500 Subject: [PATCH 4/8] DATACASS-71 - Completed refactoring for compliance with property placeholders. --- .../config/CassandraClusterFactoryBean.java | 120 ++++----- ...eyspaceActionSpecificationFactoryBean.java | 134 ++++++++-- .../config/PoolingOptionsConfig.java | 62 ----- .../config/PoolingOptionsFactoryBean.java | 224 ++++++++++++++++ .../cassandra/config/SocketOptionsConfig.java | 89 ------- .../config/SocketOptionsFactoryBean.java | 185 +++++++++++++ .../java/AbstractCassandraConfiguration.java | 15 +- .../config/xml/CassandraClusterParser.java | 252 ++++++++++++------ .../cassandra/config/spring-cassandra-1.0.xsd | 73 +++-- ...dKeyspaceCreatingXmlConfigTest-context.xml | 10 +- ...lKeyspaceCreatingXmlConfigTest-context.xml | 2 +- ...NamespaceCreatingXmlConfigTest-context.xml | 42 ++- .../config/xml/XmlConfigTest-context.xml | 2 +- .../integration/config/xml/ppncxct.properties | 24 ++ 14 files changed, 863 insertions(+), 371 deletions(-) delete mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsConfig.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsFactoryBean.java delete mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsConfig.java create mode 100644 spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsFactoryBean.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java index 5c7610159..69453baec 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java @@ -39,7 +39,6 @@ import org.springframework.util.StringUtils; import com.datastax.driver.core.AuthProvider; import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.HostDistance; import com.datastax.driver.core.PoolingOptions; import com.datastax.driver.core.ProtocolOptions.Compression; import com.datastax.driver.core.Session; @@ -60,12 +59,13 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia public static final String DEFAULT_CONTACT_POINTS = "localhost"; public static final boolean DEFAULT_METRICS_ENABLED = true; + public static final boolean DEFAULT_DEFERRED_INITIALIZATION = false; + public static final boolean DEFAULT_JMX_REPORTING_ENABLED = true; public static final int DEFAULT_PORT = 9042; protected static final Logger log = LoggerFactory.getLogger(CassandraClusterFactoryBean.class); private Cluster cluster; - private boolean accumulating = true; /* * Attributes needed for cluster builder @@ -73,14 +73,17 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia private String contactPoints = DEFAULT_CONTACT_POINTS; private int port = CassandraClusterFactoryBean.DEFAULT_PORT; private CompressionType compressionType; - private PoolingOptionsConfig localPoolingOptions; - private PoolingOptionsConfig remotePoolingOptions; - private SocketOptionsConfig socketOptions; + private PoolingOptions poolingOptions; + private SocketOptions socketOptions; private AuthProvider authProvider; + private String username; + private String password; private LoadBalancingPolicy loadBalancingPolicy; private ReconnectionPolicy reconnectionPolicy; private RetryPolicy retryPolicy; private boolean metricsEnabled = DEFAULT_METRICS_ENABLED; + private boolean deferredInitialization = DEFAULT_DEFERRED_INITIALIZATION; + private boolean jmxReportingEnabled = DEFAULT_JMX_REPORTING_ENABLED; private Set> keyspaceSpecifications = new HashSet>(); private List keyspaceCreations = new ArrayList(); private List keyspaceDrops = new ArrayList(); @@ -124,20 +127,20 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia builder.withCompression(convertCompressionType(compressionType)); } - if (localPoolingOptions != null) { - builder.withPoolingOptions(configPoolingOptions(HostDistance.LOCAL, localPoolingOptions)); - } - - if (remotePoolingOptions != null) { - builder.withPoolingOptions(configPoolingOptions(HostDistance.REMOTE, remotePoolingOptions)); + if (poolingOptions != null) { + builder.withPoolingOptions(poolingOptions); } if (socketOptions != null) { - builder.withSocketOptions(configSocketOptions(socketOptions)); + builder.withSocketOptions(socketOptions); } if (authProvider != null) { builder.withAuthProvider(authProvider); + + if (username != null) { + builder.withCredentials(username, password); + } } if (loadBalancingPolicy != null) { @@ -152,10 +155,18 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia builder.withRetryPolicy(retryPolicy); } + if (deferredInitialization) { + builder.withDeferredInitialization(); + } + if (!metricsEnabled) { builder.withoutMetrics(); } + if (!jmxReportingEnabled) { + builder.withoutJMXReporting(); + } + cluster = builder.build(); generateSpecificationsFromFactoryBeans(); @@ -256,15 +267,11 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia this.compressionType = compressionType; } - public void setLocalPoolingOptions(PoolingOptionsConfig localPoolingOptions) { - this.localPoolingOptions = localPoolingOptions; + public void setPoolingOptions(PoolingOptions poolingOptions) { + this.poolingOptions = poolingOptions; } - public void setRemotePoolingOptions(PoolingOptionsConfig remotePoolingOptions) { - this.remotePoolingOptions = remotePoolingOptions; - } - - public void setSocketOptions(SocketOptionsConfig socketOptions) { + public void setSocketOptions(SocketOptions socketOptions) { this.socketOptions = socketOptions; } @@ -322,55 +329,6 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia throw new IllegalArgumentException("unknown compression type " + type); } - private static PoolingOptions configPoolingOptions(HostDistance hostDistance, PoolingOptionsConfig config) { - PoolingOptions poolingOptions = new PoolingOptions(); - - if (config.getMinSimultaneousRequests() != null) { - poolingOptions - .setMinSimultaneousRequestsPerConnectionThreshold(hostDistance, config.getMinSimultaneousRequests()); - } - if (config.getMaxSimultaneousRequests() != null) { - poolingOptions - .setMaxSimultaneousRequestsPerConnectionThreshold(hostDistance, config.getMaxSimultaneousRequests()); - } - if (config.getCoreConnections() != null) { - poolingOptions.setCoreConnectionsPerHost(hostDistance, config.getCoreConnections()); - } - if (config.getMaxConnections() != null) { - poolingOptions.setMaxConnectionsPerHost(hostDistance, config.getMaxConnections()); - } - - return poolingOptions; - } - - private static SocketOptions configSocketOptions(SocketOptionsConfig config) { - SocketOptions socketOptions = new SocketOptions(); - - if (config.getConnectTimeoutMls() != null) { - socketOptions.setConnectTimeoutMillis(config.getConnectTimeoutMls()); - } - if (config.getKeepAlive() != null) { - socketOptions.setKeepAlive(config.getKeepAlive()); - } - if (config.getReuseAddress() != null) { - socketOptions.setReuseAddress(config.getReuseAddress()); - } - if (config.getSoLinger() != null) { - socketOptions.setSoLinger(config.getSoLinger()); - } - if (config.getTcpNoDelay() != null) { - socketOptions.setTcpNoDelay(config.getTcpNoDelay()); - } - if (config.getReceiveBufferSize() != null) { - socketOptions.setReceiveBufferSize(config.getReceiveBufferSize()); - } - if (config.getSendBufferSize() != null) { - socketOptions.setSendBufferSize(config.getSendBufferSize()); - } - - return socketOptions; - } - /** * @return Returns the keyspaceSpecifications. */ @@ -389,16 +347,30 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia } /** - * @return Returns the accumulating. + * @param username The username to set. */ - public boolean isAccumulating() { - return accumulating; + public void setUsername(String username) { + this.username = username; } /** - * @param accumulating The accumulating to set. + * @param password The password to set. */ - public void setAccumulating(boolean accumulating) { - this.accumulating = accumulating; + public void setPassword(String password) { + this.password = password; + } + + /** + * @param deferredInitialization The deferredInitialization to set. + */ + public void setDeferredInitialization(boolean deferredInitialization) { + this.deferredInitialization = deferredInitialization; + } + + /** + * @param jmxReportingEnabled The jmxReportingEnabled to set. + */ + public void setJmxReportingEnabled(boolean jmxReportingEnabled) { + this.jmxReportingEnabled = jmxReportingEnabled; } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java index 5fb25d3fe..e44d16587 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java @@ -17,7 +17,8 @@ package org.springframework.cassandra.config; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Set; @@ -31,11 +32,17 @@ import org.springframework.cassandra.core.keyspace.DefaultOption; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.KeyspaceActionSpecification; import org.springframework.cassandra.core.keyspace.KeyspaceOption; +import org.springframework.cassandra.core.keyspace.KeyspaceOption.ReplicationStrategy; import org.springframework.cassandra.core.keyspace.Option; import org.springframework.util.Assert; /** - * @author David Webb (dwebb@brightmove.com) + * A single keyspace XML Element can result in multiple actions. Example: {@literal CREATE_DROP}. + * + * This FactoryBean inspects the action required to satisfy the keyspace element, and then returns a Set of atomic + * {@link KeyspaceActionSpecification} required to satisfy the configuration action. + * + * @author David Webb * */ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean>>, @@ -45,7 +52,10 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean replicationOptions = new LinkedHashMap(); + private List networkTopologyDataCenters = new LinkedList(); + private List networkTopologyReplicationFactors = new LinkedList(); + private String replicationStrategy; + private long replicationFactor; private boolean durableWrites = false; private boolean ifNotExists = false; @@ -53,8 +63,11 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean 0) { - create.with(KeyspaceOption.REPLICATION, replicationOptions); - } else { - Map defaultReplicationStrategyMap = new HashMap(); - defaultReplicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true), - KeyspaceOption.ReplicationStrategy.SIMPLE_STRATEGY); - defaultReplicationStrategyMap.put(new DefaultOption("replication_factor", String.class, true, false, false), "1"); - create.with(KeyspaceOption.REPLICATION, defaultReplicationStrategyMap); + + Map replicationStrategyMap = new HashMap(); + replicationStrategyMap.put(new DefaultOption("class", String.class, true, false, true), ReplicationStrategy + .valueOf(replicationStrategy).getValue()); + + /* + * Just set replication factor for SimpleStrategy + */ + if (replicationStrategy.equals(ReplicationStrategy.SIMPLE_STRATEGY.name())) { + replicationStrategyMap.put(new DefaultOption("replication_factor", Long.class, true, false, false), + replicationFactor); } + + if (replicationStrategy.equals(ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY.name())) { + int i = 0; + for (String datacenter : networkTopologyDataCenters) { + replicationStrategyMap.put(new DefaultOption(datacenter, Long.class, true, false, false), + networkTopologyReplicationFactors.get(i)); + i++; + } + } + + create.with(KeyspaceOption.REPLICATION, replicationStrategyMap); + return create; } + /** + * Generate a {@link DropKeyspaceSpecification} for the keyspace. + * + * @return The {@link DropKeyspaceSpecification} + */ private DropKeyspaceSpecification generateDropKeyspaceSpecification() { DropKeyspaceSpecification drop = new DropKeyspaceSpecification(); drop.name(getName()); @@ -154,20 +194,6 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean getReplicationOptions() { - return replicationOptions; - } - - /** - * @param replicationOptions The replicationOptions to set. - */ - public void setReplicationOptions(Map replicationOptions) { - this.replicationOptions = replicationOptions; - } - /** * @return Returns the durableWrites. */ @@ -182,4 +208,60 @@ public class KeyspaceActionSpecificationFactoryBean implements FactoryBean getNetworkTopologyDataCenters() { + return networkTopologyDataCenters; + } + + /** + * @param networkTopologyDataCenters The networkTopologyDataCenters to set. + */ + public void setNetworkTopologyDataCenters(List networkTopologyDataCenters) { + this.networkTopologyDataCenters = networkTopologyDataCenters; + } + + /** + * @return Returns the networkTopologyReplicationFactors. + */ + public List getNetworkTopologyReplicationFactors() { + return networkTopologyReplicationFactors; + } + + /** + * @param networkTopologyReplicationFactors The networkTopologyReplicationFactors to set. + */ + public void setNetworkTopologyReplicationFactors(List networkTopologyReplicationFactors) { + this.networkTopologyReplicationFactors = networkTopologyReplicationFactors; + } + + /** + * @return Returns the replicationFactor. + */ + public long getReplicationFactor() { + return replicationFactor; + } + + /** + * @param replicationFactor The replicationFactor to set. + */ + public void setReplicationFactor(long replicationFactor) { + this.replicationFactor = replicationFactor; + } + } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsConfig.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsConfig.java deleted file mode 100644 index e982e217d..000000000 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.config; - -/** - * Pooling options. - * - * @author Alex Shvid - * @author Matthew T. Adams - */ -public class PoolingOptionsConfig { - - private Integer minSimultaneousRequests; - private Integer maxSimultaneousRequests; - private Integer coreConnections; - private Integer maxConnections; - - public Integer getMinSimultaneousRequests() { - return minSimultaneousRequests; - } - - public void setMinSimultaneousRequests(Integer minSimultaneousRequests) { - this.minSimultaneousRequests = minSimultaneousRequests; - } - - public Integer getMaxSimultaneousRequests() { - return maxSimultaneousRequests; - } - - public void setMaxSimultaneousRequests(Integer maxSimultaneousRequests) { - this.maxSimultaneousRequests = maxSimultaneousRequests; - } - - public Integer getCoreConnections() { - return coreConnections; - } - - public void setCoreConnections(Integer coreConnections) { - this.coreConnections = coreConnections; - } - - public Integer getMaxConnections() { - return maxConnections; - } - - public void setMaxConnections(Integer maxConnections) { - this.maxConnections = maxConnections; - } -} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsFactoryBean.java new file mode 100644 index 000000000..d9f4489b3 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/PoolingOptionsFactoryBean.java @@ -0,0 +1,224 @@ +/* + * Copyright 2011-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cassandra.config; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +import com.datastax.driver.core.HostDistance; +import com.datastax.driver.core.PoolingOptions; + +/** + * Pooling Options Factory Bean. + * + * @author Matthew T. Adams + * @author David Webb + */ +public class PoolingOptionsFactoryBean implements FactoryBean, InitializingBean, DisposableBean { + + private Integer localMinSimultaneousRequests; + private Integer localMaxSimultaneousRequests; + private Integer localCoreConnections; + private Integer localMaxConnections; + private Integer remoteMinSimultaneousRequests; + private Integer remoteMaxSimultaneousRequests; + private Integer remoteCoreConnections; + private Integer remoteMaxConnections; + + PoolingOptions poolingOptions; + + @Override + public void destroy() throws Exception { + localMinSimultaneousRequests = null; + localMaxSimultaneousRequests = null; + localCoreConnections = null; + localMaxConnections = null; + remoteMinSimultaneousRequests = null; + remoteMaxSimultaneousRequests = null; + remoteCoreConnections = null; + remoteMaxConnections = null; + } + + @Override + public void afterPropertiesSet() throws Exception { + + poolingOptions = new PoolingOptions(); + + if (localMinSimultaneousRequests != null) { + poolingOptions.setMinSimultaneousRequestsPerConnectionThreshold(HostDistance.LOCAL, localMinSimultaneousRequests); + } + + if (localMaxSimultaneousRequests != null) { + poolingOptions.setMaxSimultaneousRequestsPerConnectionThreshold(HostDistance.LOCAL, localMaxSimultaneousRequests); + } + + if (localCoreConnections != null) { + poolingOptions.setCoreConnectionsPerHost(HostDistance.LOCAL, localCoreConnections); + } + + if (localMaxConnections != null) { + poolingOptions.setMaxConnectionsPerHost(HostDistance.LOCAL, localMaxConnections); + } + + if (remoteMinSimultaneousRequests != null) { + poolingOptions.setMinSimultaneousRequestsPerConnectionThreshold(HostDistance.REMOTE, + remoteMinSimultaneousRequests); + } + + if (remoteMaxSimultaneousRequests != null) { + poolingOptions.setMaxSimultaneousRequestsPerConnectionThreshold(HostDistance.REMOTE, + remoteMaxSimultaneousRequests); + } + + if (remoteCoreConnections != null) { + poolingOptions.setCoreConnectionsPerHost(HostDistance.REMOTE, remoteCoreConnections); + } + + if (remoteMaxConnections != null) { + poolingOptions.setMaxConnectionsPerHost(HostDistance.REMOTE, remoteMaxConnections); + } + + } + + @Override + public PoolingOptions getObject() throws Exception { + return poolingOptions; + } + + @Override + public Class getObjectType() { + return PoolingOptions.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + /** + * @return Returns the localMinSimultaneousRequests. + */ + public Integer getLocalMinSimultaneousRequests() { + return localMinSimultaneousRequests; + } + + /** + * @param localMinSimultaneousRequests The localMinSimultaneousRequests to set. + */ + public void setLocalMinSimultaneousRequests(Integer localMinSimultaneousRequests) { + this.localMinSimultaneousRequests = localMinSimultaneousRequests; + } + + /** + * @return Returns the localMaxSimultaneousRequests. + */ + public Integer getLocalMaxSimultaneousRequests() { + return localMaxSimultaneousRequests; + } + + /** + * @param localMaxSimultaneousRequests The localMaxSimultaneousRequests to set. + */ + public void setLocalMaxSimultaneousRequests(Integer localMaxSimultaneousRequests) { + this.localMaxSimultaneousRequests = localMaxSimultaneousRequests; + } + + /** + * @return Returns the localCoreConnections. + */ + public Integer getLocalCoreConnections() { + return localCoreConnections; + } + + /** + * @param localCoreConnections The localCoreConnections to set. + */ + public void setLocalCoreConnections(Integer localCoreConnections) { + this.localCoreConnections = localCoreConnections; + } + + /** + * @return Returns the localMaxConnections. + */ + public Integer getLocalMaxConnections() { + return localMaxConnections; + } + + /** + * @param localMaxConnections The localMaxConnections to set. + */ + public void setLocalMaxConnections(Integer localMaxConnections) { + this.localMaxConnections = localMaxConnections; + } + + /** + * @return Returns the remoteMinSimultaneousRequests. + */ + public Integer getRemoteMinSimultaneousRequests() { + return remoteMinSimultaneousRequests; + } + + /** + * @param remoteMinSimultaneousRequests The remoteMinSimultaneousRequests to set. + */ + public void setRemoteMinSimultaneousRequests(Integer remoteMinSimultaneousRequests) { + this.remoteMinSimultaneousRequests = remoteMinSimultaneousRequests; + } + + /** + * @return Returns the remoteMaxSimultaneousRequests. + */ + public Integer getRemoteMaxSimultaneousRequests() { + return remoteMaxSimultaneousRequests; + } + + /** + * @param remoteMaxSimultaneousRequests The remoteMaxSimultaneousRequests to set. + */ + public void setRemoteMaxSimultaneousRequests(Integer remoteMaxSimultaneousRequests) { + this.remoteMaxSimultaneousRequests = remoteMaxSimultaneousRequests; + } + + /** + * @return Returns the remoteCoreConnections. + */ + public Integer getRemoteCoreConnections() { + return remoteCoreConnections; + } + + /** + * @param remoteCoreConnections The remoteCoreConnections to set. + */ + public void setRemoteCoreConnections(Integer remoteCoreConnections) { + this.remoteCoreConnections = remoteCoreConnections; + } + + /** + * @return Returns the remoteMaxConnections. + */ + public Integer getRemoteMaxConnections() { + return remoteMaxConnections; + } + + /** + * @param remoteMaxConnections The remoteMaxConnections to set. + */ + public void setRemoteMaxConnections(Integer remoteMaxConnections) { + this.remoteMaxConnections = remoteMaxConnections; + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsConfig.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsConfig.java deleted file mode 100644 index 562377415..000000000 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsConfig.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2011-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.config; - -/** - * Socket options. - * - * @author Alex Shvid - * @author Matthew T. Adams - */ -public class SocketOptionsConfig { - - private Integer connectTimeoutMls; - private Boolean keepAlive; - private Boolean reuseAddress; - private Integer soLinger; - private Boolean tcpNoDelay; - private Integer receiveBufferSize; - private Integer sendBufferSize; - - public Integer getConnectTimeoutMls() { - return connectTimeoutMls; - } - - public void setConnectTimeoutMls(Integer connectTimeoutMls) { - this.connectTimeoutMls = connectTimeoutMls; - } - - public Boolean getKeepAlive() { - return keepAlive; - } - - public void setKeepAlive(Boolean keepAlive) { - this.keepAlive = keepAlive; - } - - public Boolean getReuseAddress() { - return reuseAddress; - } - - public void setReuseAddress(Boolean reuseAddress) { - this.reuseAddress = reuseAddress; - } - - public Integer getSoLinger() { - return soLinger; - } - - public void setSoLinger(Integer soLinger) { - this.soLinger = soLinger; - } - - public Boolean getTcpNoDelay() { - return tcpNoDelay; - } - - public void setTcpNoDelay(Boolean tcpNoDelay) { - this.tcpNoDelay = tcpNoDelay; - } - - public Integer getReceiveBufferSize() { - return receiveBufferSize; - } - - public void setReceiveBufferSize(Integer receiveBufferSize) { - this.receiveBufferSize = receiveBufferSize; - } - - public Integer getSendBufferSize() { - return sendBufferSize; - } - - public void setSendBufferSize(Integer sendBufferSize) { - this.sendBufferSize = sendBufferSize; - } -} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsFactoryBean.java new file mode 100644 index 000000000..164aac8e5 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/SocketOptionsFactoryBean.java @@ -0,0 +1,185 @@ +/* + * Copyright 2011-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cassandra.config; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +import com.datastax.driver.core.SocketOptions; + +/** + * Socket Options Factory Bean. + * + * @author Matthew T. Adams + * @author David Webb + */ +public class SocketOptionsFactoryBean implements FactoryBean, InitializingBean, DisposableBean { + + private Integer connectTimeoutMillis; + private Boolean keepAlive; + private Integer readTimeoutMillis; + private Boolean reuseAddress; + private Integer soLinger; + private Boolean tcpNoDelay; + private Integer receiveBufferSize; + private Integer sendBufferSize; + + SocketOptions socketOptions; + + @Override + public SocketOptions getObject() throws Exception { + return socketOptions; + } + + @Override + public Class getObjectType() { + return SocketOptions.class; + } + + @Override + public void destroy() throws Exception { + connectTimeoutMillis = null; + keepAlive = null; + readTimeoutMillis = null; + reuseAddress = null; + soLinger = null; + tcpNoDelay = null; + receiveBufferSize = null; + sendBufferSize = null; + } + + @Override + public void afterPropertiesSet() throws Exception { + + socketOptions = new SocketOptions(); + + if (connectTimeoutMillis != null) { + socketOptions.setConnectTimeoutMillis(connectTimeoutMillis); + } + + if (keepAlive != null) { + socketOptions.setKeepAlive(keepAlive); + } + + if (readTimeoutMillis != null) { + socketOptions.setReadTimeoutMillis(readTimeoutMillis); + } + + if (reuseAddress != null) { + socketOptions.setReuseAddress(reuseAddress); + } + + if (soLinger != null) { + socketOptions.setSoLinger(soLinger); + } + + if (tcpNoDelay != null) { + socketOptions.setTcpNoDelay(tcpNoDelay); + } + + if (receiveBufferSize != null) { + socketOptions.setReceiveBufferSize(receiveBufferSize); + } + + if (sendBufferSize != null) { + socketOptions.setSendBufferSize(sendBufferSize); + } + + } + + @Override + public boolean isSingleton() { + return true; + } + + public Boolean getKeepAlive() { + return keepAlive; + } + + public void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + public Boolean getReuseAddress() { + return reuseAddress; + } + + public void setReuseAddress(Boolean reuseAddress) { + this.reuseAddress = reuseAddress; + } + + public Integer getSoLinger() { + return soLinger; + } + + public void setSoLinger(Integer soLinger) { + this.soLinger = soLinger; + } + + public Boolean getTcpNoDelay() { + return tcpNoDelay; + } + + public void setTcpNoDelay(Boolean tcpNoDelay) { + this.tcpNoDelay = tcpNoDelay; + } + + public Integer getReceiveBufferSize() { + return receiveBufferSize; + } + + public void setReceiveBufferSize(Integer receiveBufferSize) { + this.receiveBufferSize = receiveBufferSize; + } + + public Integer getSendBufferSize() { + return sendBufferSize; + } + + public void setSendBufferSize(Integer sendBufferSize) { + this.sendBufferSize = sendBufferSize; + } + + /** + * @return Returns the connectTimeoutMillis. + */ + public Integer getConnectTimeoutMillis() { + return connectTimeoutMillis; + } + + /** + * @param connectTimeoutMillis The connectTimeoutMillis to set. + */ + public void setConnectTimeoutMillis(Integer connectTimeoutMillis) { + this.connectTimeoutMillis = connectTimeoutMillis; + } + + /** + * @return Returns the readTimeoutMillis. + */ + public Integer getReadTimeoutMillis() { + return readTimeoutMillis; + } + + /** + * @param readTimeoutMillis The readTimeoutMillis to set. + */ + public void setReadTimeoutMillis(Integer readTimeoutMillis) { + this.readTimeoutMillis = readTimeoutMillis; + } + +} diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java index 9ec7b70e1..6e116716f 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/java/AbstractCassandraConfiguration.java @@ -6,8 +6,6 @@ import java.util.List; import org.springframework.cassandra.config.CassandraClusterFactoryBean; import org.springframework.cassandra.config.CassandraSessionFactoryBean; import org.springframework.cassandra.config.CompressionType; -import org.springframework.cassandra.config.PoolingOptionsConfig; -import org.springframework.cassandra.config.SocketOptionsConfig; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.DropKeyspaceSpecification; import org.springframework.context.annotation.Bean; @@ -15,6 +13,8 @@ import org.springframework.context.annotation.Configuration; import com.datastax.driver.core.AuthProvider; import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.PoolingOptions; +import com.datastax.driver.core.SocketOptions; import com.datastax.driver.core.policies.LoadBalancingPolicy; import com.datastax.driver.core.policies.ReconnectionPolicy; import com.datastax.driver.core.policies.RetryPolicy; @@ -40,11 +40,10 @@ public abstract class AbstractCassandraConfiguration { bean.setKeyspaceCreations(getKeyspaceCreations()); bean.setKeyspaceDrops(getKeyspaceDrops()); bean.setLoadBalancingPolicy(getLoadBalancingPolicy()); - bean.setLocalPoolingOptions(getLocalPoolingOptions()); bean.setMetricsEnabled(getMetricsEnabled()); bean.setPort(getPort()); bean.setReconnectionPolicy(getReconnectionPolicy()); - bean.setRemotePoolingOptions(getRemotePoolingOptions()); + bean.setPoolingOptions(getPoolingOptions()); bean.setRetryPolicy(getRetryPolicy()); bean.setShutdownScripts(getShutdownScripts()); bean.setSocketOptions(getSocketOptions()); @@ -69,7 +68,7 @@ public abstract class AbstractCassandraConfiguration { return Collections.emptyList(); } - protected SocketOptionsConfig getSocketOptions() { + protected SocketOptions getSocketOptions() { return null; } @@ -85,7 +84,7 @@ public abstract class AbstractCassandraConfiguration { return null; } - protected PoolingOptionsConfig getRemotePoolingOptions() { + protected PoolingOptions getPoolingOptions() { return null; } @@ -97,10 +96,6 @@ public abstract class AbstractCassandraConfiguration { return CassandraClusterFactoryBean.DEFAULT_METRICS_ENABLED; } - protected PoolingOptionsConfig getLocalPoolingOptions() { - return null; - } - protected LoadBalancingPolicy getLoadBalancingPolicy() { return null; } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java index 231aa1e3c..aafdd3a38 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java @@ -18,9 +18,7 @@ package org.springframework.cassandra.config.xml; import static org.springframework.data.config.ParsingUtils.getSourceBeanDefinition; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +26,7 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; @@ -35,19 +34,22 @@ import org.springframework.cassandra.config.CassandraClusterFactoryBean; import org.springframework.cassandra.config.KeyspaceActionSpecificationFactoryBean; import org.springframework.cassandra.config.KeyspaceAttributes; import org.springframework.cassandra.config.MultiLevelSetFlattenerFactoryBean; -import org.springframework.cassandra.config.PoolingOptionsConfig; -import org.springframework.cassandra.config.SocketOptionsConfig; -import org.springframework.cassandra.core.keyspace.DefaultOption; +import org.springframework.cassandra.config.PoolingOptionsFactoryBean; +import org.springframework.cassandra.config.SocketOptionsFactoryBean; +import org.springframework.cassandra.core.keyspace.KeyspaceActionSpecification; import org.springframework.cassandra.core.keyspace.KeyspaceOption.ReplicationStrategy; -import org.springframework.cassandra.core.keyspace.Option; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -import com.datastax.driver.core.AuthProvider; +import com.datastax.driver.core.HostDistance; +import com.datastax.driver.core.PoolingOptions; +import com.datastax.driver.core.SocketOptions; /** - * @author Alex Shvid + * Parses the {@literal } element of the XML Configuration. + * * @author Matthew T. Adams * @author David Webb */ @@ -55,11 +57,6 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { private final static Logger log = LoggerFactory.getLogger(CassandraClusterParser.class); - // @Override - // protected Class getBeanClass(Element element) { - // return CassandraClusterFactoryBean.class; - // } - @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { @@ -89,6 +86,13 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { return builder.getBeanDefinition(); } + /** + * Parses the attributes on the top level element, then parses all children. + * + * @param element The Element being parsed + * @param context The Parser Context + * @param builder The parent {@link BeanDefinitionBuilder} + */ protected void doParse(Element element, ParserContext context, BeanDefinitionBuilder builder) { String contactPoints = element.getAttribute("contactPoints"); @@ -108,13 +112,59 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { String authProvider = element.getAttribute("auth-info-provider-ref"); if (StringUtils.hasText(authProvider)) { - log.info(authProvider); builder.addPropertyReference("authProvider", authProvider); } + String username = element.getAttribute("username"); + if (StringUtils.hasText(username)) { + builder.addPropertyValue("username", username); + } + + String password = element.getAttribute("password"); + if (StringUtils.hasText(password)) { + builder.addPropertyValue("password", password); + } + + String deferredInitialization = element.getAttribute("deferredInitialization"); + if (StringUtils.hasText(deferredInitialization)) { + builder.addPropertyValue("deferredInitialization", deferredInitialization); + } + + String metricsEnabled = element.getAttribute("metricsEnabled"); + if (StringUtils.hasText(metricsEnabled)) { + builder.addPropertyValue("metricsEnabled", metricsEnabled); + } + + String jmxReportingEnabled = element.getAttribute("jmxReportingEnabled"); + if (StringUtils.hasText(jmxReportingEnabled)) { + builder.addPropertyValue("jmxReportingEnabled", jmxReportingEnabled); + } + + String loadBalancingPolicy = element.getAttribute("load-balancing-policy-ref"); + if (StringUtils.hasText(loadBalancingPolicy)) { + builder.addPropertyReference("loadBalancingPolicy", loadBalancingPolicy); + } + + String reconnectionPolicy = element.getAttribute("reconnection-policy-ref"); + if (StringUtils.hasText(reconnectionPolicy)) { + builder.addPropertyReference("reconnectionPolicy", reconnectionPolicy); + } + + String retryPolicy = element.getAttribute("retry-policy-ref"); + if (StringUtils.hasText(retryPolicy)) { + builder.addPropertyReference("retryPolicy", retryPolicy); + } + parseChildElements(element, context, builder); } + /** + * Parse the Child Elemement of {@link BeanNames.CASSANDRA_CLUSTER} + * + * @param element The Element being parsed + * @param context The Parser Context + * @param builder The parent {@link BeanDefinitionBuilder} + */ protected void parseChildElements(Element element, ParserContext context, BeanDefinitionBuilder builder) { ManagedSet keyspaceActionSpecificationBeanDefinitions = new ManagedSet(); @@ -124,17 +174,26 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { List elements = DomUtils.getChildElements(element); BeanDefinition keyspaceActionSpecificationBeanDefinition = null; - // parse nested elements + /* + * PoolingOptionsBuilder has two potential parsing cycles so it is defined + * before the child elements are iterated over, then converted to a BeanDefinition + * just in time. + */ + BeanDefinitionBuilder poolingOptionsBuilder = null; + + /* + * Parse each of the child elements + */ for (Element subElement : elements) { String name = subElement.getLocalName(); if ("local-pooling-options".equals(name)) { - builder.addPropertyValue("localPoolingOptions", parsePoolingOptions(subElement)); + poolingOptionsBuilder = parsePoolingOptions(subElement, poolingOptionsBuilder, HostDistance.LOCAL); } else if ("remote-pooling-options".equals(name)) { - builder.addPropertyValue("remotePoolingOptions", parsePoolingOptions(subElement)); + poolingOptionsBuilder = parsePoolingOptions(subElement, poolingOptionsBuilder, HostDistance.REMOTE); } else if ("socket-options".equals(name)) { - builder.addPropertyValue("socketOptions", parseSocketOptions(subElement)); + builder.addPropertyValue("socketOptions", getSocketOptionsBeanDefinition(subElement, context)); } else if ("keyspace".equals(name)) { keyspaceActionSpecificationBeanDefinition = getKeyspaceSpecificationBeanDefinition(subElement, context); @@ -147,6 +206,13 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { } } + /* + * If the PoolingOptionsBuilder was initilized during parsing, process it now. + */ + if (poolingOptionsBuilder != null) { + builder.addPropertyValue("poolingOptions", getSourceBeanDefinition(poolingOptionsBuilder, context, element)); + } + builder.addPropertyValue("keyspaceSpecifications", getKeyspaceSetFlattenerBeanDefinition(element, context, keyspaceActionSpecificationBeanDefinitions)); builder.addPropertyValue("startupScripts", startupScripts); @@ -156,10 +222,10 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { /** * Create the Single Factory Bean that will flatten all List> * - * @param element - * @param context - * @param keyspaceActionSpecificationBeanDefinitions - * @return + * @param element The Element being parsed + * @param context The Parser Context + * @param keyspaceActionSpecificationBeanDefinitions The List of Definitions to flatten + * @return A single level List of KeyspaceActionSpecifications */ private Object getKeyspaceSetFlattenerBeanDefinition(Element element, ParserContext context, ManagedSet keyspaceActionSpecificationBeanDefinitions) { @@ -171,114 +237,136 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { } /** - * Parses the keyspace replication options and adds them to the supplied BeanDefinitionBuilder. + * Parses the keyspace replication options and adds them to the supplied {@link BeanDefinitionBuilder}. * - * @param element - * @param builder - */ - /** - * @param element - * @param builder + * @param element The Element being parsed + * @param builder The {@link BeanDefinitionBuilder} to add the replication to */ protected void parseReplication(Element element, BeanDefinitionBuilder builder) { - if (element == null) { - return; - } - - String strategyClass = element.getAttribute("class"); - if (!StringUtils.hasText(strategyClass)) { - strategyClass = KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY; - } - + ManagedList networkTopologyDataCenters = new ManagedList(); + ManagedList networkTopologyReplicationFactors = new ManagedList(); + String strategyClass = null; String replicationFactor = null; - if (strategyClass.equals(ReplicationStrategy.SIMPLE_STRATEGY.getValue())) { + + if (element != null) { + + strategyClass = element.getAttribute("class"); + if (!StringUtils.hasText(strategyClass)) { + strategyClass = KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY; + } replicationFactor = element.getAttribute("replication-factor"); - - if (replicationFactor == null) { + if (!StringUtils.hasText(replicationFactor)) { replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR + ""; } - } - - Map replicationMap = new HashMap(); - replicationMap.put(new DefaultOption("class", String.class, false, false, true), strategyClass); - if (replicationFactor != null) { - replicationMap.put(new DefaultOption("replication_factor", Long.class, true, false, false), replicationFactor); - } - - /* - * DataCenters only apply to NetworkTolopogyStrategy - */ - if (strategyClass.equals(ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY.getValue())) { + /* + * DataCenters only apply to NetworkTolopogyStrategy + */ List dcElements = DomUtils.getChildElementsByTagName(element, "data-center"); for (Element dataCenter : dcElements) { - replicationMap.put(new DefaultOption(dataCenter.getAttribute("name"), Long.class, true, false, true), - dataCenter.getAttribute("replication-factor")); + networkTopologyDataCenters.add(dataCenter.getAttribute("name")); + networkTopologyReplicationFactors.add(dataCenter.getAttribute("replication-factor")); } + } else { + strategyClass = ReplicationStrategy.SIMPLE_STRATEGY.name(); + replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR + ""; } - builder.addPropertyValue("replicationOptions", replicationMap); + builder.addPropertyValue("replicationStrategy", strategyClass); + builder.addPropertyValue("replicationFactor", replicationFactor); + builder.addPropertyValue("networkTopologyDataCenters", networkTopologyDataCenters); + builder.addPropertyValue("networkTopologyReplicationFactors", networkTopologyReplicationFactors); } + /** + * Parse CQL Script Elements + * + * @param element The Element being parsed + * @return + */ protected String parseScript(Element element) { return element.getTextContent(); } - protected BeanDefinition parsePoolingOptions(Element element) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PoolingOptionsConfig.class); + /** + * Returns a {@link BeanDefinition} for a {@link PoolingOptions} object. + * + * @param element The Element being parsed + * @param builder The {@link BeanDefinition} to use for building if one already exists + * @param hostDistance The scope of the PoolingOptions to apply + * @return The {@link BeanDefinitionBuilder} + */ + protected BeanDefinitionBuilder parsePoolingOptions(Element element, BeanDefinitionBuilder builder, + HostDistance hostDistance) { - ParsingUtils.setPropertyValue(builder, element, "min-simultaneous-requests", "minSimultaneousRequests"); - ParsingUtils.setPropertyValue(builder, element, "max-simultaneous-requests", "maxSimultaneousRequests"); - ParsingUtils.setPropertyValue(builder, element, "core-connections", "coreConnections"); - ParsingUtils.setPropertyValue(builder, element, "max-connections", "maxConnections"); + if (builder == null) { + builder = BeanDefinitionBuilder.genericBeanDefinition(PoolingOptionsFactoryBean.class); + } - return builder.getBeanDefinition(); + if (hostDistance.equals(HostDistance.LOCAL)) { + ParsingUtils.setPropertyValue(builder, element, "min-simultaneous-requests", "localMinSimultaneousRequests"); + ParsingUtils.setPropertyValue(builder, element, "max-simultaneous-requests", "localMaxSimultaneousRequests"); + ParsingUtils.setPropertyValue(builder, element, "core-connections", "localCoreConnections"); + ParsingUtils.setPropertyValue(builder, element, "max-connections", "localMaxConnections"); + } + if (hostDistance.equals(HostDistance.REMOTE)) { + ParsingUtils.setPropertyValue(builder, element, "min-simultaneous-requests", "remoteMinSimultaneousRequests"); + ParsingUtils.setPropertyValue(builder, element, "max-simultaneous-requests", "remoteMaxSimultaneousRequests"); + ParsingUtils.setPropertyValue(builder, element, "core-connections", "remoteCoreConnections"); + ParsingUtils.setPropertyValue(builder, element, "max-connections", "remoteMaxConnections"); + } + + return builder; } - protected BeanDefinition parseSocketOptions(Element element) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsConfig.class); + /** + * Returns a {@link BeanDefinition} for a {@link SocketOptions} object. + * + * @param element The Element being parsed + * @param context The ParserContext + * @return The {@link BeanDefinition} + */ + protected BeanDefinition getSocketOptionsBeanDefinition(Element element, ParserContext context) { - ParsingUtils.setPropertyValue(builder, element, "connect-timeout-mls", "connectTimeoutMls"); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsFactoryBean.class); + + ParsingUtils.setPropertyValue(builder, element, "connect-timeout-mls", "connectTimeoutMillis"); ParsingUtils.setPropertyValue(builder, element, "keep-alive", "keepAlive"); + ParsingUtils.setPropertyValue(builder, element, "read-timeout-mls", "readTimeoutMillis"); ParsingUtils.setPropertyValue(builder, element, "reuse-address", "reuseAddress"); ParsingUtils.setPropertyValue(builder, element, "so-linger", "soLinger"); ParsingUtils.setPropertyValue(builder, element, "tcp-no-delay", "tcpNoDelay"); ParsingUtils.setPropertyValue(builder, element, "receive-buffer-size", "receiveBufferSize"); ParsingUtils.setPropertyValue(builder, element, "send-buffer-size", "sendBufferSize"); - return builder.getBeanDefinition(); + return getSourceBeanDefinition(builder, context, element); } /** - * Returns a {@link BeanDefinition} for a {@link AuthProvider} object. + * Returns a {@link BeanDefinition} for a {@link KeyspaceActionSpecification} object. * - * @param element - * @param context - * @return the {@link BeanDefinition} or {@literal null} if auth-info-provider is not given. + * @param element The Element being parsed + * @param context The Parser Context + * @return The {@link BeanDefinition} or {@literal null} if action is not given. */ private BeanDefinition getKeyspaceSpecificationBeanDefinition(Element element, ParserContext context) { - String name = element.getAttribute("name"); String action = element.getAttribute("action"); - String durableWrites = element.getAttribute("durable-writes"); - if (!StringUtils.hasText(action)) { - return null; - } + Assert.notNull(action, "Keyspace Action must not be null!"); BeanDefinitionBuilder keyspaceBuilder = BeanDefinitionBuilder .genericBeanDefinition(KeyspaceActionSpecificationFactoryBean.class); - keyspaceBuilder.addPropertyValue("name", name); - keyspaceBuilder.addPropertyValue("action", action); - keyspaceBuilder.addPropertyValue("durableWrites", durableWrites); + + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "name", "name"); + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "action", "action"); + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "durableWrites", "durableWrites"); Element replicationElement = DomUtils.getChildElementByTagName(element, "replication"); - if (replicationElement != null) { - parseReplication(replicationElement, keyspaceBuilder); - } + parseReplication(replicationElement, keyspaceBuilder); return getSourceBeanDefinition(keyspaceBuilder, context, element); } diff --git a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd index 03fdb1ac6..45bb9ddfc 100644 --- a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd @@ -59,22 +59,16 @@ Defines a Cassandra cluster. - - - - - - + + + @@ -133,13 +127,15 @@ The native CQL port to connect to. Default is 9042. ]]> - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - @@ -267,7 +298,7 @@ More connections are created up to a configurable maximum number of connections. - + + + + + + - + - - - + - - + diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest-context.xml index 688cb3dba..05939b0d0 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest-context.xml +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalKeyspaceCreatingXmlConfigTest-context.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + 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 2c7f28a31..81d7ff8ce 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,13 +10,51 @@ location="classpath:/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties" /> + + + + + + + + + + + + compression="${cluster.compression}" auth-info-provider-ref="authProvider" + load-balancing-policy-ref="loadBalancingPolicy" username="${auth.username}" password="${auth.password}" + deferredInitialization="${cluster.deferredInit}" metricsEnabled="${cluster.metricsEnabled}" + jmxReportingEnabled="${cluster.jmxReportingEnabled}" + reconnection-policy-ref="reconnectionPolicy" + retry-policy-ref="retryPolicy"> + + + - + 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 39e2ff612..3681a711e 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 @@ -18,7 +18,7 @@ min-simultaneous-requests="25" max-simultaneous-requests="100" core-connections="1" max-connections="2" /> diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties index b89cbde8e..b8d517dc4 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties @@ -1,9 +1,33 @@ cluster.contactPoints=localhost cluster.port=9042 cluster.compression=SNAPPY +cluster.deferredInit=true +cluster.metricsEnabled=false +cluster.jmxReportingEnabled=false +cluster.reconnection.delayMillis=5000 keyspace.name=ppncxct keyspace.action=CREATE dc1.name=DCJAX dc1.rf=2 dc2.name=DCCTL dc2.rf=3 +lb.policy.dcAware.remoteHosts=4 +lb.policy.dcAware.localDc=DCJAX +auth.username=test +auth.password=pass +socket.connectTimeoutMillis=5000 +socket.keepAlive=true +socket.readTimeoutMillis=60000 +socket.receiveBufferSize=1024 +socket.sendBufferSize=2048 +socket.reuseAddress=true +socket.soLinger=5 +socket.tcpNoDelay=false +local.min.requests=10 +local.max.requests=20 +local.core.connections=30 +local.max.connections=40 +remote.min.requests=5 +remote.max.requests=10 +remote.core.connections=15 +remote.max.connections=20 From bfcb48fb63b4ef85bbae7013b47d157662f4f134 Mon Sep 17 00:00:00 2001 From: David Webb Date: Tue, 21 Jan 2014 15:56:43 -0500 Subject: [PATCH 5/8] Merged DATACASS-71 to master. --- ...tyPlaceholderNamespaceCreatingXmlConfigTest.java | 13 +++++-------- ...holderNamespaceCreatingXmlConfigTest-context.xml | 2 +- .../config/CassandraNamespaceTests-context.xml | 2 +- .../UserRepositoryIntegrationTests-context.xml | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest.java index eebe70ac6..93d8bd205 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest.java @@ -9,7 +9,7 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cassandra.core.CassandraOperations; -import org.springframework.cassandra.test.integration.AbstractEmbeddedCassandraIntegrationTest; +import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; import org.springframework.cassandra.test.integration.config.IntegrationTestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -18,15 +18,10 @@ import com.datastax.driver.core.Session; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class PropertyPlaceholderNamespaceCreatingXmlConfigTest extends AbstractEmbeddedCassandraIntegrationTest { +public class PropertyPlaceholderNamespaceCreatingXmlConfigTest extends AbstractKeyspaceCreatingIntegrationTest { private static Logger log = LoggerFactory.getLogger(PropertyPlaceholderNamespaceCreatingXmlConfigTest.class); - @Override - protected String keyspace() { - return "ppncxcx"; - } - @Inject Session s; @@ -36,8 +31,10 @@ public class PropertyPlaceholderNamespaceCreatingXmlConfigTest extends AbstractE @Test public void test() { IntegrationTestUtils.assertSession(s); - IntegrationTestUtils.assertKeyspaceExists(keyspace(), s); + + IntegrationTestUtils.assertKeyspaceExists("ppncxct", s); assertNotNull(ops); } + } 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 81d7ff8ce..81fbb2471 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 @@ -53,7 +53,7 @@ send-buffer-size="${socket.sendBufferSize}" /> - + 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 56723073e..c536d25c6 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 @@ -20,7 +20,7 @@ min-simultaneous-requests="25" max-simultaneous-requests="100" core-connections="1" max-connections="2" /> diff --git a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryIntegrationTests-context.xml b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryIntegrationTests-context.xml index 3601a030b..0b7d750d8 100644 --- a/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryIntegrationTests-context.xml +++ b/spring-data-cassandra/src/test/resources/org/springframework/data/cassandra/test/integration/repository/UserRepositoryIntegrationTests-context.xml @@ -22,7 +22,7 @@ min-simultaneous-requests="25" max-simultaneous-requests="100" core-connections="1" max-connections="2" /> From 03600bf4518a409bb9d962ce80f68e0145fc6775 Mon Sep 17 00:00:00 2001 From: Matthew Adams Date: Wed, 22 Jan 2014 09:46:50 -0600 Subject: [PATCH 6/8] DATACASS-73 - no longer assuming any port numbers are available --- pom.xml | 48 +++++++++++++++++++ shared/test/resources/build.properties | 2 + ...tractEmbeddedCassandraIntegrationTest.java | 5 +- .../test/integration/config/java/Config.java | 4 +- .../java/KeyspaceCreatingJavaConfig.java | 4 +- .../integration/config/xml/XmlConfigTest.java | 2 +- .../support/AbstractTestJavaConfig.java | 16 +++++++ .../integration/support/BuildProperties.java | 45 +++++++++++++++++ .../src/test/resources/logback-test.xml | 2 +- ...dKeyspaceCreatingXmlConfigTest-context.xml | 4 +- ...lKeyspaceCreatingXmlConfigTest-context.xml | 5 +- .../xml/MinimalXmlConfigTest-context.xml | 5 +- .../config/xml/XmlConfigTest-context.xml | 5 +- .../src/test/resources/spring-cassandra.yaml | 8 ++-- .../CassandraNamespaceTests-context.xml | 2 +- .../integration/config/cassandra.properties | 6 +-- ...UserRepositoryIntegrationTests-context.xml | 2 +- .../repository/cassandra.properties | 6 +-- 18 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 shared/test/resources/build.properties create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/AbstractTestJavaConfig.java create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/BuildProperties.java diff --git a/pom.xml b/pom.xml index 1dad257bd..e2a6daf93 100644 --- a/pom.xml +++ b/pom.xml @@ -230,6 +230,28 @@ + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + reserve-network-port + + reserve-network-port + + process-resources + + + cassandra.native_transport_port + cassandra.rpc_port + cassandra.storage_port + cassandra.ssl_storage_port + + + + + org.apache.maven.plugins maven-dependency-plugin @@ -285,5 +307,31 @@ + + + + src/main/resources + true + + **/* + + + + + + src/test/resources + true + + **/* + + + + ../shared/test/resources + true + + **/* + + + diff --git a/shared/test/resources/build.properties b/shared/test/resources/build.properties new file mode 100644 index 000000000..94b576b8a --- /dev/null +++ b/shared/test/resources/build.properties @@ -0,0 +1,2 @@ +cassandra.native_transport_port=@cassandra.native_transport_port@ +cassandra.rpc_port=@cassandra.rpc_port@ diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java index 3315c021a..12c3dc4ed 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java @@ -8,6 +8,7 @@ import org.cassandraunit.utils.EmbeddedCassandraServerHelper; import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cassandra.test.integration.support.BuildProperties; import org.springframework.cassandra.test.unit.support.Utils; import com.datastax.driver.core.Cluster; @@ -22,9 +23,11 @@ public class AbstractEmbeddedCassandraIntegrationTest { static Logger log = LoggerFactory.getLogger(AbstractEmbeddedCassandraIntegrationTest.class); + protected static final BuildProperties PROPS = new BuildProperties("/build.properties"); + protected static final String CASSANDRA_CONFIG = "spring-cassandra.yaml"; protected static final String CASSANDRA_HOST = "localhost"; - protected static final int CASSANDRA_NATIVE_PORT = 9042; + protected static final int CASSANDRA_NATIVE_PORT = PROPS.getInt("cassandra.native_transport_port"); /** * The session connected to the system keyspace. diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/Config.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/Config.java index 7768526a0..502fc574d 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/Config.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/Config.java @@ -1,10 +1,10 @@ package org.springframework.cassandra.test.integration.config.java; -import org.springframework.cassandra.config.java.AbstractCassandraConfiguration; import org.springframework.context.annotation.Configuration; +import org.springframework.cassandra.test.integration.support.AbstractTestJavaConfig; @Configuration -public class Config extends AbstractCassandraConfiguration { +public class Config extends AbstractTestJavaConfig { @Override protected String getKeyspaceName() { diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfig.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfig.java index 4b0f9be88..3db7d94f4 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfig.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/java/KeyspaceCreatingJavaConfig.java @@ -4,13 +4,13 @@ import java.util.ArrayList; import java.util.List; import org.springframework.cassandra.config.KeyspaceAttributes; -import org.springframework.cassandra.config.java.AbstractCassandraConfiguration; import org.springframework.cassandra.core.keyspace.CreateKeyspaceSpecification; import org.springframework.cassandra.core.keyspace.KeyspaceOption; +import org.springframework.cassandra.test.integration.support.AbstractTestJavaConfig; import org.springframework.context.annotation.Configuration; @Configuration -public class KeyspaceCreatingJavaConfig extends AbstractCassandraConfiguration { +public class KeyspaceCreatingJavaConfig extends AbstractTestJavaConfig { public static final String KEYSPACE_NAME = "foo"; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java index 28300bb12..7f77c3791 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest.java @@ -12,7 +12,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.datastax.driver.core.Session; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration +@ContextConfiguration(locations = "classpath:/org/springframework/cassandra/test/integration/config/xml/XmlConfigTest-context.xml") public class XmlConfigTest extends AbstractKeyspaceCreatingIntegrationTest { public static final String KEYSPACE = "xmlconfigtest"; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/AbstractTestJavaConfig.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/AbstractTestJavaConfig.java new file mode 100644 index 000000000..6a3cfacd1 --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/AbstractTestJavaConfig.java @@ -0,0 +1,16 @@ +package org.springframework.cassandra.test.integration.support; + +import org.springframework.cassandra.config.java.AbstractCassandraConfiguration; +import org.springframework.context.annotation.Configuration; + +@Configuration +public abstract class AbstractTestJavaConfig extends AbstractCassandraConfiguration { + + public static BuildProperties PROPS = new BuildProperties("/build.properties"); + public static final int PORT = PROPS.getInt("cassandra.native_transport_port"); + + @Override + protected int getPort() { + return PORT; + } +} diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/BuildProperties.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/BuildProperties.java new file mode 100644 index 000000000..acee7c001 --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/support/BuildProperties.java @@ -0,0 +1,45 @@ +package org.springframework.cassandra.test.integration.support; + +import java.io.InputStream; +import java.util.Properties; + +@SuppressWarnings("serial") +public class BuildProperties extends Properties { + + public BuildProperties() { + this("build.properties"); + } + + public BuildProperties(String resourceName) { + + InputStream in = null; + try { + in = getClass().getResourceAsStream(resourceName); + if (in == null) { + return; + } + load(in); + + } catch (Exception x) { + throw new RuntimeException(x); + + } finally { + if (in != null) { + try { + in.close(); + } catch (Exception e) { + // gulp + } + } + } + } + + public int getInt(String key) { + String property = getProperty(key); + return Integer.parseInt(property); + } + + public boolean getBoolean(String key) { + return Boolean.parseBoolean(getProperty(key)); + } +} diff --git a/spring-cassandra/src/test/resources/logback-test.xml b/spring-cassandra/src/test/resources/logback-test.xml index 7db55523f..94db3089c 100644 --- a/spring-cassandra/src/test/resources/logback-test.xml +++ b/spring-cassandra/src/test/resources/logback-test.xml @@ -9,7 +9,7 @@ - + diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml index 24b776df2..d8074169c 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml @@ -8,9 +8,9 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + location="classpath:build.properties,classpath:/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest.properties" /> - + - + + + diff --git a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest-context.xml index e0939fdc3..7b8d56381 100644 --- a/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest-context.xml +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/MinimalXmlConfigTest-context.xml @@ -6,7 +6,10 @@ 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"> - + + + 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 1a31974f8..7e0d32808 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 @@ -6,8 +6,11 @@ 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"> + + + contactPoints="localhost" port="${cassandra.native_transport_port}"> diff --git a/spring-cassandra/src/test/resources/spring-cassandra.yaml b/spring-cassandra/src/test/resources/spring-cassandra.yaml index 67c59aba4..aba533b09 100644 --- a/spring-cassandra/src/test/resources/spring-cassandra.yaml +++ b/spring-cassandra/src/test/resources/spring-cassandra.yaml @@ -303,11 +303,11 @@ trickle_fsync: false trickle_fsync_interval_in_kb: 10240 # TCP port, for commands and data -storage_port: 7000 +storage_port: ${cassandra.storage_port} # SSL port, for encrypted communication. Unused unless enabled in # encryption_options -ssl_storage_port: 7001 +ssl_storage_port: ${cassandra.ssl_storage_port} # Address to bind to and tell other Cassandra nodes to connect to. You # _must_ change this if you want multiple nodes to be able to @@ -334,7 +334,7 @@ listen_address: localhost # same as the rpc_address. The port however is different and specified below. start_native_transport: true # port for the CQL native transport to listen for clients on -native_transport_port: 9042 +native_transport_port: ${cassandra.native_transport_port} # The minimum and maximum threads for handling requests when the native # transport is used. They are similar to rpc_min_threads and rpc_max_threads, # though the defaults differ slightly. @@ -352,7 +352,7 @@ start_rpc: true # (i.e. it will be based on the configured hostname of the node). rpc_address: localhost # port for Thrift to listen for clients on -rpc_port: 9160 +rpc_port: ${cassandra.rpc_port} # enable or disable keepalive on rpc connections rpc_keepalive: true 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 56723073e..9bbfdf681 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,7 +11,7 @@ location="classpath:/org/springframework/data/cassandra/test/integration/config/cassandra.properties" /> Date: Wed, 22 Jan 2014 10:02:58 -0600 Subject: [PATCH 7/8] DATACASS-73 - added the rest of the network ports to build.properties --- shared/test/resources/build.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/test/resources/build.properties b/shared/test/resources/build.properties index 94b576b8a..bd0ef161e 100644 --- a/shared/test/resources/build.properties +++ b/shared/test/resources/build.properties @@ -1,2 +1,4 @@ cassandra.native_transport_port=@cassandra.native_transport_port@ cassandra.rpc_port=@cassandra.rpc_port@ +cassandra.storage_port=@cassandra.storage_port@ +cassandra.ssl_storage_port=@cassandra.ssl_storage_port@ From d348f353220b3b094da3093fcc5ca50f3e8b6f75 Mon Sep 17 00:00:00 2001 From: David Webb Date: Wed, 22 Jan 2014 13:44:28 -0500 Subject: [PATCH 8/8] DATACASS-38 - Cluster Connection Listener Added Host.StateListener Added LatencyTracker Added SSL/SSLOptions --- .../config/CassandraClusterFactoryBean.java | 52 ++++++++++ .../config/xml/CassandraClusterParser.java | 30 +++++- .../cassandra/config/spring-cassandra-1.0.xsd | 99 +++++++++++++++---- ...tractEmbeddedCassandraIntegrationTest.java | 2 +- .../config/xml/TestHostStateListener.java | 52 ++++++++++ .../config/xml/TestLatencyTracker.java | 37 +++++++ ...NamespaceCreatingXmlConfigTest-context.xml | 8 +- .../integration/config/xml/ppncxct.properties | 1 + 8 files changed, 255 insertions(+), 26 deletions(-) create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestHostStateListener.java create mode 100644 spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestLatencyTracker.java diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java index 69453baec..acb7375af 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/CassandraClusterFactoryBean.java @@ -39,8 +39,11 @@ import org.springframework.util.StringUtils; import com.datastax.driver.core.AuthProvider; import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Host; +import com.datastax.driver.core.LatencyTracker; import com.datastax.driver.core.PoolingOptions; import com.datastax.driver.core.ProtocolOptions.Compression; +import com.datastax.driver.core.SSLOptions; import com.datastax.driver.core.Session; import com.datastax.driver.core.SocketOptions; import com.datastax.driver.core.policies.LoadBalancingPolicy; @@ -61,6 +64,7 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia public static final boolean DEFAULT_METRICS_ENABLED = true; public static final boolean DEFAULT_DEFERRED_INITIALIZATION = false; public static final boolean DEFAULT_JMX_REPORTING_ENABLED = true; + public static final boolean DEFAULT_SSL_ENABLED = false; public static final int DEFAULT_PORT = 9042; protected static final Logger log = LoggerFactory.getLogger(CassandraClusterFactoryBean.class); @@ -84,6 +88,10 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia private boolean metricsEnabled = DEFAULT_METRICS_ENABLED; private boolean deferredInitialization = DEFAULT_DEFERRED_INITIALIZATION; private boolean jmxReportingEnabled = DEFAULT_JMX_REPORTING_ENABLED; + private boolean sslEnabled = DEFAULT_SSL_ENABLED; + private SSLOptions sslOptions; + private Host.StateListener hostStateListener; + private LatencyTracker latencyTracker; private Set> keyspaceSpecifications = new HashSet>(); private List keyspaceCreations = new ArrayList(); private List keyspaceDrops = new ArrayList(); @@ -167,8 +175,24 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia builder.withoutJMXReporting(); } + if (sslEnabled) { + if (sslOptions == null) { + builder.withSSL(); + } else { + builder.withSSL(sslOptions); + } + } + cluster = builder.build(); + if (hostStateListener != null) { + cluster.register(hostStateListener); + } + + if (latencyTracker != null) { + cluster.register(latencyTracker); + } + generateSpecificationsFromFactoryBeans(); executeSpecsAndScripts(keyspaceCreations, startupScripts); @@ -373,4 +397,32 @@ public class CassandraClusterFactoryBean implements FactoryBean, Initia public void setJmxReportingEnabled(boolean jmxReportingEnabled) { this.jmxReportingEnabled = jmxReportingEnabled; } + + /** + * @param sslEnabled The sslEnabled to set. + */ + public void setSslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + /** + * @param sslOptions The sslOptions to set. + */ + public void setSslOptions(SSLOptions sslOptions) { + this.sslOptions = sslOptions; + } + + /** + * @param hostStateListener The hostStateListener to set. + */ + public void setHostStateListener(Host.StateListener hostStateListener) { + this.hostStateListener = hostStateListener; + } + + /** + * @param latencyTracker The latencyTracker to set. + */ + public void setLatencyTracker(LatencyTracker latencyTracker) { + this.latencyTracker = latencyTracker; + } } diff --git a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java index aafdd3a38..b750d4faf 100644 --- a/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/xml/CassandraClusterParser.java @@ -110,11 +110,6 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { builder.addPropertyValue("compressionType", compression); } - String authProvider = element.getAttribute("auth-info-provider-ref"); - if (StringUtils.hasText(authProvider)) { - builder.addPropertyReference("authProvider", authProvider); - } - String username = element.getAttribute("username"); if (StringUtils.hasText(username)) { builder.addPropertyValue("username", username); @@ -140,6 +135,16 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { builder.addPropertyValue("jmxReportingEnabled", jmxReportingEnabled); } + String sslEnabled = element.getAttribute("sslEnabled"); + if (StringUtils.hasText(sslEnabled)) { + builder.addPropertyValue("sslEnabled", sslEnabled); + } + + String authProvider = element.getAttribute("auth-info-provider-ref"); + if (StringUtils.hasText(authProvider)) { + builder.addPropertyReference("authProvider", authProvider); + } + String loadBalancingPolicy = element.getAttribute("load-balancing-policy-ref"); if (StringUtils.hasText(loadBalancingPolicy)) { builder.addPropertyReference("loadBalancingPolicy", loadBalancingPolicy); @@ -155,6 +160,21 @@ public class CassandraClusterParser extends AbstractBeanDefinitionParser { builder.addPropertyReference("retryPolicy", retryPolicy); } + String sslOptions = element.getAttribute("ssl-options-ref"); + if (StringUtils.hasText(sslOptions)) { + builder.addPropertyReference("sslOptions", sslOptions); + } + + String hostStateListener = element.getAttribute("host-state-listener-ref"); + if (StringUtils.hasText(hostStateListener)) { + builder.addPropertyReference("hostStateListener", hostStateListener); + } + + String latencyTracker = element.getAttribute("latency-tracker-ref"); + if (StringUtils.hasText(latencyTracker)) { + builder.addPropertyReference("latencyTracker", latencyTracker); + } + parseChildElements(element, context, builder); } diff --git a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd index 45bb9ddfc..6eed171ab 100644 --- a/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd +++ b/spring-cassandra/src/main/resources/org/springframework/cassandra/config/spring-cassandra-1.0.xsd @@ -136,23 +136,6 @@ The protocol compression option. Default is "NONE". - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -479,7 +540,7 @@ Provides the ability to specify replication factors by data center. default="SimpleStrategy"> diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java index 3315c021a..6cbf0563a 100644 --- a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/AbstractEmbeddedCassandraIntegrationTest.java @@ -22,7 +22,7 @@ public class AbstractEmbeddedCassandraIntegrationTest { static Logger log = LoggerFactory.getLogger(AbstractEmbeddedCassandraIntegrationTest.class); - protected static final String CASSANDRA_CONFIG = "spring-cassandra.yaml"; + protected static String CASSANDRA_CONFIG = "spring-cassandra.yaml"; protected static final String CASSANDRA_HOST = "localhost"; protected static final int CASSANDRA_NATIVE_PORT = 9042; diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestHostStateListener.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestHostStateListener.java new file mode 100644 index 000000000..9ea86c48c --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestHostStateListener.java @@ -0,0 +1,52 @@ +/* + * Copyright 2011-2014 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.test.integration.config.xml; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.datastax.driver.core.Host; +import com.datastax.driver.core.Host.StateListener; + +/** + * @author David Webb + * + */ +public class TestHostStateListener implements StateListener { + + private final static Logger log = LoggerFactory.getLogger(TestHostStateListener.class); + + @Override + public void onAdd(Host host) { + log.info("Host Added: " + host.getAddress()); + } + + @Override + public void onUp(Host host) { + log.info("Host Up: " + host.getAddress()); + } + + @Override + public void onDown(Host host) { + log.info("Host Down: " + host.getAddress()); + } + + @Override + public void onRemove(Host host) { + log.info("Host Removed: " + host.getAddress()); + } + +} diff --git a/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestLatencyTracker.java b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestLatencyTracker.java new file mode 100644 index 000000000..221e408be --- /dev/null +++ b/spring-cassandra/src/test/java/org/springframework/cassandra/test/integration/config/xml/TestLatencyTracker.java @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2014 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.test.integration.config.xml; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.datastax.driver.core.Host; +import com.datastax.driver.core.LatencyTracker; + +/** + * @author David Webb + * + */ +public class TestLatencyTracker implements LatencyTracker { + + private final static Logger log = LoggerFactory.getLogger(TestLatencyTracker.class); + + @Override + public void update(Host host, long newLatencyNanos) { + log.info("Latency Tracker: " + host.getAddress() + ", " + newLatencyNanos + " nanoseconds."); + } + +} 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 81fbb2471..317317470 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 @@ -21,6 +21,10 @@ + + + + + retry-policy-ref="retryPolicy" + host-state-listener-ref="hostStateListener" + latency-tracker-ref="latencyTracker">