If you are using JPA 2.0 with Hibernate and you want to do audit logging from middle-ware itself, I believe you landed up on the exact place where you should be. You can try audit logging in your local environment by following this post.

Required JPA/Hibernate Maven Dependencies

<dependency>
         <groupId>org.hibernate.java-persistence</groupId>
         <artifactId>jpa-api</artifactId>
         <version>2.0-cr-1</version>
</dependency>
<dependency>
         <groupId>org.hibernate</groupId>
         <artifactId>hibernate-entitymanager</artifactId>
         <version>3.6.4.Final</version>   
</dependency>
<dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-orm</artifactId>
         <version>3.0.5.RELEASE</version>
</dependency>

JPA Configuration in Spring Application Context File

For JPA/Hibernate, you need to configure entity manager, transaction, data source and JPA vendor in your ‘applicationContext.xml’ file.

<bean id="dataSource"
         class="org.springframework.jdbc.datasource.DriverManagerDataSource"
         p:driverClassName="oracle.jdbc.driver.OracleDriver"  
        p:url="jdbc:oracle:thin:@127.0.0.1:1521/mydbservice "
         p:username="nvuser" p:password="nvpass" />

<bean id="entityManagerFactory"
         class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
         p:persistenceUnitName="MyPersistentUnit"
         p:persistenceXmlLocation="classpath*:persistence.xml"
         p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
         <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
         </property>              
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
                 p:entityManagerFactory-ref="entityManagerFactory" />

<bean id="jpaAdapter"
                 class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
                 p:database="ORACLE" p:showSql="true" />

Persistence.xml Configuration

To enable the hibernate audit logging you need to configure a property called ‘hibernate.ejb.interceptor’ in persistence.xml file and the value of this property should be the class that extends the Hibernate ‘EmptyInterceptor’.

xml version=”1.0” encoding=”UTF-8”?>
<persistence version="2.0"
         xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
         <persistence-unit name=" MyPersistentUnit ">
               
                 //JPA Entity classes are configured here
                 <class>com.mycom.entities.User </class>           

                 <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

                 <properties>
                 <property name="hibernate.ejb.interceptor"
                          value=" com.xxx.entities.baseentity.AuditLogInterceptor" />
                 </properties>
         </persistence-unit>
</persistence>

Audit Logging Interceptor Implementation

Hibernate provides an interceptor called ‘EmptyInterceptor’ with following methods.
  • onSave() – This method is called when an entity is saved, but the entity is not saved into database yet.
  • onFlushDirty() – This method is called when an entity is updated, the entity is not update into database yet.
  • onDelete () – This method is called when an entity is deleted , the entity is not deleted into database yet.
  • preFlush() – This method is called before the saved, updated or deleted entities are not committed to the database.
  • postFlush() – This method is called after the saved, updated or deleted entities are committed to database. This is called after preFlush() method.

To perform audit logging follow below steps:
  • Create data base table to capture audit data:
          Audit [ Row_ID, Old_Value, New_Value, Column_Changed, Table_Changed,
                    Transaction_Id, User_ID]
  • Create JPA entity say ‘AuditTrail’ for above table
  • Create class ‘AuditLogInterceptor’ by extending Hibernate ‘EmptyInterceptor’.
  • Override interceptor methods in ‘AuditLogInterceptor’
  • Capture required information into AuditTrail entity and save entity

public class AuditLogInterceptor extends EmptyInterceptor {  
    
  private List auditTrailList = new ArrayList();    
   . . . . . .
 
   // First Parameter: The entity which is being updated     
   // Second Parameter: The primary key of updated row 
   // Third Parameter:  Current states of updated entity    
   // Fourth Parameter: Previous states of updated entity  
   // Fifth Parameter: The variable names of updated entity
   // Sixth Parameter: The type of variables of updated entity
   @Override
   public boolean onFlushDirty(Object entity, Serializable id,
            Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {                     
           // Get the table name from entity
           Class entityClass = entity.getClass();
            Table tableAnnotation = entityClass.getAnnotation(Table.class);            
          
           for (int i = 0; i < currentState.length; i++) {              
                // Track changes only for the states which are String or Number types           
                 if(!(previousState[i] instanceof String
                     || previousState[i] instanceof Long
                     || previousState[i] instanceof BigDecimal
                      || previousState[i] instanceof Integer
                     || previousState[i] instanceof Timestamp))
                 {
                          continue;
                 }                
                // Check whether column value is updated
                 if(previousState[i] != null && currentState[i] != null
                   &&  !(previousState[i].equals(currentState[i]))){
                         
                  AuditTrail auditTrail = new AuditTrail();               
                 // Set updated table name
                 auditTrail.setTableName(tableAnnotation.name());
                 // Set updated column name
                 Column col = null;
                 try {                                                  
                        String filedName = propertyNames[i];
                        if(filedName.contains("serialVersionUID")){
                                  continue;
                        }
                        Character firstChar =  filedName.charAt(0);
                        firstChar = Character.toUpperCase(firstChar);
                        String filedNameSubStr = filedName.substring(1);
                        String methodName = "get"+ firstChar+filedNameSubStr;                       
                        Class[] parameterTypes = new Class[]{};                           
                        Method ueMethod = entityClass.getDeclaredMethod(methodName, parameterTypes);
                        col = ueMethod.getAnnotation(Column.class);                         
                    } catch (Exception e) {
                                e.printStackTrace();
                    }
                   if(col != null){
                          auditTrail.setColumnIdentifier(col.name());       
                   }               
                // Set old value
                if(previousState[i] != null){
                          auditTrail.setOldValue(previousState[i] == null  ? null :
                         previousState[i].toString());    
                }               
                // Set new value
                if(currentState[i] != null){
                          auditTrail.setNewValue(currentState[i] == null ? null:
                         currentState[i].toString());
                }               
                // Set database operation
                auditTrail.setOperation("Update");               
                // Set row primary key value
                auditTrail.setRowIdentifier(id.toString());  
                auditTrailList.add(auditTrail);                         
              }           
            }                        
        return false;
    }        
    @Override  
    public void postFlush(Iterator iterator) throws CallbackException {            
           // Set unique transaction id in all AuditTrail Entities
           String transId = UUID.randomUUID().toString();     
           for (AuditTrail auditTrailauditTrailList) {
               auditTrail.setTransactionId(transId);
           }   
          // Save ‘AuditTrail’ entity list into database using AuditDAO 
    }  
 } 

Audit logging Sequence Flow




Issue Faced during Audit Logging Implementation

I faced an interesting issue during audit logging implementation in my application. Let me share that issue detail.

I had ‘AbstractDAO’ and 'AuditDAO' class extends this ‘AbstractDAO’. 'AuditDAO' class is configured as ‘@Repository’. AbstractDAO’ has entity manager property which is initialized by Spring Container. I wanted to auto wire ‘AuditDAO’ in ‘AuditLogInterceptor’ to save ‘AuditTrail’ entity. But, this couldn’t happen. The reason was ‘AuditLogInterceptor’ was not defined as spring resource (like @Component or @Resources or @Repository). Hence's its property can’t be auto wired. Since, this interceptor is initialized by hibernate, I couldn't configure this as spring resource. Finally, I had to take the help of StackOverflow and got the idea to solve this problem. You can refer this issue and solution from here.
0

Add a comment

You might be seeking for the option to profile (capturing method's execution time) your spring application. Spring provides different ways to profile the application. Profiling should be treated as a separate concern and spring AOP facilitates easy approach to separate this concern. With the help of spring AOP you can profile your methods without making any changes in actual classes. 

You just need to perform pretty simple steps to configure your application for profiling using spring AOP:

In application context file, add below tag to enable the load time weaving          context:load-time-weaver      

With this configuration, AspectJ's Load-time weaver is registered with current class loader.

Why Locking is required?

When two concurrent users try to update database row simultaneously, there are absolute chances of losing data integrity. Locking comes in picture to avoid simultaneous updates and ensure data integrity.

Types of Locking

There are two types of locking, Optimistic and Pessimistic. In this post optimistic locking is described with example.

Optimistic Locking: This locking is applied on transaction commit.

I have got a problem in coding contest. In this post, I would like to share the approach to solve this problem. I would definitely not say that I have invented something new and I am not trying to reinvent the wheel again. I just described the end to end approach that I followed to solve this problem.It was really a great brain storming activity.

Jenkins is an open source tool which provides continuous integration services for software development. If you want to get more detail about Jenkins and it's history I would suggest refer this link. This post will help you installing and configuring Jenkins and creating jobs to trigger maven builds. Setting up Jenkins is not a rocket science indeed. In this post, I would like to concise the installation and configuration steps.

Peer code review is an important activity to find and fix mistakes which are overlooked during development. It helps improving both software quality as well as developers skills. Though, it’s a good process for quality improvement. But this process becomes tedious if you need to share files and send review comments through mails, you need to organize formal meetings and you need to communicate peers who are in different time zone.

If you are using Hudson as continuous integration server and you might feel lazy about accessing Hudson explicitly to check the build status or checking Hudson build status mails, there is an option to monitor Hudson build and perform build activities in Eclipse IDE itself. This post describes about installing, configuring and using the Hudson in Eclipse IDE.

As a developer or architect, you always need to draw some sequence diagrams to demonstrate or document your functionality. And of course, if you do this manually you have to spare much time for this activity. Just think about, what If sequence diagram is generated automatically and you get it free of cost. Your reaction would be ‘wow’ this is great. But the next question will be ‘how’.

If you are using JPA 2.0 with Hibernate and you want to do audit logging from middle-ware itself, I believe you landed up on the exact place where you should be. You can try audit logging in your local environment by following this post.

Required JPA/Hibernate Maven Dependencies

JPA Configuration in Spring Application Context File

For JPA/Hibernate, you need to configure entity manager, transaction, data source and JPA vendor in your ‘applicationContext.xml’ file.

If any issue is observed in production, there are two major aspects related to providing the solution. First ‘how quick you can analyze the root cause’ and second ‘how quick you can fix the issue’. Story starts from analyzing the root cause. Unless, you find the root cause, you can’t even think about providing solution for that. Can you?

Now let’s think about actual production environment. There may be multiple JVMs where application is deployed.
Loading