With the recent introduction of the Java Persistence API (JPA), WebSphere is forging new trails with their JPA solution. Built on top of the Apache OpenJPA
project, the WebSphere JPA solution provides exceptional function, performance, and support. The postings on this site are our own and don’t necessarily represent IBM’s positions, strategies, or opinions.

Tuesday, January 13, 2009

Auditing with OpenJPA

A transactional application often requires to audit the persistent entities. In this brief example, I will present a small code example to outline how an application can audit changes via the life cycle callback methods. The JPA specification prescribes how an entity or a separate, stateless entity listener instance can receive callback notification during various life cycle state transition of an entity. Seven transition events are defined: PrePersist, PostPersist, PreRemove, PostRemove, PreUpdate, PostUpdate and PostLoad. To receive these callbacks either annotate certain methods a) of the entity class itself or b) of a separate entity listener class. The signature of the method in the entity class itself should be

void <METHOD>()

Whereas, callback methods defined on an entity listener class have the following signature

void <METHOD>(Object pc)

In the later case, the managed entity is passed as an argument.

Given this basic framework, how can we implement audit facility such that an application can track any update made on a persistent entity? Of course, to compute this change, the application must be able to access the entity state before and after the update. The central question is:

how can an application access the previous state of an entity inside a callback method?

The answer lies in the fact that OpenJPA can store the state of an entity as it enters the managed lifecycle. This internal copy is used by OpenJPA when a transaction is rolled back. The state of the entities participating in the failed transaction is restored to the original state i.e. as it were before the transaction started. The simple example presented here shows how to access that copy. Of course, a real audit facility has to figure out how to compute the difference between the current and previous state of an entity and where to record those differences.

The meat is in audit() method annotated with @PostUpdate to receive the callback notification. Now, within the method body, following steps are carried out to access the previous state of the instance.

Line 36: Cast this instance to org.apache.openjpa.enhance.PersistenceCapable interface. The cast is safe. Because OpenJPA ensures that every persistent class implements PersistenceCapable. OpenJPA actually modifies original bytecode of audit.PObject class. This bytecode modification process is called enhancement and is described in detail in OpenJPA documentation.

Line 37: Get the StateManager. Every PersistenceCapable instance is managed by a StateManager. It is the StateManager who intercepts every access and mutation of entity state and calls the requisite underlying OpenJPA infrastructure to load or store the state of an entity which in turn may access the database via JDBC.

Line 38: Get the SaveFieldManager. A StateManager may refer to a SaveFieldManager to which StateManager delegates the task of maintaining a copy of managed instance.

Line 39: Get the old state. The old state is maintained by the SaveFieldManager.

Line 40: Cast is back to audit.PObject. This entity refers to the state of PObject as it entered a transaction.

At this point, a real audit application perhaps will do some sort of state comparison to determine what has changed between current and the previous state of this instance. I am just printing their values on the console.

Now let us write a simple "Hellow JPA World" style application to check that the callback is received. I am omitting the scaffolding code to get a JPA EntityManagerFactory etcetera and just listing the transactional method.

Lens,When a <jta-data-source> is specified, OpenJPA will attempt to do the global jndi lookup for this datasource first. If that lookup fails, then OpenJPA attempts to use the openjpa.Connection* properties to resolve the datasource. So, in this case, it looks like the lookup of the "jdbc/test" data-source is not working.

You mention that the datasource works in RAD and the "websphere connection". In the latter, are you referring to doing the test connection from within the WebSphere admin console?

Are you attempting to use the datasource from a Java EE application (ear or war)? If you have the proper bindings in your application, you may actually have a local (component) level jndi name for this datasource. Something like "java:comp/env/jdbc/test".

From your application, you should be able to do the jndi lookup using the name provided in your <jta-data-source> element. If that doesn't work, then OpenJPA won't be able to get access either. It's a straight forward lookup operation.

You might also want to consider Tracing this process to find out whether the problem is in the configuration or WebSphere or OpenJPA. The trace spec for OpenJPA is "openjpa=all=enabled".

What you are attempting should work just fine. I just don't think we have enough information to figure out the source of the problem yet.

The trace setting mentioned (openjpa=all=enabled) is for the WebSphere environment. When this trace setting is specified, the trace output from OpenJPA will be merged with the normal trace output of the WebSphere Application Server. So, under default configurations, the OpenJPA trace output will be merged with the log file in this location:

c:\WebSphere\AppServer\profiles\AppSrv01\logs\server1

If you are not running within WebSphere, then you will need to set your trace settings for OpenJPA via your persistence.xml file. In this case, you will need a property setting along these lines:

<property name="openjpa.Log" value="DefaultLevel=TRACE" />

More information on logging in general can be found in the InfoCenter:

I was able to achieve to get the trace to trace.log. I am working with RAD 7.5 and was wondering if there is a way to redirect the trace output to the console view. I know that System.out logs can be viewed in the console, do you know if there is something similar for trace logs?

Good. I'm glad that you got your trace output to trace.log, at least. As far as redirecting this to your console within RAD, I'm not aware of that ability. I checked with one on my experts and it sounds like you already had a similar conversation with him (Mike) on FreeNode... :-)

Really really wish Websphere was not intercepting the logging mechanism with its own tracing annoyance.Thanks for the help though. Each time I run a query, will have to reload the trace.log file to see the SQL, whereas a simple redirect to the console would have made development and testing so much easier.

Hi,I can understand your frustration, but we're kind of stuck in a hard place on this one. In the container-managed environment, if WebSphere didn't intercept the trace settings, then a developer's persistence.xml could wreak havoc on a production system.

One approach that we suggest quite often is to test your Entities outside of a container-managed environment. Since you are interested in seeing which SQL is being produced, the same SQL will be generated whether the Entities are used within or outside of a container. Granted, you might need an alternative driver to test the Entities, but the ease of unit testing outside of a container may be worth the effort.

I am attempting a slightly more sophisticated auditing solution than you have outlined here. Perhaps you can tell me I'm doing a bad thing.

I have two entities Entity and EntityAudit. EntityAudit has all fields of Entity plus some audit info (ie datetime, user, etc). In a PostUpdate lifecycle method of Entity I am using OpenJPAPersistence.getEntityManager(this); to grab an entity manager and call persist against it passing the newly constructed EntityAudit that contains essentially an audit snapshot of Entity.

First of all this doesn't actually work. OpenJpa issues an update statement against the Entity table and issues a select statement against the EntityAudit table and no insert into the Audit table. After seeing Pinaki's post in this thread http://n2.nabble.com/Updates-to-entities-via-Lifecycle-callback-methods-td1110631.html I am almost positive this is a bad idea.

I guess I am looking for suggestions on alternatives. I'm not happy with the idea of a service method that all updates to my entity would have to pass through because that is really only suitable when an entity has been detached and is being brought back into context. Right now I'm leaning towards db triggers and I'm sure that will lead to issues with jpa caching; I would definately prefer to stay with a jpa-only solution.

hello,I aint able to get "System.out" and "System.err" messages of Java filein SystemOut or SystemErr log of WebSphere. I am getting all kind of server messages in log files but i m not getting the print message of System.err or System.out in log files. What changes i should do to get that messages?

Hi ( H@n|)@|\|,By default, WebSphere should be intercepting the System.out and System.err println's and redirecting them to the SystemOut or SystemErr log files. No additional configuration should be required to make this happen. If this is not working as desired, I would open a problem report (PMR) with IBM support.

Hi Meenakshi,Your post didn't format very well, so it was a little difficult to read. You might want to try posting to the OpenJPA users mailing list [1], just to help with the formatting. And, there may be a larger Auditing audience there.

But, from what it looks like you are experiencing a classloader issue. The "missing" Auditor class is part of the normal OpenJPA artifacts. What is your environment? Are you running OpenJPA standalone in a JSE environment? Or, as part of WebSphere? Or, some other app server? What version of the app server are you using?

Also i am not able to autowire any Spring beans inside the OpenJPA2.2.2 auditor and the reason it seems is the Auditor gets instantiated by the persistence so we are not able to inject any spring beans inside the auditor.Does any one has does any Dependency injection inside the OpenJPA 2.2.2 auditor.

Hi Meenakshi,Auditing should work just fine with JTA transactions as well as non-JTA transactions. Your posting of the persistence.xml file didn't work very well. I would suggest turning on trace to see if you can determine why your auditing facility is not getting invoked (or at least check your logs for any unexpected errors).

Also, as my previous reply suggested, you might want to post the OpenJPA Users mailing list to get a larger population reading your question(s).