Jul
18
18
This is a continuation of the post “Audit Logging via Hibernate Interceptor“.
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.

Leave a Reply