Mapper Logo
.

3. Pages and Forms Architecture

3.1 ServletForm

The Mapper presentation framework for the web is modeled after a the architechure of a typical GUI/Swing type application. A single servlet dispatches requests to the appropriate ServletForm. The ServletForm is a container similar to a Swing JFrame. You create the various forms for your application by extending this base class.

The ServletForm dispatches commands and data from the request object to the individual components it contains. The dispatching occurs in the doGet method of the ServletForm.

The ServletForm is a reusable object. The ServletApp (master servlet) will normally recycle ServletForms unless their recycle method returns false. The recycle method in the ServletForm dispatches a RecycleEvent to all components.

The ServletForm is composed of many subcomponents to support the processing of the request: ServletParameterDispatcher, ServletInputDispatcher, RenderDispatcher, ServletTriggerDispatcher, RecycleDispatcher, DocumentAdapter and Responder.

The following is an example of a very simple ServletForm which contains a single component to display the current time:

public class FirstPage extends ServletForm {
  TextField currentTime = new TextField(DataTypes.TYPE_DATE);

  public FirstPage() {
    this.setDocumentURI("resource:///forms/FirstPage.html");
    currentTime.setId("currentTime");
    this.add(currentTime);
  }

  protected void initForm() throws java.lang.Exception {
    super.initForm();
  }

  protected void openForm() throws java.lang.Exception {
    currentTime.setValue(new Date());
  }
        

The following is the HTML which is used for by this ServletForm:

<html>
<head>
  <title>MapperXML Example: The FirstPage</title>
</head>
<body>
  <h1>MapperXML Example: The FirstPage</h1>
  <p>The current time is: <span id="currentTime">00:00:00</span></p>
</body>
</html>
        

3.2 Request Processing Cycle - doGet

The ServletApp invokes the ServletForm's doGet method to process the request. This method is invoked by the ServletApp for all request types (GET, POST, etc). The doGet method breaks the single request up into a sequence of events:
  1. set Request/Response - Saves request and response in public properties. They are then accessable using the getRequest() and getResponse() methods.

  2. initForm - It is typically only only once for a ServletForm instance (checks the isInitialized flag). You will normally override this method and create the DOM during this method.

  3. multi-part request check - If this is a multipart type request, uses the request wrapper returned by createRequestWrapper in place of the original request

  4. dispatchInitContext - Distributes a shared context to all components.

  5. dispatchParameters - Fetches input values from request for all Parameter components (and other input components which have their earlyInputNotify flag set).

  6. openForm - You will normally override this method and implement application specific behavior. You would typically do things like invoke a BusinessDelegate to retrieve requested data and place it in ValueHolders.

  7. dispatchInput - Fetches input values from request for all other input components which do not have their earlyInputNotify flag set.

  8. dispatchActions - Causes Triggers and Buttons events to occur if their triggering values are present in the request.

  9. sendResponse - Provide a response back to the client. This method delegates the work to a Responder. The default Responder is an HTMLResponder which sends the HTML page back with values updated.

  10. handleException - this method is only invoked if an exception occurs in any of the above methods. Override the method to provide an appropriate response to problems.
  11. closeForm - this always occurs at the end of the request cycle. If needed, it provides a way to do clean-up.

3.3 Dispatchers

The ServletForm uses a set of specialized subcomponents called dispatchers. These subcomponents dispatch messages to the components you define in the ServletForm. As you add components (TextFields, Triggers, Parameters) to the ServletForm (Container), those components register with one or more dispatchers to receive notification about events they are interested in.

It is critical to make sure you ADD components you create to the ServletForm (or a nested container). If you omit this, your components will not be notified of any events. Container hierarchy is supported, so as long as a component is added to some container (who is ultimately a child of the ServletForm), it will receive notification.

There are separate dispatchers for each type of event. Some of these include:

  • initContextDispacher
  • initFormDispacher
  • openFormDispacher
  • parameterDispacher
  • renderDispatcher
  • recycleDispatcher
  • inputDispatcher
  • triggerDispatcher

3.4 Document Adapter

The actual XML/HTML document is rendered through the use of a XML/HTML template. The XML/HTML template does NOT contain any code. The Components are bound to the template through the use of the ID attribute:

<p id=greeting>Hello greeting-name</p>

A TextField would have its ID property set to "greeting". When instructed by its container (ServletForm), it would render its value to the XML/HTML template.

The XML/HTML template is loaded, parsed into a Document Object Model (DOM) and cached by the ServletForm. The template is normally reused by the ServletForm.

The DocumentAdapter can be created automatically by the container if you set the documentURI property. For automated creation, the container uses a DocumentAdapterBuilderFactory. This factory is configured to try using: JTidy, Xerces, JAXP or a user supplied DocumentAdapterBuilder to parse the DOM and create the DocumentAdapter. You can also manually code the creation of the DocumentAdapter.

The Document is normally created and attached to this ServletForm in the initForm method. This method is normally only called once when the ServletForm is first invoked. Below is an example using the Xerces parser:

		public MyPage() {
		  this.setDocumentURI("resource:///forms/MyPage.html");
		}
        protected void initForm() throws Exception {
          super.initForm();
        }
        

3.5 Multi-Part Request Wrapper

The ServletForm also supports multipart type requests. A multipart request is sent by the browser when form data contains 1 or more uploaded files. To support this feature, if the incoming request has a content type of "multipart/form-data", the request is wrapped in another request object which is capable of processing multipart requests.

The wrapper request is created via the createRequestWrapper method. By default, createRequestWrapper returns a HttpMultiPartServletRequest object which has a maximum file size of 1 megabyte and maximum single line size of 4,096 bytes. You can change this by overriding the createRequestWrapper method:

        protected HttpServletRequest createRequestWrapper(HttpServletRequest rq)
            throws Exception {
          HttpMultiPartServletRequest multi = new HttpMultiPartServletRequest(rq);
          // set maximum sizes if defaults if needed
          multi.setMaxFileSize(2048);
          multi.setMaxLineLength(80);
          // parse the request
          multi.parseRequest();
          return multi;
        }
        

3.6 Responders

You can control the response of this ServletForm by changing the Responder subcomponent at runtime. The default Responder is an HTMLResponder which sends the DOM Document as the response. There are a number of other types of responders you can use:

  • ClientRedirectResponder - redirects the client to a URL as the response.
  • ErrorResponder - sends status code and optional message as response.
  • HTMLResponder (default) - provides rendering and sending Document as response.
  • NoResponseResponder - responds by doing nothing. You must handle generating the reponse through your own code.
  • StreamResponder - responds by sending its input stream as the response. This can be used for sending images or other data back to the client.

If your ServletForm needs to send more than one type of response, then you should create the appropriate set of responders and invoke the setResponder method of the ServletForm to change the responder.

Below is a typical example of using responders. In this example, the ServletForm can respond by either sending the page, or redirecting the client to another page:

  ...
  HTMLResponder htmlResponder = new HTMLResponder();
  ClientRedirectResponder clientRedirectResponder = new ClientRedirectResponder();
  ...
  private void jbInit() throws Exception {
    ...
    clientRedirectResponder.setRedirectURL("/mapperex/index.html");
    ...
  }
  ...
  protected void openForm() throws java.lang.Exception {
    // Set default responder
    setResponder(htmlResponder);
    ...
  }
  ...
  void saveButton_actionPerformed(TriggerEvent e) throws Exception {
    // Save changes and go to main menu
    delegate.saveChanges((PersonVO)holder.getValue);
    setResponder(clientRedirectResponder);
  }
  ...
        

You can create your own responders by extending one of the existing responders or implementing the Responder interface.

3.7 Components

In typical applications, you will add Components to this ServletForm and set their properties to bind to the DOM Document elements, the http request parameters, and value objects. These components are capable of modifying the DOM Document, storing and retrieving value from bound objects, and reading parameters from the http request, and parsing/converting between text values and java data types. This ServletForm is the base container for these components and contains dispatchers which dispatch events to the components. This ServletForm generates the events within the doGet method in a fixed sequence (see javadoc for doGet).

The below example creates a HTMLInputText component which binds to an HTML form input text field. It also binds to the lastName property of a Java value object class.

        public class MyPage extends ServletForm {
          HTMLInputText lastName = new HTMLInputText();
          VOValueHolder person = new VOValueHolder();

          public MyPage() {
            lastName.setPropertyName("lastName");
            lastName.setValueHolder(person);
            lastName.setId("lastName");
            this.add(lastName);
          }
          ...
          protected void openForm() throws Exception {
            // Retrieve or create the value object
            Person personVO = new Person(1629, "Pulaski", "Katherine", null);
            // Bind value object to person ValueHolder
            person.setValueObject(personVO);
          }
        

There are many components you can use in a ServletForm. Some of these include: Parameter, Template, TextField, Trigger, Button, HTMLAnchorURL, HTMLCheckBox, HTMLInputText, HTMLSelect, HTMLTextArea, and others. The next 2 chapters will describe the components in detail.

3.8 Component Factories

MapperXML provides a feature to automate the creation of components at runtime. This feature can create the proper type of component, set many of its properties including:

  • id
  • valueHolder
  • propertyName
  • parameter
  • format and formatPattern

This feature is provided by the HTMLComponentFactory.createComponents method. The createComponents method scans the HTML document looking for elements with an id attribute that match a specific syntax. It then creates the component, adds it to the container (in the proper heirarchy), binds it to the specified ValueHolder, sets the specified properties.

This feature allows you to mix manually coded components with automatically created components. As it scans the HTML document to create components, it first checks the container for existing components. If a component already exists, it does not duplicate or modify the existing component. It does, however, move it to the correct position in the component heirarchy.

The createComponents method builds a component heirarchy which matches the document order and heirarchy. If any newly created component is itself a Container type, then all children of that component are added to it rather than its parent container.

Only components which are bound to one of the given ValueHolders are created. The id's must also follow a strict syntax to be automatically created.

3.8.1 General Syntax for ID Attribute

The id must begin with a ValueHolder's alias. It must then be followed by a double-underscore ("__"). Next the propertyName must appear or the keyword "TEMPLATE_NODE". An optional suffix can be added to ensure unique id's (as required by spec). The optional suffix must be separated from the property name by a double-underscore("__"). The following are examples of valid id format:

  • Person__lastName
  • Person__lastName__2
  • Invoices__TEMPLATE_NODE
  • Invoices__TEMPLATE_NODE__2

The alias of the id (first part), must match an alias of a ValueHolder in the given array of ValueHolders, otherwise no Component will be created. The ValueHolder with a matching alias will be set as the new Component's valueHolder.

If the new Component is an AbstractField subclass, then its propertyName will be set to the propertyName of the id (second part). If the given Element has a "name" attribute, the Component's parameter will be set to the value of the "name" attribute.

If the new Component is a Template (or subclass), then only its collectionValueHolder property will be set. The associated ValueHolder for this Component must be a CollectionValueHolder, otherwise no Component will be created.

3.8.2 Format Control Syntax for ID Attribute

This method will also attempt to setup the formatting properties for the new Component. This only applies to AbstractField subclasses. The format is extracted from the document within the element's "value" attribute, "href" attribute, or text node. The format must be specified as TYPE:pattern, where TYPE is one of: DATE NUMBER or MSG. The pattern should be a valid pattern for the format type. The following are examples of use:

  • <span id="Person__birthdate">DATE:MM/dd/yyyy</span>
  • <input type="text" id="InvoiceItem__unitPrice" value="NUMBER:###,##0.00" />
  • <a id="Person__personID" href="MSG:/PersonProfile.mxform?personID={0}">Link to Person</a>

3.8.3 ID Attribute Syntax Examples

HTML Resulting Component, Subcomponents and Properties
<span id="Person__birthdate">01/01/1901</span>
Component Class com.taursys.xml.TextField
Property: id Person__birthdate

3.8.4 Example Code for HTMLComponentFactory

The following is an example of a ServletForm which uses an HTMLComponentFactory to automatically create and bind all of the components (this example can be found in the distribution examples)

  public class ComponentFactoryPage extends ServletForm {
    private VOCollectionValueHolder invoice = new VOCollectionValueHolder();
    private VOCollectionValueHolder invoiceItem = new VOCollectionValueHolder();
    private BusinessDelegate bd;

    public ComponentFactoryPage() {
      try {
        jbInit();
      }
      catch(Exception e) {
        e.printStackTrace();
      }
    }

    private void jbInit() throws Exception {
      this.setDocumentURI("resource:///forms/ComponentFactoryPage.html");
      invoice.setAlias("Invoice");
      invoice.setValueObjectClass(
          com.taursys.examples.simpleweb.InvoiceVO.class);
      invoiceItem.setValueObjectClass(
          com.taursys.examples.simpleweb.InvoiceItemVO.class);
      invoiceItem.setAlias("InvoiceItem");
      invoiceItem.setParentValueHolder(invoice);
      invoiceItem.setParentPropertyName("items");
    }

    protected void initForm() throws java.lang.Exception {
      super.initForm();
      // Fetch reference to the Business delegate
      bd = BusinessDelegate.getInstance();

      // ========= Use HTMLComponentFactory to create components ===========

      HTMLComponentFactory.getInstance().createComponents(this,
        new ValueHolder[] {invoice, invoiceItem});

      // ===================================================================
    }

    protected void openForm() throws java.lang.Exception {
      invoice.setCollection(bd.getAllInvoices());
    }
  }
        

3.8.5 Component Inventory

The following is the table of components that the HTMLComponentFactory uses. You can override this table if you want.

Element Tag Type Attribute Suggested Component(s)
a n/a HTMLAnchorURL
select na HTMLSelect
span n/a TextField
td n/a TextField
textarea n/a HTMLInputText
input hidden HTMLInputText
input password HTMLInputText
input submit Button, Trigger
input text HTMLInputText
input checkbox HTMLCheckBox

3.8.6 Customizing Component Factories

MapperXML provides an Abstract Class called ComponentFactory which is intended to automate the creation of Compents. You can create your own extensions of this class for specialized XML documents. MapperXML also provides a concrete subclass called HTMLComponentFactory.

This class contains two primary methods:

  • getSuggestedComponents - for use by design tools.
  • createComponents - to automatically create and bind Components at runtime.

The HTMLComponentFactory initializes the factory's tagTable with suggested components for HTML documents. During the automated Component creation, only the first suggestion is used. The other suggestions are intended for use by design tools. The following are the suggestions created by this class:

.