My friend Richard Bair recently told the world that the colloquial definition of JavaBeans as an object with getters and setters is incomplete, and demonstrated how a handy base class can make adding PropertyChangeListener
support easier.
I’d like to pick up where his discussion left off. Over the past few years, I’ve been pondering how to make it easier to develop Swing applications. One area of pain in Swing is binding; that is, copying values from Swing widgets to business objects and vice-versa.
Swing components, as Richard mentioned, are observable via PropertyChangeListener
s and other types of listeners, so detecting when widget values are changed and copying those values to objects as a result is pretty easy (though there are a few subtleties to get right). The reverse, however, is not free. In order to detect when values in an object have changed, one needs to use some mechanism like the JavaBean spec to introduce observability into JavaBeans.
And for me, therein lies the rub. Firstly, I hate repeating myself, and writing firePropertyChange("property", old, new)
over and over again seems a big pain. Second, the string literal gives me the heeby-jeebies. If I’m going to all the bother of using a strongly-typed language, I hate having the bugs of loose-typing introduced with none of the benefit. Third, what if I’m using some pre-constructed business objects that aren’t observable and can’t easily be made to be observable?
(A quick tangent: Everyone ought to know that walking stacktraces is one of the most expensive VM operations around. Yet, just for fun, I created a version of firePropertyChange(...)
that automatically obtains the property name by walking the stacktrace. Here it is:
protected void firePropertyChange(Object oldValue, Object newValue) { try { throw new Exception("stacktrace"); } catch (Exception e) { StackTraceElement[] st = e.getStackTrace(); String property = ReflectionUtils.getPropertyFromAccessor(st[1].getMethodName()); firePropertyChange(property, oldValue, newValue); } }
Never use this code in a tight loop.)
For all these reasons, the binding solution I use takes a hybrid approach. To save myself a lot of explanation, let me just say that when the Swing widget’s value is changed, the framework can copy the value to the business object automatically, but the framework does not copy the value back to the Swing widget until the developer specifically requests that the value be copied from the business object back to the Swing widget.
In practice, it looks something like this:
binder.defineBinding(businessObject, swingWidget, "fieldName"); // at some future point, call this next line to update the UI from the object binder.updateWidgets();
I’ve used variations on this technique for a while now, and it works very well. When combined with a container-managed form system, it actually works nearly as well as true bi-directional binding as the form lifecycle can introduce automatic calls to binder.updateWidgets()
at various flow points (and the automatic behavior can of course be disabled).
But I’ve been aware for a long time what the real solution to this problem is: Aspect-Oriented Programming (AOP). Using AOP, you can easily add PropertyChangeListener
support to any object, even those for which you don’t have source code, and generally solve all three of the problems I have with manually adding PropertyChangeListener
support.
Unfortunately, I don’t use an IDE that has AOP support, so I haven’t been able to explore AOP much up to now. But the latest release of my favorite AOP framework, AspectJ 5, enables a new type of AOP syntax based on Java 5 annotations (called @AspectJ).
Using @AspectJ, I can create a simple class that handles all of this property firing for me:
@Aspect public class JavaBeanObservabilityAspect { @Around("execution(* *.set*(..))") public void timeElapsed(ProceedingJoinPoint jp) throws Throwable { if (jp.getTarget() instanceof JavaBean) { // only fire listener if a JavaBean JavaBean o = (JavaBean) jp.getTarget(); String property = ReflectionUtils.getPropertyFromAccessor(jp.getSignature().getName()); Object oldValue = ReflectionUtils.invokeAccessor(o, property)); // proceed with the invocation of the method jp.proceed(); Object newValue = jp.getArgs()[0]; o.firePropertyChange(property, oldValue, newValue); } else { // if not a bean, just proceed with the invocation normally jp.proceed(); } } }
The above is a crude beginners example of how to automatically fire the event on all JavaBeans — a more refined example would show you how to actually add PropertyChangeListener
support dynamically to any object. The example I gave is also problematic in that if the setter already fired the event, it would be fired again by the aspect. And, I coded this version here in my blog, so if it doesn’t compile, don’t sue me. But… you get the idea.
The beauty of @AspectJ is that because it just uses normal Java 5 annotations, you can use your IDE to create and compile the aspects like any other part of your code base. To actually use the aspects at run-time, you must weave the aspect into your code.
Weaving is the process of augmenting Java classes with the additional instructions defined by one or more aspects (such as the one above). You do that with a special compiler that comes with Ant tasks that can be easily introduced into your build process.
But for the impatient, there’s this cool new thing in ApsectJ 5 called load-time weaving (“LTW”). With LTW, you pass your JVMTI compliant (Java 1.4+) VM a property that configures an agent that can weave your classes as they are loaded:
-javaagent:aspectj/aspectjweaver.jar
You then create an XML file (META-INF/aop.xml
) that specifies with aspects to weave, and into which classes, such as:
<aspectj> <aspects> <aspect name="org.galbraiths.beans.JavaBeanObservabilityAspect"/> </aspects> <weaver options="-verbose -showWeaveInfo"> <include within="org.galbraiths.beans.JavaBean"/> </weaver> </aspectj>
And presto! You’ve got AOP in your project (along with a handy speed hit whenever the affected classes are loaded for the first time). There are all kinds of fun things to do with AOP, such as introducing ultra-low-intrusion microprofiling, verifying proper Swing threading, and so forth. Blogs for other days.
So — for the purposes of binding, you may not need observability in your objects, but if you do want it, consider AOP for adding observability without repeating yourself all over your codebase — and for adding it to code you don’t control.
If the pretty good AspectJ project documentation doesn’t do it for you, check out AspectJ in Action, written by Ramnivas Laddad — a friend and collegue whom I greatly respect. His book doesn’t cover @AspectJ, but is widely considered the best AspectJ book around.
I know, I know: the code in this blog is messed up if you’re reading this here on my blog’s webpage. Its truncated and has all these backslashes before the quotes (which aren’t present in the blog’s source, btw). I need to change my theme, but in the meantime, check out a better rendering over at java.net.
thanks for posting this. my immediate thought after reading richard’s post on java.net was ‘..aop..’ but i was too lazy to write up anything on it.
the more time goes by, the more i think aspectj should be baked into the java language. we shouldn’t have to jump through so many hoops to exploit the power of aspects. it solves too many problems (such as the one above) to simply ignore.
eitan: Thanks for reading. I agree; I thank Sun for being cautious about radical new features, but for my money, AspectJ would be a wonderful addition to the JDK.
I admit I tear up a little when thinking about how wonderful it would be if little old IDEA had incredible support (like Eclipse) for AspectJ. I would use it all the time all over the place in so many ways (e.g., mix-ins).
Ben,
Please blog about any other aspects that you’ve found useful.
Thanks,
Curt
PS–Is there some reason you need to throw and catch the exception? Why not just use something like this?
StackTraceElement[] st = new Throwable().getStackTrace();
or
StackTraceElement[] st = Thread.currentThread().getStackTrace();
Take a look at the Joda-Beans prejocts, where I’m building some property-support code. Maybe your work could build on that?