Saturday, June 22, 2013

Spring MongoDB Auditing and Extensions

In this blog I will explain how to do update of audit fields (create/modified) and version in MongoDB using spring-data-mongodb library. I would also show how to workaround some of the limitations. 


A Model can be made Auditable by one of the following ways.
  1. Model implementing interface.
  2. use @CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate annotations defined in package
For maintaining version use @Version annotation.

To make it simple, create a base class as shown below  which all Model classes will extend.

 public class Audit implements Auditable<String,String> {  
      private String id;  
      private Long version;  
      private String createdBy;  
      private DateTime createdDate;  
      private String lastModifiedBy;  
      private DateTime lastModifiedDate;  
      public boolean isNew() {  
      /**Assuming the Id is generated by database. If not, then need to override in each model depending on how to identify if a Model is a new one or already saved Model.  
           return id == null;  
 // remaining getters & setters.  
 public class MyModel extends Audit{  
   private String firstName;  
      private String lastName;  
 //getters & setters.  

In above, isNew() is crucial and used to differentiate if a model is new or already saved model. The auditing listeners will use this method to decide if created fields to be updated or not. The above implementation is on assumption that the Ids are generated by DB and hence it will be NULL for models created newly.

We also require to define beans which provide auditing principal and current time.

Auditing principal

The auditing principal can be obtained by implementing the interface A sample implementation is as below.

 public class MyAuditor implements AuditorAware<String> {  
      public String getCurrentAuditor() {  
 //If you are using spring-security, you may get this from SecurityContext.  
           return "System";  

Date Provider

To get the auditing time, we need an implemenation of interface Spring provides a defulat implementation for current time is

Spring configuration to make all this work is defined as below.

 <mongo:auditing auditor-aware-ref="myAuditor" date-time-provider-ref="datTimeProvider"/>  
      <bean id="myAuditor" class="poorna.samples.mongo.audit.MyAuditor"/>  
      <bean id="datTimeProvider" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">  
           <property name="staticField" value=""></property>  
This configuration will create listener bean which listens for BeforConvertEvent that is fired by MongoTemplate(MongoOperations) during save operations.


  1. If due to some reason <mongo:auditing> is included multiple times in your application context, then spring creates multiple instances of AuditingEventListener i.e., the audit update logic gets executed multiple times. This happens as the spring NameSpaceHandler creates a different listener bean each time.  
  2. MongoTemplate fires BeforeConvert event only during save and insert operations i.e., the AuditEventListener and hence the audit fields are updated only if the whole entity is being saved (either by insert or update). But MongoTemplate provides many other methods like update*(**) and findAndModify(**) methods which will update the model, but not the audit and version.

Custom AuditEventListener

The first gap can be overcome by creating custom AuditEventListener and registering this bean instead of <mongo:auditing>. A Sample implementation is defined as below.
 public class MyAuditingEventListner implements  
           ApplicationListener<BeforeConvertEvent<Object>> {  
      private AuditorAware<String> auditorAware;  
      private DateTimeProvider dateTimeProvider;  
      public MyAuditingEventListner(AuditorAware<String> auditorAware,  
                DateTimeProvider dateTimeProvider) {  
           this.auditorAware = auditorAware;  
           this.dateTimeProvider = dateTimeProvider;  
      public void onApplicationEvent(BeforeConvertEvent<Object> event) {  
           Object obj = event.getSource();  
           if (Audit.class.isAssignableFrom(obj.getClass())) {  
                Audit entity = (Audit) obj;  
                if (entity.isNew()) {  

Following is updated bean configuration. Note that we no longer require <mongo:auditing>. As bean id is specified for MyAuditEventListener, the bean will be registered only once even if the file containing this bean is imported multiple times.

 <bean id="myAuditor" class="poorna.samples.mongo.audit.myAuditor" />  
      <bean id="dateTimeProvider"  
           <property name="staticField"  
      <bean id="myAuditingEventListener"  
           <constructor-arg ref="myAuditor"></constructor-arg>  
           <constructor-arg ref="dateTimeProvider"></constructor-arg>  

Aspects for Audit Update

The second gap can be overcome by writing some Aspects that inject the audit fields before invoking update methods of MongoTemplate. Following is implementation for Aspect.

 public class MyMongoAuditAspect {  
      private AuditorAware<String> auditorAware;  
      private DateTimeProvider dateTimeProvider;  
 @Before(value = "(execution(public **(..)) ||"  
                + "execution(public * && args(query,update,.. )")  
      public void setAuditFields(Query query, Update update) {  
           if (update != null) {  
      “version”, 1);  
                update.set(“lastModifiedBy”, auditorAware.getCurrentAuditor())  
                     .set(“lastModifiedDate”, dateTimeProvider.getDateTime());  

Note the following from above Aspect code.

  • The Aspect works on MongoOperations interface and hence on MongoTemplate.
  • PointCut expression specifies to work on all update and findAndModify methods.
  • This is @Before aspect and updates Update object with auditing fields.
  • The same dateTimeProvider and myAuditor used in MyAuditingEventListener are used in MyMongoAuditAspect also.
The application context should be configured to register this aspect as bean and also configured for AOP using <aop:aspectj-autoproxy/>


I have tried to explain how to achieve auditing while using spring framework for mongodb. I have also explained with code snippets on how AOP can be used to fill some gaps. I have explained AOP for few methods. The same can be extended to work for upsert method and also for update method when upsert flag is true.