FormBuilder

Introduction

For most Java EE web applications, entity beans directly determine the contents of many view forms. For example, a person is represented in the domain model by a Person entity bean with properties like name, password, etc. The properties are then typically propagated directly to a corresponding view form. Manual view form development and maintenance is error-prone and tedious. When developing the view form, we must focus not only on accurately reproducing field names and types but also on additional properties, such as default values, constraints, etc. Any changes at the entity bean level must be propagated to the corresponding view forms. In addition, view form errors are difficult to detect because of weak type safety and limited mechanisms for constraint verification. The FormBuilder tool provides automated construction and maintenance of view forms based on entity bean fields and annotations.
In this guide I expect that you have a basic knowledge of JBoss Hibernate validation.

Features

 

Description

Quick steps overview

Configuration

To start the description of the tool I will show its life cycle at Figure 1. The input is an entity bean that you want to use for form generation and then your configuration.

Figure 1.

Tool also offers two prepared tag libraries. First uses only simple JSF and second Seam UI and RichFaces. If you are going to develop your application under JBoss Seam you can extend existing configuration and take a look at example that is provided with the tool. If you want to use the tool somewhere else you will need to define the mapping of field types to inputTags. You can refine your mapping according guard conditions. Guard conditions are evaluated and first satisfied condition will determine inputTag to use. Simple example: <use-types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:edu.baylor.icpc.formbuilder.config.UseType">
    <default-length>255</default-length>
    <default-size>30</default-size>
    <name>String</name>
    <file-path>inputTextTag.xhtml</file-path>
    <guards xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:java.lang.String">email == true</guards>
    <guards xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:java.lang.String">maxLength &gt; 255</guards>
    <guard-file-paths xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:java.lang.String">emailTag.xhtml</guard-file-paths>
    <guard-file-paths xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:java.lang.String">inputTextAreaTag.xhtml</guard-file-paths>
</use-types>
The code says that String field is mapped in tag inputTextTag.xhtml. And that default field length is 255 and default input size is 30. But there is also an refinement that if Java field annotation email is used then tool will use emailTag.xhtml instead. Another refinement is that if field maximal length is greater that 255 the the inputTextAreaTag.xhtml is used instead. Please note that if the field would have an annotation @email and @maxLength=512 then the emailTag.xhtml tag is used. For special cases you can define variable condition. Which is true for field name equal to the one that is specified (email in the example) <guards xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:java.lang.String">var:email</guards> If mapping is not defined then field is just ignored.
Configuration also defines file paths for tag library and output directory, header and tail file for your form and output file suffix. Last important thing in the configuration are filters. Often you have fields that you do not want to use even though that you have defined mapping for its type. You can just specify field name to ignore <ignore-fields xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="java:edu.baylor.icpc.formbuilder.config.IgnoreField">
    <name>id</name>
</ignore-fields>

Tags

Lets take a look how a tag look like. It is nothing else then usage of your component. You have defined many variables that tool knows. To find what the tool knows please see usage chapter and help. Help contains all marks (variables) that the tool knows. You probably have a question where from the variables are coming. They are defined at your field. @Column(name = "email", nullable = false, length = 100, unique = true)
@NotNull
@Email
@Length(max = 100)
@FormOrder(3)
public String getEmail() {
      return this.email;
}
Generator recognizes the field annotations and propagates them to the mapped tag. In this example :
  • $notNull=true,
  • $minLength=0,
  • $maxLength=100,
  • $unique=true,
  • $email=true,
  • field position in the form is 3,
  • $id=email,
  • $value=bean.email,
  • $label=Email:,
  • $entityBean=Person (Person.class),
  • $entityManager=personManager,
  • $size=30 (from configuration),
Configuration can map email to inputText. Generator then reads the particular tag and gathered information values are used in the tag.

Template

<util:inputText label="$label"
      edit="#{edit}"
     value="#{$value}"
  required="$notNull"
      size="$size"
     email="true"
   pattern="'$pattern'"
 minlength="$minLength"
 maxlength="$maxLength"
  rendered="#{empty $idRender ? 'true' : $idRender}"
        id="$id"/>
This template for email field will be resolved as following (since no pattern is defined the attribute is erased): <util:inputText label="Email:"
      edit="#{edit}"
     value="#{bean.email}"
  required="true"
      size="30"
     email="true"
 minlength="0"
 maxlength="100"
  rendered="#{empty emailRender ? 'true' : emailRender}"
        id="email"/>
Please use the form of one parameter per line aligned by equality mark. This makes your code much more readable. Tool expects this format. Tool uses marks (variables) that has the form php like $var. You can use these variables everywhere in your tag. If mark is not used, tool will erase unused mark and its assignment.
Various tags can be used in different way. Simple Java types does not need any special workaround. Enums and IdEntity based objects can use custom or Jboss Seam converters. I expect that you can use a session bean as the one in example application and get all possible selections from there. You also can define simple color handling component or complicated searching component for Entity objects.
Back to the example the inputText is defined as a facelets tag

Tag

<my:inputText .../> --> inputText.xhtml <ui:composition template="/WEB-INF/tags/form/viewEditSwitch.xhtml">
  <ui:define name="edit">
   <s:decorate id="#{id}Decoration" template="/layout/formElement/edit.xhtml">
    <ui:define name="label">#{label}</ui:define>

    <h:inputText
           onblur="validateInputText(this,#{empty required ? 'false' : required},
             #{empty minlength ? '0' : minlength}, #{empty maxlength ? '255' : maxlength},
             #{empty email ? 'false' : email}, #{empty pattern ? '\'\'' : pattern},
             '#{text['client.validator.text']}','#{empty pattern ? '' : text[util:replaceSpaceJSPattern(pattern)]}');"
         rendered="#{empty rendered ? 'true' : rendered}"
         required="#{empty required ? 'false' : required}"
               id="#{id}"
        maxlength="#{empty maxlength ? '255' : maxlength}"
             size="#{empty size ? '20' : size}"
            title="#{empty title ? label : title}"
            value="#{value}">

         <ui:insert />
     </h:inputText>
   </s:decorate>
  </ui:define>

</ui:composition>
Example application offers to you following (edit/read-only) tags (table shows dependencies for Seam library, JSF one need only JSF and Facelets):
name dependencies
checkBox seam UI (decorate)
inputColor seam UI, richFaces and entity value setter red, green, blue
inputDate seam UI
inputHTML seam UI
inputLink seam UI
inputNumber seam UI, richFaces
inputPassword seam UI, richFaces
inputRichDate seam UI, richFaces
inputText seam UI
inputTextArea seam UI
selectionMenu seam UI
selectionOption seam UI

Please note that these tags offers both edit and read-only (outputText) rendering which makes them more powerful.

Client side validation

Validation can be used in your tags as is demonstrated in supplied tags. All information from entity bean is propagated to view now so we can define simple mechanism for validation. Validation is used for String, Number and Date tags and also for password match. Other components like Enum which uses selection, Color or Entity elements does not need client side validation. (try password tag in example application!). Validation has two evaluations, one is onblur javaScript call and second is on save button onclick which requires you to use javaScript call on your submit button. <h:commandButton id="save"
         value="Save"
       onclick="return allowSubmitCheckAll(this);"
        action="#{personHome.persist}"
      rendered="#{!personHome.managed}"/>
<h:commandButton id="save"
         value="Save"
       onclick="return allowSubmitCheckAllNoAlert(this)"
        action="#{personHome.persist}"
      rendered="#{!personHome.managed}"/>
You can also use internationalization for your validation. Usage can be found in example application.

Security consideration

Your generated form can be used as a facelet tag or decorated, I recommend the tag, which significantly decreases coupling. The only parameters that you need to pass in are Boolean variable determining if the form should be read-only or editable and then active instance. Optionally you pass your session bean as well (Entity search). <util:person bean="#{personHome.instance}" edit="#{empty edit ? false : edit}"/> Configuration by exception is used for form field rendering. Default behavior is to display all fields. If your security rules define field visibility you can set not to render the field. Second option is to update tags and use Map<String,Boolean> holding information if field name will be rendered. This way all your form field security renders field in dynamic way. <util:person bean="#{personHome.instance}" edit="#{empty edit ? false : edit}"
      lastNameRender="false"
      salaryRender="false" />
Definition of your tag is simple in your facelets tag library <tag>
    <tag-name>person</tag-name>
    <source>tags/gen-form/person.xhtml</source>
</tag>

Annotations

I found useful to define more annotations for your entity beans.
  • formOrder - this defines the position in view form (since Java reflections provides only set)
  • formHtml - this marks field as html String
  • formLink - this marks field as link String
  • formPassword- this marks field as secret String
  • formPattern - this allows you to specify pattern that will be used for javaScript (it differs from Java!)
  • formTableType - this is more a future annotation for table generation
New annotations as credit card, phone number or passport will be defined after you will send me feedback.

Development

You can use form generation of one file at the time. But you also can use multi-file generation at the time. One branch of the tool expectation also goes farther. Tool should be able to generate a form and programmer should not touch the form code at all. If entity bean is changed you can just regenerate the form. To make sure that all forms are fresh in every build you can define a tool call every time you are building the application. In this way you are sure that all forms reflects the fresh backend!
Another advantage is the uniform approach with the way how form are saved and managed among large applications by different programmers.

Usage

help shows usage, possible variables for tags and guard for configuarion
java -jar formBuilder.jar --help
run tool from command line: need to add your files to classpath and use parameter package.className
you can build batch file
java -cp ./classes/model:formBuilder.jar edu.baylor.icpc.formbuilder.FormBuilder edu.baylor.icpc.formbuilder.example.Person
run tool from java code: you can build your deployment script
FormBuilder.genetateTagFromClass(Entity.class)
FormBuilder.genetateTagFromString(package.class)
Tool expects JBoss Hibernate validator usage. For using prepared tags you need Facelets, (optionally JBoss Seam UI, and RichFaces). (Jar is compiled under Java 6)

Libraries

Form builder provides two libraries one for Seam and one for basic JSF, I expect that new libraries for Trinidad, Tomahawk and ICEfaces are easy to make and will be available in future. Please note (JSF library):
  • server side validation for fields does not work as in Seam with JSF.
  • JSF does not offer regular expression and email validator (see tomahawk or use custom validator as in example)
  • for server side validation you can use in your tags following attributes
    • javaPattern="$javaPattern" for JSF validation
    • jsPattern="$jsPattern" for JS validation
    • pattern="$pattern" (if empty jsPattern then javaPattern else jsPattern)


  • Why there is a difference between Java and javaScript?
    @Pattern(regex = "^[a-z._-]+") - java
    @FormPattern("^[a-z._-]+$") - javaScript
    for pattern "^[a-z._-]+" and word "aHoj" - java fails and JS passes
    solution is to use for javaSript its own pattern

Future Expectation

I expect that in the future the application will offer more libraries to choose from. Annotations can be extended with credit card, passport number and so on. The best would be if Hibernate would extend the annotations and offer the server side validation. Hibernate is also missing field compare to field validation which could be offered in client side. The same approach as used for field ordering can be used for building tables. The annotation will just say which fields to use for table generation. Form Builder can be use with AOP approach for entity bean generation, developer will simply specify a pattern for entity classes which are used for form generation.

Contact Me

I hope it will help you and I will be happy for your feedback tom.cerny@gmail.com. I am using LGPL licence for the tool. Tomas Cerny

Acknowledgement

I would like to thank for help, review and for many ideas to Associate Professor Michael J. Donahoo. The thank is also for Mr. Ray Holder for the comments and code and guide review. Person that also contributed to the project with MDA experiences is Assistant Professor Eunjee Song. This project would not exist without all people who are responsible for JSF, Seam, Facelets and Java.