Skip to content

Technical overview

Mark Reeves edited this page Jan 4, 2018 · 3 revisions

WComponents is a component based web UI tool kit. It interacts with clients via a request/response paradigm and assumes responsibility for key concerns such as persistent state storage and URL construction for HTTPS transport.

A web application using WComponents is made up of a collection of many WComponents. These components make individual fields, fragments of pages, and pages. Visibility attributes determine which components of a page are rendered to the user. Session information (data entered by a user) is automatically remembered by each WComponent. This means that as the user switches between the pages of an application, all the data entered by the user is kept. When the user returns to a previously visited page, nothing will be missing.

Developing an application using WComponents normally involves creating composite components which contain existing WComponents such as text entry fields and buttons. These are arranged in the composite component using a supplied layout or Velocity template. Control logic which makes sense within the context of the composite component should be implemented within it. With WComponents developers create applications in terms of objects and the methods and properties of those objects: not in terms of URLs and query parameters. WComponents brings true object oriented development to web applications. A WComponents application doesn't just get visual reuse: it also gets reuse of the control logic attached to the components.

The standard component library includes components for standard HTML entry fields such as buttons and text fields and extensions for other commonly used types of UI controls such as menus, tab-sets and date-pickers.

There are no untraceable XML configuration or registration files. The default internal configuration can be overridden by applications or during development using the Apache Configuration API.

The hierarchical structure of WComponents

A WComponent instance can have a list of child WComponent instances which it is using to support its own functionality. Each WComponent instance added as a child remembers who their parent WComponent instance is. It is important to note that each WComponent instance can only have one parent and the framework will enforce this. If a WComponent is to be part of a running web application it will always have a parent unless it is the top component that represents the entire application.

The hierarchical structure of WComponents is the mechanism by which the framework knows about components. It can be considered as a type of registration with the framework. When a WComponents application is running there is one top component that represents the application being executed. Only components added to the hierarchy below the top component take part in request/response handling.

Note that in all real applications the top-level component must be an instance of WApplication. The themes use this to determine the application boundaries when there are multiple applications rendered to the same screen.

Position in the overall Architecture

The WComponents UI framework is a self contained framework designed to support web UI development. It supports Java Servlet and JSR-286 Portlet development yet is decoupled from both of these APIs. The decoupling ensures that a WComponent can be used in either environment, can be unit tested and can be integrated into other environments.

WServlet

WServlet is an implementation of the Servlet interface. It is used as the default container to serve WComponents in a Servlet environment. You use it by extending and implementing the method getUI() to specify which WComponent to run.

For example:

public class HelloWServlet extends WServlet {
    public WComponent getUI(Object httpServletRequest) {
        return new WLabel("Hi there from Servlet land.  [" + new Date() + "]");
    }
}

In order to run your WServlet you must deploy it like any other Servlet: to a Servlet enabled Web-server. Alternatively, during development of a WComponent, you can use the Local Development Environment (LDE) to make things easy.

Servlet request/response sequence

WComponents are run within a container like Servlets. The container is responsible for orchestrating the request/response sequence.

WComponents have several container implementations. WServlet is the WComponents container for a Servlet environment. An extension of WServlet called PlainLauncher is the container that supports the LDE environment.

The WComponents user interface content is served up to the browser in XML format and undergoes client-side XSLT transformation. This is primarily done to decouple the Java code from the client code and to reduce bandwidth requirements for complex user interfaces. It is possible to configure the WComponents framework to perform server-side transformation in situations where it is not possible to serve XML content.

High level view of the request/response sequence

The following diagram shows the high-level request/response sequence for a WComponents application.

 high level request/response cycle

The sequence of events is as follows:

  1. The client makes a request to the WComponents application; either on initial access of the application or as a result of submitting a form. The Servlet container handles orchestration of the HTTP request to the appropriate application and invokes the WComponents framework to handle the request.
  2. An important responsibility of the framework is to fetch the WComponent that is intended to service a request. This component is often referred to as the "top" component or "UI" component. Control is passed to the top-level WComponent (the UI) to handle the request.
  3. The top-level component is asked to render the user interface, and produces declarative XML which describes the UI.
  4. The Servlet container returns the XML mark-up to the client (web browser).
  5. The browser examines the XML prologue contained in the response and requests the appropriate XSL template which is required to transform the XML. Note that the template(s) will be cached by the browser so this step should only occur on the first access of the application.
  6. The browser performs translation from the declarative XML to HTML5 based on the XSL template. The browser requests additional resources such as CSS, JavaScript and images. Note that the majority of these are also be cached by the browser where appropriate so this step should only occur on the first access of the application and where applications have dynamic resources.

Breakdown of the action and render phases

In the WComponents framework request handling is broken into two phases: an action phase and a rendering phase. These are visible as calls from the WComponents framework to the top-level component in steps 2 and 3 in the high-level view above. During the action phase data is read from the client request and the application state is modified. When the render phase occurs the application should be in a stable state. Note that under some circumstances there may be multiple calls to the render phase without an action phase.

The action phase

During the action phase the framework calls the serviceRequest method on the top level component. The first task performs a depth first collation of all visible descendant WComponents. Components are processed in depth-first order to ensure that when a parent component is processed its children have already handled the request and are in a stable state. The top level component then calls the handleRequest method for each WComponent in the list. When the list has been processed, the doInvokeLaters method is called to perform any processing that needs to occur after all components have had a chance to handle the request. The handleRequest method is the standard hook for WComponents to override.

Control logic is best done after all the WComponents in the hierarchy have obtained any information in the request that was intended for them. For example control logic supplied to a WButton via an implementation of the Action interface will invariably need to access information from sibling components or components higher up the hierarchy than the button itself. When a WButton runs an Action it uses the invokeLater feature to ensure the application behaves correctly.

The render phase

The following methods are called on the top component during the render phase:

preparePaintComponent

The framework calls preparePaintComponent on all visible descendant WComponents in a top down traversal of the hierarchy. The preparePaintComponent method is a standard hook for WComponents to override and place code which initialises things for the "paint" phase. You should only initialise user-specific attributes here not shared attributes. The UIContext section discusses shared versus user-specific attributes.

paint

The framework calls "paint" on all visible descendant WComponents in a top down traversal of the hierarchy. This method invokes the methods beforePaint, paintComponent, and afterPaint in order.

The beforePaint method is a hook for WComponents to override to render content before the main component is painted. The default implementation does nothing.

The paintComponent method is where most of the painting work is normally done. If a layout has been supplied, for example a velocity template, then painting is delegated to the layout manager. If there is no layout, the default behaviour is to paint the child components in sequence. Most core WComponents delegate their painting to a "LayoutManager" so they can be rendered in different formats.

The afterPaint method is a hook for WComponents to override to render any additional content after the main component has been painted. The default implementation does nothing.

Controlling application flow using Actions

Application flow control logic is placed inside implementations of the "Action" interface. There are no restrictions on what can be done in actions. For example, they are allowed to change the application's UI state or interact with external resources through Web Services.

Actions are attached to interface elements and are triggered depending on the element. For example an action attached to a WButton is triggered when the button is pressed, while an action attached to a WDropdown can be triggered when the user changes the selection in the drop-down.

An example action implementation is shown in the code snippet below. The action simply prints a message to the console whenever the button is pressed.

WButton button = new WButton("Submit");
add(button);

button.setAction(new Action() {
    public void execute(ActionEvent event) {
        System.out.println("The submit button was pressed!");
    }
});

Actions do not need to be defined inline every time they are needed. They can be made into reusable classes and action instances shared between many components.

Rendering

WComponents usually delegate their rendering to a LayoutManager. There are two main types of LayoutManagers. The first type extends from AbstractJavaLayoutManager and is used internally by the framework to write the mark-up for the standard component directly from Java code.

Layout using Velocity

The other type of LayoutManager is the VelocityLayout, which can be used by application developers to render components using an Apache Velocity template file. An alternative implementation for the simple UI discussed earlier is shown using the code snippets below. It consists of a parent component which contains a WDropdown and WButton. A velocity template "myTemplate.vm" is associated with the component, and is used to arrange the components and output the text label.

Inside the constructor of the top-level component:

setTemplate("myTemplate.vm");

add(new WDropdown(new String[]{ "Option 1", "Option 2", "Option 3"}), "myDropdown");
add(new WButton("Submit"), myButton);

The contents of "myTemplate.vm":

Select an option: $!myDropdown $!myButton

Templates should be used with caution as they may degrade performance or cause incompatibilities in different client-side themes.

XML Rendering

Early WComponents versions rendered to multiple heavy-weight HTML formats, using complicated logic to switch between layout classes based on which theme was in use. This resulted in maintenance issues during development and performance issues in production environments. As a result of this, it was decided to switch WComponents rendering to a controlled XML format. See Why XSLT for more information.

The XML document describes the structure of the user interface, but not how it is to be visually presented on the client. The client-side appearance depends on the theme in use. The simple UI tree structure previously discussed renders to the following snippet of XML (with randomised IDs):

<ui:field id="app_L_0">
    <ui:label id="app_L_0_0" for="app_L_0_1">Select an option:</ui:label>
    <ui:input>
        <ui:dropdown id="app_L_0_1" name="L_0_1">
             <ui:option value="1" selected="true">Option 1</ui:option>
             <ui:option value="2">Option 2</ui:option>
             <ui:option value="3">Option 3</ui:option>
         </ui:dropdown>
     </ui:input>
 </ui:field>

The XML structure reflects the UI structure. There is a "ui:field" (WField) which contains a "ui:label" (WLabel) and a "ui:dropdown" (WDropdown). The first option in the drop down is selected.

Each individual WComponent and LayoutManager is unit-tested to ensure that it produces output which is valid according to the schema. It is an application developer's responsibility to ensure that any mark-up which the application generates is valid. Most often, problems occur when an application generates mark-up externally to WComponents, such as in Velocity templates or incorrectly uses un-escaped String content in components where this is not allowed.

User session management

Session management is an important responsibility of the WComponents framework. Only user-specific state information is stored against the user's session. This is to ensure optimal use of the underlying session storage. There is only a single instance of the WComponents UI tree instantiated per application and WComponents only stores the differences between the default application state and the user's current state.

The framework stores the state information in an object called the "UIContext". The UIContext is stored in the underlying container's session storage mechanism, for example WComponents served by WServlet will have their UIContext stored in the underlying HTTPSession. See the section on Lifecycle and session for more information.

Clone this wiki locally