
Full Name Form with Validation
Introduction
This is the second installment of a series of blog entries relating to a proof of concept for a JavaFX Forms Framework. Before I specify the requirements and a simple design of the FXForms Framework, I want to follow-up on comments about tough issues relating to enterprise application development and JavaFX. If you recall in Part 1 we discussed about MVC form based technologies. I listed many technologies that may only provide a means to separate content from presentation, but they don’t necessarily deal with real plumbing problems such as Threading, Binding, Validation and Transactions. I believe these issues are one of the most important areas that must be understood in the JavaFX world, thus positioning itself as a premiere enterprise development platform (not just rich client a hint to Oracle). As Richard Obaldeston mentions in the comments regarding enterprise level support for JavaFX controls similar to Swinglabs / SwingX. I cannot speak of the future concerning Swing & Swinglabs, but I believe the JavaFX teams are well aware of them, (very much so) due to the fact that the same project owners of the SwingX-WS are leads on the JavaFX teams (I don’t mean to put them on the spot). Many of these difficult issues are mostly solved in the Browser based Web Application Development world and can also be done in the JavaFX world (Check out Exadel). Of course I can’t answer all the hurdles faced in just one blog entry especially because every enterprise application has varied types of architectures such as (Corba, RMI, EJB, Soap, RESTful, JSON, Jini, JMS, DAO, JDO, JDBC, JNI, etc). It would literally take a book or two. But, I will try to address some of them later in this blog as we discuss JavaFX and Java interoperability. One should always re-evaluate one’s architecture to adhere to best practices or industry standards and conventions. However, some standards seem to stall or are slow to be accepted (JSR-296 or JSR-295) with many reasons of course. It is often the nature of the beast when it comes to new technology solutions and refactoring that subsequently boils down to when and how you want to pick your battles. As readers of this blog for the first time who might be new to JavaFX or Java Swing I strongly encouraged you to explore and learn about the pitfalls of thread safety, asynchronous behaviors, best practices, and UI concepts like (UI blocking, disabling, progress bar, etc.) . So, let’s get started with requirements and the analysis phase of our FXForms Framework.
Requirements
My main goal of these series of blog entries are to share with others my experience with creating an application called iSF86 which hopefully will end up in the Java App Store and make tons of cash so I can quit my day job like Ethan (wishful thinking). iSF86 is an application which contains many form elements to capture a person’s information to apply for a job doing classified work for the United States Government. When dealing with so many forms I thought of building a mini Forms Framework where domain objects can be mapped to forms. The application will save a person’s SF86 information locally as an XML file. This is purely a client-side application which doesn’t involve any server-side communications. Each form would be bound to domain objects in a bi-directional way. A domain object that is bound to an existing form can be swapped out with a different domain object, thus making the old bean ready to be garbage collected and unbound to the form. Important Note: One requirement is that the domain objects will be JavaBeans (see disclaimer below).
Disclaimer: For brevity, the requirements are for JavaBeans (change support) and not POJOs (there is a difference)! Yes, I know I said POJOs in Part 1, because I wanted to tackle the problem using byte code engineering for boiler plate code for property change support on POJOs, but it would be beyond the scope of the blog series’ goal. So, this tutorial is really geared for people migrating Swing/SWT applications to JavaFX applications.
FX Forms Framework Requirements:
- It shall bind JavaFX Forms to JavaBeans as domain objects
- It shall enable JavaBeans to be swapped in a JavaFX Form
- It shall provide validation on property values
- It shall provide Action bindings to controls
To keep the framework simple shown below is a list of features that the framework does not provide. Interesting features will later be discussed in Part 4 Enhancements.
What the FX Forms Framework does NOT provide:
- Application Framework Features (like JSR-296)
- Application Lifecycle
- Resource injection
- Task Monitoring
- Saved Preferences
- Thread Pool
- Forms Building
- RCP Features
- Window Docking
- Menu bar
- Etc.
Design
Before going into the design I want to mention other technologies which provide beans to forms binding or RIA MVC capable. The mentioned technologies are primarily Java based. Some of my classes will use similar names because I’ve taken a lot of ideas from these frameworks in the past. Here are just a few that come to mind:
As a reminder regarding rapid application development that there are two main areas on building applications quickly ‘Forms building’ and ‘Forms binding’. We will only focus on ‘Forms binding’. Ok, already! Let’s get to the designing.
User of the API
At times when it comes to API development I’m a fan of user driven design and the user of the API is who I’m targeting. So, I pretend the FXForms Framework is ready to be used. Below is the developer obtaining a JavaBean and setting it into some Form.
// normally loaded from external source
var personBean1:PersonBean = new PersonBean(); // change support enabled
personBean1.setFirstName("SpongeBob");
.
.
.
// generate a form and bind to bean. Req #1
var personForm:NameForm = NameForm{
jBean:personBean1
};
// subsequent time another domain object is loaded Req #2
var personBean2:PersonBean = … // loaded from external source.
personForm.jBean = personBean2; // old person bean is no longer attached
// set property to an invalid value. Req #3
personBean2.setFirstName(“#$”);
// validator will fire to output error message
// create navigation panel area Req #4
var mainButtonForm:PresentationModel = MainButtonForm {
jBean:personBean2
};
var nextButton:Button = mainButtonForm.getControl(‘nextButton’);
// simulate a mouse press to test action binding
Let’s step through the code to see what is happening and what the FXForms Framework will provide.
Line 1: Creating domain object or loading from external source. Notice the new on the PersonBean (Java Object).
Line 8: Instantiating a Form to use the current JavaBean loaded. All the properties will be bound to form elements bi-directionally. Changes in the GUI will affect properties and changes to the bean properties will change the GUI fields. (Requirement #1)
Line 14: Swapping a domain object with the existing form. Old bean is disconnected from the form and replaced with new bean. Presentation Model Pattern (Requirement #2)
Line 17: Validation of a property when set with invalid data. The user can type into the textbox to fire the validator code. ValueModel updated (Requirement #3)
Line 20-25: A button panel form has buttons mapped to actions. Command Pattern. Yes, this is strange but I will explain later. (Requirement #4)
You will notice the comment on line 25 where you could test a control such as obtaining a button independent of the application being launched (testing individual forms). This brings up a good point when using MVC Frameworks. In the Swing world I highly recommend using FEST Swing a tool to test Swing applications with ease. Also, the team is working on FEST for JavaFX with a joint effort with the JFXtras team.
JavaFX and Java Interoperability
While the user of the API appears simple, it is the behind-the-scenes work that some may consider unorthodox when using a combination of Triggers, Bindings, Reflection, Threading, etc. Until the JavaFX teams expose (with JavaFX blessed code) better interoperability against Java and JavaFX, I will resort to techniques that are known and official. When using triggers on properties this enables us to follow the basic proxy pattern: where a property is changed you will have an opportunity to intercept the change and update value models, which also updates GUI widgets mapped to them. One concern is that object-based memory leaks could occur with inverse bind (Maybe we need a unbind keyword). Another problem is properly populating a form on or off the JavaFX’s main process thread (EDT on the desktop environment) without blocking the GUI thread or causing some race condition. What follows are the necessary classes to implement the FXForms framework.
Class Design
// Java
DomainModel <abstract Java class>
pcs:PropertyChangeSupport
// Java
PersonBean extends DomainModel <JavaBean>
firstName:String
middleName:String
lastName:String
suffixName:String
// JavaFX
FieldSetter <mixin class> Adapter
valueModel:ValueModel
boundValue:String
// JavaFX
MyTextBox extends TextBox, FieldSetter
updateField(obj:Object):Void
// JavaFX
ValueModel <adapter class>
propertyName:String
guiControls:FieldSetter[]
boundValue:Object
jBean:Object
// JavaFX
PresentationModel extends PropertyChangeListener <mixin class>
propertyValues: Map <String, ValueModel>
propertyValidatorMap: Map <String, List<Validator>
jBean: DomainModel // java class javabean inherit
getModel(propertyName:String):ValueModel
getControl(propertyName:String):Control
propertyChange(changeEvent: PropertyChangeEvent):Void
//JavaFX
Form
guiControls:[] // Scene.lookup(id) is currently a bug in jfx 1.2
presentationModel:PresentationModel
// JavaFX
NameForm extends CustomNode, Form, PresentationModel <JavaFX node>
// JavaFX
Validator
validate():ValidationResult
// JavaFX
ValidationResult
messages:[]
getMessages():Message
// JavaFX
Message
key:String
propertyName:String
errorType:[“warn”,”err”]
text:String
You will notice the NameForm inherits the PresentationModel mixin class which provides the controller functionality mixed in with the view. I’ve done this to make the example short Sometimes I’m not a big fan of inheritance especially in this case and decided to refactor in order to separate the “Presentation Model” from the “View Form” for form reuse. There are situations such as reusing the same GUI form for “Adding” and “Editing“, but the validation of the form data is different. Developers may also create a concrete presentation model that inherits from the mixin class to be independent of the view so that it decouples the Form and the Presentation Model. An EditPresentationModel and an AddPresentationModel should be created to have two different validations using the same GUI form. Again for a slim down version I’ve omitted the ability to consume an XML file detailing the form metadata and mappings such as actions, mandatory fields, validators, and custom bindings; the named property is the same name as the ‘id’ attribute on the JavaFX GUI form element (TextBox). I will discuss in Part 4 Enhancements. Relating to validation at some point I would like to provide a way to display warnings and error icons beside fields whenever a Validation Result is updated. The validation results will contain the error messages to be displayed. One last thing to mention is GUI component factories aren’t in the design. You will also notice when creating a TextBox I needed additional behavior, so I simply created a MyTextBox instead of something like var field1 = WidgetFactory.create(valueModel, “TextBox”) to wrap, return and put it into form.
Conclusion
This is surely not a thorough or complete design, but an attempt to demonstrate how to make a simple forms framework from scratch. Hopefully, I’ve captured the requirements and articulated the object relationships within the textual design (No UML sorry). Next, we will look into how to implement the FXForms Framework in Part 3. Please be advised that this design is subject to change as any development shop would.
Personal Note: Sometimes I wonder if Sun had focused on finishing the often highly controversial JSR 295 Beans Binding and Java Properties before JavaFX would it render this blog entry totally useless.
Any comments are welcome. Thanks!
-Carl
References
JavaFX w/JMS 307-311 JavaFX RIA book Server Call Back- James Clarke updates code for JavaFX – Developing RIA book regarding Code Recipe for JMS pages 307-3011 http://tinyurl.com/lzej5x
Best practices: http://www.greggbolinger.com/blog/2008/11/11/1226423940000.html
Fuse http://weblogs.java.net/blog/joconner/archive/2007/11/index.html
Exadel, http://mkblog.exadel.com/ria/javafx-and-spring-crud/
Swing worker / swing/ http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html
Beans Binding: https://beansbinding.dev.java.net/
RESTful : http://jnb.ociweb.com/jnb/jnbAug2009.html
jfxtras JFXWorker http://jfxtras.googlecode.com/svn/site/javadoc/release-0.5/org.jfxtras.async/org.jfxtras.async.JFXWorker.html
Java Properties: http://tech.puredanger.com/java7#property
Fxbuilder: http://www.jroller.com/aalmiray/entry/griffon_gfxbuilder_fxbuilder_side_by
Disable Swing Container:http://weblogs.java.net/blog/alexfromsun/archive/2007/06/_enablingdisabl_1.html
Java Properties without Getters and Setters: http://www.artima.com/forums/flat.jsp?forum=270&thread=247837
Beans Binding (JSR 295) & Properties on JDK7: http://www.javalobby.org/java/forums/t101998.html
JSR 295: http://jcp.org/en/jsr/detail?id=295
RCP Spring rich- http://spring-rich-c.sourceforge.net/1.0.0/index.html
SAF:https://appframework.dev.java.net/intro/index.html
Oracle buys Sun : http://www.oracle.com/us/corporate/press/018363