Using a valueChangeListener to build an Audit Trail

A few weeks ago Tony McGuckin posted a new XSnippet on how to use a valueChangeListener bean in XPages to detect when a value was changed in a field on an XPage and it got me thinking, how can I make use of this to create an audit trail/edit log for an application.

There are lots of different methods for creating an audit trail for a Notes/Domino application, the most common is to take a copy of all the editable fields when the document is opened and then when the user hits the save button ( and all validation has passed ) compare the old field values to the new field values. Anything that is different must be an edit so you can add something to your audit trail.

My first attempt at using the valueChangeListener for my audit trail was a bit of a disaster because it was recording changes before the document would even be saved, this is because the valueChangeListener event is fired during the PROCESS_VALIDATIONS phase, anything on the form that would trigger a partial refresh could potentially trigger validation and thus would result in items saved to the audit log. The issue with this is that if the user decided not to save the document those items would already be in the audit log for an event that didnt actually happen.

After discussing the concept with a few other XPagers Nathan T Freeman suggested recording the changes into the view scope and then doing something in the save event to move it to the audit log. I played around with this for a while and I managed to get it working.

In the valueChangeListener bean I added the following to the //do something useful block. This uses two java hashmaps to store the old value and the new value, the key for both hashmaps is the name of the field that was changed. If the hashmaps already exist in the viewScope then it will update that hashmap, if not it will create a new hashmap. This allows for multiple changes to be recorded on a single XPage.

UIComponent c = valueChangeEvent.getComponent();

String changedExpression = c.getValueBinding("value").getExpressionString();
int firstDot = changedExpression.indexOf(".") + 1;
int closingBracket = changedExpression.indexOf("}");
String changedField = changedExpression.substring(firstDot,closingBracket);

String oldValue = valueChangeEvent.getOldValue().toString();
String newValue = valueChangeEvent.getNewValue().toString();

Map<String,Object> viewScope = ExtLibUtil.getViewScope();
HashMap<String,Object> oldValuesMap = new HashMap<String,Object>();
HashMap<String,Object> newValuesMap = new HashMap<String,Object>();

if (viewScope.containsKey("oldValues")){
    oldValuesMap = (HashMap<String, Object>) viewScope.get("oldValues");
    newValuesMap = (HashMap<String, Object>) viewScope.get("newValues");

oldValuesMap.put(changedField, oldValue);
newValuesMap.put(changedField, newValue);

viewScope.put("oldValues", oldValuesMap);
viewScope.put("newValues", newValuesMap);

For the save event I added the following script, it checks to see if the hashmap exists in the viewScope and if it does it will loop through all the keys in one of the hashmaps and record the keyname, old value and new value to the audit trail. In my case I’m just calling another function that does the job of creating a new document in a different database, recording the users name and timestamp and then recording the ‘audit note’ that I’m passing into the function.

if (viewScope.containsKey("oldValues")){
	var oldValues:java.util.HashMap = viewScope.get("oldValues");
	var newValues:java.util.HashMap = viewScope.get("newValues");

	for (key in oldValues.keySet()) {
		// Record Audit Trail Documents
		recordAuditTrail("Changed The Field " + key + " from " + oldValues.get(key) + " to " + newValues.get(key))

While this method works very well and is easy to implement, just add the valueChangeListener to each field you need it on, it is not perfect. It only works for a single datasource and it has a few issues with Date/time fields that I need to sort out, but it is a good starting point for anybody who needs to implement an audit trail in their Xpage applications.

Tagged with: , , ,
Posted in None
One comment on “Using a valueChangeListener to build an Audit Trail
  1. Reynald says:

    This is only applicable when you use a single Field. When i implemented this in one of our project, I use Tony McGuckin changeValueListener binding the component and upon change, it will record or create a Audit Trail. But if you look intently in the XPages LifeCycle, the ChangeValueListener is processed in the “Apply Request Values “. And this is drawback because only the changes must be done after clicking the Button.


Comments are closed.

%d bloggers like this: