Sunday, February 28, 2010

Eclipse Enterprise: Not so optimistic locking - a simple approach with XMPP

We work an a rather small enterprise app with a "Plain Old" Eclipse RCP Client. Due to some constraints our domain model is based on Pojos (Java Beans) that are persisted with a hibernate backend. In the meantime I look on those who use EMF/CDO/Riena with somehow jealous eyes, because of all the nice things they can do, but we cant do so easily. Since we don't have much concurrency, we rely on hibernates optimistic locking strategies to avoid conflicts. That means if two users work on the same entity, the one who saves early wins, the other one looses his changes and gets an OptimisticLock Exception. Thats sometimes annoying for the users. So I did a simple "not so optimistic" locking approach today that cost me less than half a day to implement for the whole thing. Our server communication -in terms of services- relies on Spring's HttpInvoker so far (keep it simple) but we cannot do callbacks to the client with that technology. We tried different ideas to solve that issue starting from JMS to finally ending with an XMPP based messaging solution. Every client sets up a permanent XMPP connection to the server over wich we can send messages to whatever party we want. With XMPP we can even see who is online for free. Now every time we open an editor we send a simple XMPP Message to the server that stores a unique id and the userId of the entity in a simple HashMap. If two users open the same entity, they both get a warning message about a potential conflict. With a simple PartListener on the client we know when an editor gets closed, send an unlock message and notify the other user(s) about the unlock. Thats what we get with about three hours of work. Never thought it would that simple. I become more and more a fan of XMPP Messaging.

Outlook:
What we could do now is register an hibernate "onPersist" event listener and send a message to the other parties that the object has changed on the server, so that they could refresh their editor content. Maybe thats something for the next weekend session.

PS: Some hours later...

I build an abstract base class for my Editors that does the following:


public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
super.init(site, input);
Object entityFromInput = getEntityFromInput(input);
EntityPacketFilter filter = new EntityPacketFilter(entityFromInput);
Activator.getDefault().getXmppConnection().addPacketListener(this,filter);
LockHelper.sendLockMessage(entityFromInput, true);
site.getPage().addPartListener(LockPartListener.instance);


}


The Editor sends a lock message on init, and registers a Partlistener to send an unlock on close; It adds itself as a PacketListener to the XMPP Connection. The hibernate PostUpdateEvenListener sends an Update Stanza which goes to:



EntityChangedDialog dlg = 
new EntityChangedDialog(AbstractLockingFormEditor.this);
int ret = dlg.open();
if (ret == Dialog.CANCEL) {
 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
  .getActivePage().closeEditor(AbstractLockingFormEditor.this,   
  false);
  return;
}
// reload from db
IEditorInput newInput = EntityEditorManager.getInstance()
 .createInputForEntity(dlg.getEntityClass(),dlg.getEntityId());
setInput(newInput);
rebind() // rebuild databinding;
firePropertyChange(EditorPart.PROP_INPUT);
dirty = false;
setDirty(false);
firePropertyChange(EditorPart.PROP_DIRTY);


That's it. No more Optimistic Lock Exceptions from today.

Friday, February 26, 2010

XMPP Server Apache Vysper

For those who are in trouble with the GPLv3 license of openfire and other XMPP servers, I found a subproject of Apache Mina called Vysper which aims to implement an XMPP Server under an Apache License. It is still  not released yet, I just build a snapshot with maven and started off to play with it. It seems easy to provide your own extensions (I need to connect to my users database) and it works fine with the smack client api. There come InMemory and JCR (jackrabbit) storage adapters with it, no support for jdbc storage right now. I guess I will go with InMemory because I don't need persistence over Server restart right now. You can find the project here: http://mina.apache.org/vysper/

Sunday, February 21, 2010

Auditing with hibernate envers

I used the weekend to play around with hibernate envers. Envers is a framework that enables you to audit your database changes with hibernate. It was really easy to setup, just add the jar and set some configuration parameters. You have to add the envers listeners to your EntityManagerFactory and from then on every entity annotated with @Audited stores all changes in a seperate audit table. Envers enables you to look at each revision of your entity and track back your complete history. Since Envers stores eachs revision completely, I wrote some code to make a diff on my entities. That was quite tricky, because of my entities equals and hashcode methods compare id and version. Since different revisions have different versions I could not rely on that to work. But with some "frickeling" I made it work in a day. I now can show the change history of my Objects, you can even see the user that made the changes (a little spring aspect sets a threadlocal on the envers audit listener).


Envers will be part of hibernate 3.5, and as I have seen so many tricky auditing solutions before, I guess it could become a standard for hibernate auditing issues.

Using Mapstruct with Protobuf3