OK, so you can see what I'm trying to do:
The xml config which sets up the listeners:
Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<event type="pre-update">
<listener class="com.fcl.util.HibernateEventListener"/>
</event>
<event type="post-commit-update">
<listener class="com.fcl.util.HibernateEventListener"/>
<listener class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
<event type="post-commit-insert">
<listener class="com.fcl.util.HibernateEventListener"/>
<listener class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
<event type="post-commit-delete">
<listener class="com.fcl.util.HibernateEventListener"/>
<listener class="org.hibernate.lucene.event.LuceneEventListener"/>
</event>
</session-factory>
</hibernate-configuration>
The entity representing a database update:
Code:
package entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Lob;
import java.util.*;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostDeleteEvent;
import javax.ejb.SessionContext;
@Entity
@Table(name = "AUDIT_LOG")
public class AuditLog implements Serializable
{
public static final int CREATE = 0;
public static final int UPDATE = 1;
public static final int DELETE = 2;
private long id;
private String className;
private Serializable entityId;
private String userId;
private int operation;
private List<String> changedProperties;
private List<Object> oldValues;
private List<Object> newValues;
public static final long serialVersionUID = 0xfc10765;
public void audit(SessionContext ctx, PostInsertEvent e)
{
className = e.getEntity().getClass().getCanonicalName();
className = className.substring(className.lastIndexOf('.') + 1);
entityId = e.getId();
userId = ctx.getCallerPrincipal().getName();
operation = CREATE;
changedProperties = Arrays.asList(e.getPersister().getPropertyNames());
newValues = Arrays.asList(e.getState());
}
public void audit(SessionContext ctx, PostUpdateEvent e)
{
className = e.getEntity().getClass().getCanonicalName();
className = className.substring(className.lastIndexOf('.') + 1);
entityId = e.getId();
userId = ctx.getCallerPrincipal().getName();
operation = UPDATE;
changedProperties = new ArrayList<String>();
oldValues = new ArrayList<Object>();
newValues = new ArrayList<Object>();
String[] propNames = e.getPersister().getPropertyNames();
Object[] oldState = e.getOldState();
Object[] newState = e.getState();
for (int i = 0; i < oldState.length; i++)
{
if (!newState[i].equals(oldState[i]))
{
changedProperties.add(propNames[i]);
oldValues.add(oldState[i]);
newValues.add(newState[i]);
}
}
}
public void audit(SessionContext ctx, PostDeleteEvent e)
{
className = e.getEntity().getClass().getCanonicalName();
className = className.substring(className.lastIndexOf('.') + 1);
entityId = e.getId();
userId = ctx.getCallerPrincipal().getName();
operation = DELETE;
changedProperties = Arrays.asList(e.getPersister().getPropertyNames());
oldValues = Arrays.asList(e.getDeletedState());
}
public String getOperation()
{
switch (operation)
{
case CREATE:
return "CREATE";
case UPDATE:
return "UPDATE";
case DELETE:
return "DELETE";
}
return "UNKNOWN";
}
public void setOperation(int o)
{
operation = o;
}
/**
*/
@Column(name="CLASS_NAME", nullable=false)
public String getClassName()
{
return this.className;
}
public void setClassName(String s)
{
className = s;
}
/**
*/
@Column(name="ENTITY_ID", nullable=false)
public Serializable getEntityId()
{
return entityId;
}
public void setEntityId(Serializable e)
{
this.entityId = e;
}
/**
*/
@Column(name="USER", nullable=false)
public String getUserId()
{
return userId;
}
public void setUserId(String u)
{
this.userId = u;
}
/**
*/
@Column(name="CHANGED_PROPS", nullable=true)
@Lob
public List<String> getChangedProperties()
{
return this.changedProperties;
}
public void setChangedProperties(List<String> p)
{
changedProperties = p;
}
/**
*/
@Column(name="OLD_VALUES", nullable=true)
@Lob
public List<Object> getOldValues()
{
return oldValues;
}
public void setOldValues(List<Object> v)
{
oldValues = v;
}
/**
*/
@Column(name="NEW_VALUES", nullable=true)
@Lob
public List<Object> getNewValues()
{
return newValues;
}
public void setNewValues(List<Object> v)
{
newValues = v;
}
/**
* @return Returns the id.
*/
@Column(name = "ID", nullable = false)
@Id(generate = GeneratorType.AUTO)
public long getId()
{
return id;
}
/**
* @param id
* The id to set.
*/
public void setId(long id)
{
this.id = id;
}
public String toString()
{
StringBuilder sb = new StringBuilder(256);
if (operation == CREATE)
{
sb.append("User ").append(getUserId()).append(" created ")
.append(getClassName()).append(" ID ").append(getEntityId()).append("\r\n");
for (int i = 0; i < changedProperties.size(); i++)
{
int j = sb.length();
Object newValue = newValues.get(i);
if (newValue instanceof Collection)
displayCollection(sb, changedProperties.get(i), (Collection)newValue);
else
sb.append(changedProperties.get(i)).append(" = ").append(newValue).append("\r\n");
sb.replace(j, j + 1, sb.substring(j, j + 1).toUpperCase());
}
return sb.toString();
}
if (operation == UPDATE)
{
sb.append("User ").append(this.getUserId()).append(" updated ")
.append(getClassName()).append(" ID ").append(getEntityId()).append("\r\n");
for (int i = 0; i < changedProperties.size(); i++)
{
int j = sb.length();
Object oldValue = oldValues.get(i);
Object newValue = newValues.get(i);
if (newValue instanceof Collection)
displayCollections(sb, changedProperties.get(i), (Collection)oldValue, (Collection)newValue);
else
sb.append(changedProperties.get(i)).append(" ").append(oldValue.toString())
.append(" -> ").append(newValue.toString()).append("\r\n");
sb.replace(j, j + 1, sb.substring(j, j + 1).toUpperCase());
}
return sb.toString();
}
if (operation == DELETE)
{
sb.append("User ").append(this.getUserId()).append(" deleted ")
.append(getClassName()).append(" ID ").append(getEntityId()).append("\r\n");
for (int i = 0; i < changedProperties.size(); i++)
{
int j = sb.length();
Object oldValue = oldValues.get(i);
if (oldValue instanceof Collection)
displayCollection(sb, changedProperties.get(i), (Collection)oldValue);
else
sb.append(changedProperties.get(i)).append(" = ").append(oldValue).append("\r\n");
sb.replace(j, j + 1, sb.substring(j, j + 1).toUpperCase());
}
return sb.toString();
}
return "Uknown operation";
}
private static void displayCollection(StringBuilder sb, String name, Collection c)
{
sb.append(name).append(':');
for (Iterator i = c.iterator(); i.hasNext(); )
{
sb.append('\t').append(i.next());
}
}
private static void displayCollections(StringBuilder sb, String name, Collection c, Collection c1)
{
sb.append(name).append(':');
int m = 0;
Iterator i1 = c1.iterator();
for (Iterator i = c.iterator(); i.hasNext() || i1.hasNext(); )
{
if (i.hasNext())
{
String v = i.next().toString();
if (v.length() > m)
m = v.length();
sb.append('\t').append(v);
}
else
{
sb.append('\t');
for (int j = 0; j < m; j++)
sb.append(' ');
}
if (i1.hasNext())
sb.append(' ').append(i1.next());
}
}
}
The listener:
Code:
package com.fcl.util;
import com.fcl.util.EntityAuditor;
import com.fcl.util.EntityAuditorBean;
import java.lang.reflect.Method;
import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PreUpdateEvent;
import org.hibernate.event.PreUpdateEventListener;
import org.hibernate.event.PostUpdateEvent;
import org.hibernate.event.PostUpdateEventListener;
/**
*/
public class HibernateEventListener implements PostDeleteEventListener,
PostInsertEventListener, PreUpdateEventListener, PostUpdateEventListener
{
private static final long serialVersionUID = 12345678L;
private EntityAuditor auditBean = null;
public HibernateEventListener()
{
}
private EntityAuditor getEntityAuditor()
{
if (auditBean == null)
auditBean = EntityAuditorBean.getInstance();
return auditBean;
}
public void onPostInsert(PostInsertEvent event)
{
getEntityAuditor().onPostInsert(event);
}
public void onPostUpdate(PostUpdateEvent event)
{
getEntityAuditor().onPostUpdate(event);
}
public void onPostDelete(PostDeleteEvent event)
{
getEntityAuditor().onPostDelete(event);
}
public boolean onPreUpdate(PreUpdateEvent event)
{
new Exception().printStackTrace();
final Object entity = event.getEntity();
System.out.println("About to update " + entity);
Class c = entity.getClass();
Class[] intf = c.getInterfaces();
if (intf != null && intf.length > 0)
{
for (int i = 0; i < intf.length; i++)
{
System.out.println("Customer entity implements " + intf[i].getName());
}
}
Method[] m = c.getMethods();
if (m != null && m.length > 0)
{
for (int i = 0; i < m.length; i++)
{
System.out.println(m[i].getName() + "()");
}
}
Object[] state = event.getState();
if (state != null && state.length > 0)
{
for (int i = 0; i < state.length; i++)
{
System.out.println(state[i].toString());
}
}
return true;
}
}
The EntityAuditorBean
Code:
package com.fcl.util;
import entity.AuditLog;
import org.hibernate.event.PostDeleteEvent;
import org.hibernate.event.PostInsertEvent;
import org.hibernate.event.PostUpdateEvent;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ejb.Stateless;
import javax.ejb.SessionContext;
import javax.annotation.Resource;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.apache.log4j.*;
import org.apache.log4j.spi.LoggingEvent;
@Stateless
public class EntityAuditorBean implements EntityAuditor
{
@PersistenceContext(unitName = "cal")
private EntityManager em;
@Resource
private SessionContext ctx;
private static EntityAuditor theInstance = null;
private Logger auditLogger = null;
public static EntityAuditor getInstance()
{
if (theInstance == null)
{
try
{
InitialContext c = new InitialContext();
theInstance = (EntityAuditor)c.lookup(EntityAuditor.class.getName());
theInstance.createEntityManager("cal");
theInstance.setAuditLogger("entity.log");
}
catch (Exception e)
{
e.printStackTrace();
}
}
return theInstance;
}
private static final long serialVersionUID = 155266L;
public void setAuditLogger(String fileName)
{
auditLogger = Logger.getLogger(getClass());
auditLogger.removeAllAppenders();
try
{
auditLogger.addAppender(new FileAppender(new PatternLayout("%d{ISO8601} [%C{1}] %m"), fileName));
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void createEntityManager(String persistenceUnitName)
{
//em = Persistence.createEntityManagerFactory(persistenceUnitName).createEntityManager();
}
public void onPostInsert(PostInsertEvent event)
{
AuditLog l = new AuditLog();
l.audit(ctx, event);
auditLogger.info(l.toString());
em.persist(l);
}
public void onPostUpdate(PostUpdateEvent event)
{
AuditLog l = new AuditLog();
l.audit(ctx, event);
auditLogger.info(l.toString());
em.persist(l);
}
public void onPostDelete(PostDeleteEvent event)
{
AuditLog l = new AuditLog();
l.audit(ctx, event);
auditLogger.info(l.toString());
em.persist(l);
}
}
So Hibernate fires the post-update, post-insert, post-delete event, and in my listener, I invoke a session bean which creates the entity to represent that change, and attempt to persist it.