5.4. Bebop - Reusable Web UI Components

Bebop is a web-based UI component framework. It is named after Swing™, the Java UI toolkit from Sun.

Calling Bebop a web UI component framework has a very specific meaning:

A component-based system is designed for reuse in varying contexts, bundles useful behaviors with useful state, and operates in and responds to a service-rich environment. Bebop is intended to turn a developer into a deployer by providing components which need only be told what to do, rather than how to do it.

In Figure 5-1, Bebop components provide the tabbed pane. This tabbed pane component remembers which are the currently visible components, and it knows internally how to generate the correct URLs to other tabs. This functionality is gained just in using the component.

Figure 5-1. Tabbed Pane Using Bebop Components

5.4.1. Working With Bebop

Chapter 12 Presentation (Bebop) Tutorial discusses specifics of implementing Bebop. This section is a more abstract look into the design and usage of Bebop.

Bebop components follow these guidelines:

The code in Example 5-2 gives an idea of how Bebop is used.

public void service(HttpServletRequest sreq, HttpServletResponse sresp) {
    Page page = new Page("Content Section");

    page.add(new Label("Content Section"));

    TabbedPane tabs = new TabbedPane();
    page.add(tabs);

    tabs.addTab("Browse", new BrowsePane());
    tabs.addTab("Search", new SearchPane());
    tabs.addTab("Roles", new RoleAdminPane());

    page.lock();

    Document doc = page.buildDocument(sreq, sresp);

    transform(sreq, sresp, doc);
}

Example 5-2. Basic Bebop Page

5.4.1.1. Bebop Lifecycles

Bebop components used in a page design have a regular lifecycle.

UI Lifecycle

  1. Register each component.

    1. Each component adds any state parameters and event listeners it needs.

    2. Each component is added to the page and given a unique key so that any state carried in the query string is safe from collisions.

  2. Lock the component tree.

    1. Components can no longer be added or removed. The component instances are now safe for reuse across requests.

  3. Service requests.

Request Lifecycle

  1. Client sends an HTTP request.

  2. A new page state object is created.

    Using the servlet request and the parameter models defined by the developer, the page builds an object representing the state of the current request.

    In addition to component state parameters, the selected component and the visibility state are recovered from the servlet request state.

  3. Fire the request event.

    Any request listeners added when each component registered itself run at this time. Note that this runs before the page request state is validated; in fact, the request event runs before much of any work is done.

  4. Validate page state.

    This is when custom validators, e.g. a zip-code validation listener, are run. Parameters are also typed at this step.

    If there are errors, they are saved so that the component whose state is invalid may choose how to present the issues.

  5. Fire the control event.

    When a client sends a request to a Bebop page (e.g. a mouse-click on a tab); only one component receives the request. This triggers the respond method of that component only; no other components response methods are involved.

    If the request state is deemed valid, the selected component has the opportunity to respond, perhaps changing the request state and thus the outcome of further processing.

    A control event handles form submission, control links, and has influence on the state and visibility before any output is produced.

  6. Fire the action event.

    The action event is an opportunity for any component to run code before the response is committed and after the controlling component has responded.

    Any component may listen and edit state and visibility before any output is produced. This is the last opportunity to edit before XML is written out.

  7. Generate XML.

    The components generate the XML. They write to the document created in the first step. Each component generates its own semantic XML (such as XUL, Mozilla's presentation-based cross-platform markup language) and also delegates to its children. A DOM is built.

  8. Transform the XML.

    The XML document is transformed with an XSLT stylesheet and sent back to the client in the form of HTML and CSS.

    By following this method, it is easy to define and maintain a style and layout uniformity. A developer or designer can drop a different XSLT into the last step in a page request cycle.

  9. The client receives the transformed results of the page request.

5.4.2. JSP Integration with Bebop

JSP is integrated with Bebop components (XML sources) and XSLT by using a JSP tag library to allow the use of Bebop components in an otherwise standard JSP. The JSP tag libraries accomplish this by performing transformations on the XML document produced by a Bebop page. This JSP tag library also directs the generation of output by rendering the resulting XML document through XSLT, so that the included components displayed in a JSP are styled with the standard template rules that are used on the rest of the site. See Appendix A Bebop Tag Library Reference.

Java developers can also construct their Bebop pages in JSP. The tag library for declaring Bebop pages is separate from the one for displaying Bebop components, but they are intended to work together. This is different from using a Bebop component inside of JSP. In this instance, the specific Bebop JSP tags takeover JSP similar to writing and controlling a servlet.

There are several motivating factors for integrating JSP with Bebop. First, laying out pages using JSP integration will allow web developers who are not Java programmers to use third-party web publishing tools (Dreamweaver, etc.) to alter the layout of pages, add new components to an existing page, edit form field labels or prompts, etc. It would also be possible to make completely new pages with JSP that display components from existing Bebop pages.

Second, JSP is a convenience for Java developers creating Bebop pages, because JSP shortens the development cycle for individual pages by eliminating the need for a manual recompile and server restart when a page is changed.

The overall request pipeline for constructing a page with Bebop and displaying it with JSP follows this sequence:

  1. The requested JSP obtains an XML document from a Bebop page object and the current request state.

  2. The tags in the requested JSP construct a new XML document, copying pieces from the Bebop page where needed.

  3. The resulting XML is passed as the XML input to an XSLT transformation step, using global stylesheets.

  4. The final result is sent to the user's browser.

5.4.3. Relationship Between Static and Dynamic Pages

The JSP tag libraries for Bebop are designed to work together with Bebop's model of static, shared Page and Component objects, while providing familiar semantics to JSP authors. The division in Bebop between static Page objects that produce dynamic output on each request is reflected by the division of tags into two separate libraries:

A library of define:... tags.

Used for creating Bebop components.

The define:... tag library defines a Bebop page and its components with JSP tags. Since the page structure may be static, JSP code in the define:... tag library is not guaranteed to run on each request. Care must be taken to not put any runtime expressions or scriptlets into a <define:page> block that are expected to run on each request.

A library of show:... tags.

Used for displaying the output form Bebop components inside a JSP page.

The show:.... tag library is dynamic. Any Java code inside a <show:page> block is guaranteed to run on each request, even if the components used are static. This is because the show:... tags manipulate the XML output produced by Bebop components on each request, and not the components themselves. Any valid JSP runtime expressions, tags, or scriptlets may be used in conjunction with show:... tags and they should yield the same results they would in any other JSP.

Caching in <define:page> can be suppressed with the JSP tag attribute cache="no". This will cause the JSP define:page tag, along with the code and all the other tags inside it, to execute on each request; the entire Page object will be re-created and garbage-collected on each request.

WarningWarning
 

Although it seems there could be some benefit to changing the structure of a Page on each request, it is not recommended.

Variations on the structure of a page will interfere with Bebop's state maintenance, because the URL and form variables that preserve state for individual components are linked to the component's position in the overall page structure. Runtime errors or unpredictable results may occur if a page's structure differs between subsequent requests.

Certain types of non-structural page variations between requests are permissible. Most importantly, the individual options in option groups (radio groups, select widgets, checkbox groups) are not components themselves, so the following code is permissible:

<define:radioGroup>
   <% loop { %>
      <define:option label="<%= rtexpr %>" value="<%= expr %>"/>
   <% } %>
</define:radioGroup>

This is especially true if the options in the group do not change frequently. Also, the contents of stateless components (Labels, Links, Images, etc.) may change between requests, though the position of the component in the page should not.

Further examples can be found at Appendix A Bebop Tag Library Reference.