so basically what you need to do is to absract from the way how it is determined which classes and properties are versioned.
That's of course possible, but you'll need to change the Envers code a bit. If you look in org.jboss.envers.configuration.EntiiesConfigurator.configure, you'll notice that there is a loop over persistent classes. For each persistent class, an AnnotationsMetadataReader instance is created, which reads the @Versioned annotatio information from the class and the fields. It returns a PersistentClassVersioningData instance.
What you would need to do, is replace the AnnotationsMetadataReader with your "XmlMetatadaReader" or whatever the name, which would return PersistentClassVersioningData.
If you could make it in some way pluggable and contribute to Envers, that would be great :)
Thanks Adam for your help! I will keep you posted. Since I want to implement versioning as a cross-cutting behaviour, I have few quesions to clarify before I proceed with:
1. In the Envers source,I believe the configuration for versioned entities are done in the same persistence unit as the versioning entities. If I want to eliminate this dependancy, I assume I just have to pass in my own Configuration object for VersionsConfiguration getFor method. Is my assumption correct? You can assune that I will be removing all dependencies in the code where persistent class is retrieved from the config object.
Can you also let me know how i can contribute to Envers? Thanks!
yes, versioned entities are placed in the same persistence unit. But I think it's changeable.
Your assumption about passing your own Configuration object to the getFor method is not correct. You need to pass the original Configuration object there, as the information about the persistent classes (which classes correspond to entities, which fields from them, how do they map to columns etc) is read from it - and Envers needs that information.
However, maybe you could add a second Configuration parameter - that would be "your" configuration - and you could add the generated versions-entities mappings to it.
How can you contributo to Envers? Well, if you'll have some working modified Envers code which you think can be used by more people, just send it as a patch, and then if you wish you'll get access to the repository :).
By changing few classes (around 10), I got to the point where I pass in a list of PersistentClass obtained from the original configuration and my new Configuration object to Envers, get the entities versioned (voo hoo!). Not many changes, I will cleanup my code and post it here for people to use.
Since I want to turn on/off my versioning of entities regardless of my application lifecycle, is there anyway I can retrieve or create the configuration object or the list of PersistentClasses at runtime from Hibernate so that I can pass on to construct VersionsConfiguration object?
I know this is hibernate specific question not envers. Any help/pointers is much appreciated. Thanks!
Also, is there a revert functionality in envers which reverts the original table with a particular version?
well, I suppose the configuration of entities and creation of versions entities should be done at startup - do you want to add/remove/change entities at runtime? That would be unusual :).
If you want to temporary turn of versioning, maybe just change the entity listener to check the necessary condition.
The revert functionality isn't there - yet :). It's been mentioned a couple of times, so I guess at some point it will be added.
Exactly. I want to add and remove entities to be versioned at runtime. Only thing that I am struck with is that to access the Configuration object to construct versions configuration.
I dont't know whether I have specified it clearly before. I am implementing versioning as a standalone service which can be started and stopped regardless of the main application. So, when I start my versioning service, it has to read list of classes to be versioned from an XML file, have to read the Configuration object of main application (this is where envers uses listeners initialize method), construct the versions configuration and finally intercept the persisting methods to do versioning.
The idea is to take down vesioning service at any point in time. Add/Remove classes in the XML file. restart the versioning service. And I will get my newly added entities versioned.
Since I have clustered Jboss environment, I was thinking to implement a distributed cache and put all the persistent class into the cache in the initialise method. Therefore when I have to construct my versions configuration (at versions service startup), I can access the list of persistent classes from the distributed cache. It should work. But there has to be a simple way to access the current list of persistent classes in memory from hibernate. Is it possible?
I'm not sure if Hibernate allows you to add and remove versioning of classes dynamically.
First of all, versioning entities requires that the table for the version is created. Would you also like to dynamically change the database schema? That's normally fixed.
Then, you could try creating a new configuration when you have new entities, and calling the build() method to create the mappings. Anyway, I think you should first experiment with plain Hibernate :). And consider if you really want to add entities at run-time.
No.. No.. You got me wrong. My database schema remains the same. Its just the marking of entities to be versioned is done at run time.
Good news is I got it working. Here is what I did:
1. I modified Envers project so that creation of Versions Configuration is not tied to the original entities Configuration object. Rather it accepts a list of persistent classes (PersistentClass) and a new Configuration object for the versioned entities. The list of persistent classes passed above is the list of entities that needs to be persisted. (I will clean the code and post it here. :-) ). We can even specify a separate datasource for versioned entities.
2. In the initialize method of the interceptor of original entities, I basically put the persistent classes from the configuration object to Cache. This will be accessed later to construct the versions configuration.
3. When the entity gets persisted, the interceptor asks a versions service(MBean) if the entity need to be versioned. Since I can access this servicevia JMX console, I can add and remove classes to be versioned at run-time. If the entity needs to be versioned, it constructs the work unit (without the versions config) and passes it to to a controller. The controller is basically a processing MDB sitting behind a queue. It takes the work unit from the queue, creates the version config(if needed) and persists the version entity.
The main advantage of this is
1. Versioning doesn't tied up to the original configuration object. Hence not tied to the same database as original entities.
2. Versioning is done asynchronously. All the interceptor does is post the work unit to queue and return.
3. I can control which entities needs to be versioned or not at run-time
Few things that would have been nicer (atleast for me):
1. Versions config should take in a list of class mappings metadata (from session factory) to construct the versions entity configuration rather using the PersistentClass from Configuration. I dont know it is possible but it will remove the dependancy of having the initialize method and using the configuration object.
2. In my opinion, most of the users doesn't want versioning to affect the performance of main application. So, there is no need to tie this to the same transaction/session to original entity session.
Having said that, I should agree Envers is a great tool to do for what is supposed to do.. Versioning!
Good Job! Well done!
ok, now I understand :) So you basically make the calls to your mbean in the VersionsEventListener?
I'm curious to see the code :).
Asychronous storing of versions may be a good option for some, but I guess many people would also like to have it synchronous, like it is now. It simplifies the design (no need for a persistent MDB, etc).