Monday, January 3, 2011

A transactional application often requires to audit the persistent entities. The reasons for this are different. Sometimes you simply have to be able to track down changes and build up a history of changes. Other frequent requirements are related to long term rollback features or even archiving in accordance with legal audit requirements. There are different approaches to auditing in general. Here is an excerpt of what I know, you could come up with more I am sure.

Change log
You only need to know, who changed which entity type.

Simple change tracking
You are only interested to know if something changed. Can audit a complete entity or any subset of information from it.

Complete history
You need a complete auditing about who changed what and when. Need to keep all old entity versions.

Complex change tracking
Need to know who changed which value to which new value and when. Even combined with additional requirements.

All of the above could be addressed with the below approaches. They generally differ by implementation complexity and performance. The more complex you auditing needs are the more impact you can expect on performance in general. You should also keep in mind, that depending on audit level and change frequency your data volume will grow very fast.
To implement it, there are a couple of technical options. Here are the five ways I would come up with, if you would ask me.

EJB Interceptors
You could use EJB Interceptors. All you need is a facade around your entity operations. Create an AditInterceptor and add it to your EJB methods. Adam Bien has a nice and simple example on his blog.

Pro: Simple programatical approach.
Con: This is not a very entity centric approach. You have to find the right repository facades and methods for auditing. If this is a usable approach depends on the amount of auditing needed.

CDI Interceptors
If you don't have EJBs in your application you could also use plain CDI interceptors. Refer to this older post of mine to see a simple example.

Pros/Cons: Same as EJB Interceptors.

JPA Event listeners
If you have plain JPA you could make your application audit changes via the life cycle callback methods. The JPA specification describes how an entity or a separate, stateless entity listener instance can receive callbacks during various life cycle state transition of an entity. Seven transitions are defined: @PrePersist, @PostPersist, @PreRemove, @PostRemove, @PreUpdate, @PostUpdate and @PostLoad. To receive these callbacks either annotate certain methods of the entity class itself or of a separate entity listener class. In addition to this, most of the JPA implementations also have some kind of listeners available, which could be used for this.

Pro: Simple programatical approach. Extended features available from the JPA implementations.
Con: You have to get hand on "old" entities. This is not too simple in general. And you have to understand the lifecycle methods needed here.

Hibernate EnversEnvers is library for Hibernate that help to easily achieve audit functionality. It has been created by Adam Warski and is part of Hibernate core since 3.5. Basically you annotate an Entity with @Audited to tell Envers that this class needs to be audited. All fields except those annotated with @NotAudited will be audited. You configure the org.hibernate.envers.event.AuditEventListener with any of the following types: post-insert, post-update, post-delete, pre-collection-update, pre-collection-remove, post-collection-recreate. The org.hibernate.envers.auditTablePrefix and org.hibernate.envers.auditTableSuffix properties are helping to add a prefix or suffix your audit tables. The audit tables contain a REV column, which specifies the object access type. 0 means creation, 1 means modification and 2 means deletion. You could even get an object with a previous version using the AuditReader.find() method.

Pro: Very easy to implement. Rich API to use.
Con: You more or less archive the complete entity. There is now way ootb to audit which attributes really changed. This is an very generic all or nothing approach.

Database triggers
You can for example use some generic "before update" triggers to compare new and old values and log the changes. (See this Ask Tom answer on how to do this.)

Pro: If you know how, it's very easy and right at the heart.
Con: You have a very high database dependency, so it's in general no good idea if you have to have a portable solution at hand.

Bottom line
Whatever requirements you have. I can promise you, that it will never be a simple solution. In general it's best to evaluate your purpose for auditing in detail. In order to have an appropriate auditing strategy and to avoid unnecessary auditing, you must have a clear understanding of the reasons for auditing. In order to prevent unnecessary audit information from cluttering the meaningful information, it is important to audit the minimum number of statements, users, or objects required to get the targeted information.