As i mentioned in the previous entry, one of the disadvantages of overriding the
paint() method for validation overlays is that you need to do it for each one of the components that can have validation errors. So, you no longer use a “pure”
JTextField, for example. You either override the
paint() method when you create your text field, or use a custom class that extends
JTextField and overrides the
paint() method. Needless to say that the impact on your UI creation code is rather large and unpleasant. Wouldn’t it be nice if you could just inject the required functionality to all Swing components?
Although i might not be the biggest fan of aspect-oriented programming (AOP), it certainly sounds like a promising approach. In fact, AOP has been shown to address other Swing-related problems by Ben Galbraith (for adding property change support on Java beans) and by Alexander Potochkin (for tracing EDT violations). Disregarding all the hype around AOP (that seems to have thankfully died over the past year), the eventual result is that you inject the functionality you need at the specific methods in your classes. In fact, i used a very similar approach in one of my look-and-feel related projects. Isn’t this exactly what we’re looking for?
Well, it almost is. The main caveat with the bytecode injection (which is the eventual result of applying aspects to the classes) is that it is done on the bytecode class that you can control. The examples mentioned above apply this technique on the application classes. Where do we want to apply this? Mostly (unless you override the
paint() method) on the core JVM classes. And this is where our problem lies.
AspectJ is one of the leading implementations of AOP in Java (especially since it has joined forces with AspectWerkz some time ago). Its load-time weaving (LTW) explicitly excludes the core classes (that would be
JComponent among the rest). In addition, your application most probably doesn’t call the
paint() method itself (that is left to the Swing painting pipeline, and as we have seen in the discussion on the repaint managers, there is no extension point that paints a specific dirty component). And so, if you want to use AspectJ, the only other possible option is to weave the core classes at compilation time.
You can download and build Swing using the latest JDK 7.0 weekly builds. Then, you can weave your aspect into the created swing.jar and put it in front of the bootstrap classpath. While certainly doable, this approach is far from optimal. First, you bundle an extra 4.1MB jar. Second, you’re confining yourself to the specific JDK build, including all its bugs. Third, you might run into classloader problems in specialized runtime environments, such as WebStart, applets and others.
A way around the limitation of weaving core classes would be to extend all the core Swing components, override the
paint() method to call the super implementation and the weave the validation overlay logic. While it will work, it is hardly an improvement over overriding the
paint() directly (without the overhead hassle of using aspects); you’ll still end up not using the core components directly in your code.
While the list of Java AOP frameworks contains other options (besides AspectJ), i am not aware of another AOP-based approach that will allow weaving core classes at runtime without any limitations. If you know of any, i will be more than happy to hear about them in the comments.