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..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 @@ -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; @@ -37,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; @@ -51,32 +52,39 @@ 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 { 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; - /** - * Comma-delimited string of servers. + /* + * Attributes needed for cluster builder */ 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(); private List startupScripts = new ArrayList(); @@ -119,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) { @@ -147,14 +155,44 @@ 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(); + 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 +205,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(); @@ -229,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; } @@ -295,52 +329,48 @@ 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; + /** + * @return Returns the keyspaceSpecifications. + */ + public Set> getKeyspaceSpecifications() { + return keyspaceSpecifications; } - private static SocketOptions configSocketOptions(SocketOptionsConfig config) { - SocketOptions socketOptions = new SocketOptions(); + /** + * 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; + } - 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()); - } + /** + * @param username The username to set. + */ + public void setUsername(String username) { + this.username = username; + } - return socketOptions; + /** + * @param password The password to set. + */ + 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/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..e44d16587 --- /dev/null +++ b/spring-cassandra/src/main/java/org/springframework/cassandra/config/KeyspaceActionSpecificationFactoryBean.java @@ -0,0 +1,267 @@ +/* + * 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.LinkedList; +import java.util.List; +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.KeyspaceOption.ReplicationStrategy; +import org.springframework.cassandra.core.keyspace.Option; +import org.springframework.util.Assert; + +/** + * 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>>, + InitializingBean, DisposableBean { + + private final static Logger log = LoggerFactory.getLogger(KeyspaceActionSpecificationFactoryBean.class); + + private KeyspaceAction action; + private String name; + private List networkTopologyDataCenters = new LinkedList(); + private List networkTopologyReplicationFactors = new LinkedList(); + private String replicationStrategy; + private long replicationFactor; + private boolean durableWrites = false; + private boolean ifNotExists = false; + + private Set> specs = new HashSet>(); + + @Override + public void destroy() throws Exception { + action = null; + name = null; + networkTopologyDataCenters = null; + networkTopologyReplicationFactors = null; + replicationStrategy = 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: + // Assert.notNull(replicationStrategy, "Replication Strategy is required to create a Keyspace"); + specs.add(generateCreateKeyspaceSpecification()); + break; + case ALTER: + break; + } + + } + + /** + * Generate a {@link CreateKeyspaceSpecification} for the keyspace. + * + * @return The {@link CreateKeyspaceSpecification} + */ + private CreateKeyspaceSpecification generateCreateKeyspaceSpecification() { + + CreateKeyspaceSpecification create = new CreateKeyspaceSpecification(); + create.name(name).ifNotExists(ifNotExists).with(KeyspaceOption.DURABLE_WRITES, durableWrites); + + 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()); + 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 durableWrites. + */ + public boolean isDurableWrites() { + return durableWrites; + } + + /** + * @param durableWrites The durableWrites to set. + */ + public void setDurableWrites(boolean durableWrites) { + this.durableWrites = durableWrites; + } + + /** + * @return Returns the replicationStrategy. + */ + public String getReplicationStrategy() { + return replicationStrategy; + } + + /** + * @param replicationStrategy The replicationStrategy to set. + */ + public void setReplicationStrategy(String replicationStrategy) { + this.replicationStrategy = replicationStrategy; + } + + /** + * @return Returns the networkTopologyDataCenters. + */ + public List 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/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/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 b9f3bb682..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,10 +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.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; @@ -17,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; @@ -42,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()); @@ -71,7 +68,7 @@ public abstract class AbstractCassandraConfiguration { return Collections.emptyList(); } - protected SocketOptionsConfig getSocketOptions() { + protected SocketOptions getSocketOptions() { return null; } @@ -87,7 +84,7 @@ public abstract class AbstractCassandraConfiguration { return null; } - protected PoolingOptionsConfig getRemotePoolingOptions() { + protected PoolingOptions getPoolingOptions() { return null; } @@ -99,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 b931fb325..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 @@ -15,44 +15,47 @@ */ package org.springframework.cassandra.config.xml; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import static org.springframework.data.config.ParsingUtils.getSourceBeanDefinition; +import java.util.ArrayList; +import java.util.List; + +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.ManagedList; +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.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.Option; +import org.springframework.cassandra.config.MultiLevelSetFlattenerFactoryBean; +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.util.Assert; 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.HostDistance; +import com.datastax.driver.core.PoolingOptions; +import com.datastax.driver.core.SocketOptions; /** - * Parser for <cluster;gt; definitions. + * Parses the {@literal } element of the XML Configuration. * - * @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 String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) @@ -63,7 +66,34 @@ 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(); + } + + /** + * 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"); if (StringUtils.hasText(contactPoints)) { @@ -77,41 +107,98 @@ 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)) { + 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); } - protected void parseChildElements(BeanDefinitionBuilder builder, Element element) { + /** + * 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) { - 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 + /* + * 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)) { - 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,123 +206,169 @@ public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser { } } - builder.addPropertyValue("keyspaceCreations", creates); - builder.addPropertyValue("keyspaceDrops", drops); + /* + * 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); builder.addPropertyValue("shutdownScripts", startupScripts); } - protected KeyspaceSpecifications parseKeyspace(Element element) { + /** + * Create the Single Factory Bean that will flatten all List> + * + * @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) { - 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 {@link BeanDefinitionBuilder}. + * + * @param element The Element being parsed + * @param builder The {@link BeanDefinitionBuilder} to add the replication to + */ + protected void parseReplication(Element element, BeanDefinitionBuilder builder) { + ManagedList networkTopologyDataCenters = new ManagedList(); + ManagedList networkTopologyReplicationFactors = new ManagedList(); String strategyClass = null; + String replicationFactor = null; + if (element != null) { + strategyClass = element.getAttribute("class"); - } - if (strategyClass == null || (strategyClass = strategyClass.trim()).length() == 0) { - 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; - } - - 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 (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), - dataCenter.getAttribute("replication-factor")); + if (!StringUtils.hasText(strategyClass)) { + strategyClass = KeyspaceAttributes.DEFAULT_REPLICATION_STRATEGY; } + + replicationFactor = element.getAttribute("replication-factor"); + if (!StringUtils.hasText(replicationFactor)) { + replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR + ""; + } + + /* + * DataCenters only apply to NetworkTolopogyStrategy + */ + List dcElements = DomUtils.getChildElementsByTagName(element, "data-center"); + for (Element dataCenter : dcElements) { + networkTopologyDataCenters.add(dataCenter.getAttribute("name")); + networkTopologyReplicationFactors.add(dataCenter.getAttribute("replication-factor")); + } + } else { + strategyClass = ReplicationStrategy.SIMPLE_STRATEGY.name(); + replicationFactor = KeyspaceAttributes.DEFAULT_REPLICATION_FACTOR + ""; } - return create.with(KeyspaceOption.REPLICATION, 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); } - protected static class KeyspaceSpecifications { + /** + * Returns a {@link BeanDefinition} for a {@link KeyspaceActionSpecification} object. + * + * @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) { - public KeyspaceSpecifications(CreateKeyspaceSpecification create, DropKeyspaceSpecification drop) { - this.create = create; - this.drop = drop; - } + String action = element.getAttribute("action"); - public CreateKeyspaceSpecification create; - public DropKeyspaceSpecification drop; - // TODO: public AlterKeyspaceSpecification alter; + Assert.notNull(action, "Keyspace Action must not be null!"); + + BeanDefinitionBuilder keyspaceBuilder = BeanDefinitionBuilder + .genericBeanDefinition(KeyspaceActionSpecificationFactoryBean.class); + + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "name", "name"); + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "action", "action"); + ParsingUtils.setPropertyValue(keyspaceBuilder, element, "durableWrites", "durableWrites"); + + Element replicationElement = DomUtils.getChildElementByTagName(element, "replication"); + 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..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,32 +127,16 @@ The native CQL port to connect to. Default is 9042. ]]> - + - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - @@ -285,7 +298,7 @@ More connections are created up to a configurable maximum number of connections. - + + + + + + - + - + - - - - - - - - - - - - - - - - - - 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/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/FullySpecifiedKeyspaceCreatingXmlConfigTest-context.xml index 24b776df2..7958b7eba 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 @@ -11,18 +11,16 @@ location="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/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 new file mode 100644 index 000000000..81d7ff8ce --- /dev/null +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/PropertyPlaceholderNamespaceCreatingXmlConfigTest-context.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..2c52e09dd 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 @@ -15,7 +15,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 new file mode 100644 index 000000000..b8d517dc4 --- /dev/null +++ b/spring-cassandra/src/test/resources/org/springframework/cassandra/test/integration/config/xml/ppncxct.properties @@ -0,0 +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