JavaBean Aspect

Note: this page is very dated at the moment. If you are interested in an AspectJ version of this aspect, please visit the Handy-Aspects project on Java.net GitHub. In the near future, a more refined JBoss AOP version will be released in that project as well. The JBoss AOP version will no longer be maintained and any future work will be based on AspectJ. You can follow that work on GitHub here.

Like some, I was wondering if there is there a practical use for AOP outside of the typical logging and transaction examples? I recently came accross a situation where I realized I had a cross-cutting concern and I figured I’d see if I could create an aspect to address it. I had 29 classes I created for an application that uses Hibernate. These classes were nothing more that value objects with simple setter and getter methods. I was going to use these classes in a Swing application. Additionally, I was looking into the JGoodies Binding framework to ease binding data to GUI components. JGoodies Binding requires that your beans support bound properties and my classes did not. I didn’t want to have to update every class in my application as it would have required a lot of extra testing. So I starting looking deeper into AOP frameworks to see what could be done.

Solutions

Before getting itno the aspect, lets cover what needs to be done get the classes to support bound properties. There’s not much to it really, you can simply add a field to your class that holds an instance of

PropertyChangeSupport, as well as the two methods to add and remove listeners, preferably:

  • addPropertyChangeListener
  • removePropertyChangeListener

JGoodies looks for the presence of these two methods in order to verify that the bean support s bound properties. Additionally, in all your setter methods, you should compare the new value to the current value and fire a property change event if the values are different. While this isn’t a terribly difficult task, the amount of effort in adding this new behavior to your POJOs grows with the number of classes you have to change times the number of properties in each each class. If you have existing classes that don’t support bound properties but now need to, updating each one individually could take a while. Additionally if you don’t have access to the sources, you’d have to develop a proxy, or sub-class, that can handle bound properties.

The Aspect

I really didn’t love the idea of adding a whole bunch of new code my existing classes that already worked just fine. I started to tinker with the JBoss AOP framework and realized that I could create an aspect that could make my classes support bound properties without having to change the code. By using an introduction, you can force a Java class to implement an interface. Additioanlly, you can also define a mixin class that will provide an implementation to support your introduction. I ended up with what I call the JavaBeanAspect, since it will make a regular value object a propert JavaBean. The JavaBeanAspect is based on the the GOFObservable aspect found on the JBoss wiki. The concepts in this aspect were what I wanted, but it did nothing to support the JavaBean specification. So with the GOFObservable aspect in mind, I started designing an aspect that would do the following:

  • Introduce JavaBean style behavior to POJOs
  • Support Java 5 annotations, beans marked @Observable would automatically inherit this behavior
  • By default, all fields of the advised class are bound.
  • Fields marked @NotBound will not fire property changes.
  • Fields marked @Constrained will fire vetoable property changes. Below is a class diagram depicting the architecture of the aspect:

Class Diagram of the JavaBean Aspect

*Note: AOP UML notation taken from Renaud Pawlak’s research report: A Notation for Aspect-Oriented Distributed Software Design * The JavaBeanAspect defines the introduction, which declares that the advised class will implement the JavaBean interface. It also defines a mixin class, JavaBeanMixin, which provides the default implementation of the JavaBeaninterface. The JavaBeanMixinclass would be the equivent of adding PropertyChangeSupportplus the addPropertyChangeListener, and removePropertyChangeListenermethods to your existing class. The PropertyChangeAspectis responsible for firing change events for all classes that are advised by the JavaBeanAspect. Whenever a field value changes,this aspect will determine weather or not to fire an event. And finally, we have the @ValueBeanannotation that is used to mark classes that you want to apply the aspect to. You can download the source code (Java 5 required) here:

How to Use it

Currently, this aspect works only with Java 5. Using it in your code is quite simple, you can simply annotate your class with the

@Observable annotation, compile it with AopC, and your classwill now support bound properties. Here’s an example:

@Observable
public class ValueObject {
    private String name;
    private String description;
    private int value;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

If you can’t or don’t want to annotate the classes because you either don’t like annotaions or you don’t have access to the sources, you can also apply the advice using an XML descriptor:

< ?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<aop>
    <aspect class="com.damnhandy.aspects.bean.PropertyChangeAspect"
    scope="PER_VM"/>

        <bind pointcut="set(* ValueObject->*)">
        <advice name="fieldChangeAdvice"
    aspect="com.damnhandy.aspects.bean.PropertyChangeAspect"/>
    </bind>

    <introduction class="ValueObject">
        <mixin>
            <interfaces>com.damnhandy.aspects.bean.JavaBean</interfaces>
            <class>com.damnhandy.aspects.bean.JavaBean</class>
        <construction>new com.damnhandy.aspects.bean.JavaBeanMixin(this)</construction>
        </mixin>
    </introduction>
</aop>

By contrast, this is how the same class might might look if coded in a more traditional fashion:

public class ValueObject {
    private String name;
    private String description;
    private int value;
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        String oldName = getName();
        this.name = name;
        firePropertyChange("name",oldName,name);
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        int oldValue = getValue();
        this.value = value;
        firePropertyChange("value",oldValue,value);
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        String oldDescription = this.getDescription();
        this.description = description;
        firePropertyChange("description",oldDescription,description);
    }

    private void firePropertyChange(String propertyName,
        Object oldValue, Object newValue) 
    {
        if (oldValue == null && newValue != null ||
        !oldValue.equals(newValue)) {
        this.pcs.firePropertyChange(propertyName, oldValue, newValue);
        }
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String propertyName,
    PropertyChangeListener listener) {
    pcs.addPropertyChangeListener(propertyName, listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener  listener) {
        pcs.removePropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(String propertyName,
    PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(propertyName, listener);
    }
}

The two versions of this class will behave the exactly same way. The big difference here is the amount of code that needs to be added to the class in order to support bound properties. The advised class contains half as many lines of code than the non-advised class. Admittely, the non-advised class could benefit by extending a base class, such as the JGoodies

Modelclass, that would provide the base implementation for bound properties. However, no base class can aleviate the donkywork involed in firing the property change events within every setter method. This is where I think the aspect approach really excells.

The Aspect in Action

Since my motivation around this aspect started with my need to make classes work with the JGoodies Binding framework, I have prepared an example using some tutorial code from the JGoodies Binding tutorial. I chose the

CommitStylesExample as it was the simplest example in the bunch. Both examples use Java Web Start and require Java 5 and they both use a self-sign certificate. Here’s the original example:

CommitStylesExample Orginal
And here’s the advised version:

CommitStylesExample with JavaBean Aspect
As you can see, there’s no difference in behavior between the two examples, nor should there be. The only difference is in the code, which you can download here:

JavaBeanAspect Eclipse Project

Considerations

While JBoss AOP can save you a lot of time and effort, there are some things you should take into consideration:

  • The JBoss AOP runtime will add about 2.4MB to your application. This is quite a lot for a WebStart application or applet.
  • Using JBoss AOP in a Web Start application is challenging. I have not been able to get loadtime weaving working, nor have I been successful using annotated aspects to work in a Web Start app. No matter what I try, Web Start can’t seem to properly read the jboss.aop.class.path property. These Web Start demos use the XML configuration that gets placed in the META-INF folder
  • It was a pain in the ass to set up :) But once it’s working, it’s several time easier than updating every setter in every class.
  • You can use this aspect with existing class files, you do not need the sources. You’ll need to assess you own situation to see if an AOP framework can be of value to you. Personally, I’ve found JBoss AOP to be incredibly useful asset in my work. The more you understand how to identify cross-cutting concerns, the more useful AOP can become. As for the JavaBeanAspect, there’s still some work to do. For example:

  • The name of the Aspect will probably change. “JavaBean” doesn’t fully convey what the aspect does. Any suggestions?

  • Adder methods(i.e. Order.addItem(Item item)), and other methods that might alter an internal collection or other value, currently won’t trigger change events.
  • Tweak the pointcut so that change events are not fired when set in a constructor. This usually isn’t preferred behavior.
  • The @NotBound annotation should be handled through a pointcut expression rather than in code.
  • Similarly, @Constrained fields should have it’s own advice and bound to a pointcut expression
  • Not sure if vetoable change support is totally appropriate in an aspect
  • Implement the same aspect using AspectJ 5 to compare against JBoss AOP Hopefully, this can illustrate how aspects can be used to assist you in solving a real-world problem.
Be Sociable, Share!
    • http://www.bushe.com Michael Bushe

      Nice piece of work! This has been on my back burner. Thank you for going through the trouble and then sharing your results, including the pitfalls (2.4MB download and weaving on any machine over the Internet is now appealing).

      I’m wondering if there is another non-aspect way to do this. All Swing components support putClientProperty() and getClientProperty() methods, which fire property change events when the values change. This is a little known and rarely used feature. Maybe the Swing guys were way ahead of us!

    • Kev Palfreyman

      Question – why do you need the local method firePropertyChange()? The same method in PropertyChangeSupport already does the checks for you.
      JavaDoc: “Report a bound property update to any registered listeners. No event is fired if old and new are equal and non-null.”

      I have not looked into AOP – is it a restriction there that means you need it to be a local method or something? Of maybe its just an oversight…

    • Mathias Kluba

      OUAH, I really want to see that fully works !
      But it doesn’t work with me… the JPNL just doesn’t do what it must, and the elcipse project doesn’t want to build because of “@Aspect” and other annotations… I’m really a noob with AOP, but I think it’s the most elegant way to do binding (MVC) without changing the model :)

    • http://damnhandy.com Mr. H

      Mathias,

      You need to build the application using Java 5. If you have Java 5 and Eclipse installed, Eclipse usually defaults to using JDK 1.4 compatibility. You will need to change that. Also, since you are building from the sources, you will also need to sign the application. I made a post about it here:

      http://www.damnhandy.com/?p=25

      I also have some updates in the works that may correct the issues. But you will still need to use Java 5 Check back later in the week.

    • Pingback: DamnHandy : Archive » ValueBean Aspect and AspectJ 5()

    • Martijn Mergenthaler

      Great work! It inspired me to use JBoss AOP in my current project and it freed me from extra codings! Finally i got the aspectized Hibernate POJOs running on JBoss AS and I’m now able to use them in my Swing Client via JGoodies Binding.

      I had to perform one modification in class ‘PropertyChangeAspect’ otherwise it might throw a NullPointerException

      private void firePropertyChange(Object before, Object after, String name, ValueBean target) {

      if (!(before == null && after == null) && (before == null && after != null || !before.equals(after))) {
      target.firePropertyChange(name, before, after);
      }
      }

    • LCB

      An additional advantage: If the bean is used in different contexts (server vs. client) then different Advisors can be used, or none at all if not needed. This way the PropertyChange support is not tightly coupled to the bean.

    • zapflash

      I am using MyEclipse and the reverse engineering code generation of my database to create the hibernate and pojos classes.

      Would modifying the velocity template to inject the listners and firePropertyChange code be a valid aproach. (not sure I like the extra 2.4Mb for the runtime for Java WebStart)

      Zap

    • ATom

      I found better solution here: https://izvin.bountysource.com/, article: Better bound properties management. It use cglib which has only 280kB.

    • http://damnhandy.com Ryan

      ATom,

      I don’t know how doing the same thing qualifies as “better”. Considering my primary driver for this demo was to make my Hibernate POJOs JavaBeans, I have some concerns on how a CGLib propxy of a CGLib proxy would behave. Besides, if the runtime size is an issue my HandyAspects project utilizes AspectJ and the AspectJ runtime is only 108k. That’s 172k less than CGLib, so this one must be better ;)

    • Jeroen Cranendonk

      Great idea! I’m using this in our project now :)

      Any hope of adding the JBoss AOP version to handyaspects too which in other blog posts was suggested you would be doing ? :)
      Or did life catch up on this project and has it all been mothballed ? ;)

      Reason I’m choosing JBoss AOP is that I’m developing a JBoss 4.2.0 app, seems to make sense to use the available AOP implementation :)

    • http://www.tila-nguyen.org/tila-nguyen/tila-nguyen-pic.htm Tila Nguyen

      Hi…I Googled for fashion model photography, but found your page about JavaBean Aspect…and have to say thanks. nice read. Tila Nguyen

    • Pingback: My Weblog()

    • Plamen

      Ryan,

      I’ve found your HandyAspects project on Java.net quite useful. However, I had to modify it a little bit. I did changes concerning the following issues:

      1. Adder methods(i.e. Order.addItem(Item item)), and other methods that might alter an internal collection or other value, currently won’t trigger change events.
      2. Tweak the pointcut so that change events are not fired when set in a constructor. This usually isn’t preferred behavior.
      3. The @NotBound annotation should be handled through a pointcut expression rather than in code.

      I also added (indexed) nested property change support for (collection of) JavaBean properties with a new @Nested annotation. This means that once you annotate your JavaBean property field/accessor/setter with @Nested you will receive property change modifications of the nested JavaBean properties.

      For example: you can register a property change listener for “company.name” property of a Person and it will handle any events fired by modification of the company name. However, Company and Person should be annotated with @Observable and the property field Person.company should be annotated with @Nested.

      Is anybody interested in this behavior?

      So, I’d like to contribute my work to the project. I wrote on the developer’s forum of the project on java.net but did not receive any answer. It seems this project is dead (but still usefull) since October, 2006.

      Plamen

    • http://elespaciodejaime.wordpress.com Jaime

      Hi Plamen, could you please share your code and an example with me?
      hablutzel1 (at) hotmail (dot) com

    • http://www.HendyIrawan.com/ Hendy Irawan

      Me too, Plamen… :-)

    • http://www.HendyIrawan.com/ Hendy Irawan

      Use AspectJ, it’s now the de facto AOP engine, also used by SpringSource. aspectjrt 1.6.8.jar is only 114 KB.

    • http://www.HendyIrawan.com/ Hendy Irawan

      Use AspectJ, it’s now the de facto AOP engine, also used by SpringSource. aspectjrt 1.6.8.jar is only 114 KB.

    • http://www.HendyIrawan.com/ Hendy Irawan
    • plamkata

      I committed the changes in CVS on https://handyaspects.dev.java.net/ . However, I updated only the AspectJ implementation. The nested property feature is not yet implemented in Jboss AOP. You can check out the code!