When I started coding JavaFX I quickly found that the great bind mechanism doesn’t work together very well with my legacy Java code. In fact, it doesn’t work at all. In my case I wanted to reuse my domain objects and just add a fancy user interface on top of it. This happen to be harder that I anticipated. Very annoying. Since bind is a new feature in JavaFX and all primitive types have been replaced with special object equivalents they can’t interoperate with the Java primitive types. (This is another reasons why primitive types should have been objects in Java from the start.)
After searching on Google I found that there are two solutions for the problem which both includes an adapter class:
- Polling – The adapter class polls for changes in the POJO. While this solution is transparent to our POJOs it does not keep the state synchronised and is not very resource efficient.
- Observer-pattern – The adapter class listens for changes in the POJO. This implies that we add listener mechanism to our POJOs and thus not very transparent.
Michael Heinrichs at Sun have a great post about it. The problem is that none of these solutions are really adequate.
The best of both worlds
Since I didn’t want to change my POJOs or coding adapters I had to come up with a new strategy. We’ve seen how listener mechanisms can be added using AOP. Great! By adding an aspect we can mixin the functionality we need for JavaFX to be able to bind to my POJOs. Furthermore we can generate the adapters. We only need to decide what fields we need to bind to in our POJOs. These will serve as our pointcuts. The default strategy would be to select all JavaBean properties. What a great day for coding a tool coding! The result is an easy to use tool that I hosted on Google Code:
http://code.google.com/p/javafxbinder/
For example if I have a POJO like this:
package se.jayway.javafxbinder.example.domain;
public class Person {
private String name;
private int age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Name was set: " + name);
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Using JavaFX binder I can run the following command to generate some JavaFX code:
javafxbinder.sh -classpath ./target/classes -includes se.jayway.javafxbinder.example.domain.* -out ./output
And here is what gets generated:
package se.jayway.javafxbinder.example.domain;
import se.jayway.javafxbinder.example.domain.Person;
import se.jayway.javafxbinder.infrastructure.Bean;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class JfxPerson extends PropertyChangeListener {
public-init var pojo : Person on replace oldValue {
var oldBean : Bean = oldValue as Bean;
oldBean.removePropertyChangeListener(this);
var newBean : Bean = pojo as Bean;
newBean.addPropertyChangeListener(this);
};
public var age : Integer = pojo.getAge() on replace {
pojo.setAge(age);
};
public var name : String = pojo.getName() on replace {
pojo.setName(name);
};
override public function propertyChange(event : PropertyChangeEvent) {
// Make sure that we're updating in the right thread
FX.deferAction(function() {
var bean : Bean = pojo as Bean;
bean.removePropertyChangeListener(this);
if (event.getPropertyName().equals("age")) {
age = event.getNewValue() as Integer;
}
if (event.getPropertyName().equals("name")) {
name = event.getNewValue() as String;
}
bean.addPropertyChangeListener(this);
});
}
}
To enable the listener functionality in my POJO I need to define an aspect:
package se.jayway.javafxbinder.example.domain;
import se.jayway.javafxbinder.infrastructure.Bean;
public aspect MyJavaBeans {
declare parents: (se.jayway.javafxbinder.example.domain.Person) implements Bean;
}
Conclusion
Using AOP and a simple code generator is a good solution to the annoying JavaFX bind to Java problem. We use this tool for one of our clients and it works really good. However, my hope goes to someone at Sun/Oracle to integrate a similar functionality in the JavaFX compiler itself… Transparency looks good both in GUIs and in software design!
Pingback: itemprop="name">Magnus Robertsson presents another *aspect* on JavaFX MOJO binding ;-) - Use JavaFX
Pingback: itemprop="name">Java desktop links of the week, May 18 | Jonathan Giles
Pingback: itemprop="name">st.mind@ : Eclipse Land » favorite » favorite programming site
This is a clever solution. Others used attributes to get things done.
Unfortunately, instead of concentrating on the job at hand, we have to surf the net to find solutions for problems that should not exist in the first place. Although I do not specially like Microsoft, they do come up with consistant solutions that serve their developers right out of the box without any need for third parties. I hope Sun will wake up and add native property support in the Java language and make data binding trivial using a statements such as:
someTable.DataSource = someArrayList;
We could have properties with attributes such as:
@Bindable(UpdateStrategy.ReadWrite) Integer someProperty;
Behind the scenes all the plumbing should be done by the framework.
Magnus, Nice solution. I used javafxbinder in our project but i’m getting null pointer exception when loading the page that contains fxpojo with Boolean attribute
Another question, if my pojo contains another pojo as attribute the tool generates adapter class for attribute pojo but the field in the main fx pojo class is the originial pojo.
Looks like tool does handle if the pojo has tree of pojos.
Please suggest.
The post is written in very a good manner and it entails many useful information for me. I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement the concept.
I like your solution. It’s very cool.
I have been struggling with this problem for a long time too, and came up with a different solution. You are goos inspiration.
I ended up proxying POJOs into JavaFX Beans on the fly using javassist. That’s also cool, I think :)
Cheers
It’s laborious to find educated people on this topic, but you sound like you understand what you’re talking about! Thanks