diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java new file mode 100644 index 000000000..cce1cd9af --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/EnableMongoAuditing.java @@ -0,0 +1,60 @@ +/* + * Copyright 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.data.mongodb.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; +import org.springframework.data.auditing.DateTimeProvider; + +/** + * Annotation to enable auditing in MongoDB via annotation configuration. + * + * @author Thomas Darimont + */ +@Inherited +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import(MongoAuditingRegistrar.class) +public @interface EnableMongoAuditing { + + /** + * @return References a bean of type AuditorAware to represent the current principal. + */ + String auditorAwareRef() default ""; + + /** + * @return Configures whether the creation and modification dates are set and defaults to {@literal true}. + */ + boolean setDates() default true; + + /** + * @return Configures whether the entity shall be marked as modified on creation and defaults to {@literal true}. + */ + boolean modifyOnCreate() default true; + + /** + * @return Configures a {@link DateTimeProvider} that allows customizing which DateTime shall be used for setting + * creation and modification dates. + */ + String dateTimeProviderRef() default ""; +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java new file mode 100644 index 000000000..299a942dc --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingRegistrar.java @@ -0,0 +1,100 @@ +/* + * Copyright 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.data.mongodb.config; + +import java.lang.annotation.Annotation; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.auditing.config.AnnotationAuditingConfiguration; +import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport; +import org.springframework.data.mapping.context.MappingContextIsNewStrategyFactory; +import org.springframework.data.mongodb.core.mapping.event.AuditingEventListener; +import org.springframework.data.support.IsNewStrategyFactory; +import org.springframework.util.Assert; + +/** + * {@link ImportBeanDefinitionRegistrar} to enable {@link EnableMongoAuditing} annotation. + * + * @author Thomas Darimont + */ +class MongoAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { + + /* (non-Javadoc) + * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation() + */ + @Override + protected Class getAnnotation() { + return EnableMongoAuditing.class; + } + + /* (non-Javadoc) + * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry) + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { + + Assert.notNull(annotationMetadata, "annotationMetadata must not be null!"); + Assert.notNull(annotationMetadata, "registry must not be null!"); + + registerIsNewStrategyFactoryIfNecessary(registry); + super.registerBeanDefinitions(annotationMetadata, registry); + } + + /* (non-Javadoc) + * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AnnotationAuditingConfiguration) + */ + @Override + protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AnnotationAuditingConfiguration configuration) { + + Assert.notNull(configuration, "configuration must not be null!"); + + return configureDefaultAuditHandlerAttributes(configuration, + BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class)).addConstructorArgReference( + BeanNames.IS_NEW_STRATEGY_FACTORY); + } + + /* (non-Javadoc) + * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry) + */ + @Override + protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition, + BeanDefinitionRegistry registry) { + + Assert.notNull(auditingHandlerDefinition, "auditingHandlerDefinition must not be null!"); + Assert.notNull(registry, "registry must not be null!"); + + registerInfrastructureBeanWithId(BeanDefinitionBuilder.rootBeanDefinition(AuditingEventListener.class) + .addConstructorArgValue(auditingHandlerDefinition).getRawBeanDefinition(), + AuditingEventListener.class.getName(), registry); + } + + /** + * @param registry, the {@link BeanDefinitionRegistry} to use to register an {@link IsNewStrategyFactory} to. + */ + private void registerIsNewStrategyFactoryIfNecessary(BeanDefinitionRegistry registry) { + + if (!registry.containsBeanDefinition(BeanNames.IS_NEW_STRATEGY_FACTORY)) { + registry.registerBeanDefinition(BeanNames.IS_NEW_STRATEGY_FACTORY, + BeanDefinitionBuilder.rootBeanDefinition(MappingContextIsNewStrategyFactory.class) + .addConstructorArgReference(BeanNames.MAPPING_CONTEXT).getBeanDefinition()); + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java new file mode 100644 index 000000000..493f7f2f0 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/AuditingViaJavaConfigRepositoriesTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 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.data.mongodb.config; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.AuditorAware; +import org.springframework.data.mongodb.core.AuditablePerson; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.SimpleMongoDbFactory; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.stereotype.Repository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mongodb.MongoClient; + +/** + * Integration tests for auditing via Java config. + * + * @author Thomas Darimont + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class AuditingViaJavaConfigRepositoriesTests { + + @Autowired AuditablePersonRepository auditablePersonRepository; + @Autowired AuditorAware auditorAware; + AuditablePerson auditor; + + @Configuration + @EnableMongoAuditing(auditorAwareRef = "auditorProvider") + @EnableMongoRepositories(basePackageClasses = AuditablePersonRepository.class, considerNestedRepositories = true) + static class Config { + + @Bean + public MongoOperations mongoTemplate() throws Exception { + return new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "database")); + } + + @Bean + public MongoMappingContext mappingContext() { + return new MongoMappingContext(); + } + + @Bean + public AuditorAware auditorProvider() { + return mock(AuditorAware.class); + } + } + + @Before + public void setup() { + auditablePersonRepository.deleteAll(); + this.auditor = auditablePersonRepository.save(new AuditablePerson("auditor")); + } + + @Test + public void basicAuditing() { + + doReturn(this.auditor).when(this.auditorAware).getCurrentAuditor(); + + AuditablePerson user = new AuditablePerson("user"); + + AuditablePerson savedUser = auditablePersonRepository.save(user); + System.out.println(savedUser); + + AuditablePerson createdBy = savedUser.getCreatedBy(); + assertThat(createdBy, is(notNullValue())); + assertThat(createdBy.getFirstname(), is(this.auditor.getFirstname())); + } + + @Repository + static interface AuditablePersonRepository extends MongoRepository {} +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java new file mode 100644 index 000000000..622138130 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/AuditablePerson.java @@ -0,0 +1,62 @@ +/* + * Copyright 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.data.mongodb.core; + +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.DBRef; + +/** + * Domain class for auditing functionality testing. + * + * @author Thomas Darimont + */ +public class AuditablePerson { + + @Id private String id; + private String firstname; + @DBRef @CreatedBy private AuditablePerson createdBy; + + public AuditablePerson() {} + + public AuditablePerson(String firstName) { + this.firstname = firstName; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstName) { + this.firstname = firstName; + } + + public AuditablePerson getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(AuditablePerson createdBy) { + this.createdBy = createdBy; + } +} diff --git a/src/docbkx/reference/mongodb.xml b/src/docbkx/reference/mongodb.xml index 4cd0ed4d9..4f21b1e85 100644 --- a/src/docbkx/reference/mongodb.xml +++ b/src/docbkx/reference/mongodb.xml @@ -571,6 +571,66 @@ public class MongoConfiguration { +
+ General auditing configuration + + Activating auditing functionality is just a matter of adding the + Spring Data Mongo auditing namespace element to your + configuration: + + + Activating auditing in the Spring configuration + + <mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/> + + + + Auditing via Java Config + + Since Spring Data MongoDB 1.4 Auditing can be enabled by + annotating a configuration class with the + EnableMongoAuditing annotation. + + @Configuration +@EnableMongoAuditing(auditorAwareRef="myAuditorProvider") +@EnableMongoRepositories +class Config { + + @Bean + public AuditorAware<AuditableUser> myAuditorProvider() { + return new AuditorAwareImpl(); + } +} + + Note that if you want to record information about the create-User + or modify-User, you can delcare a bean that implements the + AuditorAware interface. The bean name can also be + configured explicitly via the auditorAwareRef attribute of + the EnableMongoAuditing annotation, this can be + used to resolve an ambiguity. If no auditorAwareRef is + specified explicitly we try to discover and autowire an + AuditorAware implementation. + + + As you can see you have to provide a bean that implements the + AuditorAware interface which looks as + follows: + + + <interfacename>AuditorAware</interfacename> interface + + public interface AuditorAware<T, ID extends Serializable> { + + T getCurrentAuditor(); +} + + + Usually you will have some kind of authentication component in your + application that tracks the user currently working with the system. This + component should be AuditorAware and thus + allow seamless tracking of the auditor. +
+
Introduction to MongoTemplate @@ -2465,8 +2525,8 @@ project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, b In this introductory example we want to aggregate a list of tags to get the occurence count of a particular tag from a MongoDB - collection called "tags" sorted by the occurrence count in - descending order. This example demonstrates the usage of grouping, + collection called "tags" sorted by the occurrence count + in descending order. This example demonstrates the usage of grouping, sorting, projections (selection) and unwinding (result splitting).