DATAMONGO-792 - Add support to configure auditing via JavaConfig.
Introduces the necessary infrastructure in form of MongoAuditingRegistrar to configure auditing with MongoDB via Annotation config. The MongoDB auditing feature can be enabled by annotating a configuration class with the EnableMongoAuditing annotation. Added section to reference documentation. Original pull request: #94.
This commit is contained in:
committed by
Oliver Gierke
parent
ea33e8b8c6
commit
e2d0220cea
@@ -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 "";
|
||||
}
|
||||
@@ -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<? extends Annotation> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<AuditablePerson> 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<AuditablePerson> 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<AuditablePerson, String> {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -571,6 +571,66 @@ public class MongoConfiguration {
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="mongo.auditing">
|
||||
<title>General auditing configuration</title>
|
||||
|
||||
<para>Activating auditing functionality is just a matter of adding the
|
||||
Spring Data Mongo <literal>auditing</literal> namespace element to your
|
||||
configuration:</para>
|
||||
|
||||
<example>
|
||||
<title>Activating auditing in the Spring configuration</title>
|
||||
|
||||
<programlisting language="xml"><mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/></programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Auditing via Java Config</title>
|
||||
|
||||
<para>Since Spring Data MongoDB 1.4 Auditing can be enabled by
|
||||
annotating a configuration class with the
|
||||
<classname>EnableMongoAuditing</classname> annotation.</para>
|
||||
|
||||
<programlisting language="java">@Configuration
|
||||
@EnableMongoAuditing(auditorAwareRef="myAuditorProvider")
|
||||
@EnableMongoRepositories
|
||||
class Config {
|
||||
|
||||
@Bean
|
||||
public AuditorAware<AuditableUser> myAuditorProvider() {
|
||||
return new AuditorAwareImpl();
|
||||
}
|
||||
}</programlisting>
|
||||
|
||||
<para>Note that if you want to record information about the create-User
|
||||
or modify-User, you can delcare a bean that implements the
|
||||
<classname>AuditorAware</classname> interface. The bean name can also be
|
||||
configured explicitly via the <code>auditorAwareRef</code> attribute of
|
||||
the <classname>EnableMongoAuditing</classname> annotation, this can be
|
||||
used to resolve an ambiguity. If no <code>auditorAwareRef</code> is
|
||||
specified explicitly we try to discover and autowire an
|
||||
<classname>AuditorAware</classname> implementation.</para>
|
||||
</example>
|
||||
|
||||
<para>As you can see you have to provide a bean that implements the
|
||||
<interfacename>AuditorAware</interfacename> interface which looks as
|
||||
follows:</para>
|
||||
|
||||
<example>
|
||||
<title><interfacename>AuditorAware</interfacename> interface</title>
|
||||
|
||||
<programlisting language="java">public interface AuditorAware<T, ID extends Serializable> {
|
||||
|
||||
T getCurrentAuditor();
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>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 <interfacename>AuditorAware</interfacename> and thus
|
||||
allow seamless tracking of the auditor.</para>
|
||||
</section>
|
||||
|
||||
<section id="mongo-template">
|
||||
<title>Introduction to MongoTemplate</title>
|
||||
|
||||
@@ -2465,8 +2525,8 @@ project("a","b").and("foo").as("bar") // will generate {$project: {a: 1, b: 1, b
|
||||
|
||||
<para>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 <code>"tags"</code> sorted by the occurrence count in
|
||||
descending order. This example demonstrates the usage of grouping,
|
||||
collection called <code>"tags"</code> sorted by the occurrence count
|
||||
in descending order. This example demonstrates the usage of grouping,
|
||||
sorting, projections (selection) and unwinding (result
|
||||
splitting).</para>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user