ADF 11g : Log Data Manipulation

Today I had a request to implement a generic logging mechanism for all changes of data (DML) in an ADF application. The log needs to be persisted in a database table. This application uses ADF-Business Components and an Oracle Database. My first idea was to use database triggers for all tables (insert, delete, update for each row) and write the data to a log table. However that was not generic enough because this has to be implemented on every new table. I had to come up with an ADF solution. It’s basic, no rocket science, but useful
The solution I chose is very straightforward and can be implemented in a few steps. It is based on the fact that the EntityImpl class handles all DML, and that all EntitityImpl classes can extend a base class. This post doesn’t describe how to write the log to the database (see Final Remark), but I will show how you can gather all necessary data for logging.

Step 1: Create a base class and configure your project to use it

First I created an empty class extending the EntityImpl class.

package nl.amis.technolgy.baseClasses;

public class AmisEntityImpl extends oracle.jbo.server.EntityImpl {
    public AmisEntityImpl() {
    }

After this I made sure that the project uses this class as a base class for all Entity Objects. This can be configured in the project properties.

ADF 11g : Log Data Manipulation baseClass
Step 2: Configure pre-existing Entity Objects to use the base class

All entities in the project will use this base class. That is, all new Entity Objects, but not all existing Entity Objects. That is one of the reasons why you should create and configure base classes at the start of your project. Even if you base classes do not contain any logic, it is much easier to create them before you create any Entity Objects.

However, there is a way to configure existing Entity Objects. The easiest way is to use the ‘source’ tab of the entity object editor. There I added the RowClass property, and gave it the value of my base class.
ADF 11g : Log Data Manipulation
I had to do it for all existing Entity Objects.

Step 3: Write the code for the base class

The code is simple. I want all DML to be intercepted and logged. Therefor I had to create my own doDML() method, that calls super first, and after that calls a method to do the logging (line 9 below). The logging method does all the work like gathering all attributes of the entity object, their old and new values (lines 37 and 38) , and things like the user (line 32) and timestamp (line 30).

package nl.amis.technolgy.baseClasses;

public class AmisEntityImpl extends oracle.jbo.server.EntityImpl {
    public AmisEntityImpl() {
    }

    protected void doDML(int operation, TransactionEvent e) {
        super.doDML(operation, e);
        callLoggingProcedure(operation, e);
    }

    protected void callLoggingProcedure(int operation, TransactionEvent e) {

        String entityName = getEntityDef().getName();
        int count = getEntityDef().getAttributeCount();
        String name = "";
        Object val = null;
        Object oldval = null;
        String dmlAction = null;
        Object theKey = getPrimaryKey().getAttribute(0);

        if (operation == DML_INSERT) {
            dmlAction = "an Insert";
        } else if (operation == DML_UPDATE) {
            dmlAction = "an Update";
        } else if (operation == DML_DELETE) {
            dmlAction = "a Delete";
        }

        Timestamp now = new Timestamp(System.currentTimeMillis());

        String currentUser =
            ADFContext.getCurrent().getSecurityContext().getUserPrincipal().getName();

        for (int i = 0; i < count; i++) {

            val = getAttribute(i);
            oldval = getPostedAttribute(i);
            name = getEntityDef().getAttributeDef(i).getColumnName();

            if (isAttributeChanged(i)) {
               System.out.println("______________       Start Change Log   _________________");
               System.out.println("There was a change in " + entityName);
               System.out.println("Key of this entity =  " + theKey);
               System.out.println("_________________________________________________________");
               System.out.println("Change was " + dmlAction);
               System.out.println("It was changed by " + currentUser);
               System.out.println("Changed on = " + now.toString());
               System.out.println("_________________________________________________________");
               System.out.println(name + " is changed. ");
               System.out.println("OldValue =  " + oldval );
               System.out.println("NewValue = " + val);
               System.out.println("______________       End Change Log     _________________");
            }
        }
// here goes code to write to database via callStoredProcedure.
}

When running the app and commit changed data the console will show what I just did.

ADF 11g : Log Data Manipulation log

Final Remark.

I did not describe how to persist the log in a database table but that is very easy. Use a stored procedure and call it by a callStoredProcedure() method. Depending on requirements you can write the log to a single database column (large varchar2 or clob) or create columns for old value, new value, entity, user and so on.

6 Comments

  1. gb February 10, 2012
  2. Erdenebayar Erdenebileg November 1, 2011
  3. Aino Andriessen January 9, 2011
  4. Lucas Jellema January 7, 2011
  5. Luc Bors January 7, 2011
  6. Lucas Jellema January 7, 2011