So, I was quite excited when I found PropertyChangeSupport class in Java API specification. However, functionality provided by this class requires changes in bean class code. I would have to modify the Person class, add code for PropertyChangeSupport object, and add a firePropertyChange method call to each setter method in the bean class (or at least for setter methods of those properties my system cared about). This, wouldn't be a big problem if all bean classes were known in advance and their source code preferably available and open for editing, however, in my case, it wasn't so.
After several failed experiments, including but not limited to AspectJ, I found a solution which you can see int this article - Javassist framework for runtime bytcode manipulation. I used it to generate dynamic proxy wrappers around any Java bean with firePropertyChange called in each of the chosen setter methods. Yeah, I know it's heavy thinking already, so let's get to example.
We have a Java class:
And our generated proxy class would look like this:
And here is how to make a proxy object:
The list contains names of all properties to be bound, so that we can let some property changes be ignored. As simple as that! Now, whenever we change any bound property on the returned object, the assigned listener would do its job. Exactly how should listener class look like depends on the particular system. For the purpose of this tutorial, I created a dummy implementation:
And at any time, if we want our original untracked bean we can do this:
Ok, that's how you use it. Now, how it's made. We had several problems here. First was to create a runtime wrapper proxy around a bean object, then to make our object "trackable" by adding PropertyChangeSupport support (no pun intended). From another point of view, we first had to generate a runtime class, then to instaniate it around our original bean.
So, the solution contains WrapperProxyFactory class with a concrete method createWrapperProxy and abstract methods adjustProxyClass and adjustProxyObject. The concrete method handles all the proxy-wrap-and-delegate stuff and leaves the proxy purpose itself to a subclass implementing abstract methods. In this case subclass is named BoundBeanWrapperProxyFactory. Its adjustProxyClass method alters all bound setters to fire event on each call, and adjustProxyObject adds listeners to wrapper objects. We also have interfaces WrapperProxy and BoundBeanWrapperProxy which expose methods which are added to our proxies in runtime, such as retrieveOriginal.
Separation of these factories did complicate things a little, however a proxy that wraps around a bean and does something else may be useful, too. Then all we have to do is write a different factory subclass.
Anyway, you can download code from the link here or digg it out from my hg repository on Google Code. Some code changes (i.e. package names) may be required for use.