This is a continuation of the post “Audit Logging via Hibernate Interceptor (1/2)“.
Other helper functions are shown below:
CrudUtil is the class responsible for retrieving object values using Java reflections:
import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.Collection; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; import com.ideyatech.core.InvalidImplementationException; import com.ideyatech.core.bean.Auditable; import com.ideyatech.core.bean.BaseCriteria; import com.ideyatech.core.bean.BaseEntity; /** * @author allanctan * */ public class CrudUtil { private static Logger _log = Logger.getLogger(CrudUtil.class); private static final String SQL_PARAM = ":([^\\s]+)"; private static final Pattern SQL_PARAM_PATTERN = Pattern.compile( SQL_PARAM, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); /** * Creates the logging message for new audit logs * @param obj * @return */ public static String buildCreateMessage(Auditable obj) { StringBuffer message = new StringBuffer("Added "); message.append(obj.getClass().getSimpleName()) // class name .append(" ") .append(obj.getPrimaryField()) .append(":"); Object value = retrieveObjectValue(obj, obj.getPrimaryField()); if (value!=null) message.append(value.toString()) .append(" - "); // loop through the fields list List auditFields = obj.getAuditableFields(); int count = 0; for (String property:auditFields) { Object ret = retrieveObjectValue(obj, property); if (ret!=null && ret.toString().trim().length()>0) { if (count > 0) message.append(" and "); message.append(property) .append("=") .append(ret.toString()); count++; } } return message.toString(); } /** * Creates the logging message for update audit logs * @param obj * @return */ public static String buildUpdateMessage(Auditable oldObject, Auditable newObject) { StringBuffer message = new StringBuffer("Changed "); message.append(oldObject.getClass().getSimpleName()) // class name .append(" ") .append(oldObject.getPrimaryField()) .append(":"); Object value = retrieveObjectValue(oldObject, oldObject.getPrimaryField()); if (value!=null) message.append(value.toString()) .append(" - "); // loop through the fields list List auditFields = oldObject.getAuditableFields(); int count = 0; for (String property:auditFields) { Object oldValue = retrieveObjectValue(oldObject, property); Object newValue = retrieveObjectValue(newObject, property); if (oldValue == null) oldValue = new String(""); if (newValue == null) newValue = new String(""); if (!oldValue.equals(newValue)) { if (count > 0) message.append(" and "); message.append(property) .append(" from '") .append(oldValue.toString()) .append("' to '") .append(newValue.toString()) .append("'"); count++; } } return message.toString(); } /** * Creates the logging message for new audit logs * @param obj * @return */ public static String buildDeleteMessage(Auditable obj) { StringBuffer message = new StringBuffer("Deleted "); message.append(obj.getClass().getSimpleName()) // class name .append(" ") .append(obj.getPrimaryField()) .append(":"); Object value = retrieveObjectValue(obj, obj.getPrimaryField()); if (value!=null) message.append(value.toString()); return message.toString(); } /** * Retrieves the property name for a method name. * (e.g. getName will return name) * @param methodName * @return */ public static String getPropertyName(String methodName) { if (StringUtil.isEmpty(methodName) || methodName.length()<=3) return null; if (methodName.startsWith("get") || methodName.startsWith("set")) { String prop = methodName.substring(4); char c = Character.toLowerCase(methodName.charAt(3)); return c+prop; } else return null; } /** * Retrieves the getter method name for a given property. * (e.g. name will return getName) * @param propertyName * @return */ public static String getGetterMethodName(String propertyName) { if (StringUtil.isEmpty(propertyName) || propertyName.length()<=0) return null; char c = Character.toUpperCase(propertyName.charAt(0)); return "get"+c+propertyName.substring(1); } /** * This method retrieves the object value that corresponds to the property specified. * This method can recurse inner classes until specified property is reached. * * For example: * obj.firstName * obj.address.Zipcode * * @param obj * @param property * @return */ public static Object retrieveObjectValue(Object obj, String property) { if (property.contains(".")) { // we need to recurse down to final object String props[] = property.split("\\."); try { Method method = obj.getClass().getMethod(getGetterMethodName(props[0])); Object ivalue = method.invoke(obj); if (ivalue==null) return null; return retrieveObjectValue(ivalue,property.substring(props[0].length()+1)); } catch (Exception e) { _log.error("Failed to retrieve value for "+property); throw new InvalidImplementationException("CrudUtil","retrieveObjectValue",null,"", e); } } else { // let's get the object value directly try { Method method = obj.getClass().getMethod(getGetterMethodName(property)); return method.invoke(obj); } catch (Exception e) { _log.error("Failed to retrieve value for "+property); throw new InvalidImplementationException("CrudUtil","retrieveObjectValue",null,"", e); } } } /** * This method retrieves the object type that corresponds to the property specified. * This method can recurse inner classes until specified property is reached. * * For example: * obj.firstName * obj.address.Zipcode * * @param obj * @param property * @return */ public static Class retrieveObjectType(Object obj, String property) { if (property.contains(".")) { // we need to recurse down to final object String props[] = property.split("\\."); try { Method method = obj.getClass().getMethod(getGetterMethodName(props[0])); Object ivalue = method.invoke(obj); return retrieveObjectType(ivalue,property.substring(props[0].length()+1)); } catch (Exception e) { _log.error("Failed to retrieve value for "+property); throw new InvalidImplementationException("CrudUtil","retrieveObjectValue",null,"", e); } } else { // let's get the object value directly try { Method method = obj.getClass().getMethod(getGetterMethodName(property)); return method.getReturnType(); } catch (Exception e) { _log.error("Failed to retrieve value for "+property); throw new InvalidImplementationException("CrudUtil","retrieveObjectValue",null,"", e); } } } }
HibernateUtil is a utility to retrieve hibernate session separate from the usual EntityManager.
import java.net.URL; import java.util.Set; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; import org.scannotation.AnnotationDB; import org.scannotation.ClasspathUrlFinder; /** * This utility allows creation of Hibernate session directly. * Used for logging purposes. * * @author allantan */ public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { URL[] urls = ClasspathUrlFinder.findResourceBases("META-INF/persistence.xml"); AnnotationDB db = new AnnotationDB(); db.scanArchives(urls); Set entityClasses = db.getAnnotationIndex().get(javax.persistence.Entity.class.getName()); // Create the SessionFactory AnnotationConfiguration ac = new AnnotationConfiguration(); ac.setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/ideyatech"); ac.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); for (String clazz:entityClasses) { ac.addAnnotatedClass(Class.forName(clazz)); } sessionFactory = ac.buildSessionFactory(); } catch (Throwable ex) { // Make sure you log the exception, as it might be swallowed System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
And finally, the sample implementation of an Auditable class:
public class InboundDocument implements Auditable { private static final long serialVersionUID = -7019861759834380358L; @Column(name = "DATE_RECEIVED") @Field(index = Index.UN_TOKENIZED, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date dateReceived; @Column(name = "NUMBER_OF_DAYS") private Integer numberOfDays; @Column(name = "DATE_DUE") @Field(index = Index.UN_TOKENIZED, store = Store.YES) @DateBridge(resolution = Resolution.DAY) private Date dateDue; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "ACTION_NEEDED") @Field(index = Index.TOKENIZED) @FieldBridge(impl = SystemCodesBridge.class) private SystemCodes actionNeeded; ... ... public List getAuditableFields() { List props = new ArrayList(); props.add("documentType"); props.add("actionNeeded"); props.add("dateReceived"); props.add("dateDue"); props.add("summary"); return props; } public String getPrimaryField() { return "barcodeNumber"; } }
That’s it! Just implement all your auditable classes with Auditable interface. All the codes above are extracted from open-tides – an open-source web-foundation framework that can be used to quickly setup an enterprise project.
3 thoughts on “Audit Logging via Hibernate Interceptor (2/2)”
superb
Hi
I need the full example, would you please put that here
tnx
hi, can you provide the database script?
or can you tell me what is the data type for object class?
private Class entityClass;