I have 2 maven projects that share a JPA project as a dependency in their individual POMs. The JPA project is on ver 2.1 and has hibernate envers implemented successfully.
However I now need to create a custom RevisionEntity as I need to audit extra properties such as the logged in user id. Problems are:
i. I cant implement this directly in the JPA project
ii. The implementation for retrieving the logged in user differs for the parent maven projects.
The real challenge is actually in implementing the RevisionListener. I tried creating an abstract class revisionlistener within the JPA project itself which I can extend and implement within each parent maven project. Lol! This didnt work!
I would appreciate any help whatsoever that gets me through this. Is there a way out?
I cant implement this directly in the JPA project
If the information you want to collect about each revision is identical regardless of the two applications that use your JPA application, then there should be no problem implementing the revision entity and listener functionality in the common JPA project. The only reason you wouldn't be able to do this is if the information, in other words the table structure differed.
To accomplish this, we'll need a layer of abstraction. Rather than extending the RevisionListener
as you were attempting, you would have another interface that represents a service the listener will require and then implement that service in your subprojects.
/**
* Interface that implementations should extend to provider revision
* information to the Envers revision listener.
*/
public interface RevisionInfoProvider {
String getUserName();
}
/**
* A class that holds a RevisionInfoProvider as a ThreadLocal.
*/
public class RevisionInfoProviderHolder {
public static RevisionInfoProvider getProvider() {
// impl details here to get a RevisionInfoProvider.
}
}
Then your revision listener implementation may look something like the following:
public class RevisionListenerImpl implements RevisionListener {
@Override
public void newRevision(Object revisionEntity) {
if ( CustomRevisionEntity.class.isInstance( revisionEntity ) ) {
CustomRevisionEntity entity = (CustomRevisionEntity) revisionEntity;
// get provider from thread local and set user details
RevisionInfoProvider provider = RevisionInfoProviderHolder.getProvider();
entity.setUserName( provider.getUserName() );
}
// todo: may want to assert in case you change entity types?
}
}
At this point, just create your CustomRevisionEntity
, annotated it correctly, including pointing it to the RevisionListenerImpl
class as the listener implementation Envers should use.
Now in each of your subprojects, all you need to do is:
- Implement RevisionInfoProvider
- Populate RevisionInfoProviderHolder
The first is simply the gateway to how the JPA project can get the details from each subproject. In a spring web application, this may likely just be some spring service bean for example.
The second is just something that each thread needs to do before calling into any JPA stuff. Again in a spring web application, this would likely be some type of web filter that executes at the top of the filter chain before user code is invoked that makes sure the ThreadLocal is set appropriately.
The implementation for retrieving the logged in user differs for the parent maven projects.
Coming back to this point, as you can see above, the layer of abstraction with the RevisionInfoProvider
handles this problem nicely. You build out a common framework for Envers to do it's job while leaving it flexible enough so that each of the parent projects can diverge as necessary.
What if each parent project stores differing structured information?
If you do find yourself in this case, you'll need to implement both the revision entity and listener in each parent project separately or follow what I mentioned above but use a revision entity that satisfies requirements of both parent modules.
You just need to make sure the revision entity can be picked up by the JPA module during the bootstrap phase when entities are being examined.
Hope this helps.