From c75218387b1cabd3055fc41816eccdfa04f45587 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Tue, 24 May 2011 02:08:17 -0400 Subject: [PATCH] DATADOC-42 - Provide option for configuring replica sets using the Mongo namespace --- .../InvalidMongoDbApiUsageException.java | 31 ++++++ .../UncategorizedMongoDbException.java | 27 ++++++ .../mongodb/config/MongoDbFactoryParser.java | 3 +- .../document/mongodb/config/MongoParser.java | 54 +++-------- .../document/mongodb/config/ParsingUtils.java | 97 +++++++++++++++++++ .../mongodb/config/spring-mongo-1.0.xsd | 2 +- .../config/MongoNamespaceReplicaSetTests.java | 66 +++++++++++++ .../mongodb/config/MongoNamespaceTests.java | 22 +---- .../mongodb/config/NamespaceTestSupport.java | 43 ++++++++ .../MongoNamespaceReplicaSetTests-context.xml | 17 ++++ 10 files changed, 296 insertions(+), 66 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/InvalidMongoDbApiUsageException.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/UncategorizedMongoDbException.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/ParsingUtils.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/NamespaceTestSupport.java create mode 100644 spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests-context.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/InvalidMongoDbApiUsageException.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/InvalidMongoDbApiUsageException.java new file mode 100644 index 000000000..046e7ad28 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/InvalidMongoDbApiUsageException.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb; + +import org.springframework.dao.InvalidDataAccessApiUsageException; + +public class InvalidMongoDbApiUsageException extends InvalidDataAccessApiUsageException { + + public InvalidMongoDbApiUsageException(String msg) { + super(msg); + } + + public InvalidMongoDbApiUsageException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/UncategorizedMongoDbException.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/UncategorizedMongoDbException.java new file mode 100644 index 000000000..6d35f8a0c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/UncategorizedMongoDbException.java @@ -0,0 +1,27 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb; + +import org.springframework.dao.UncategorizedDataAccessException; + +public class UncategorizedMongoDbException extends UncategorizedDataAccessException { + + public UncategorizedMongoDbException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoDbFactoryParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoDbFactoryParser.java index 87b1961ed..c487ef73d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoDbFactoryParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoDbFactoryParser.java @@ -90,7 +90,8 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { mongoBuilder.addPropertyValue("host", (StringUtils.hasText(overrideHost) ? overrideHost : host)); String overridePort = mongoEl.getAttribute("port"); mongoBuilder.addPropertyValue("port", (StringUtils.hasText(overridePort) ? overridePort : port)); - new MongoParser().parseOptions(parserContext, mongoEl, mongoBuilder); + ParsingUtils.parseMongoOptions(parserContext, mongoEl, mongoBuilder); + ParsingUtils.parseReplicaSet(parserContext, mongoEl, mongoBuilder); } else { mongoBuilder.addPropertyValue("host", host); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoParser.java index 33c020b14..91910d144 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoParser.java @@ -19,6 +19,7 @@ package org.springframework.data.document.mongodb.config; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.document.mongodb.MongoFactoryBean; @@ -27,6 +28,8 @@ import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; +import com.mongodb.ServerAddress; + /** * Parser for <mongo;gt; definitions. If no name * @@ -42,46 +45,17 @@ public class MongoParser extends AbstractSingleBeanDefinitionParser { protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { super.doParse(element, builder); - setPropertyValue(element, builder, "port", "port"); - setPropertyValue(element, builder, "host", "host"); + ParsingUtils.setPropertyValue(element, builder, "port", "port"); + ParsingUtils.setPropertyValue(element, builder, "host", "host"); - parseOptions(parserContext, element, builder); + ParsingUtils.parseMongoOptions(parserContext, element, builder); + ParsingUtils.parseReplicaSet(parserContext, element, builder); } - /** - * Parses the options sub-element. Populates the given attribute factory with the proper attributes. - * - * @param element - * @param attrBuilder - * @return true if parsing actually occured, false otherwise - */ - boolean parseOptions(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) { - Element optionsElement = DomUtils.getChildElementByTagName(element, "options"); - if (optionsElement == null) - return false; - - BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder - .genericBeanDefinition(MongoOptionsFactoryBean.class); - - setPropertyValue(optionsElement, optionsDefBuilder, "connections-per-host", "connectionsPerHost"); - setPropertyValue(optionsElement, optionsDefBuilder, "threads-allowed-to-block-for-connection-multiplier", - "threadsAllowedToBlockForConnectionMultiplier"); - setPropertyValue(optionsElement, optionsDefBuilder, "max-wait-time", "maxWaitTime"); - setPropertyValue(optionsElement, optionsDefBuilder, "connect-timeout", "connectTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "socket-timeout", "socketTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive"); - setPropertyValue(optionsElement, optionsDefBuilder, "auto-connect-retry", "autoConnectRetry"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-timeout", "writeTimeout"); - setPropertyValue(optionsElement, optionsDefBuilder, "write-fsync", "writeFsync"); - setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk"); - - - - mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); - return true; - } + + + @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) @@ -93,11 +67,5 @@ public class MongoParser extends AbstractSingleBeanDefinitionParser { return name; } - private void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName, String propertyName) { - String attr = element.getAttribute(attrName); - if (StringUtils.hasText(attr)) { - builder.addPropertyValue(propertyName, attr); - } - } - + } \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/ParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/ParsingUtils.java new file mode 100644 index 000000000..7b0893107 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/ParsingUtils.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 by the original author(s). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.data.document.mongodb.MongoOptionsFactoryBean; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +import com.mongodb.ServerAddress; + +abstract class ParsingUtils { + + /** + * Parses the mongo replica-set element. + * @param parserContext the parser context + * @param element the mongo element + * @param mongoBuilder the bean definition builder to populate + * @return true if parsing actually occured, false otherwise + */ + static boolean parseReplicaSet(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) { + + + String replicaSetString = element.getAttribute("replica-set"); + if (StringUtils.hasText(replicaSetString)) { + ManagedList serverAddresses = new ManagedList(); + String[] replicaSetStringArray = StringUtils.commaDelimitedListToStringArray(replicaSetString); + for (int i = 0; i < replicaSetStringArray.length; i++) { + String[] hostAndPort = StringUtils.delimitedListToStringArray(replicaSetStringArray[i], ":"); + BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(ServerAddress.class); + defBuilder.addConstructorArgValue(hostAndPort[0]); + defBuilder.addConstructorArgValue(hostAndPort[1]); + serverAddresses.add(defBuilder.getBeanDefinition()); + } + if (!serverAddresses.isEmpty()) { + mongoBuilder.addPropertyValue("replicaSetSeeds", serverAddresses); + } + } + return true; + + } + /** + * Parses the mongo:options sub-element. Populates the given attribute factory with the proper attributes. + * + * @return true if parsing actually occured, false otherwise + */ + static boolean parseMongoOptions(ParserContext parserContext, Element element, BeanDefinitionBuilder mongoBuilder) { + Element optionsElement = DomUtils.getChildElementByTagName(element, "options"); + if (optionsElement == null) + return false; + + BeanDefinitionBuilder optionsDefBuilder = BeanDefinitionBuilder + .genericBeanDefinition(MongoOptionsFactoryBean.class); + + setPropertyValue(optionsElement, optionsDefBuilder, "connections-per-host", "connectionsPerHost"); + setPropertyValue(optionsElement, optionsDefBuilder, "threads-allowed-to-block-for-connection-multiplier", + "threadsAllowedToBlockForConnectionMultiplier"); + setPropertyValue(optionsElement, optionsDefBuilder, "max-wait-time", "maxWaitTime"); + setPropertyValue(optionsElement, optionsDefBuilder, "connect-timeout", "connectTimeout"); + setPropertyValue(optionsElement, optionsDefBuilder, "socket-timeout", "socketTimeout"); + setPropertyValue(optionsElement, optionsDefBuilder, "socket-keep-alive", "socketKeepAlive"); + setPropertyValue(optionsElement, optionsDefBuilder, "auto-connect-retry", "autoConnectRetry"); + setPropertyValue(optionsElement, optionsDefBuilder, "write-number", "writeNumber"); + setPropertyValue(optionsElement, optionsDefBuilder, "write-timeout", "writeTimeout"); + setPropertyValue(optionsElement, optionsDefBuilder, "write-fsync", "writeFsync"); + setPropertyValue(optionsElement, optionsDefBuilder, "slave-ok", "slaveOk"); + + + + mongoBuilder.addPropertyValue("mongoOptions", optionsDefBuilder.getBeanDefinition()); + return true; + } + + static void setPropertyValue(Element element, BeanDefinitionBuilder builder, String attrName, String propertyName) { + String attr = element.getAttribute(attrName); + if (StringUtils.hasText(attr)) { + builder.addPropertyValue(propertyName, attr); + } + } +} diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/document/mongodb/config/spring-mongo-1.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/document/mongodb/config/spring-mongo-1.0.xsd index 4d82c8db5..61ab9ff8d 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/document/mongodb/config/spring-mongo-1.0.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/document/mongodb/config/spring-mongo-1.0.xsd @@ -283,7 +283,7 @@ The host to connect to a MongoDB server. Default is localhost The comma delimited list of host:port entries to use for replica set/pairs. ]]> - + diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests.java new file mode 100644 index 000000000..7607c34f7 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.config; + +import static org.junit.Assert.*; + +import java.lang.reflect.Field; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.data.document.mongodb.MongoFactoryBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mongodb.Mongo; +import com.mongodb.MongoOptions; +import com.mongodb.ServerAddress; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class MongoNamespaceReplicaSetTests extends NamespaceTestSupport { + + @Autowired + private ApplicationContext ctx; + + + @Test + public void testMongoWithReplicaSets() throws Exception { + assertTrue(ctx.containsBean("replicaSetMongo")); + MongoFactoryBean mfb = (MongoFactoryBean) ctx.getBean("&replicaSetMongo"); + String host = readField("host", mfb); + Integer port = readField("port", mfb); + List replicaSetSeeds = readField("replicaSetSeeds", mfb); + assertNotNull(replicaSetSeeds); + + assertEquals("127.0.0.1", replicaSetSeeds.get(0).getHost()); + assertEquals(27017, replicaSetSeeds.get(0).getPort()); + + assertEquals("localhost", replicaSetSeeds.get(1).getHost()); + assertEquals(27018, replicaSetSeeds.get(1).getPort()); + + Mongo mongo = mfb.getObject(); + + //TODO test infrastructure to have replica sets + //assertEquals(2, mongo.getAllAddress().size()); + + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceTests.java index f42b9614e..433f21e05 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/MongoNamespaceTests.java @@ -18,8 +18,6 @@ package org.springframework.data.document.mongodb.config; import static org.junit.Assert.*; -import java.lang.reflect.Field; - import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -33,7 +31,7 @@ import com.mongodb.MongoOptions; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class MongoNamespaceTests { +public class MongoNamespaceTests extends NamespaceTestSupport { @Autowired private ApplicationContext ctx; @@ -81,23 +79,5 @@ public class MongoNamespaceTests { } - @SuppressWarnings({ "unchecked" }) - public static T readField(String name, Object target) throws Exception { - Field field = null; - Class clazz = target.getClass(); - do { - try { - field = clazz.getDeclaredField(name); - } catch (Exception ex) { - } - - clazz = clazz.getSuperclass(); - } while (field == null && !clazz.equals(Object.class)); - if (field == null) - throw new IllegalArgumentException("Cannot find field '" + name + "' in the class hierarchy of " - + target.getClass()); - field.setAccessible(true); - return (T) field.get(target); - } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/NamespaceTestSupport.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/NamespaceTestSupport.java new file mode 100644 index 000000000..69d0b097a --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/config/NamespaceTestSupport.java @@ -0,0 +1,43 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.document.mongodb.config; + +import java.lang.reflect.Field; + +public class NamespaceTestSupport { + + + @SuppressWarnings({ "unchecked" }) + public static T readField(String name, Object target) throws Exception { + Field field = null; + Class clazz = target.getClass(); + do { + try { + field = clazz.getDeclaredField(name); + } catch (Exception ex) { + } + + clazz = clazz.getSuperclass(); + } while (field == null && !clazz.equals(Object.class)); + + if (field == null) + throw new IllegalArgumentException("Cannot find field '" + name + "' in the class hierarchy of " + + target.getClass()); + field.setAccessible(true); + return (T) field.get(target); + } +} diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests-context.xml new file mode 100644 index 000000000..4e5c2159c --- /dev/null +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/config/MongoNamespaceReplicaSetTests-context.xml @@ -0,0 +1,17 @@ + + + + + + +