Tuesday, January 10, 2012

Audit Trail in Hibernate

 Spring Configuration:
In ApplicationContext.xml for sessionFactory bean provide the listeners.

<property name="eventListeners">
            <map>
                <entry key="merge">
                    <bean
                        class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener" />
                </entry>
                <entry key="merge">
                    <bean
                        class="com.nmmc.common.utils.MergeHibernateListener" />
                </entry>
                 <entry key="save-update">
                    <bean class="com.nmmc.common.utils.SaveOrUpdateHibernateListener" />
                </entry>
                <entry key="post-update">
                    <bean class="com.nmmc.common.utils.AuditTrailHibernateListener" />
                </entry>
                <entry key="post-insert">
                    <bean class="com.nmmc.common.utils.AuditTrailHibernateListener" />
                </entry>
                <entry key="post-delete">
                    <bean class="com.nmmc.common.utils.AuditTrailHibernateListener" />
                </entry>
            </map>
        </property>


Java Class for Audit trail

package com.nmmc.common.utils;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostUpdateEventListener;
import org.hibernate.event.def.DefaultSaveOrUpdateEventListener;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.EntityPersister;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * Audit Log Listener is used to log insert, update, Complete list of
 * listeners/events.
 *
 * @see org.hibernate.event.EventListeners
 * @author Balaji H P
 */
public final class AuditTrailHibernateListener extends DefaultSaveOrUpdateEventListener implements PostDeleteEventListener, PostUpdateEventListener, PostInsertEventListener
{
    private static final long serialVersionUID = 1L;

    private static Log log = LogFactory.getLog(AuditTrailHibernateListener.class);

    public static final String OPERATION_TYPE_INSERT = "INSERT";

    public static final String OPERATION_TYPE_UPDATE = "UPDATE";

    public static final String OPERATION_TYPE_DELETE = "DELETE";

    public static boolean auditTrailenabled = false;

    public void onPostDelete(PostDeleteEvent event)
    {
    if (!auditTrailenabled)
    {
        return;
    }
    try
    {

        final Serializable entityId = event.getPersister().hasIdentifierProperty() ? event.getPersister().getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity()))
            : null;
        final String entityName = event.getEntity().getClass().getSimpleName();
        final String transDate = null;//CommonUtils.getCurrentStringDate();
        if (!event.getEntity().getClass().getPackage().getName().startsWith("org.jbpm") && !entityName.equalsIgnoreCase("AuditTrail"))
        {
        final String userName = getUserName();
        // need to have a separate session for audit save
        Session session = event.getPersister().getFactory().openSession();
        session.beginTransaction();

        if (log.isDebugEnabled())
        {
            log.debug("{} for: {}, ID: {}, actor: {}, date: {}" + new Object[] { entityName, entityId, userName, transDate });
        }

        // Creating AuditTrail Object
        AuditTrail auditTrail = new AuditTrail();
        auditTrail.setEntityId(entityId.toString());
        auditTrail.setEntityName(entityName);
        auditTrail.setNewPropValue(" ");
        auditTrail.setOldPropValue(" ");
        auditTrail.setOperationType(OPERATION_TYPE_DELETE);
        auditTrail.setActorName(userName);
        auditTrail.setPropertyName(OPERATION_TYPE_DELETE);
        auditTrail.setTransactionDate(transDate);
        Date a = new Date();
        auditTrail.setTransactionTime(a.getHours() + ":" + a.getMinutes());

        session.save(auditTrail);
        session.getTransaction().commit();
        session.close();
        }
    } catch (HibernateException e)
    {
        log.error("Unable to process audit log for DELETE operation", e);
    }
    return;
    }

    public void onPostInsert(PostInsertEvent event)
    {
    if (!auditTrailenabled)
    {
        return;
    }
    try
    {
        EntityPersister persister = event.getPersister();
        final Serializable entityId = persister.hasIdentifierProperty() ? persister.getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity())) : null;
        final String entityName = event.getEntity().getClass().getSimpleName();
        final EntityMode entityMode = persister.guessEntityMode(event.getEntity());

        if (!event.getEntity().getClass().getPackage().getName().startsWith("org.jbpm") && !entityName.equalsIgnoreCase("AuditTrail"))
        {
        final String userName = getUserName();
        Session session = event.getPersister().getFactory().openSession();
        String[] propertyNames = persister.getPropertyNames();
        Transaction tx = session.beginTransaction();
        for (int i = 0; i < propertyNames.length; i++)
        {
            Object childObject = event.getPersister().getPropertyValue(event.getEntity(), propertyNames[i].toString(), entityMode);
            if (!(childObject instanceof Collection))
            {
            if (childObject == null)
                childObject = " ";

            AuditTrail auditTrail = new AuditTrail();
            auditTrail.setActorName(userName);
            auditTrail.setEntityId(entityId.toString());
            auditTrail.setEntityName(entityName);
            auditTrail.setPropertyName(propertyNames[i]);
            auditTrail.setTransactionDate(null);
            auditTrail.setTransactionTime(new Date().getHours() + ":" + new Date().getMinutes());
            auditTrail.setOperationType(OPERATION_TYPE_INSERT);
            auditTrail.setNewPropValue(null);
            auditTrail.setOldPropValue(" ");

            if (childObject.getClass().getPackage().getName().startsWith("com.egov."))
            {
                String updatedValue = null;
                ClassMetadata childMetadata = session.getSessionFactory().getClassMetadata(childObject.getClass());
                if (childMetadata != null)
                {
                updatedValue = childMetadata.getIdentifier(childObject, entityMode).toString();
                if (updatedValue != null && updatedValue.trim().length() > 0)
                    auditTrail.setNewPropValue(updatedValue.toString());
                }
            } else
            {
                if (childObject != null && childObject instanceof java.util.Date)
                {
                auditTrail.setNewPropValue(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(childObject));
                } else if (childObject != null && childObject.toString().trim().length() > 0)
                {
                auditTrail.setNewPropValue(StringUtils.replaceChars(childObject.toString(), "\n", "<br/>"));
                }
            }

            if (auditTrail.getNewPropValue() != null)
                session.save(auditTrail);
            }
        }
        tx.commit();
        session.close();
        }
    } catch (HibernateException e)
    {
        e.printStackTrace();
        log.error("Unable to process audit log for INSERT operation", e);
    } catch (Exception ex)
    {
        ex.printStackTrace();
    }
    return;
    }

    public void onPostUpdate(PostUpdateEvent event)
    {
    if (!auditTrailenabled)
    {
        return;
    }
    try
    {
        EntityPersister persister = event.getPersister();
        SessionImplementor sessionImplementor = event.getSession();

        final Serializable entityId = persister.hasIdentifierProperty() ? persister.getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity())) : null;
        final String entityName = event.getEntity().getClass().getSimpleName();
        final EntityMode entityMode = persister.guessEntityMode(event.getEntity());
        if (!event.getEntity().getClass().getPackage().getName().startsWith("org.jbpm") && !entityName.equalsIgnoreCase("AuditTrail"))
        {
        final String userName = getUserName();
        // need to have a separate session for audit save
        Session session = event.getPersister().getFactory().openSession();

        Object[] databseObject = persister.getDatabaseSnapshot(event.getId(), sessionImplementor);
        if (databseObject != null)
        {

            Object[] currentObject = persister.getPropertyValues(event.getEntity(), entityMode);
            String[] propertyNames = persister.getPropertyNames();

            int[] indexes = persister.findModified(databseObject, currentObject, event.getEntity(), sessionImplementor);

            // if no changes made, return
            if (indexes == null)
            {
            return;
            }
            Transaction transaction = session.beginTransaction();
            for (int i = 0; i < indexes.length; i++)
            {
            int chagnedIndex = indexes[i];

            if (currentObject[chagnedIndex] == null)
                currentObject[chagnedIndex] = " ";
            if (databseObject[chagnedIndex] == null)
                databseObject[chagnedIndex] = " ";

            Object childObject = currentObject[chagnedIndex];
            if (!(childObject instanceof Collection))
            {
                AuditTrail auditTrail = new AuditTrail();
                auditTrail.setActorName(userName);
                auditTrail.setEntityId(entityId.toString());
                auditTrail.setEntityName(entityName);
                auditTrail.setPropertyName(propertyNames[chagnedIndex]);
                auditTrail.setTransactionDate(null);
                auditTrail.setTransactionTime(new Date().getHours() + ":" + new Date().getMinutes());
                auditTrail.setOperationType(OPERATION_TYPE_UPDATE);
                if (childObject.getClass().getPackage().getName().startsWith("com.egov."))
                {
                String updatedValue = null;
                try
                {
                    ClassMetadata childMetadata = session.getSessionFactory().getClassMetadata(childObject.getClass());
                    updatedValue = childMetadata.getIdentifier(childObject, entityMode).toString();
                    auditTrail.setNewPropValue(updatedValue);
                } catch (Exception exception)
                {
                }
                } else
                {
                if (childObject != null && childObject instanceof java.util.Date)
                {
                    auditTrail.setNewPropValue(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(childObject));
                } else
                {
                    auditTrail.setNewPropValue(StringUtils.replaceChars(childObject.toString(), "\n", "<br/>"));
                }
                }

                if (auditTrail.getNewPropValue() == null || auditTrail.getNewPropValue().trim().length() == 0)
                {
                auditTrail.setNewPropValue(" ");
                }

                auditTrail.setOldPropValue(StringUtils.replaceChars(databseObject[chagnedIndex].toString(), "\n", "<br/>"));
                if ((auditTrail.getOldPropValue() != null && auditTrail.getNewPropValue() != null)
                    && !auditTrail.getOldPropValue().trim().equalsIgnoreCase(auditTrail.getNewPropValue().trim()))
                {
                session.save(auditTrail);
                } else if (auditTrail.getOldPropValue() == null && auditTrail.getNewPropValue() != null)
                {
                session.save(auditTrail);
                } else if (auditTrail.getOldPropValue() != null && auditTrail.getNewPropValue() == null)
                {
                session.save(auditTrail);
                }
            }
            }
            transaction.commit();
        }
        }

    } catch (Exception e)
    {
        e.printStackTrace();
        log.error("Unable to process audit log for UPDATE operation", e);
    }
    return;
    }

    /**
     * Gets the current user's id from the Acegi secureContext
     *
     * @return current user's userId
     */
    private String getUserName()
    {
    SecurityContext secureContext = (SecurityContext) SecurityContextHolder.getContext();
    String userName = "anonymousUser";
    // secure context will be null when running unit tests so leave userId
    // as null
    try
    {
        if (secureContext != null)
        {
        Authentication auth = (Authentication) secureContext.getAuthentication();

        if (auth.getPrincipal() instanceof UserDetails)
        {
            UserDetails userDetails = (UserDetails) auth.getPrincipal();
            userName = userDetails.getUsername();
        } else
        {
            userName = auth.getPrincipal().toString();
        }

        if (userName == null || userName.equals(""))
        {
            return userName;
        } else
        {
            return userName;
        }

        } else
        {
        return userName;
        }

    } catch (Exception e)
    {
        // to handle , JBPM process deployments during webapp deployment.
        e.printStackTrace();
        log.error("Error Auit trail getUserName ", e);
    }
    return userName;
    }

}

No comments: