One Bean to Bind Them All

JavaFX and the JavaFX observable property model

After a few years of having pushed Java as a primarily server-side technology, Oracle have recently been making a lot of noise about JavaFX as a toolkit for desktop (and potentially mobile) application development. With the release of Java 8 earlier this year, JavaFX is now a fully-fledged part of JavaSE.

JavaFX supports the MVC design pattern and its relatives. In order to support binding between properties in model objects and the view, JavaFX introduced a new observable property pattern that extends the age-old Java Beans pattern. The basic pattern is

public class Person {
    private StringProperty firstName = new SimpleStringProperty(this, "firstName");
    public String getFirstName() {
        return firstName ;
    }
    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }
    public StringProperty firstNameProperty() {
        return firstName ;
    }
}

The get() and set(…) methods behave in exactly the same way as the same JavaBean methods; they retrieve and update the values of the property. The “property accessor method” returning the StringProperty instance gives access to the observable property itself. You can register listeners with this object that will be notified if its value is changed, and there is a rich Binding API for binding values between observable properties. The tutorial has more details, and this excellent document from Oracle shows some nice patterns, one of which we’ll exploit later in this article.

JavaFX Properties on the Server Side

JavaFX is being pitched as a good choice for developing enterprise client applications, and the support for observable properties (enhancing the ability to implement MVC-type architectures), along with other features such as high-level concurrency support make this a viable argument. This has led to a number of discussions on forums recently as to the possibility of using “observable beans” backed by FX properties as JPA entities and for other purposes on the server side of an application. The motivation for this comes from client-server type applications, in which the desktop client is presenting data retrieved via some remoting service (web service or remote EJB, for example) backed by a database. The entities represented on the client side – using JavaFX properties if JavaFX is to be the client technology – are typically the same entities represented on the server side by JPA persistent entities or entities transported in JSON or XML representations. The alternatives to using FX properties on server side objects are to either have parallel representations of the entities – one for the client and one for the server – or to avoid the use of FX properties at all. The former option introduces maintenance problems and redundant (replicate) code; the latter of course forfeits the automatic wiring between the model and view that the FX properties provide.

Issues associated with server-side JavaFX properties

Let’s consider some of the reasons you might not want to include JavaFX properties in server-side beans.

Additional dependencies

Using JavaFX properties to represent server-side beans imposes a dependency on the server-side code on JavaFX, which is inherently a client-side technology.

Performance overhead

JavaFX properties are memory intensive, compared to their plain bean-represented counterparts. At the extreme end of the spectrum, an int occupies just 4 bytes, whereas an IntegerProperty has to maintain a reference, a reference to an int, and at least two references to collections of listeners.

Additional API

A JavaFX-based bean exposes the usual set/get methods along with a “property accessor” method. The property accessor method is typically used for binding or observing the property. It’s hard to see any real use case for this on the server side, so this amounts to exposing unnecessary API which could potentially be mis- or ab-used.

Serialization

JavaFX property classes do not implement the Serializable interface. Most server-side APIs explicitly require serializability of entities (see, for example, the JPA specifications). If a bean uses JavaFX properties to represent its state, simply implementing the Serializable interface will not make it serializable.

Compatibility

Server-side API implementations are not designed to work with JavaFX properties. It is reasonable to ask whether they can, and will in the future, work with them.

A potential solution

Compatibility:

Steven Van Impe (at least partially) addressed the question of JPA compatibility in a recent blog post. The essential conclusion from his work is that JPA will work with JavaFX properties as long as you set up JPA to use “property access” rather than “field access”. This essentially means placing annotations on the ‘get’ method rather than on the field. There’s a little more to it than that, and I strongly recommend reading Steven’s blog, but that’s the basic message.

All other server-side technologies with which I’m familiar (JSON and XML representation, etc)  basically work via the JavaBean model. That means to say, they retrieve property values by calling a get..() method, and update them by calling a set..(…) method. Since the JavaFX properties extend the JavaBean model, these technologies work directly with JavaFX-based beans right out of the box.

Serialization

For quite a long time, I thought the lack of support for Serialization from JavaFX properties really prevented them from being used in any server-side capacity. While not many people are using serialization directly these days, it is used behind the scenes by remote EJBs. Furthermore, and perhaps more importantly, the JPA specification basically mandates that JPA entities should be serializable. While current JPA implementations don’t seem to rely on this much, were Serialization to come “back into fashion” at some point, it would be completely within the remit of JPA providers to use serialization in any new implementations. Any entity bean should really support Serialization if it wants to be considered as a long-term solution to a client-server application.

JavaFX properties are not serializable. I can’t speak to why this decision was made, but on the surface it would seem to be a road block to what we are trying to achieve.

Fortunately, there is a little-known, but long-existant, sub-interface of Serializable called Extermalizable. The Externalizable interface specifies two methods: readExternal(ObjectInput) and writeExternal(ObjectOutput). The key observation here is that to serialize a bean that employs JavaFX properties, you only need serialize the state of the properties, not the properties themselves.

Here is our prototype for a Serializable JavaFX-based entity:

//imports ommitted
public class Person implements Externalizable {
    private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
    public int getId() {
        return id.get();
    }
    public void setId(int id) {
        this.id = id ;
    }
    public IntegerProperty idProperty() {
        return id ;
    }

    private final StringProperty name = new StringProperty(this, "name");
    // get/set/propertyAccessor methods...

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(getId());
        out.writeObject(getFirstName());
        // write other properties...
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        setId(in.readInt());
        setFirstName((String)in.readObject());
        //etc.. order matters!
    }
}

Since Externalizable is a subinterface of Serializable, this makes the bean Serializable, and essentially solves the problem of non-serializable JavaFX properties. Notice that only the state of the property is preserved over serialization, so (for example) the listeners are effectively transient. It seems to me this would be acceptable, and even desirable, in any real use cases.

Overhead

JavaFX properties are bigger than the pure representation of their state. In extreme cases (small state with a large number of listeners), they can be orders of magnitude bigger. In high-throughput server environments, this has the potential to be an issue that requires addressing.

Oracle provided a document a while back that outlined various usage patterns for JavaFX properties. In the “super-lazy” implementation, a “shadow field” stores the value of the (lightweight) state itself, whereas the (heavyweight) JavaFX property is only instantiated if specifically  requested. The definition of such a “super-lazy” property looks like this:

private StringProperty firstName ;
private String _firstName ;
public String getFirstName() {
    if (firstName == null) {
        return _firstName ;
    } else {
        return firstName.get();
    }
}
public void setFirstName(String firstName) {
    if (this.firstName == null) {
        _firstName = firstName ;
    } else {
        this.firstName.set(firstName);
    }
}
public StringProperty firstNameProperty() {
    if (firstName == null) {
        firstName = new SimpleStringProperty(this, "firstName", _firstName);
    }
    return firstName ;
}

The behavior here is identical to that explained before: get/set retrieve and update the value of the property. The property accessor fistNameProperty() returns an object that will notify any listeners to changes in its underlying value.

The only loss here is to the verbosity of the code. I have an example of an entity with five properties: implemented this way the code runs to ~180 lines, which is too much for a class this simple. Assuming that properties are never accessed directly on the server side, there is merely a one-reference overhead on the server side representation compared to the “pure JavaBean” representation.

Additional API

This is really an unsolved problem. As mentioned above, it’s hard to envisage a situation where listeners would be registered with the bean on the server-side. It’s therefore arguable that the property accessor methods should not be exposed on the server side. Assuming you want JavaFX properties available on the client side, the only other option is to maintain a parallel set of classes for server- and client-side representation. On balance, the over-engineered API on the server side seems the smaller price to pay.

Dependencies

Using JavaFX properties on the server side requires that the server has access to the JavaFX libraries. Since, as of Java 8, JavaFX is an integral part of JavaSE, this seems like a non-issue.

Using JPA-enabled entities as beans on the client side requires the client side code has access to some JPA libraries (the JPA annotations have Runtime retention). This does add a small burden to the client-side code, and this could be an issue worth weighing if you are targeting mobile devices with relatively small memory capabilities.

An Example

We run an academic genomics service core facility, performing high-throughput genomic sequencing (and other related experiments) for (primarily academic) scientific investigators. We’ve been weighing up the possibility of building a laboratory information management system (“lims”) for a while. The data are somewhat complex (I will present a massively simplified version here.) We’d like our lab staff to be able to create new projects and manage samples for those projects, and for our clients to view the status of their experiment and download files with analysis results.

For our customers, the interface is pretty simple and can be managed via a web front end. For our lab staff, the interface is pretty complex and would better be served by a desktop (rather than web-based) toolkit.

We’d like both front ends to communicate with the same server back-end, preferably via the same API interface.

JavaFX seems well suited to the lab staff front end.

JSF or even JSP seem well suited to the web interface.

A JSON-based web service would be an appropriate way to serve the data (and provide entry points for updates and deletes).

The (enormously) simplified data model is as follows:

We keep a collection of Investigators. Each investigator is represented by a (database) id, a first name, last name, email, and institution.

We keep a collection of projects. Each project has a database id, an owning investigator, and a title.

We also keep a collection of Samples. Each Sample belongs to a unique project, has a database id, and a sampleId (which is only required to be unique within a given project).

Here, as an example, is the implementation of the Project entity class:

package edu.marshall.genomics.lims.entities;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

/**
 * Entity implementation class for Entity: Project
 *
 */
@Entity
@Access(AccessType.PROPERTY)
@Table(name="projects")

@NamedQueries(
		{
			@NamedQuery(name="Project.findAll", query="SELECT p from Project p"),
			@NamedQuery(name="Project.byInvestigator", query="SELECT p FROM Project p WHERE p.investigator=:investigator"),
		}
)

public class Project implements Externalizable {

	private static final long serialVersionUID = 1L;

	private IntegerProperty id;
	private int _id;

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name = "project_id")
	public final int getId() {
		if (id == null) {
			return _id;
		} else {
			return id.get();
		}
	}

	public final void setId(int id) {
		if (this.id == null) {
			_id = id;
		} else {
			this.id.set(id);
		}
	}
	
	public IntegerProperty idProperty() {
		if (id == null) {
			id = new SimpleIntegerProperty(this, "id", _id);
		} 
		return id ;
	}
	
	private StringProperty title ;
	private String _title ;
	@Column(name="title")
	public final String getTitle() {
		if (this.title == null) {
			return _title ;
		} else {
			return title.get();
		}
	}
	public final void setTitle(String title) {
		if (this.title == null) {
			_title = title ;
		} else {
			this.title.set(title);
		}
	}
	public StringProperty titleProperty() {
		if (title == null) {
			title = new SimpleStringProperty(this, "title", _title);
		}
		return title ;
	}
	
	private ObjectProperty investigator ;
	private Investigator _investigator ;
	@ManyToOne(cascade=CascadeType.MERGE, fetch=FetchType.EAGER)
	@JoinColumn(name="investigator", referencedColumnName="inv_id")
	public final Investigator getInvestigator() {
		if (investigator == null) {
			return _investigator ;
		} else {
			return investigator.get();
		}
	}
	public final void setInvestigator(Investigator investigator) {
		if (this.investigator == null) {
			_investigator = investigator ;
		} else {
			this.investigator.set(investigator);
		}
	}
	public ObjectProperty investigatorProperty() {
		if (investigator == null) {
			investigator = new SimpleObjectProperty(this, "investigator", _investigator);
		}
		return investigator ;
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeInt(getId());
		out.writeObject(getTitle());
		out.writeObject(getInvestigator());
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		setId(in.readInt());
		setTitle((String)in.readObject());
		setInvestigator((Investigator) in.readObject());
	}

}


The code for the implementation of all the entities is posted as a gist.

This is a pretty verbose way to write a basic entity class. To be honest, it was fairly tedious (and hence error-prone) to put this part of the project together. Hopefully, if this pattern takes hold, someone will write one (or more) Eclipse actions to ease the pain of doing this.

One important observation here is that this implementation is completely back-compatible with the equivalent implementation using the plain Java Bean (no JavaFX properties) model. So it’s possible to use this technique to “upgrade” existing entity classes to support JavaFX properties, without breaking existing code.

The benefits are pretty good. This gist shows some of the server-side classes; the business tier and data integration tier. Note in particular the JpaDao class, which is where the entities are retrieved from the database.

This gist shows the REST controllers for the Spring MVC application. Spring is configured to use the Jackson mapping framework, which is invoked due to the “produces” or “consumes” attributes in the RequestMapping annotation.

Finally, here’s the code for the JavaFX client. The interesting class (from the perspective of this post, at least), is the DataAccessor class, which uses a Jersey client to communicate with the REST web service; and the InvestigatorDetailController, which takes advantage of the Java FX Properties in the Investigator entity class to bind the values directly to labels.

Note that both the JavaFX controller classes leverages Tomas Mikula’s excellent EasyBind framework. I was exposed to this framework a couple of months ago, and now find it hard to envisage coding a JavaFX application without it.

Screen Shot

Here’s a screen shot of the prototype application. The data is held in a MySQL database and accessed via a Spring-MVC web application hosted on Tomcat 7, using Java 8.

LIMS-screenshot

Conclusion

What we’ve done here is pretty neat. The very same entity classes work directly with JPA for persistence, can be bound on the JavaFX client end to the JavaFX controls, are fully serializable, and can be transported via JSON or XML using a web service technology such as Jersey or the Jackson mapping API. Since they include regular JavaBean-style properties, they can also be bound to web view technologies using JSF or Spring JSP form tags, or other view technologies. The super-lazy idiom means the server-side overhead is minimal.

Here then, is truly a bean that binds to anything.

One bean, to bind them all.

One thought on “One Bean to Bind Them All

Leave a Reply

Your email address will not be published. Required fields are marked *