From b22412fa98dcee45c0c0681d5a8c9935f5c50b2e Mon Sep 17 00:00:00 2001 From: Jon Brisbin Date: Tue, 22 Mar 2011 15:14:43 -0500 Subject: [PATCH 1/6] Fixed a bug introduced in the last commit with importing a morphia exception class, added a Log4J appender module for MongoDB. --- spring-data-log4j-appender/pom.xml | 163 ++++++++++++++++++ .../mongodb/log4j/MongoLog4jAppender.java | 162 +++++++++++++++++ .../document/mongodb/log4j/AppenderTest.java | 71 ++++++++ .../src/test/resources/log4j.properties | 17 ++ .../mapping/MappingConfigurationHelper.java | 2 +- 5 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 spring-data-log4j-appender/pom.xml create mode 100644 spring-data-log4j-appender/src/main/java/org/springframework/data/document/mongodb/log4j/MongoLog4jAppender.java create mode 100644 spring-data-log4j-appender/src/test/java/org/springframework/data/document/mongodb/log4j/AppenderTest.java create mode 100644 spring-data-log4j-appender/src/test/resources/log4j.properties diff --git a/spring-data-log4j-appender/pom.xml b/spring-data-log4j-appender/pom.xml new file mode 100644 index 000000000..d73e5e185 --- /dev/null +++ b/spring-data-log4j-appender/pom.xml @@ -0,0 +1,163 @@ + + 4.0.0 + + org.springframework.data + spring-data-document-parent + 1.0.0.BUILD-SNAPSHOT + ../spring-data-document-parent/pom.xml + + spring-data-log4j-appender + jar + Spring Data MongoDB Log4J Appender + + + 2.3 + + + + + + + org.springframework + spring-beans + + + org.springframework + spring-tx + + + org.springframework + spring-expression + + + org.springframework + spring-test + test + + + + + org.springframework.data + spring-data-document-core + ${project.version} + + + org.springframework.data + spring-data-commons-core + ${data.commons.version} + + + + + org.mongodb + mongo-java-driver + ${mongo.version} + + + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + compile + + + org.slf4j + slf4j-log4j12 + runtime + + + log4j + log4j + + + javax.mail + mail + + + javax.jms + jms + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + compile + + + + + org.mockito + mockito-all + test + + + + org.hamcrest + hamcrest-all + 1.1 + test + + + + junit + junit + test + + + + joda-time + joda-time + 1.6 + test + + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + + + + com.mysema.maven + maven-apt-plugin + 1.0 + + + generate-test-sources + + test-process + + + target/generated-sources/test-annotations + org.springframework.data.document.mongodb.repository.MongoAnnotationProcessor + + + + + + + + + + querydsl + Mysema QueryDsl + http://source.mysema.com/maven2/releases + + false + + + + diff --git a/spring-data-log4j-appender/src/main/java/org/springframework/data/document/mongodb/log4j/MongoLog4jAppender.java b/spring-data-log4j-appender/src/main/java/org/springframework/data/document/mongodb/log4j/MongoLog4jAppender.java new file mode 100644 index 000000000..0620be138 --- /dev/null +++ b/spring-data-log4j-appender/src/main/java/org/springframework/data/document/mongodb/log4j/MongoLog4jAppender.java @@ -0,0 +1,162 @@ +/* + * 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.log4j; + +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Map; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.Mongo; +import com.mongodb.WriteConcern; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.spi.LoggingEvent; + +/** + * @author Jon Brisbin + */ +public class MongoLog4jAppender extends AppenderSkeleton { + + public static final String LEVEL = "level"; + public static final String NAME = "name"; + public static final String TIMESTAMP = "timestamp"; + public static final String PROPERTIES = "properties"; + public static final String TRACEBACK = "traceback"; + public static final String MESSAGE = "message"; + + protected String host = "localhost"; + protected int port = 27017; + protected String database = "logs"; + protected String collection = null; + protected Mongo mongo; + protected DB db; + + public MongoLog4jAppender() { + } + + public MongoLog4jAppender(boolean isActive) { + super(isActive); + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public String getCollection() { + return collection; + } + + public void setCollection(String collection) { + this.collection = collection; + } + + protected void connectToMongo() throws UnknownHostException { + this.mongo = new Mongo(host, port); + this.db = mongo.getDB(database); + } + + @SuppressWarnings({"unchecked"}) + @Override + protected void append(final LoggingEvent event) { + if (null == db) { + try { + connectToMongo(); + } catch (UnknownHostException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + BasicDBObject dbo = new BasicDBObject(); + dbo.put(NAME, event.getLogger().getName()); + dbo.put(LEVEL, event.getLevel().toString()); + dbo.put(TIMESTAMP, String.format("%s", event.getTimeStamp())); + + // Copy properties into document + Map props = event.getProperties(); + if (null != props && props.size() > 0) { + BasicDBObject propsDbo = new BasicDBObject(); + for (Map.Entry entry : props.entrySet()) { + propsDbo.put(entry.getKey().toString(), entry.getValue().toString()); + } + dbo.put(PROPERTIES, propsDbo); + } + + // Copy traceback info (if there is any) into the document + String[] traceback = event.getThrowableStrRep(); + if (null != traceback && traceback.length > 0) { + BasicDBList tbDbo = new BasicDBList(); + tbDbo.addAll(Arrays.asList(traceback)); + dbo.put(TRACEBACK, tbDbo); + } + + // Put the rendered message into the document + dbo.put(MESSAGE, event.getRenderedMessage()); + + // Insert the document + if (null == collection) { + // Use the category name + collection = event.getLogger().getName(); + } else { + Calendar now = Calendar.getInstance(); + collection = String.format(collection, + now.get(Calendar.YEAR), + now.get(Calendar.MONTH), + now.get(Calendar.DAY_OF_MONTH), + now.get(Calendar.HOUR_OF_DAY), + event.getLevel().toString(), + event.getLogger().getName()); + } + + WriteConcern wc = WriteConcern.NORMAL; + if (event.getLevel().isGreaterOrEqual(Level.WARN)) { + wc = WriteConcern.SAFE; + } + db.getCollection(collection).insert(dbo, wc); + } + + public void close() { + mongo.close(); + } + + public boolean requiresLayout() { + return true; + } +} diff --git a/spring-data-log4j-appender/src/test/java/org/springframework/data/document/mongodb/log4j/AppenderTest.java b/spring-data-log4j-appender/src/test/java/org/springframework/data/document/mongodb/log4j/AppenderTest.java new file mode 100644 index 000000000..de0793e5c --- /dev/null +++ b/spring-data-log4j-appender/src/test/java/org/springframework/data/document/mongodb/log4j/AppenderTest.java @@ -0,0 +1,71 @@ +/* + * 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.log4j; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.net.UnknownHostException; + +import com.mongodb.DB; +import com.mongodb.DBCursor; +import com.mongodb.Mongo; +import org.apache.log4j.Logger; +import org.apache.log4j.MDC; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Jon Brisbin + */ +public class AppenderTest { + + private static final String NAME = AppenderTest.class.getName(); + private Logger log = Logger.getLogger(NAME); + private Mongo mongo; + private DB db; + + @Before + public void setup() { + try { + mongo = new Mongo("localhost", 27017); + db = mongo.getDB("logs"); + db.getCollection(NAME).drop(); + } catch (UnknownHostException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + @Test + public void testLogging() { + log.debug("DEBUG message"); + log.info("INFO message"); + log.warn("WARN message"); + log.error("ERROR message"); + + DBCursor msgs = db.getCollection(NAME).find(); + assertThat(msgs.count(), is(4)); + + } + + @Test + public void testProperties() { + MDC.put("property", "one"); + log.debug("DEBUG message"); + } + +} diff --git a/spring-data-log4j-appender/src/test/resources/log4j.properties b/spring-data-log4j-appender/src/test/resources/log4j.properties new file mode 100644 index 000000000..b21f9aece --- /dev/null +++ b/spring-data-log4j-appender/src/test/resources/log4j.properties @@ -0,0 +1,17 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n +log4j.appender.stdout.host = localhost +log4j.appender.stdout.port = 27017 +log4j.appender.stdout.database = logs + +log4j.category.org.apache.activemq=ERROR +log4j.category.org.springframework.batch=DEBUG +log4j.category.org.springframework.data.document.mongodb=DEBUG +log4j.category.org.springframework.transaction=INFO + +log4j.category.org.hibernate.SQL=DEBUG +# for debugging datasource initialization +# log4j.category.test.jdbc=DEBUG diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java index c347d2b63..c6eb716d3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import com.google.code.morphia.mapping.MappingException; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBObject; @@ -45,6 +44,7 @@ import org.springframework.data.document.mongodb.index.IndexDirection; import org.springframework.data.document.mongodb.index.Indexed; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.event.MappingContextEvent; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; From 39a323099f848068f6e8950e9537bebba8b54428 Mon Sep 17 00:00:00 2001 From: Jon Brisbin Date: Tue, 22 Mar 2011 15:24:14 -0500 Subject: [PATCH 2/6] Added README to document the Log4J appender. --- spring-data-log4j-appender/README.md | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 spring-data-log4j-appender/README.md diff --git a/spring-data-log4j-appender/README.md b/spring-data-log4j-appender/README.md new file mode 100644 index 000000000..3a96793b6 --- /dev/null +++ b/spring-data-log4j-appender/README.md @@ -0,0 +1,40 @@ +# MongoDB Log4J Appender + +This module sets up a Log4J appender that puts logging events in MongoDB. It is fully configurable +and connects directly to the MongoDB server using the driver. It has no dependency on any Spring package. + +To use it, configure a host, port, and database property in your Log4J configuration: + + log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender + log4j.appender.stdout.layout=org.apache.log4j.PatternLayout + log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + log4j.appender.stdout.host = localhost + log4j.appender.stdout.port = 27017 + log4j.appender.stdout.database = logs + +It will even support properties in your MDC (so long as they're Strings or support .toString()). + +The collection name is configurable as well. If you don't specify anything, it will use the Category name. +If you want to specify a collection name, you can give it a String.format() string which will be passed the +following parameters: + + 1. Calendar.YEAR + 2. Calendar.MONTH + 3. Calendar.DAY_OF_MONTH + 4. Calendar.HOUR_OF_DAY + 5. event.getLevel().toString() + 6. event.getLogger().getName() + +An example log entry might look like: + + { + "_id" : ObjectId("4d88ff0975cce9d7655bda46"), + "name" : "org.springframework.data.document.mongodb.log4j.AppenderTest", + "level" : "DEBUG", + "timestamp" : "1300823817891", + "properties" : { + "property" : "one" + }, + "message" : "DEBUG message" + } + From 18b0f459852755af7fb95c96b5873175109f6c2a Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 23 Mar 2011 07:41:04 +0100 Subject: [PATCH 3/6] Adapt latest changes in Spring Data Commons. Moved functionality from MongoMappingConfigurationBuilder into MongoMappingContext. Adapted namespace parser to the removal of the MappingConfigurationBuilder component. --- .../config/MongoMappingConverterParser.java | 38 ++++------ .../MongoMappingConfigurationBuilder.java | 71 ------------------- .../mongodb/mapping/MongoMappingContext.java | 55 +++++++++++--- .../mongodb/config/spring-mongo-1.0.xsd | 18 ----- .../mongodb/mapping/GenericMappingTests.java | 8 +-- .../MappingMongoConverterUnitTests.java | 6 -- ...rsonRepositoryIntegrationTests-context.xml | 6 +- 7 files changed, 62 insertions(+), 140 deletions(-) delete mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java index 93d3c7a2b..105d75a58 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java @@ -25,8 +25,8 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; -import org.springframework.data.document.mongodb.mapping.MappingConfigurationHelper; -import org.springframework.data.document.mongodb.mapping.MongoMappingConfigurationBuilder; +import org.springframework.data.document.mongodb.mapping.Document; +import org.springframework.data.document.mongodb.mapping.MongoPersistentEntityIndexCreator; import org.springframework.data.document.mongodb.mapping.MongoMappingContext; import org.w3c.dom.Element; @@ -39,10 +39,8 @@ import org.w3c.dom.Element; */ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { - private static final String CONFIGURATION_BUILDER = "mappingConfigurationBuilder"; private static final String MAPPING_CONTEXT = "mappingContext"; private static final String MAPPING_CONFIGURATION_HELPER = "mappingConfigurationHelper"; - private static final String CONFIGURATION_LISTENER = "mappingConfigurationListener"; private static final String TEMPLATE = "mongoTemplate"; private static final String BASE_PACKAGE = "base-package"; @@ -55,17 +53,15 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); - String builderRef = element.getAttribute("mapping-config-builder-ref"); - if (null == builderRef || "".equals(builderRef)) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingConfigurationBuilder.class); - registry.registerBeanDefinition(CONFIGURATION_BUILDER, builder.getBeanDefinition()); - builderRef = CONFIGURATION_BUILDER; - } - String ctxRef = element.getAttribute("mapping-context-ref"); - if (null == ctxRef || "".equals(ctxRef)) { + if (!StringUtils.hasText(ctxRef)) { BeanDefinitionBuilder mappingContextBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoMappingContext.class); - mappingContextBuilder.addPropertyReference("mappingConfigurationBuilder", builderRef); + + Set classesToAdd = getInititalEntityClasses(element, mappingContextBuilder); + if (classesToAdd != null) { + mappingContextBuilder.addPropertyValue("initialEntitySet", classesToAdd); + } + registry.registerBeanDefinition(MAPPING_CONTEXT, mappingContextBuilder.getBeanDefinition()); ctxRef = MAPPING_CONTEXT; } @@ -74,27 +70,21 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { converterBuilder.addPropertyReference("mappingContext", ctxRef); String autowire = element.getAttribute("autowire"); - if (null != autowire || !"".equals(autowire)) { + if (StringUtils.hasText(autowire)) { converterBuilder.addPropertyValue("autowirePersistentBeans", Boolean.parseBoolean(autowire)); } - // Need a reference to a MongoTemplate + // Need a reference to a Mongo instance String mongoRef = element.getAttribute("mongo-ref"); - if (null == mongoRef || "".equals(mongoRef)) { - mongoRef = "mongo"; - } - converterBuilder.addPropertyReference("mongo", mongoRef); + converterBuilder.addPropertyReference("mongo", StringUtils.hasText(mongoRef) ? mongoRef : "mongo"); try { registry.getBeanDefinition(MAPPING_CONFIGURATION_HELPER); } catch (NoSuchBeanDefinitionException ignored) { String templateRef = element.getAttribute("mongo-template-ref"); - if (null == templateRef || "".equals(templateRef)) { - templateRef = TEMPLATE; - } - BeanDefinitionBuilder mappingConfigHelperBuilder = BeanDefinitionBuilder.genericBeanDefinition(MappingConfigurationHelper.class); + BeanDefinitionBuilder mappingConfigHelperBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoPersistentEntityIndexCreator.class); mappingConfigHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(ctxRef)); - mappingConfigHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(templateRef)); + mappingConfigHelperBuilder.addConstructorArgValue(new RuntimeBeanReference(StringUtils.hasText(templateRef) ? templateRef : TEMPLATE)); registry.registerBeanDefinition(MAPPING_CONFIGURATION_HELPER, mappingConfigHelperBuilder.getBeanDefinition()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java deleted file mode 100644 index c75f4794c..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingConfigurationBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.mapping; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.util.Set; - -import org.bson.types.CodeWScope; -import org.bson.types.ObjectId; -import org.springframework.data.mapping.BasicMappingConfigurationBuilder; -import org.springframework.data.mapping.MappingBeanHelper; -import org.springframework.data.mapping.model.MappingConfigurationException; -import org.springframework.data.mapping.model.MappingContext; -import org.springframework.data.mapping.model.PersistentEntity; -import org.springframework.data.mapping.model.PersistentProperty; -import org.springframework.data.util.TypeInformation; - -/** - * @author Jon Brisbin - */ -public class MongoMappingConfigurationBuilder extends BasicMappingConfigurationBuilder { - - public MongoMappingConfigurationBuilder() { - augmentSimpleTypes(); - } - - protected void augmentSimpleTypes() { - // Augment simpleTypes with MongoDB-specific classes - Set> simpleTypes = MappingBeanHelper.getSimpleTypes(); - simpleTypes.add(com.mongodb.DBRef.class); - simpleTypes.add(ObjectId.class); - simpleTypes.add(CodeWScope.class); - } - - @Override - public PersistentProperty createPersistentProperty(Field field, - PropertyDescriptor descriptor, - TypeInformation information) throws MappingConfigurationException { - return new MongoPersistentProperty(field, descriptor, information); - } - - @Override - public PersistentEntity createPersistentEntity(TypeInformation typeInformation, - MappingContext mappingContext) throws MappingConfigurationException { - return new MongoPersistentEntity(mappingContext, typeInformation); - } - - @Override - public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { - if (field.isAnnotationPresent(DBRef.class)) { - return true; - } - return super.isAssociation(field, descriptor); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java index dabeca864..2766b3a03 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoMappingContext.java @@ -16,28 +16,65 @@ package org.springframework.data.document.mongodb.mapping; -import java.util.HashSet; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.util.Collection; import java.util.Set; +import org.bson.types.CodeWScope; +import org.bson.types.ObjectId; import org.springframework.data.mapping.BasicMappingContext; +import org.springframework.data.mapping.BasicPersistentEntity; +import org.springframework.data.mapping.BasicPersistentProperty; +import org.springframework.data.mapping.MappingBeanHelper; +import org.springframework.data.mapping.model.MappingConfigurationException; +import org.springframework.data.mapping.model.MappingContext; +import org.springframework.data.util.TypeInformation; /** * @author Jon Brisbin */ public class MongoMappingContext extends BasicMappingContext { - - protected Set initialEntitySet = new HashSet(); - + public MongoMappingContext() { - builder = new MongoMappingConfigurationBuilder(); + augmentSimpleTypes(); } - public Set getInitialEntitySet() { - return initialEntitySet; + protected void augmentSimpleTypes() { + // Augment simpleTypes with MongoDB-specific classes + Set> simpleTypes = MappingBeanHelper.getSimpleTypes(); + simpleTypes.add(com.mongodb.DBRef.class); + simpleTypes.add(ObjectId.class); + simpleTypes.add(CodeWScope.class); } - public void setInitialEntitySet(Set initialEntitySet) { - this.initialEntitySet = initialEntitySet; + @Override + public boolean isAssociation(Field field, PropertyDescriptor descriptor) throws MappingConfigurationException { + if (field.isAnnotationPresent(DBRef.class)) { + return true; + } + return super.isAssociation(field, descriptor); + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.BasicMappingContext#getPersistentEntities() + */ + @Override + @SuppressWarnings("unchecked") + public Collection> getPersistentEntities() { + return (Collection>) super.getPersistentEntities(); + } + + @Override + public BasicPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, + TypeInformation information) throws MappingConfigurationException { + return new MongoPersistentProperty(field, descriptor, information); + } + + @Override + public BasicPersistentEntity createPersistentEntity(TypeInformation typeInformation, MappingContext mappingContext) + throws MappingConfigurationException { + return new MongoPersistentEntity(mappingContext, typeInformation); } } 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 8ca4483de..bf8dc72c0 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 @@ -127,13 +127,6 @@ This controls whether or not to run entities through the BeanFactory's autowirin - - - - The reference to a MappingConfigurationBuilder. Will default to 'mappingConfigBuilder'. - - - @@ -167,17 +160,6 @@ The name of the Mongo object that determines what server to monitor. (by default - - - - - - - - - - - diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java index 2a6a20939..23f331425 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/GenericMappingTests.java @@ -24,9 +24,7 @@ import com.mongodb.DBObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.context.ApplicationContext; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter; import org.springframework.data.mapping.BasicMappingContext; @@ -42,13 +40,9 @@ public class GenericMappingTests { BasicMappingContext context; MongoConverter converter; - @Mock - ApplicationContext applicationContext; - @Before public void setUp() { - context = new BasicMappingContext(new MongoMappingConfigurationBuilder()); - context.setApplicationContext(applicationContext); + context = new BasicMappingContext(); context.addPersistentEntity(StringWrapper.class); converter = new MappingMongoConverter(context); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java index 29f534393..8b049e3a2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/mapping/MappingMongoConverterUnitTests.java @@ -26,9 +26,7 @@ import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.context.ApplicationContext; import org.springframework.core.convert.converter.Converter; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.mapping.BasicMappingContext; @@ -46,13 +44,9 @@ public class MappingMongoConverterUnitTests { MappingMongoConverter converter; BasicMappingContext mappingContext; - @Mock - ApplicationContext applicationContext; - @Before public void setUp() { mappingContext = new BasicMappingContext(); - mappingContext.setApplicationContext(applicationContext); converter = new MappingMongoConverter(mappingContext); } diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml index ddc714bfb..824b82942 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml @@ -11,11 +11,7 @@ - - - - - + From bfd79377ca6baaa5ba549830527854de4b942c0a Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 23 Mar 2011 07:45:25 +0100 Subject: [PATCH 4/6] Made MongoPersistentEntityIndexCreator an ApplicationListener again. MongoPersistentEntityIndexCreator now listens to MappingContextEvents to ensure indexes for added entities *after* the initial set had been added. --- .../mapping/MappingConfigurationHelper.java | 73 ++++++------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java index c6eb716d3..f2150192f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java @@ -30,10 +30,6 @@ import com.mongodb.MongoException; import com.mongodb.util.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.dao.DataAccessException; import org.springframework.data.document.mongodb.CollectionCallback; @@ -44,67 +40,42 @@ import org.springframework.data.document.mongodb.index.IndexDirection; import org.springframework.data.document.mongodb.index.Indexed; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.event.MappingContextEvent; -import org.springframework.data.mapping.model.MappingException; -import org.springframework.data.mapping.model.PersistentEntity; import org.springframework.data.mapping.model.PersistentProperty; +import org.springframework.util.Assert; /** + * Component that inspects {@link MongoPersistentEntity} instances contained in the given {@link MongoMappingContext} + * for indexing metadata and ensures the indexes to be available. + * * @author Jon Brisbin + * @author Oliver Gierke */ -public class MappingConfigurationHelper implements ApplicationListener, ApplicationContextAware, InitializingBean { +public class MongoPersistentEntityIndexCreator implements ApplicationListener{ - private static final Logger log = LoggerFactory.getLogger(MappingConfigurationHelper.class); + private static final Logger log = LoggerFactory.getLogger(MongoPersistentEntityIndexCreator.class); private Map compoundIndexes = new HashMap(); private Map fieldIndexes = new HashMap(); private Set> classesSeen = Collections.newSetFromMap(new ConcurrentHashMap, Boolean>()); - private ApplicationContext applicationContext; - private MongoMappingContext mappingContext; - private MongoTemplate mongoTemplate; + + private final MongoTemplate mongoTemplate; - public MappingConfigurationHelper(MongoMappingContext mappingContext, MongoTemplate mongoTemplate) { - this.mappingContext = mappingContext; + public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoTemplate mongoTemplate) { + + Assert.notNull(mongoTemplate); + Assert.notNull(mappingContext); this.mongoTemplate = mongoTemplate; + + for (MongoPersistentEntity entity : mappingContext.getPersistentEntities()) { + checkForIndexes(entity); + } } - - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - + + /* (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ public void onApplicationEvent(MappingContextEvent event) { - PersistentEntity entity = event.getPersistentEntity(); - if (entity instanceof MongoPersistentEntity) { - checkForIndexes((MongoPersistentEntity) entity); - } - } - - public void afterPropertiesSet() throws Exception { - for (String className : mappingContext.getInitialEntitySet()) { - try { - Class clazz = Class.forName(className); - if (null == mappingContext.getPersistentEntity(clazz)) { - mappingContext.addPersistentEntity(clazz); - } - } catch (ClassNotFoundException e) { - throw new MappingException(e.getMessage(), e); - } - } - } - - public MongoMappingContext getMappingContext() { - return mappingContext; - } - - public void setMappingContext(MongoMappingContext mappingContext) { - this.mappingContext = mappingContext; - } - - public MongoTemplate getMongoTemplate() { - return mongoTemplate; - } - - public void setMongoTemplate(MongoTemplate mongoTemplate) { - this.mongoTemplate = mongoTemplate; + checkForIndexes((MongoPersistentEntity) event.getPersistentEntity()); } protected void checkForIndexes(MongoPersistentEntity entity) { From 7d7cf60e2310b93010622450f3ea1ed54a53975c Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 23 Mar 2011 09:12:07 +0100 Subject: [PATCH 5/6] DATADOC-69 - Repository namespace now allows referenceing a MappingContext and picks up the default one if available. --- .../config/MongoMappingConverterParser.java | 5 +- ...MongoRepositoryConfigDefinitionParser.java | 51 +++++++++++++++---- .../SimpleMongoRepositoryConfiguration.java | 24 +++++++++ .../mongodb/config/spring-mongo-1.0.xsd | 7 +++ .../MongoNamespaceIntegrationTests.java | 16 ++++++ .../src/test/resources/mapping.xml | 2 - ...MongoNamespaceIntegrationTests-context.xml | 13 ++++- 7 files changed, 101 insertions(+), 17 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java index 105d75a58..220473d21 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java @@ -39,14 +39,15 @@ import org.w3c.dom.Element; */ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { - private static final String MAPPING_CONTEXT = "mappingContext"; + static final String MAPPING_CONTEXT = "mappingContext"; private static final String MAPPING_CONFIGURATION_HELPER = "mappingConfigurationHelper"; private static final String TEMPLATE = "mongoTemplate"; private static final String BASE_PACKAGE = "base-package"; @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - return "mappingConverter"; + String id = super.resolveId(element, definition, parserContext); + return StringUtils.hasText(id) ? id : "mappingConverter"; } @Override diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoRepositoryConfigDefinitionParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoRepositoryConfigDefinitionParser.java index a66f006c3..c4f39da30 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoRepositoryConfigDefinitionParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoRepositoryConfigDefinitionParser.java @@ -15,8 +15,11 @@ */ package org.springframework.data.document.mongodb.config; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.data.document.mongodb.config.SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration; +import org.springframework.data.mapping.model.MappingContext; import org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser; import org.w3c.dom.Element; @@ -30,7 +33,9 @@ import org.w3c.dom.Element; public class MongoRepositoryConfigDefinitionParser extends AbstractRepositoryConfigDefinitionParser { - + + private static final String MAPPING_CONTEXT_DEFAULT = MongoMappingConverterParser.MAPPING_CONTEXT; + /* * (non-Javadoc) * @@ -47,20 +52,44 @@ public class MongoRepositoryConfigDefinitionParser /* - * (non-Javadoc) - * - * @see org.springframework.data.repository.config. - * AbstractRepositoryConfigDefinitionParser - * #postProcessBeanDefinition(org.springframework - * .data.repository.config.SingleRepositoryConfigInformation, - * org.springframework.beans.factory.support.BeanDefinitionBuilder, - * java.lang.Object) - */ + * (non-Javadoc) + * @see org.springframework.data.repository.config.AbstractRepositoryConfigDefinitionParser#postProcessBeanDefinition(org.springframework.data.repository.config.SingleRepositoryConfigInformation, org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) + */ @Override protected void postProcessBeanDefinition( MongoRepositoryConfiguration context, - BeanDefinitionBuilder builder, Object beanSource) { + BeanDefinitionBuilder builder, BeanDefinitionRegistry registry, Object beanSource) { builder.addPropertyReference("template", context.getMongoTemplateRef()); + + String mappingContextRef = getMappingContextReference(context, registry); + if (mappingContextRef != null) { + builder.addPropertyReference("mappingContext", mappingContextRef); + } + } + + /** + * Returns the bean name of a {@link MappingContext} to be wired. Will inspect the namespace attribute first and if no + * config is found in that place it will try to lookup the default one. Will return {@literal null} if neither one is + * available. + * + * @param config + * @param registry + * @return + */ + private String getMappingContextReference(MongoRepositoryConfiguration config, BeanDefinitionRegistry registry) { + + String contextRef = config.getMappingContextRef(); + + if (contextRef != null) { + return contextRef; + } + + try { + registry.getBeanDefinition(MAPPING_CONTEXT_DEFAULT); + return MAPPING_CONTEXT_DEFAULT; + } catch(NoSuchBeanDefinitionException e) { + return null; + } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/SimpleMongoRepositoryConfiguration.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/SimpleMongoRepositoryConfiguration.java index 3e8d21620..68468c753 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/SimpleMongoRepositoryConfiguration.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/SimpleMongoRepositoryConfiguration.java @@ -36,6 +36,8 @@ public class SimpleMongoRepositoryConfiguration extends RepositoryConfig { String getMongoTemplateRef(); + + String getMappingContextRef(); } /** @@ -150,6 +160,13 @@ public class SimpleMongoRepositoryConfiguration extends RepositoryConfig + + + + The reference to a MappingContext. Will pick up a bean named 'mappingContext' by default if available. + + + diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests.java index 9dd7cebc2..5bfbb8d7c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests.java @@ -1,5 +1,12 @@ package org.springframework.data.document.mongodb.repository.config; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.core.io.ClassPathResource; import org.springframework.data.document.mongodb.repository.AbstractPersonRepositoryIntegrationTests; import org.springframework.test.context.ContextConfiguration; @@ -14,4 +21,13 @@ import org.springframework.test.context.ContextConfiguration; public class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryIntegrationTests { + + @Test + public void assertDefaultMappingContextIsWired() { + + XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("MongoNamespaceIntegrationTests-context.xml", getClass())); + BeanDefinition definition = factory.getBeanDefinition("personRepository"); + assertThat(definition, is(notNullValue())); + assertThat(definition.getPropertyValues().getPropertyValue("mappingContext"), is(notNullValue())); + } } diff --git a/spring-data-mongodb/src/test/resources/mapping.xml b/spring-data-mongodb/src/test/resources/mapping.xml index 17e054e62..309cac73d 100644 --- a/spring-data-mongodb/src/test/resources/mapping.xml +++ b/spring-data-mongodb/src/test/resources/mapping.xml @@ -16,6 +16,4 @@ - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml index fc0d95432..3b2cf0b2f 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml @@ -6,8 +6,17 @@ xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.0.xsd"> - - + + + + + + + + + + + From 261c1ae29358b59b49bfa33064a351aa4a1d1e8d Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 23 Mar 2011 09:27:50 +0100 Subject: [PATCH 6/6] Fixed merge conflicts after applying latest changes from master. --- .../config/MongoMappingConverterParser.java | 37 ++++++++++++++++--- ...=> MongoPersistentEntityIndexCreator.java} | 0 2 files changed, 31 insertions(+), 6 deletions(-) rename spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/{MappingConfigurationHelper.java => MongoPersistentEntityIndexCreator.java} (100%) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java index 220473d21..7c7c82068 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/config/MongoMappingConverterParser.java @@ -16,26 +16,31 @@ package org.springframework.data.document.mongodb.config; +import java.util.Set; + import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.mapping.Document; import org.springframework.data.document.mongodb.mapping.MongoPersistentEntityIndexCreator; import org.springframework.data.document.mongodb.mapping.MongoMappingContext; +import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** - * Created by IntelliJ IDEA. - * User: jbrisbin - * Date: 2/28/11 - * Time: 9:26 AM - * To change this template use File | Settings | File Templates. + * @author Jon Brisbin + * @author Oliver Gierke */ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { @@ -91,5 +96,25 @@ public class MongoMappingConverterParser extends AbstractBeanDefinitionParser { return converterBuilder.getBeanDefinition(); } - + + + public Set getInititalEntityClasses(Element element, BeanDefinitionBuilder builder) { + + String basePackage = element.getAttribute(BASE_PACKAGE); + + if (!StringUtils.hasText(basePackage)) { + return null; + } + + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class)); + componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + + Set classes = new ManagedSet(); + for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { + classes.add(candidate.getBeanClassName()); + } + + return classes; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java similarity index 100% rename from spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MappingConfigurationHelper.java rename to spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java