Tuesday, May 29, 2012

Using Java reflection and Domino form to auto populate beans

>> Download DemoApp

Due to a lot of positive posts about using beans/MVC pattern for developing XPages apps, I've started writing more Java and less SSJS. I really like the performance improvements/stricter structure of code.

In one of my recent apps, I wrote a Model class for each of the forms in the app. The class has fields for all the form fields. Whilst writing the app, I thought about using Java reflection/Domino Form to auto populate the class instance. Java reflection lets you fetch methods from classes using their name (as a String). The Form class contains methods that lets you fetch all the declared field names in the form.

A couple of advantages to using auto population of fields is less code to maintain (~less bugs), and "auto discovery" of typos in either field names or getters/setters. If you call a field firs_name (typo), and write getters/setters for first_name, using the methodology I use, the code would crash, and you would instantly be aware of some error. If you use some kind of logging, you could look at the stack trace to determine what failed.

A couple of disadvantages. Using reflection/fetching field information adds a slight performance penalty. I don't notice any slowness, but it's something to be aware of if you're aiming for light speed/serving the entire Republic of China on one server. :)

In most of my apps, I use underscores to separate "words" in my field names. To make this work with reflection I convert the underscore field names to ProperCase, and prepend get/set.

E.g.
Field: first_name
ProperCased: FirstName
Getter: getFirstName
Setter: setFirstName

In the attached DemoApp, I have a helper class that deals with the most common field types. It has two methods:
setObjectFieldsFromDocument( String form, Object object, DominoDocument doc )
setDocumentFieldsFromObject( String form, Object object, DominoDocument doc ).

The first parameter is the name of the form that you want to use as "schema" for the population of the instance fields. The second parameter is the Java instance. The third parameter is a Document wrapped in a simple helper class.

The demoapp has a XPage that uses a controller bean (PersonController) to handle interaction with the model (Person). It's bound to view scope. When the XPage is loaded, it checks for a parameter (id). If id is available, the controller uses setObjectFieldsFromDocument to populate the model. When save is called, the controller uses setDocumentFieldsFromObject to update the underlying Notes Document with values from the model.

If this sounds interesting. Download the Demoapp, look at the Person XPage for the data binding, and use Package Explorer to take a look at underlying java code.

I'll just add the source code for the Person model class, in case some of you fear that everything in Java is complex

package com.dontpanic82;

import java.io.Serializable;
import java.util.Date;

public class Person implements Serializable {
 private static final long serialVersionUID = 1L;

 public static final String FORM = "Person";

 private String id;
 private String firstName;
 private String lastName;
 private String email;
 private String homepage;
 private Double age;
 private Date birthdate;

 public Person() {
 }

 public Double getAge() {
  return age;
 }

 public Date getBirthdate() {
  return birthdate;
 }

 public String getEmail() {
  return email;
 }

 public String getFirstName() {
  return firstName;
 }

 public String getHomepage() {
  return homepage;
 }

 public String getId() {
  return id;
 }

 public String getLastName() {
  return lastName;
 }

 public void setAge( Double age ) {
  this.age = age;
 }

 public void setBirthdate( Date birthdate ) {
  this.birthdate = birthdate;
 }

 public void setEmail( String email ) {
  this.email = email;
 }

 public void setFirstName( String firstName ) {
  this.firstName = firstName;
 }
 
 public void setHomepage( String homepage ) {
  this.homepage = homepage;
 }

 public void setId( String id ) {
  this.id = id;
 }

 public void setLastName( String lastName ) {
  this.lastName = lastName;
 }

}

5 comments:

Anonymous said...

Nice idea Tommy !

Regards
Tamir Ben Shoshan
http://www.systbs.com
SharePoint & Lotus plugins & Notes CAPI

Anonymous said...

Can you tell me where to find the classes in the demo app?

srieger@lmsnet.com

Tommy Valand said...

You have to use the Package Explorer.

To show this, in Designer:
Window -> Show Eclipse Views -> Other -> Java -> Package Explorer

The Java classes are available at the top, under WebContent/WEB-INF/source.

Regarding configuration of/using beans in XPages, there are a lot of good guides published.

Google XPages beans

Bertrand Goubot said...

Hi,
i agree with this beans approach for developing XPages apps.
I attempt to do same thing for my app.
But i have a "basic" problem : how to you do to set in read mode Xpage ? i can't find solution with JSFredirect.
Thanks for your great posts.
Bertrand

Tommy Valand said...

In the XPage, under All Properties -> Data, you can toggle read only mode using the readonly property. This property is also available in panels.

You can for instance use a parameter to control read-/edit-mode.