Apache Click

Apache Click User Guide

V 2.3.0


Table of Contents

1. Introduction to Apache Click
1.1. Hello World Example
1.2. Control Listener Type 1 Example
1.3. Control Listener Type 2 Example
1.4. Simple Table Example
1.5. Advanced Table Example
1.6. Simple Form Example
1.7. Advanced Form Example
1.7.1. Form Layout
2. Pages
2.1. Classes
2.2. Execution
2.3. Request Parameter Auto Binding
2.3.1. Customizing Auto Binding
2.4. Security
2.4.1. Application Authentication
2.4.2. Container Authentication
2.4.3. Container Access Control
2.4.4. Logging Out
2.5. Page Navigation
2.5.1. Forward
2.5.1.1. Forward Parameter Passing
2.5.1.2. Page Forwarding
2.5.2. Template Path
2.5.3. Redirect
2.5.3.1. Redirect Parameter Passing
2.5.3.2. Post Redirect
2.6. Page Templating
2.7. Page Actions
2.7.1. Page Action Execution
2.7.2. ActionResult
2.7.3. Page Action Example
2.7.4. Accessing Request Parameters
2.7.5. Set response headers and status code
2.8. Direct Rendering
2.9. Stateful Pages
2.9.1. Page Creation
2.9.2. Page Execution
2.9.3. Page Destruction
2.10. Error Handling
2.11. Page Not Found
2.12. Page Message Properties
2.13. Page HEAD Elements
3. Controls
3.1. Control Interface
3.2. Control Listener
3.3. Control Classes
3.4. Message Properties
3.4.1. Message Resolution
3.4.2. Control Properties
3.4.3. Accessing Messages
3.5. Control HEAD Elements
3.6. Container
3.6.1. AbstractContainer
3.6.2. AbstractContainerField
3.7. Layouts
3.7.1. Template layout
3.7.2. Programmatic layout
3.8. Behavior
3.8.1. Behavior Execution
3.8.2. Behavior Example
4. Ajax
4.1. Ajax Overview
4.2. AjaxBehavior
4.3. AjaxBehavior Execution
4.4. First Ajax Example
4.4.1. Ajax Trace Log
4.4.2. Ajax Trace Log - No Ajax Target Control Found
4.4.3. Ajax Trace Log - No Target AjaxBehavior Found
4.5. Ajax Page Action
4.6. Ajax Response Types
4.7. Ajax Error Handling
5. Configuration
5.1. Servlet Configuration
5.1.1. Servlet Mapping
5.1.2. Load On Startup
5.1.3. Type Converter Class
5.1.4. Config Service Class
5.2. Application Configuration
5.2.1. Click App
5.2.2. Pages
5.2.2.1. Multiple Pages Packages
5.2.3. Page
5.2.3.1. Page Automapping
5.2.3.2. Automapping Excludes
5.2.3.3. Page Autobinding
5.2.3.4. Page Autobinding - Using Annotations
5.2.4. Headers
5.2.4.1. Browser Caching
5.2.5. Format
5.2.6. Mode
5.2.6.1. Page Auto Loading
5.2.6.2. Click and Velocity Logging
5.2.7. Controls
5.3. Auto Deployed Files
5.3.1. Deploying resources in a restricted environment
5.3.2. Deploying Custom Resources
6. Best Practices
6.1. Security
6.1.1. Declarative Security
6.1.2. Alternative Security solutions
6.1.3. Resources
6.2. Packages and Classes
6.2.1. Page Classes
6.3. Page Auto Mapping
6.4. Navigation
6.5. Templating
6.6. Menus
6.7. Logging
6.8. Error Handling
6.9. Performance

Chapter 1. Introduction to Apache Click

Apache Click is a simple JEE web application framework for commercial Java developers.

Apache Click is an open source project, licensed under the Apache license .

Click uses an event based programming model for processing Servlet requests and Velocity for rendering the response. (Note other template engines such as JSP and Freemarker are also supported)

This framework uses a single servlet, called ClickServlet, to act as a request dispatcher. When a request arrives ClickServlet creates a Page object to process the request and then uses the page's Velocity template to render the results.

Pages provide a simple thread safe programming environment, with a new page instance created for each servlet request.

Possibly the best way to see how Click works is to dive right in and look at some examples. (The examples are also available online at http://click.avoka.com/click-examples/ under the menu "Intro Examples".)

1.1. Hello World Example

A Hello World example in Click would look something like this.

First we create a HelloWorld page class:

package examples.page;

import java.util.Date;
import org.apache.click.Page;

public HelloWorld extends Page {

    private Date time = new Date(); 1

    public HelloWorld() {
        addModel("time", time); 2
    }

}
1

Assign a new Date instance to the time variable.

2

Add the time variable to the Page model under the name "time". Click ensures all objects added to the Page model is automatically available in the Page template.

Next we have a page template hello-world.htm, where we can access the Page's time variable using the reference $time:

<html>
  <body>

    <h2>Hello World</h2>

    Hello world from Click at $time

  </body>
</html>

Click is smart enough to figure out that the HelloWorld page class maps to the template hello-world.htm. We only have to inform Click of the package of the HelloWorld class, in this case examples.page. We do that through the click.xml configuration file which allows Click to map hello-world.htm requests to the examples.page.HelloWorld page class.

<click-app>
  <pages package="examples.page"/>
</click-app>

At runtime the following sequence of events occur. The ClickSerlvet maps a GET hello-world.htm request to our page class example.page.HelloWorld and creates a new instance. The HelloWorld page creates a new private Date object, which is added to the page's model under the name time.

The page model is then merged with the template which substitutes the $time reference with the Date object. Velocity then renders the merged template as:

Hello World Screenshot

Figure 1.1. Hello World Screenshot


1.2. Control Listener Type 1 Example

Click includes a library of Controls which provide user interface functionality.

One of the commonly used controls is the ActionLink, which you can use to have an HTML link call a method on a Page object. For example:

public class ControlListenerType1Page extends Page {

    /* Set the listener to this object's "onLinkClick" method. */
    private ActionLink myLink = new ActionLink("myLink", this, "onLinkClick")

    private String msg;

    // Constructor ------------------------------------------------------------

    public ControlListenerType1Page() {
        addControl(myLink); 1
    }

    // Event Handlers ---------------------------------------------------------

    /**
     * Handle the ActionLink control click event.
     */
    public boolean onLinkClick() {
        String msg = "ControlListenerPage#" + hashCode()
            + " object method <tt>onLinkClick()</tt> invoked.";
        addModel("msg", msg);

        return true;
    }

}
1

Add the link to the page. The link will be made available to the page template under the variable myLink, which is the name of the control.

In the Page class we create an ActionLink called myLink and define the control's listener to be the page method onLinkClick(). When a user clicks on myLink control it will invoke the listener method onLinkClick().

In Click a control listener method can have any name but it must return a boolean value. The boolean return value specifies whether processing of page events should continue. This control listener pattern provides a short hand way for wiring up action listener methods without having to define anonymous inner classes.

The advantage of this style of control listener binding is that you have to write fewer lines of code. The disadvantage of this type of control listener binding is that no compile time safety is provided, and you miss out on the compiler refactoring capabilities provided with modern IDEs.

Back to our example, in the page template we define an HTML link and have the myLink control render the link's href attribute:

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="style.css"></link>
  </head>
  <body>
  
  Click myLink control <a href="$myLink.href">here</a>.

  #if ($msg)
    <div id="msgDiv"> $msg </div>
  #end

  </body>
</html>

At runtime this page would be rendered as:

Click myLink control here.

When a user clicks on the link the onLinkClick() method is invoked. This method then creates the msg model value, which is rendered in the page as:

Click myLink control here.

ControlListenerPage#12767107 object method onLinkClick() invoked.

1.3. Control Listener Type 2 Example

The second type of control listener binding uses the ActionListener interface to provide compile time safety. This compile time binding also supports code refactoring using modern IDE tools.

public class ControlListenerType2Page extends Page {

    private ActionLink myLink = new ActionLink("myLink");

    // Constructor ------------------------------------------------------------

    /**
     * Create a new Page instance.
     */
    public ControlListenerType2Page() {
        addControl(myLink);

        myLink.setActionListener(new ActionListener() {
            public boolean onAction(Control control) {
                 String msg = "ControlListenerPage#" + hashCode()
                 + " object method <tt>onAction()</tt> invoked.";
                 addModel("msg", msg);

             return true;
            }
        });
    }

}

In the Page class we create an ActionLink called myLink. In the Page constructor we set the control's action listener to an anonymous inner class which implements the method onAction(). When a user clicks on the myLink control it will invoke the action listener method onAction().

As with our previous example, in the page template we define a HTML link and have the myLink control render the link's href attribute:

<html>
  <head>
    <link type="text/css" rel="stylesheet" href="style.css"></link>
  </head>
  <body>
  
  Click myLink control <a href="$myLink.href">here</a>.

  #if ($msg)
    <div id="msgDiv"> $msg </div>
  #end

  </body>
</html>

At runtime this page would be rendered as:

Click myLink control here.

When a user clicks on the link the onAction() method is invoked. This method then creates the msg model value, which is rendered in the page as:

Click myLink control here.

ControlListenerPage#12767107 object method onAction() invoked.

1.4. Simple Table Example

One of the most useful Click controls is the Table control.

An example usage of the Table control in a customers Page is provided below:

public class SimpleTablePage extends Page {

    @Bindable protected Table table = new Table();

    // Constructor ------------------------------------------------------------
     
    public SimpleTablePage() {
        table.setClass(Table.CLASS_ITS);
        
        table.addColumn(new Column("id"));
        table.addColumn(new Column("name"));
        table.addColumn(new Column("email"));
        table.addColumn(new Column("investments"));
    }
    
    // Event Handlers ---------------------------------------------------------
     
    /**
     * @see Page#onRender()
     */
    @Override
    public void onRender() {
        List list = getCustomerService().getCustomersSortedByName(10);
        table.setRowList(list); 
    }
}

In this Page code example a Table control is declared, we set the table's HTML class, and then define a number of table Column objects. In the column definitions we specify the name of the column in the constructor, which is used for the table column header and also to specify the row object property to render.

The last thing we need to do is populate the table with data. To do this we override the Page onRender() method and set the table row list before it is rendered.

In our Page template we simply reference the $table object which is rendered when its toString() method is called.

<html>
  <head>
    $headElements
  </head>
  <body>

    $table

    $jsElements

  </body>
</html>

Note from the example above that we specify the $headElements reference so that the table can include any HEAD elements, which includes Css imports and styles, in the header. Also note we specify the $jsElements reference which include any JavaScript imports and scripts at the bottom. At runtime Click automatically makes the variables $headElements and $jsElements available to the template.

At runtime the Table would be rendered in the page as:

Simple Table

Figure 1.2. Simple Table


1.5. Advanced Table Example

The Table control also provides support for:

  • automatic rendering

  • column formatting and custom rendering

  • automatic pagination

  • link control support

A more advanced Table example is provided below:

public class CustomerPage extends Page {

    private Table table = new Table("table");
    private PageLink editLink = new PageLink("Edit", EditCustomer.class);
    private ActionLink deleteLink = new ActionLink("Delete", this, "onDeleteClick");

    // Constructor ------------------------------------------------------------

    public CustomersPage() {
        // Add controls
        addControl(table);
        addControl(editLink);
        addControl(deleteLink);

        // Setup table
        table.setClass(Table.CLASS_ITS);
        table.setPageSize(10);
        table.setShowBanner(true);
        table.setSortable(true);

        table.addColumn(new Column("id"));

        table.addColumn(new Column("name"));
        
        Column column = new Column("email");
        column.setAutolink(true);
        column.setTitleProperty("name");
        table.addColumn(column);
        
        table.addColumn(new Column("investments"));
        
        editLink.setImageSrc("/images/table-edit.png");
        editLink.setTitle("Edit customer details");
        editLink.setParameter("referrer", "/introduction/advanced-table.htm");
        
        deleteLink.setImageSrc("/images/table-delete.png");
        deleteLink.setTitle("Delete customer record");
        deleteLink.setAttribute("onclick",
            "return window.confirm('Are you sure you want to delete this record?');");

        column = new Column("Action");
        column.setTextAlign("center");
        AbstractLink[] links = new AbstractLink[] { editLink, deleteLink };
        column.setDecorator(new LinkDecorator(table, links, "id"));
        column.setSortable(false);
        table.addColumn(column);

        // Table rowList will be populated through a DataProvider which loads
        // data on demand.
        table.setDataProvider(new DataProvider() {

            public List getData() {
                return getCustomerService().getCustomers();
            }
        });

        // Below we setup the table to preserve it's state (sorting and paging)
        // while editing customers

        table.getControlLink().setActionListener(new ActionListener() {

            public boolean onAction(Control source) {
                // Save Table sort and paging state between requests.
                // NOTE: we set the listener on the table's Link control which is invoked
                // when the Link is clicked, such as when paging or sorting.
                // This ensures the table state is only saved when the state changes, and
                // cuts down on unnecessary session replication in a cluster environment.
                table.saveState(getContext()); 1
                return true;
            }
        });

        // Restore the table sort and paging state from the session between requests
        table.restoreState(getContext()); 2
    }
    
    // Event Handlers ---------------------------------------------------------
         
    /**
     * Handle the delete row click event.
     */    
    public boolean onDeleteClick() {
        Integer id = deleteLink.getValueInteger();
        getCustomerService().deleteCustomer(id);
        return true;
    }
}
1

Table is a Stateful control and provides methods for saving and restoring it's state. Here we save the Table state in the HttpSession which ensures sort and paging state is preserved while editing customers.

2

Restore the Table state that was previously saved in the HttpSession.

In this Page code example a Table control is declared and a number of Column objects are added. We set the Table's DataProvider instance which provides data on demand to the table. Data retrieved from the dataProvider will be used to populate the Table rowList before it is rendered. An editLink PageLink control is used as decorator for the "Action" column. This control navigates to the EditCustomer page. A deleteLink ActionLink control is also used as a decorator for the "Action" column. This control will invoke the Page onDeleteClick() method when it is clicked.

In our Page template we simply reference the $table object which is rendered when its toString() method is called.

<html>
  <head>
    $headElements
  </head>
  <body>

    $table

    $jsElements

  </body>
</html>

At runtime the Table would be rendered in the page as:

Advanced Table

Figure 1.3. Advanced Table


In this example, clicking on the Edit link will navigate the user to the EditCustomer page where the selected customer can be edited. When the user click on the Delete link, the onDeleteClick() method will be called on the Page deleting the customer record.

1.6. Simple Form Example

The Form and Field controls are also some of the most commonly used controls in the Click Framework.

The SimpleForm page below provides a demonstration of using these controls.

In our example code we have the page's constructor adding a TextField field and a Submit button to the form. A page method is also set as a control listener on the form. Also note in this example the page's public form field is automatically added to its list of controls.

public class SimpleForm extends Page {

    private Form form = new Form("form");

    // Constructor ------------------------------------------------------------

    public SimpleForm() {
        addControl(form);

        form.add(new TextField("name", true));
        form.add(new Submit("OK"));

        form.setListener(this, "onSubmit");
    }

    // Event Handlers ---------------------------------------------------------

    /**
     * Handle the form submit event.
     */
    public boolean onSubmit() {
        if (form.isValid()) {
            msg = "Your name is " + form.getFieldValue("name");
        }
        return true;
    }
}

Next we have the SimpleForm template simple-form.htm. The Click application automatically associates the simple-form.htm template with the SimpleForm class.

<html>
  <head>
    $headElements
  </head>
  <body>

    $form

    #if ($msg)
      <div id="msgDiv"> $msg </div>
    #end

    $jsElements

  </body>
</html>

When the SimpleForm page is first requested the $form object will automatically render itself as:

Simple Form

Figure 1.4. Simple Form


Say the user does not enter their name and presses the OK button to submit the form. The ClickServlet creates a new SimpleForm page and processes the form control.

The form control processes its fields and determines that it is invalid. The form then invokes the listener method onSubmit(). As the form is not valid this method simply returns true and the form renders the field validation errors.

Form after an invalid request

Figure 1.5. Form after an invalid request


Note the form will automatically maintain the entered state during the post and validate cycle.

Now if the user enters their name and clicks the OK button, the form will be valid and the onSubmit() add a msg to the Pages model. This will be rendered as:

Form after a valid request

Figure 1.6. Form after a valid request


1.7. Advanced Form Example

The AdvancedForm page below provides a more advanced demonstration of using Form, Field and FieldSet controls.

First we have an AdvancedForm class which setups up a Form in its constructor. The form's investment Select list is populated in the page's onInit() method. At this point any page dependencies such as the CustomerService should be available.

public class AdvancedForm extends Page {

    private Form form = new Form("form");

    private Select investmentSelect = new Select("investment");

    // Constructor ------------------------------------------------------------

    public AdvancedForm() {
        addControl(form);

        FieldSet fieldSet = new FieldSet("Customer");
        form.add(fieldSet);

        TextField nameField = new TextField("name", true);
        nameField.setMinLength(5);
        nameField.setFocus(true);
        fieldSet.add(nameField);

        fieldSet.add(new EmailField("email", true));

        fieldSet.add(investmentSelect);

        fieldSet.add(new DateField("dateJoined", true));
        fieldSet.add(new Checkbox("active"));

        form.add(new Submit("ok", " OK ", this, "onOkClicked"));
        form.add(new Submit("cancel", this, "onCancelClicked"));
    }

    // Event Handlers ---------------------------------------------------------

    /**
     * @see Page#onInit()
     */
    @Override
    public void onInit() {
        super.onInit();

        investmentSelect.setDefaultOption(Option.EMPTY_OPTION);
        investmentSelect.setDataProvider(new DataProvider() {

            public List<Option> getData() {
                List<Option> options = new ArrayList<Option>();
                for (String category : customerService.getInvestmentCategories()) {
                    options.add(new Option(category));
                }
                return options;
            }
        });
    }

    /**
     * Handle the OK button click event.
     *
     * @return true
     */
    public boolean onOkClicked() {
        if (form.isValid()) {
            Customer customer = new Customer();
            form.copyTo(customer);

            getCustomerService().saveCustomer(customer);

            form.clearValues();

            String msg = "A new customer record has been created.";
            addModel("msg", msg);
        }
        return true;
    }

    /**
     * Handle the Cancel button click event.
     *
     * @return false
     */
    public boolean onCancelClicked() {
        setRedirect(HomePage.class);
        return false;
    }
}

Next we have the AdvancedForm template advanced-form.htm. The Click application automatically associates the advanced-form.htm template with the AdvancedForm class.

<html>
  <head>
    $headElements
  </head>
  <body>

    #if ($msg)
      <div id="msgDiv"> $msg </div>
    #end

    $form

    $headElements

  </body>
</html>

When the AdvancedForm page is first requested the $form object will automatically render itself as:

Advanced Form

Figure 1.7. Advanced Form


In this example when the OK button is clicked the onOkClicked() method is invoked. If the form is valid a new customer object is created and the forms field values are copied to the new object using the Form copyTo() method. The customer object is then saved, the form's field values are cleared and an info message is presented to the user.

If the user clicks on the Cancel button the request is redirected to the applications HomePage.

1.7.1. Form Layout

In the example above the Form control automatically renders the form and the fields HTML markup. This is a great feature for quickly building screens, and the form control provides a number of layout options. See the Click Examples for an interactive Form Properties demo.

For fine grained page design you can specifically layout form and fields in your page template. See the Template Layout section and Form Javadoc for more details.

An alternative to page template design is using a programmatic approach. See Programmatic Layout for more details.

Chapter 2. Pages

Pages are the heart of web applications. In Apache Click, Pages encapsulate the processing of HTML requests and the rendering of HTML responses. This chapter discusses Apache Click pages in detail.

In Click, a logical page is composed of a Java class and a Velocity template, with these components being defined in page elements of the click.xml file:

<page path="search.htm" classname="com.mycorp.page.Search"/>

The path attribute specifies the location of the page Velocity template, and the classname attribute specifies the page Java class name. If you use the Freemarker template engine instead of Velocity, the setup is the same.

The template path should have an .htm extension which is specified in web.xml to route *.htm requests to the ClickServlet.

Please note if you want Click to process templates with a different extension e.g. .xml, you need to implement the method isTemplate(String path) and specify the extra extensions. The simplest way is to subclass XmlConfigService and override the default implementation as described here. Also remember to map the new extensions in web.xml.

If you use JSP pages for rendering, the .jsp extension must be used. For example:

<page path="search.jsp" classname="com.mycorp.page.Search"/>

Please note, Click does not handle JSP requests directly, instead it forwards JSP requests to the servlet container. Do not map the ClickServlet to handle *.jsp requests in web.xml. Instead .jsp templates are accessed with a .htm extension. At runtime Click will convert the page path from .jsp to .htm and back.

2.1. Classes

All custom Click pages must subclass the Page base class. The Page class and its associated companion classes, Context and Control, are depicted in the figure below.

Page Class Diagram

Figure 2.1. Page Class Diagram


The Page class provides a model attribute which is used to hold all the objects that are rendered in the page Velocity template. The model may also contain Control objects, which provide user interface controls on the Page.

Pages also provides access to the Context object which references all the javax.servlet objects associated with the request. When programming in Click you use the Context object to access HttpServletRequest attributes, parameters and the HttpSession object.

2.2. Execution

The Page class provide a number of empty handler methods which subclasses can override to provide functionality:

The ClickServlet relies on instantiating Pages using a public no arguments constructor, so when you create Page subclasses you must ensure you don't add an incompatible constructor. The GET request execution sequence for Pages is summarized below in the Figure 2.

GET Request Sequence Diagram

Figure 2.2. GET Request Sequence Diagram


Stepping through this GET request sequence, a new Page instance is created and the attributes for the Page are set (format, headers, path). Next, request parameter values are bound to any matching public Page variables.

Then the onSecurityCheck() handler is executed. This method can be used to ensure the user is authorized to access the page, and if necessary abort any further processing.

The next method invoked is onInit(), this is where you place any post constructor initialization code. onInit() is the ideal place to create controls such as Forms, Fields and Tables. As illustrated by the diagram, after a Page's onInit() is called, each Control, available at that stage, will have their onInit() method called.

The next step is the processing of the Page's controls. The ClickServlet gets the list of Controls from the page and then iterates through the list calling onProcess(). If any of the Control's onProcess() methods return false, processing of subsequent controls and the Page's onGet() method is aborted.

If everything is executing normally the Page's onGet() method is now called.

The next step is rendering the page template to generate the displayed HTML. The ClickServlet gets the model (Map) from the Page then adds the following objects to the model:

  • any public Page variable using the variable name

  • context  -  the Servlet context path, e.g. /mycorp

  • format  -  the Format object for formatting the display of objects.

  • headElements  -  the HEAD elements, excluding JavaScript, to include in the page header. Please see PageImports for more details.

  • jsElements  -  the JavaScript imports and script blocks to include in the pages footer. Please see PageImports for more details.

  • messages  -  the MessagesMap adaptor for the Page getMessage() method

  • path  -  the path of the page template to render

  • request  -  the pages HttpServletRequest object

  • response  -  the pages HttpServletResponse object

  • session  -  the SessionMap adaptor for the users HttpSession

It then merges the template with the page model and writes out results to the HttpServletResponse. When the model is being merged with the template, any Controls in the model may be rendered using their toString() method.

The final step in this sequence is invoking each control's onDestroy() method and lastly invoke the Page's onDestroy() method. This method can be used to clean up resource associated with the Control or Page before it is garbage collected. The onDestroy() method is guaranteed to be called even if an exception occurs in the previous steps.

The execution sequence for POST requests is almost identical, except the onPost() method is invoked instead on onGet(). See the POST Request Sequence Diagram.

Another view on the execution flow of Pages is illustrated in the Activity diagram below.

Page Execution Activity Diagram

Figure 2.3. Page Execution Activity Diagram


2.3. Request Parameter Auto Binding

Click will automatically bind any request parameter values to public Page variable with the same name. You can also use the Bindable annotation to bind private and protected Page variables. When binding these values Click will also attempt to convert them to the correct type.

The best way to understand this is to walk through an example. Our application receives a GET request:

http://localhost:8080/mycorp/customer-details.htm?customerId=7203

This request is automatically handled by our CustomerDetails page:

package com.mycorp.page;

public class CustomerDetails extends Page {

    @Bindable protected Integer customerId;
}

After the CustomerDetails page has been created the "customerId" request parameter value "7023" will be converted into an Integer and assigned to the public page variable customerId.

Another feature of Click is that any public Page variables are automatically added to the page's model before it is rendered. This will make these values available in the page template for display. In our example the public customerId variable will be added to the Page model and will be available for rendering in the page template.

Our customer-details.htm page template contains:

<html>
<body>

  Customer ID: $customerId

</body>
</html>

After processing the request our page would be rendered as:

Customer ID: 7203

2.3.1. Customizing Auto Binding

Auto binding supports the conversion of request string parameters into the Java classes: Integer, Double, Boolean, Byte, Character, Short, Long, Float, BigInteger, BigDecimal, String and the various Date classes.

By default type conversion is performed by the RequestTypeConverter class which is used by the ClickServlet method getTypeConverter().

If you need to add support for additional types, you would write your own type converter class and specify it as a ClickServlet init parameter.

For example if you wanted to automatically load a Customer object from the database when a customer id request parameter is specified, you could write your own type converter:

public class CustomTypeConverter extends RequestTypeConverter {

    private CustomerService customerService = new CustomerService();

    /**
     * @see RequestTypeConverter#convertValue(Object, Class)
     */
    protected Object convertValue(Object value, Class toType) {
        if (toType == Customer.class) {
            return customerService.getCustomerForId(value);

        } else {
            return super.convertValue(value, toType);
        }
    }
}

This type converter would handle the following request:

http://localhost:8080/mycorp/customer-details.htm?customer=7203

This request will load the customer object from the database using "7203" as the customer id value. The ClickServlet would then assign this customer object to the matching page variable:

package com.mycorp.page;

public class CustomerDetails extends Page {

    @Bindable protected Customer customer;

}

To make your custom type converter available you will need to add an init parameter to ClickServlet in web.xml. For example:

<web-app>
  ...
  <servlet>
    <servlet-name>ClickServlet</servlet-name>
    <servlet-class>org.apache.click.ClickServlet</servlet-class>
    <init-param>
       <param-name>type-converter-class</param-name>
       <param-value>com.mycorp.util.CustomTypeConverter</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>ClickServlet</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
  ...
</web-app>

2.4. Security

Pages provide an onSecurityCheck event handler which application pages can override to implement a programmatic security model.

Please note you generally don't need to use this capability, and where possible you should use the declarative JEE security model. See the Best Practices Security topic for more details.

2.4.1. Application Authentication

Applications can use the onSecurityCheck() method to implement their own security model. The example class below provides a base Secure page class which other pages can extend to ensure the user is logged in. In this example the login page creates a session when a user successfully authenticates. This Secure page then checks to make sure the user has a session, otherwise the request is redirected to the login page.

public class Secure extends Page {

    /**
     * @see Page#onSecurityCheck()
     */
    public boolean onSecurityCheck() {

        if (getContext().hasSession()) {
            return true;

        } else {
            setRedirect(LoginPage.class);
            return false;
        }
    }
}

2.4.2. Container Authentication

Alternatively you can also use the security services provided by the JEE Servlet Container. For instance to ensure users have been authenticated by the Serlvet Container you could use a Secure page of:

public class Secure extends Page {

    /**
     * @see Page#onSecurityCheck()
     */
    public boolean onSecurityCheck() {

        if (getContext().getRequest().getRemoteUser() != null) {
            return true;

        } else {
            setRedirect(LoginPage.class);
            return false;
        }
    }
}

2.4.3. Container Access Control

The Servlet Container also provides facilities to enforce role based access control (authorization). The example below is a base page to ensure only users in the "admin" role can access the page, otherwise users are redirected to the login page. Application Admin pages would extend this secure page to provide their functionality.

public class AdminPage extends Page {

    /**
     * @see Page#onSecurityCheck()
     */
    public boolean onSecurityCheck() {

        if (getContext().getRequest().isUserInRole("admin")) {
            return true;

        } else {
            setRedirect(LoginPage.class);
            return false;
        }
    }
}

2.4.4. Logging Out

To logout using the application or container based security models you would simply invalidate the session.

public class Logout extends Page {

    /**
     * @see Page#onInit()
     */
    public void onInit() {
        getContext().getSession().invalidate();
    }
}

2.5. Page Navigation

Navigation between pages is achieved by using forwards, redirects and by setting the page template path.

2.5.1. Forward

To forward to another page using the servlet RequestDispatcher, set the Page's forward property. For example to forward to a page with a path index.htm:

/**
 * @see Page#onPost()
 */
public void onPost() {
    // Process form post
    ..

    setForward("index.htm");
}

This will invoke a new Page class instance mapped to the path index.htm.

Please note when a request is forwarded to another Page, the controls on the second page will not be processed. This prevents confusion and bugs, like a form on the second page trying to process a POST request from the first page.

2.5.1.1. Forward Parameter Passing

When you forward to another page the request parameters are maintained. This is a handy way of passing through state information with the request. For example you could add a customer object as a request parameter which is displayed in the template of the forwarded page.

public boolean onViewClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Set the customer object as a request parameter
    getContext().setRequestAttribute("customer", customer);
    setForward("view-customer.htm");

    return false;
}

The snippet above forwards to the page template view-customer.htm:

<html>
 <head>
   <title>Customer Details</title>
 </head>
 <body>
   <h1>Customer Details</h1>
   <pre>
     Full Name: $customer.fullName
     Email:     $customer.email
     Telephone: $customer.telephone
    </pre>
  </body>
</html>

Request attributes are automatically added to the Velocity Context object so are available in the page template.

2.5.1.2. Page Forwarding

Page forwarding is another way of passing information between pages. In this case you create the page to be forwarded to using the Context createPage(String) method and then set properties directly on the Page. Finally set this page as the page to forward the request to. For example:

public boolean onEditClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Create a new EditPage instance based on the specified path
    EditPage editPage = (EditPage) getContext().createPage("/edit-customer.htm");
    editPage.setCustomer(customer);
    setForward(editPage);

    return false;
}

When creating a page with the createPage() method, ensure you prefix the page path with the "/" character.

You can also specify the target page using its class as long as the Page has a unique path. Using this technique the above code becomes:

public boolean onEditClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    // Create a new EditPage instance based on its class
    EditPage editPage = (EditPage) getContext().createPage(EditPage.class);
    editPage.setCustomer(customer);
    setForward(editPage);

    return false;
}

This Page forwarding technique is best practice as it provides you with compile time safety and alleviates you from having to specify page paths in your code. Please always use the Context createPage() methods to allow Click to inject Page dependencies.

Although uncommon it is possible to map more than one path to the same class. In these cases invoking Context createPage(Class) will throw an exception, because Click will not be able to determine which path to use for the Page.

2.5.2. Template Path

An alternative method of forwarding to a new page is to simply set the current Page's path to the new page template to render. With this approach the page template being rendered must have everything it needs without having its associated Page object created. Our modified example would be:

public boolean onViewClick() {
    Long id = viewLink.getValueLong();
    Customer customer = CustomerDAO.findByPK(id);

    addModel("customer", customer);

    // Set the Page's path to a new value
    setPath("view-customer.htm");

    return false;
}

Note how the customer object is passed through to the template in the Page model. This approach of using the Page model is not available when you forward to another Page, as the first Page object is "destroyed" before the second Page object is created and any model values would be lost.

2.5.3. Redirect

Redirects are another very useful way to navigate between pages. See HttpServletResponse. sendRedirect (location) for details.

The great thing about redirects are that they provide a clean URL in the users browser which matches the page that they are viewing. This is important for when users want to bookmark a page. The downside of redirects are that they involve a communications round trip with the users browser which requests the new page. Not only does this take time, it also means that all the page and request information is lost.

An example of a redirect to a logout.htm page is provided below:

public boolean onLogoutClick() {
    setRedirect("/logout.htm");
    return false;
}

If the redirect location begins with a "/" character the redirect location will be prefixed with the web applications context path. For example if an application is deployed to the context "mycorp" calling setRedirect("/customer/details.htm") will redirect the request to: "/mycorp/customer/details.htm".

You can also obtain the redirect path via the target Page's class. For example:

public boolean onLogoutClick() {
    String path = getContext().getPagePath(Logout.class);
    setRedirect(path);
    return false;
}

Note when using this redirect method, the target Page class must have a unique path.

A short hand way of redirecting is to simply specify the target Page class in the redirect method. For example:

public boolean onLogoutClick() {
    setRedirect(Logout.class);
    return false;
}

2.5.3.1. Redirect Parameter Passing

You can pass information between redirected pages using URL request parameters. The ClickServlet will encode the URL for you using HttpServletResponse.encodeRedirectURL (url).

In the example below a user will click on an OK button to confirm a payment. The onOkClick() button handler processes the payment, gets the payment transaction id, and then redirects to the trans-complete.htm page with the transaction id encoded in the URL.

public class Payment extends Page {
    ..

    public boolean onOkClick() {
        if (form.isValid()) {
            // Process payment
            ..

            // Get transaction id
            Long transId = OrderDAO.purchase(order);

            setRedirect("trans-complete.htm?transId=" + transId);

            return false;
        }
        return true;
    }
}

The Page class for the trans-complete.htm page can then get the transaction id through the request parameter "transId":

public class TransComplete extends Page {
    /**
     * @see Page#onInit()
     */
    public void onInit() {
        String transId = getContext().getRequest().getParameter("transId");

        if (transId != null) {

            // Get order details
            Order order = OrderDAO.findOrderByPK(new Long(transId));
            if (order != null) {
                addModel("order", order);
            }
        }
    }
}

2.5.3.2. Post Redirect

The parameter passing example above is also an example of a Post Redirect. The Post Redirect technique is a very useful method of preventing users from submitting a form twice by hitting the refresh button.

2.6. Page Templating

Click supports page templating (a.k.a. Tiles in Struts) enabling you to create a standardized look and feel for your web application and greatly reducing the amount of HTML you need to maintain.

To implement templating define a border template base Page which content Pages should extend. The template base Page class overrides the Page getTemplate() method, returning the path of the border template to render. For example:

package com.mycorp.page;

public class BorderedPage extends Page {

    /**
     * @see Page#getTemplate()
     */
    public String getTemplate() {
        return "/border.htm";
    }
}

The BorderedPage template border.htm:

<html>
  <head>
    <title>$title</title>
    <link rel="stylesheet" type="text/css" href="style.css" title="Style"/>
  </head>
  <body>

    <h2 class="title">$title</h2>

    #parse($path)

  </body>
</html>
    

Other pages insert their content into this template using the Velocity #parse directive, passing it their contents pages path. The $path value is automatically added to the VelocityContext by the ClickServlet.

An example bordered Home page is provided below:

<page path="home.htm" classname="com.mycorp.page.Home"/>

package com.mycorp.page;

public class Home extends BorderedPage {

    public String title = "Home";

}

The Home page's content home.htm:

<b>Welcome</b> to Home page your starting point for the application.

When a request is made for the Home page (home.htm) Velocity will merge the border.htm page and home.htm page together returning:

<html>
  <head>
    <title>Home</title>
    <link rel="stylesheet" type="text/css" href="style.css" title="Style"/>
  </head>
  <body>

    <h2 class="title">Home</h2>

    <b>Welcome</b> to Home page your application starting point.

  </body>
</html>

Which may be rendered as:

Home Page

Figure 2.4. Home Page


Note how the Home page class defines a title model value which is referenced in the border.htm template as $title. Each bordered page can define their own title which is rendered in this template.

Templating with JSP pages is also supported using the same pattern. Please see the Click Examples application for a demonstration.

2.7. Page Actions

Page Action is a feature to directly invoke a Page method from the browser. The Page Action method returns an ActionResult object that is rendered directly to the browser. In other words the Page template will not be rendered.

To invoke a Page Action, specify the parameter "pageAction" and the name of the page method, for example: "onRenderImage".

Let's take a quick look at how a Page Action can be leveraged to retrieve an image. In this example we'll create an HTML <img> element which src attribute specifies the Page Action that will return the image data.

First we create our template:

<img src="$context/mycorp/image.htm?pageAction=onRenderImage"/>

Next we create our ImagePage with a Page Action method called onRenderImage that returns an ActionResult instance:

public class ImagePage extends Page {

    public ActionResult onRenderImage() {
        byte[] imageData = getImageAsBytes();
        String contentType = ClickUtils.getMimeType("png");
        return new ActionResult(imageData, contentType);
    }
} 

A Page Action is a normal Page method with the following signature: a public no-arg method returning an ActionResult instance:

// The Page Action method is public, doesn't accept any arguments and returns an ActionResult
public ActionResult onRenderImage() {
    byte[] imageData = getImageAsBytes();
    String contentType = ClickUtils.getMimeType("png");
    return new ActionResult(imageData, contentType);
} 

The ActionResult contains the data that is rendered to the client browser. In the example above, the result will the Image byte array with a Content-Type of: "images/png".

2.7.1. Page Action Execution

Page Actions are page methods that handle the processing of a user request and render a result to the browser. The execution sequence for a Page Action being processed and rendered is illustrated in the figure below.

Page Action Request Sequence Diagram

Figure 2.5. Page Action Request Sequence Diagram


Stepping through this Page Action request sequence, a new Page instance is created and the attributes for the Page are set (format, headers). Next, request parameter values are bound to matching Page fields.

Then the onSecurityCheck() handler is executed. This method can be used to ensure the user is authorized to access the Page Action, and if necessary abort any further processing. If onSecurityCheck() return false, no response is sent back to the client. Note, if you want to send a specific response to the client you have to do that from the onSecurityCheck() event, since other Page events are not executed. Please see this example for some strategies on implementing onSecurityCheck to handle ajax requests.

Next the target page method is invoked which returns an ActionResult that is rendered to the client.

If the page method returns null no response is rendered to the browser.

2.7.2. ActionResult

An ActionResult represents the content returned by a Page Action which is then rendered to the client browser. ActionResults normally contains HTML or image data that is rendered to the browser. When a Page Action is invoked the Page template rendering is bypassed and only the ActionResult content is rendered to the browser. This allows a Page Action to return a "partial" response, as opposed to a "full" response, because the Page template (which can be viewed as a "full" response) is bypassed when invoking a Page Action.

2.7.3. Page Action Example

Let's step through a Page Action example. First we create an ImagePage class with the method "getImageData" which is the Page Action we want to invoke:

public ImagePage extends Page {

    public ActionResult getImageData() {
        byte[] imageData = loadImageData();
        String contentType = ClickUtils.getContentType("png");
        return new ActionResult(imageData, contentType);
    }
} 

Next we have the page template image.htm:

<html>
  <body>

    <img src="/mycorp/image.htm?pageAction=getImageData"/>

  </body>
</html> 

The browser renders the <img> element and requests the image src url. Click invokes the page method getImageData and renders the result to the browser.

Looking at the output log we see the following trace:

[Click] [info ] handleRequest:  /image.htm - 84 ms
[Click] [debug] GET http://localhost:8080/mycorp/image.htm
[Click] [trace]    is Ajax request: false
[Click] [trace]    request param: pageAction=getImageData
[Click] [trace]    invoked: ImagePage.<<init>>
[Click] [trace]    invoked: ImagePage.onSecurityCheck() : true
[Click] [trace]    invoked: ImagePage.getImageData() : ActionResult
[Click] [info ]    renderActionResult (image/png) - 0 ms
[Click] [trace]    invoked: ImagePage.onDestroy()
[Click] [info ] handleRequest:  /image.htm - 98 ms

2.7.4. Accessing Request Parameters

Request parameters can be accessed through the Context as shown below:

public ImagePage extends Page {

    public ActionResult getImageData() {
        // Retrieve a request parameter through the Context
        Context context = getContext();
        String imageName = context.getRequestParameter("imageName");

        byte[] imageData = loadImageData(imageName);
        String contentType = ClickUtils.getContentType("png");
        return new ActionResult(imageData, contentType);
    }
} 

2.7.5. Set response headers and status code

When handling a Page Action you might need to set the HTTP response headers or status code. You do this through the Servlet API's, HttpServetlResponse which can be accessed through the Context.

For example:

package examples.page;

import java.util.Date;
import org.apache.click.Page;

public ImagePage extends Page {

    public ActionResult getImageData() {
        // Headers and Status code are set on the HttpServletResponse
        HttpServletResponse response = getContext().getResponse();

        // The headers can be set as follows:
        response.setHeader("Content-Disposition", "attachment; filename=\"report.xls\"");

        ...

        // The response status can be set as follows:
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

        ...
    }
} 

2.8. Direct Rendering

Pages support a direct rendering mode where you can render directly to the servlet response and bypass the page template rendering. This is useful for scenarios where you want to render non HTML content to the response, such as a PDF or Excel document. To do this:

  • get the servlet response object

  • set the content type on the response

  • get the response output stream

  • write to the output stream

  • close the output stream

  • set the page path to null to inform the ClickServlet that rendering has been completed

A direct rendering example is provided below.

/**
 * Render the Java source file as "text/plain".
 *
 * @see Page#onGet()
 */
public void onGet() {
    String filename = ..

    HttpServletResponse response = getContext().getResponse();

    response.setContentType("text/plain");
    response.setHeader("Pragma", "no-cache");

    ServletContext context = getContext().getServletContext();

    InputStream inputStream = null;
    try {
        inputStream = context.getResourceAsStream(filename);

        PrintWriter writer = response.getWriter();

        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        String line = reader.readLine();

        while (line != null) {
            writer.println(line);
            line = reader.readLine();
        }

        setPath(null);

    } catch (IOException ioe) {
        ioe.printStackTrace();

    } finally {
        ClickUtils.close(inputStream);
    }
}

2.9. Stateful Pages

PLEASE NOTE: stateful pages have been deprecated in Click 2.3.0 and will be removed in a future release. Do not use stateful pages in your applications. Instead use stateful controls or HttpSession to store state between requests.

Click supports stateful pages where the state of the page is saved between the users requests. Stateful pages are useful in a number of scenarios including:

  • Search page and edit page interactions. In this scenario you navigage from a stateful search page which may have filter criteria applied to an object edit page. Once object update has been completed on the edit page, the user is redirected to the search page and the stateful filter criteria still applies.

  • Complex pages with multiple forms and or tables which need to maintain their state between interactions.

To make a page stateful you simply need to set the page stateful property to true, have the page implement the Serializable interface and set the serialVersionUID indicator. For example:

package com.mycorp.page;

import java.io.Serializable;

import org.apache.click.Page;

public class SearchPage extends Page implements Serializable {

    private static final long serialVersionUID = 1L;

    public SearchPage() {
        setStateful(true);
        ..
    }
}

Stateful page instances are stored in the user's HttpSession using the pages class name as the key. In the example above the page would be stored in the users session using the class name: com.mycorp.page.SearchPage

2.9.1. Page Creation

With stateful pages they are only created once, after which they are retrieved from the session. However page event handlers are invoked for each request, including the onInit() method.

When you are creating stateful pages you typically place all your control creation code in the Pages constructor so it is invoked only once. It is important not to place control creation code in the onInit() method which will be invoked with each request.

If you have dynamic control creation code you would typically place this in the onInit() method, but you will need to take care that controls and or models are not already present in the page.

2.9.2. Page Execution

The default Click page execution model is thread safe as a new Page instance is created for each request and thread. With stateful pages a user will have a single page instance which is reused in multiple requests and threads. To ensure page execution is thread safe, users page instances are synchronized so only one request thread can execute a page instance at any one time.

2.9.3. Page Destruction

After normal page instances have been executed, they are de-referenced and garbage collected by the JVM. However with stateful pages they are stored in the users HttpSession so care needs to be take not to store too many objects in stateful page instances which may cause memory and performance issues.

When pages have completed their execution, all the Page's controls onDestroy() methods are invoked, and then the Page's onDestroy() method is invoked. This is your opportunity to de-reference any large sets or graphs. For example the Table control by default de-references its rowList in its onDestory() method.

2.10. Error Handling

If an Exception occurs processing a Page object or rendering a template the error is delegated to the registered handler. The default Click error handler is the ErrorPage, which is automatically configured as:

<page path="click/error.htm" classname="org.apache.click.util.ErrorPage"/>

To register an alternative error handler you must subclass ErrorPage and define your page using the path "click/error.htm". For example:

<page path="click/error.htm" classname="com.mycorp.page.ErrorPage"/>

When the ClickServlet starts up it checks to see whether the error.htm template exists in the click web sub directory. If it cannot find the page the ClickServlet will automatically deploy one. You can tailor the click/error.htm template to suite you own tastes, and the ClickServlet will not overwrite it.

The default error template will display extensive debug information when the application is in development or debug mode. Example error page displays include:

When the application is in production mode only a simple error message is displayed. See Configuration for details on how to set the application mode.

Please also see the Examples web app Exception Demo for demonstrations of Clicks error handling.

2.11. Page Not Found

If the ClickServlet cannot find a requested page in the click.xml config file it will use the registered not-found.htm page.

The Click not found page is automatically configured as:

<page path="click/not-found.htm" classname="org.apache.click.Page"/>

You can override the default configuration and specify your own class, but you cannot change the path.

When the ClickServlet starts up it checks to see whether the not-found.htm template exists in the click web sub directory. If it cannot find the page the ClickServlet will automatically deploy one.

You can tailor the click/not-found.htm template to suite you own needs. This page template has access to the usual Click objects.

2.12. Page Message Properties

The Page class provides a messages property which is a MessagesMap of localized messages for the page. These messages are made available in the VelocityContext when the page is rendered under the key messages. So for example if you had a page title message you would access it in your page template as:

<h1> $messages.title </h1>

This messages map is loaded from the page class property bundle. For example if you had a page class com.mycorp.page.CustomerList you could have an associated property file containing the pages localized messages:

/com/mycorp/page/CustomerList.properties

You can also defined a application global page messages properties file:

/click-page.properties

Messages defined in this file will be available to all pages throughout your application. Note messages defined in your page class properties file will override any messages defined in the application global page properties file.

Page messages can also be used to override Control messages, see the Controls Message Properties topic for more details.

2.13. Page HEAD Elements

The Page class provides the method getHeadElements() for contributing HEAD elements such as JsImport, JsScript, CssImport and CssStyle.

Here is an example of adding HEAD elements to the MyPage class:

public class MyPage extends Page {

    public MyPage() {
        // Add the JavaScript import "/mypage.js" to the page
        getHeadElements().add(new JsImport("/mypage.js"));

        // Add some inline JavaScript content to the page
        getHeadElements().add(new JsScript("alert('Welcome to MyPage');"));

        // Add the Css import "/mypage.css" to the page
        getHeadElements().add(new CssImport("/mypage.css"));

        // Add some inline Css content to the page
        getHeadElements().add(new CssStyle("body { font-family: Verdana; }"));
    }

    ...

} 

In the example above we added the HEAD elements from the Page constructor, however you can add HEAD elements from anywhere in the Page including the event handlers onInit, onGet, onPost, onRender etc. Please see getHeadElements() for more details.

Below is the /my-page.htm template:

<html>
  <head>
    $headElements
  </head>

  <body>

    ...

    $jsElements

  </body>
</html> 

The two variables, $headElements and $jsElements, are automatically made available to the Page template. These variables references the JavaScript and Css HEAD elements specified in MyPage.

The following HTML will be rendered (assuming the application context is "/myapp"):

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/myapp/mypage.css"></link>
    <style rel="stylesheet" type="text/css">
      body { font-family: Verdana; }
    </style>
  </head>

  <body>

    ...

    <script type="text/javascript" src="/myapp/mypage.js"/>
    <script type="text/javascript">
      alert('Welcome to MyPage');
    </script>

  </body>
</html> 

A live demo showing how to add HEAD elements to a Page can be seen here.

Chapter 3. Controls

Apache Click provides a rich set of Controls which support client side rendering and server side processing. Please see the Javadoc, which provides extensive information and examples of the core Controls.

This chapter covers Control in detail including the Control life cycle, Control listeners and localization.

3.1. Control Interface

Controls provide the server side components that process user input, and render their display to the user. Controls are equivalent to Visual Basic Controls or Delphi Components.

Controls handle the processing of user input in the onProcess method and render their HTML display using the toString() method. The execution sequence for a Control being processed and rendered is illustrated in the figure below.

Post Sequence Diagram

Figure 3.1. Post Sequence Diagram


In Click all control classes must implement the Control interface. The Control interface is depicted in the figure below.

Control Interface Diagram

Figure 3.2. Control Interface Diagram


Methods on the Control interface include:

3.2. Control Listener

Click Controls provide an action listener mechanism similar to a java.awt.ActionListener.

Click supports two styles of action listeners. The first is using the ActionListener interface and setActionListener(ActionListener) method which provides compile time safety.

The second is to register the action listener via the setListener(Object, String) method where you specify the call back method via its name. This second style uses less lines of code, but has no compile time safety.

Examples of these two action listener styles are provided below:

public class ActionDemo extends BorderPage {

    // Uses listener style 1
    public ActionLink link = new ActionLink();

    // Uses listener style 2
    public ActionButton button = new ActionButton();

    public ActionDemo() {

        // Verbose but provides compile time safety
        link.setActionListener(new ActionListener() {
            public boolean onAction(Control source) {
                return onLinkClick(source);
            }
        });

        // Succinct but typos will cause runtime errors
        button.setListener(this, "onButtonClick");
    }

    // Event Handlers ---------------------------------------------------------

    public boolean onLinkClick(Control source) {
        ..
        return true;
    }

    public boolean onButtonClick() {
        ..
        return true;
    }
}

All call back listener methods must return a boolean value. If they return true the further processing of other controls and page methods should continue. Otherwise if they return false, then any further processing should be aborted. By returning false you can effectively exit at this point and redirect or forward to another page. This execution logic is illustrated in the Page Execution Activity Diagram.

Being able to stop further processing and do something else can be very handy. For example your Pages onRender() method may perform an expensive database operation. By returning false in an event handler you can skip this step and render the template or forward to the next page.

3.3. Control Classes

Core control classes are defined in the package org.apache.click.control. This package includes controls for the essential HTML elements.

Extended control classes are provided in the Click Extras package org.apache.click.extras.control. Click Extras classes can contain dependencies to 3rd party frameworks.

A subset of these control classes are depicted in the figure below.

Package Class Diagram

Figure 3.3. Package Class Diagram


The key control classes include:

  • ActionLink - provides an anchor link that can invoke action listeners.

  • Field - provides the abstract form field control.

  • Form - provides a form control for processing, validation and rendering.

  • Submit - provides an input type submit control that can invoke action listeners.

  • TextField - provides an input type text control that can invoke action listeners.

The control classes are designed to support subclassing for customized behaviour. All control fields have protected visibility and have public accessor methods.

You can also aggregate controls to build more complex controls. For example the CreditCardField uses a Select control to render the different credit card types.

3.4. Message Properties

Control strings for field validation messages and HTML formatting strings are externalized in the properties file. By using these properties files you can localize a Click application for your particular language and dialect.

3.4.1. Message Resolution

Messages are looked up in a particular order enabling taylor specific messages, for your controls, individual pages or across your entire application. The order in which localized messages are resolved is:

Page scope messages

Message lookups are first resolved to the Page classes message bundle if it exists. For example a Login page may define the message properties:

/com/mycorp/page/Login.properties

If you want to tailor messages for a particular page this is where to place them.

Global page scope messages

Next message lookups are resolved to the global pages message bundle if it exists.

/click-page.properties

If you want messages to be used across your entire application this is where to place them.

Control scope messages

Next message lookups are resolved to the Control classes message bundle if it exists. For example a CustomTextField control may define the message properties:

/com/mycorp/control/CustomTextField.properties

A custom control's messages can be placed here (or the global control scope covered next) and overridden by one of the above options.

Global control scope messages

Finally message lookups are resolved to the global application control message bundle if the message has not already been found. The global control properties file is:

/click-control.properties

Control messages can be placed here and overridden by one of the above options.

3.4.2. Control Properties

To customize the click-control.properties simply add this file to your classpath and tailor the specific values.

Note when customizing the message properties you must include all the properties, not just the ones you want to override.

# Click Control messages
field-maxlength-error={0} must be no longer than {1} characters
field-minlength-error={0} must be at least {1} characters
field-required-error=You must enter a value for {0}

file-required-error=You must enter a filename for {0}

label-required-prefix=
label-required-suffix=<span class="required">*</span>
label-not-required-prefix=
label-not-required-suffix=&nbsp;

not-checked-error=You must select {0}

number-maxvalue-error={0} must not be larger than {1}
number-minvalue-error={0} must not be smaller than {1}

select-error=You must select a value for {0}

table-first-label=First
table-first-title=Go to first page
table-previous-label=Prev
table-previous-title=Go to previous page
table-next-label=Next
table-next-title=Go to next page
table-last-label=Last
table-last-title=Go to last page
table-goto-title=Go to page
table-page-banner=<span class="pagebanner">{0} items found, displaying {1} to {2}.</span>
table-page-banner-nolinks=
  <span class="pagebanner-nolinks">{0} items found, displaying {1} to {2}.</span>
table-page-links=<span class="pagelinks">[{0}/{1}] {2} [{3}/{4}]</span>
table-page-links-nobanner=<span class="pagelinks-nobanner">[{0}/{1}] {2} [{3}/{4}]</span>
table-no-rows-found=No records found.

table-inline-first-image=/click/paging-first.gif
table-inline-first-disabled-image=/click/paging-first-disabled.gif
table-inline-previous-image=/click/paging-prev.gif
table-inline-previous-disabled-image=/click/paging-prev-disabled.gif
table-inline-next-image=/click/paging-next.gif
table-inline-next-disabled-image=/click/paging-next-disabled.gif
table-inline-last-image=/click/paging-last.gif
table-inline-last-disabled-image=/click/paging-last-disabled.gif
table-inline-page-links=Page   {0} {1} {2} {3} {4}

# Message displayed when a error occurs when the application is in "production" mode
production-error-message=
  <div id='errorReport' class='errorReport'>The application encountered an unexpected error.
  </div>
      

3.4.3. Accessing Messages

Controls support a hierarchy of resource bundles for displaying validation error messages and display messages. These localized messages can be accessed through the AbstractControl methods:

These methods use the Locale of the request to lookup the string resource bundle, and use MessageFormat for any string formatting.

3.5. Control HEAD Elements

The Control interface provides the method getHeadElements() which allows the Control to add Page HEAD elements such as JsImport, JsScript, CssImport and CssStyle.

Here is an example of adding HEAD elements to a custom Control:

public class MyControl extends AbstractControl {

    public MyControl() {

        /**
         * Override the default getHeadElements implementation to return
         * MyControl's list of HEAD elements.
         *
         * Note that the variable headElements is defined in AbstractControl.
         *
         * @return list the list of HEAD elements
         */
        public List getHeadElements() {

            // Use lazy loading to only add the HEAD elements once and when needed.
            if (headElements == null) {

                // Get the head elements from the super implementation
                headElements = super.getHeadElements();

                // Add the JavaScript import "/mycontrol.js" to the control
                headElements.add(new JsImport("/mycontrol.js"));

                // Add the Css import "/mycontrol.css" to the control
                headElements.add(new CssImport("/mycontrol.css"));
            }
            return headElements;
        }
    }

    ...

} 

In the example above we added the HEAD elements by overriding the Control's getHeadElements method, however you can add HEAD elements from anywhere in the Control including the event handlers onInit, onGet, onPost, onRender etc. Please see getHeadElements() for more details.

MyControl will add the following HEAD elements to the Page HEAD section, together with HEAD elements added by the Page and other controls (assume the application context is "/myapp"):

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/myapp/mycontrol.css"></link>
  </head>

  <body>

    ...

    <script type="text/javascript" src="/myapp/mycontrol.js"/>

  </body>
</html> 

A live demo showing how to add HEAD elements from a custom Control can be seen here.

3.6. Container

Container is a Control that can contain other Controls, thus forming a hierarchy of components. Container enables components to add, remove and retrieve other controls. Listed below are example Containers:

  • Form - an HTML form which provides default layout of fields and error feedback.

  • Panel - similar to Page, this Container provides its own template and model.

  • FieldSet - draws a legend (border) around its child Controls.

These Containers are depicted in the figure below.

Container Class Diagram

Figure 3.4. Container Class Diagram


The following classes provides convenient extension points for creating custom Containers:

Lets cover each of them here.

3.6.1. AbstractContainer

Enables easy creation of custom Containers, for example an html div or span element:

public class Div extends AbstractContainer {

    public Div(String name) {
        super(name);
    }

    public String getTag() {
        // Return the control's HTML tag.
        return "div";
    }
}

Lets try out the newly created Container above: (note the MockContext used in this test is described in the Mock Test Support documentation)

public class Test {
    public static void main (String args[]) {
        // Create mock context in which to test the container.
        MockContext.initContext();

        // Create a div instance called "mydiv"
        String containerName = "mydiv";
        Div mydiv = new Div(containerName);

        // Add a control to the container
        mydiv.add(new TextField("myfield"));

        System.out.println(mydiv);
    }
}

Executing the above example results in the following output:

<div name="mydiv" id="mydiv">
    <input type="text" name="myfield" id="myfield" value="" size="20" />
</div>

3.6.2. AbstractContainerField

AbstractContainerField extends Field and implements the Container interface. This provides a convenient base class in case you run into a situation where you need both a Field and Container.

Below is an example of how AbstractContainerField might be used:

public class FieldAndContainer extends AbstractContainerField {

    public FieldAndContainer(String name) {
        super(name);
    }

    // Return the html tag to render
    public String getTag() {
        return "div";
    }
}

To test the new class we use the following snippet:

public class Test {
    public static void main (String args[]) {
        // Create mock context in which to test the container.
        MockContext.initContext();

        // Create a FieldContainer instance called "field_container"
        String containerName = "field_container";
        FieldAndContainer fieldAndContainer = new FieldAndContainer(containerName);

        // Add a couple of fields to the container
        fieldAndContainer.add(new TextField("myfield"));
        fieldAndContainer.add(new TextArea("myarea"));

        System.out.println(fieldAndContainer);
    }
}

Executing the snippet produces the output:

<div name="field_container" id="field_container">
    <input type="text" name="myfield" id="myfield" value="" size="20"/>
    <textarea name="myarea" id="myarea" rows="3" cols="20"></textarea>
</div>

3.7. Layouts

Controls such as Form takes care of layout and error reporting automatically, and for many use cases the auto-layout approach is good enough. It is certainly very productive.

However for custom or complex layouts, auto-layout is not always the best choice. There are two approaches for creating custom layouts.

  • Template approach - use a template engine such as Velocity, Freemarker or JSP to declare the layout as HTML markup.

  • Programmatic approach - build custom layout components using Java. This option is very similar to building components using Swing.

3.7.1. Template layout

The Template approach separates the Page and layout logic. The Page is used to implement the presentation logic such as creating controls, registering listeners and copying data to domain objects, while the template is used to layout the Page controls.

Lets walk through an example using the template approach. Below we create an EmployeePage which contains a Form and a bunch of fields and submit button.

// EmployeePage.java
public EmployeePage extends Page {

    private Form form;

    public void onInit() {
        // Create form
        Form form = new Form("form");

        // Add a couple of fields to the form
        form.add(new TextField("firstname"));
        form.add(new TextField("lastname"));
        form.add(new IntegerField("age"));
        form.add(new DoubleField("salary"));

        // Add a submit button to form
        form.add(new Submit("add", "Add Employee"));

        // Add form the page
        addControl(form);
    }
}

Lets imagine we want to create a layout using the HTML tags, <div> and <ol>.

We would then provide the markup for the employee.htm template as shown below, using a template engine such as Velocity:

<!-- employee.htm -->
${form.startTag()}
    <div style="margin: 1em;">
        <ol>
            <li>
                <label for="firstname">Firstname:</label>
                ${form.fields.firstname}
            </li>
            <li>
                <label for="lastname">Lastname:</label>
                ${form.fields.lastname}
            </li>
            <li>
                <label for="age">Age:</label>
                ${form.fields.age}
            </li>
            <li>
                <label for="salary">Salary:</label>
                ${form.fields.salary}
            </li>
        </ol>
    </div>
    ${form.fields.submit}
${form.endTag()}

Using CSS the markup above can further be styled and transformed into a nice looking form.

There are pros and cons to using the template approach.

One of the advantages is that the layout is explicit and one can easily tweak it if needed. For example instead of using divs and ordered lists, one can change the template to leverage a table layout.

A disadvantage is added redundancy. In the example above we created the fields in Java, and laid them out using markup in the template. If the requirements should change to add a new field for example, one will have to add the field in the Page as well as the template.

However it is possible to "generify" the layout using template engines such as Velocity, Freemarker and JSP. Macro.vm is an example of a generic and reuable form layout using Velocity.

Panels provide another good way to build generic and reusable template based layouts.

Once your generic templates are in place, they can easily be reused in your project or even shared across multiple projects.

3.7.2. Programmatic layout

To combat the redundancy introduced by the Template approach, you can take a programmatic approach and use normal Java and some Click classes to build custom layouts.

Click extras provides two useful classes in this situation namely, HtmlForm and HtmlFieldSet.

Unlike Form and FieldSet which renders its controls using a Table layout, HtmlForm and HtmlFieldSet renders its controls in the order they were added and does not add any extra markup. HtmlForm will be used in the examples below.

To make it easy to compare the two layout approaches we will recreate the example from the template layout section, but using the programmatic approach.

When creating custom layouts, the HTML construct List <ul> is pretty useful. Since Click does not provide this component, we will create it as shown below. First we create the HTML list element <ol>, to which list item elements <li> can be added:

// HtmlList.java
public class HtmlList extends AbstractContainer {

    public String getTag() {
        return "ol";
    }

    // Can only add ListItems: <li> tags
    public Control add(Control control) {
        if (!(control instanceof ListItem)) {
            throw new IllegalArgumentException("Only list items can be added.");
        }
        return super.add(control);
    }
}

Next we create the HTML list item element <li>:

// ListItem.java
public class ListItem extends AbstractContainer {

    public String getTag() {
        return "li";
    }
}

Another component that will be used in the example is a FieldLabel which renders an HTML label element for a target Field.

// FieldLabel.java
public class FieldLabel extends AbstractControl {

    private Field target;

    private String label;

    public FieldLabel(Field target, String label) {
        this.target = target;
        this.label = label;
    }

    public String getTag() {
        return "label";
    }

    // Override render to produce an html label for the specified field.
    public void render(HtmlStringBuffer buffer) {
        // Open tag: <label
        buffer.elementStart(getTag());

        // Set attribute to target field's id
        setAttribute("for", target.getId());

        // Render the labels attributes
        appendAttributes(buffer);

        // Close tag: <label for="firstname">
        buffer.closeTag();

        // Add label text: <label for="firstname">Firstname:
        buffer.append(label);

        // Close tag: <label for="firstname">Firstname:</label>
        buffer.elementEnd(getTag());
    }

}

Now the form can be assembled. Continuing with the employee example from the template approach, we again create an EmployeePage, but this time an HtmlForm and HtmlList is used to create the custom layout:

// EmployeePage.java
public class EmployeePage extends Page {
    // A form instance variable
    private HtmlForm form;

    // Build the form when the page is initialized
    public void onInit() {
        // Create an HtmlForm which is ideal for composing manual layouts
        form = new HtmlForm("form");

        // Create a list and add it to the form.
        HtmlList list = new HtmlList();
        form.add(list);

        // Add firstname field and pass in its name, label and the list to add the field to
        addTextField("firstname", "Firstname:", list);
        addTextField("lastname", "Lastname:", list);
        addTextField("age", "Age:", list);
        addTextField("salary", "Salary:", list);

        // Add a submit button to form
        form.add(new Submit("add", "Add Employee"));

        // Add the form to the page
        addControl(form);
    }

    // Provide a helper method to add fields to the form
    private void addTextField(String nameStr, String labelStr, List list) {
        // Create a new ListItem <li> and add it to the List
        ListItem item = new ListItem();
        list.add(item);

        // Create a textfield with the specified name
        Field field = new TextField(nameStr);

        // Create a field label, which associates the label with the field id.
        // label.toString would output: <label for="firstname">Firstname:</name>
        FieldLabel label = new FieldLabel(field, labelStr);

        // Next add the label and field to the list item.
        // item.toString would then produce:
        // <li>
        //   <label for="firstname">Firstname:</name>
        //   <input type="text" name="firstname" id="form_firstname" value="" size="20"/>
        // </li>
        //
        item.add(label);
        item.add(field);
    }
}

And lastly the employee.htm template would only need to specify the name of the top level component, in this case form.

<!--employee.htm-->
${form}

which produces the following markup:

<form method="post" id="form" action="/myapp/employee.htm">
<input type="hidden" name="form_name" id="form_form_name" value="form"/>
    <ol>
        <li>
            <label for="firstname">Firstname:</label>
            <input type="text" name="firstname" id="form_firstname" value="" size="20"/>
        </li>
        <li>
            <label for="lastname">Lastname:</label>
            <input type="text" name="lastname" id="form_lastname" value="" size="20"/>
        </li>
        <li>
            <label for="age">Age:</label>
            <input type="text" name="age" id="form_age" value="" size="20"/>
        </li>
        <li>
            <label for="salary">Salary:</label>
            <input type="text" name="salary" id="form_salary" value="" size="20"/>
        </li>
    </ol>
    <input type="submit" name="add" id="form_add" value="Add Employee"/>
</form>

Again using a CSS stylesheet, the markup above can be styled and transformed into a fancy looking form.

There is a live demo showing the programmatic approach.

The advantage of the programmatic approach is that there is no redundancy. Each Field is created and added using normal Java. There is no need to specify where the Field must reside in the markup.

If new requirements arrive and more fields added, only the Page needs to change. There is no need to change the template as the layout is taken care of by CSS and the markup produced by the components.

Disadvantages are that more upfront work is needed to write the components and it is more difficult to visualize what output would be rendered by the components.

However once your custom layout components are in place, they can easily be reused in your project or even shared across multiple projects.

Whether you use the template or programmatic layout approach, is up to you. Both work well and have advantages and disadvantages over the other.

3.8. Behavior

Behaviors provide the ability to change how Controls behave at runtime.

Behavior is an interface that provides interceptor methods for certain Control life cycle events. These interceptor methods can be implemented to decorate and enhance the control and its children. This allows for making changes to Controls such as adding/removing JavaScript and CSS Elements, adding/removing attributes, etc.

Behaviors are added to Controls through the AbstractControl.addBehavior(Behavior) method, and the same Behavior can be shared by multiple Controls.

The Control, AbstractControl and Behavior, classes are shown in the figure below.

Behavior Class Diagram

Figure 3.5. Behavior Class Diagram


Control exposes the following Behavior related methods:

AbstractControl contains a Set that holds the Behaviors added to the Control. It also exposes the following methods for managing Behaviors:

The Behavior interface (interceptor methods) is covered next:

  • preResponse(Control) - defines an interceptor method that is invoked before the response is written.

  • preRenderHeadElements(Control) - defines an interceptor method that is invoked after preResponse() but before the Control getHeadElements() is called. This is a good place to add custom JavaScript or CSS elements to Controls.

  • preDestroy() - defines an interceptor method that is invoked before the Control onDestroy() event handler. This interceptor method allows the behavior to cleanup any resources.

3.8.1. Behavior Execution

When a Behavior is added to a Control, the Control is automatically registered with the ControlRegistry. Registering with the ControlRegistry allows the Click runtime to quickly and easily access controls that have Behaviors and process them. Controls without behaviors won't be registered and won't be processed.

Click will invoke all the registered Behavior's interceptor methods at the appropriate time during the Control life cycle.

3.8.2. Behavior Example

Let's look at a simple Behavior example. Say we want to put focus on a Field in our Form. Normally we would use the following JavaScript snippet somewhere in our page template:

        document.getElementById('form_nameField').focus();

If we want this behavior on another page we can copy and paste this snippet to the other page template and update the field ID. Alternatively we can create a custom FocusBehavior that adds the necessary JavaScript to a target Field:

    public class FocusBehavior implements Behavior {

        public void preRenderHeadElements(Control control) {
            String id = control.getId();
            JsScript jsScript = new JsScript("document.getElementById('" + id + "').focus();");

            // Set script to execute as soon as browser dom is ready. NOTE: The
            // JavaScript logic determining when the DOM is ready is added by
            // the Form control, through the script '/click/control.js'.
            script.setExecuteOnDomReady(true);

            // Add the JavaScript element to the Control
            control.getHeadElements().add(jsScript);
        }

        ...
    } 

Below is an example using the FocusBehavior:

    public class MyPage extends Page {

    private Form form = new Form("form");
    private TextField nameField = new TextField("nameField");

        public MyPage() {
            addControl(form);
            form.add(nameField);

            // Create the custom behavior
            FocusBehavior focus = new FocusBehavior();

            // Add the behavior to the field
            nameField.addBehavior(focus);
        }
    } 

At runtime the nameField will be registered with the ControlRegistry when the FocusBehavior is added to the field.

Before the Control's HEAD elements are rendered, Click will invoke the FocusBehavior interceptor method, preRenderHeadElements(Control), passing the nameField as an argument.

The FocusBehavior preRenderHeadElements method will add the JavaScript code to the Field HEAD elements which will be rendered as part of the server response.

Our JavaScript snippet is executed by the browser as soon as the DOM is ready, in other words after our nameField has been rendered. Focus will be set on the nameField.

Chapter 4. Ajax

4.1. Ajax Overview

Ajax is a method of using JavaScript to perform a GET or POST request and return a result without reloading the whole page.

For an introduction on Ajax please see the following articles:

Ajax is a client-side technology for creating interactive web applications. The JavaScript XMLHttpRequest object is used to perform GET and POST requests and the server can send back a response that can be processed in the browser.

Click on the other hand is a server-side technology that can handle and process incoming Ajax requests and send a response back to the browser.

Please note: Click is responsible for handling server-side requests. It is up to you to develop the client-side logic necessary to make the Ajax request, process the server response and handle errors. This is easier than it sounds though as there is a wide range of free JavaScript libraries and plugins available to help develop rich HTML front-ened. Since Click is a stateless framework and Page URLs are bookmarkable, it is easy to integrate Click with existing JavaScript technologies such as: jQuery, Prototype and MooTools to name a few.

It is also possible to write custom AjaxBehaviors (covered later) that renders the client-side code necessary to initiate Ajax requests and handle Ajax responses and errors. In fact once you become familiar Click's Ajax handling, you will likely create custom AjaxBehaviors to streamline and encapsulate your client-side code.

In this chapter we'll look at the Ajax support provided by Click. There are two basic ways to handle and process Ajax requests:

  • AjaxBehavior - AjaxBehavior is a specialized Behavior that enables Controls to handle and respond to Ajax requests

  • Page Actions - Page Actions was covered earlier and provides a simple way to handle and respond to Ajax requests

4.2. AjaxBehavior

AjaxBehavior is an interface that extends Behavior (covered earlier) and adds the ability to handle and process incoming Ajax requests.

Click also provides a default AjaxBehavior implementation, DefaultAjaxBehavior. Using this class you only need to implement the methods you are interested in.

AjaxBehaviors, like Behaviors, are added to controls through the AbstractControl.addBehavior() method.

AjaxBehaviors provides an onAction method (similar to ActionListener) that is invoked to handle Ajax requests. The onAction method returns an ActionResult containing the data to be rendered to the browser.

The Control, Behavior, AjaxBehavior and ActionResult classes are depicted in the figure below.

Ajax Behavior Class Diagram

Figure 4.1. Ajax Behavior Class Diagram


The following method is exposed by Control in order to handle Ajax requests:

  • isAjaxTarget(Context) - Returns true if the control is the Ajax request target, false otherwise. The Ajax target control is the Control which onProcess method will be invoked. Other controls won't be processed. The most common way to target a specific server side control is to give it an HTML ID attribute, which is then passed as an Ajax request parameter to the server. More on this later.

The Behavior interface has been covered already so we'll look at AjaxBehavior next:

  • isAjaxTarget(Context) - determines whether the AjaxBehavior is the request target or not. Click will only invoke the AjaxBehavior onAction method if isAjaxTarget returns true. This allows for fine grained control over the exection of the onAction method.

  • onAction(Control) - the AjaxBehavior action method for handling Ajax requests.

The onAction method returns an ActionResult instance, containing the data to be rendered to the browser. ActionResult can return any type of response: String, byte array or a Velocity (Freemarker) template.

The isAjaxTarget method controls whether or not the onAction method should be invoked. isAjaxTarget() is typically used to target the AjaxBehavior for specific JavaScript events. For example an AjaxBehavior might only handle click or blur JavaScript events. Of course the client-side code initiating the Ajax request should pass the JavaScript event to the server.

Lastly the ActionResult methods are shown below:

4.3. AjaxBehavior Execution

The execution sequence for an AjaxBehavior being processed and rendered is illustrated in the figure below. Note that it is similar to a normal HTTP request flow. The main differences are that Ajax requests do not have an onGet or onRender event and that only the Ajax target Control is processed.

AjaxBehavior Sequence Diagram

Figure 4.2. AjaxBehavior Sequence Diagram


Stepping through this Ajax GET request sequence, first a new Page instance is created.

Then the onSecurityCheck() handler is executed to authorize access to the page, and if necessary abort further processing. If the request is aborted for an Ajax request, no response is rendered to the browser. If you want to render a response you need to write to the HttpServletResponse directly or create and render an ActionResult yourself.

The next method invoked is onInit() to initialize, create and setup controls and Behaviors. onInit is an ideal place to add Behaviors to Controls. When a Behavior is added to a Control that Control is automatically registered with the ControlRegistry as a potential Ajax target control.

The next step is to find and process the Ajax target control. First the ClickServlet needs to determine which Control is the Ajax target. To resolve the target Control the ClickServlet iterates over all the Controls registered with the ControlsRegistry and invokes each Control's isAjaxTarget method. The first control which isAjaxTarget method returns true, will be the Ajax target.

The simplest isAjaxTarget implementation is to return true if the Control ID is passed as a request parameter. The client-side JavaScript code that initiate the Ajax request, must ensure the Control ID is sent as part of the Ajax request. Note, if the ClickServlet cannot find a target control, no response is rendered.

If an Ajax target control is found, the ClickServlet will invoke that control's onProcess method. Other controls are not processed.

Please note: since Click is a stateless framework, processing a control for an Ajax request has the same requirements as processing a control for a non-Ajax request. In other words, in addition to the Control ID (or other identifier), the Ajax request must include all the parameters normally expected by the target Control and its children. For example, a Field expects it's name/value parameter while an ActionLink expects its actionLink/name parameter. Putting it another way, if for example an ActionLink is clicked and we only pass the link's HTML ID parameter, Click will identify the link as the Ajax target control and invoke the link's onProcess method. The onProcess method is where the link's values are bound and if it was clicked it's action event (AjaxBehavior) will be fired. An ActionLink is "clicked" if the actionLink parameter has a value matching the link's name. If no actionLink parameter is present, the server doesn't know that the link was clicked and won't fire the AjaxBehavior's onAction event. So for an Ajax request it is still necessary to pass all the parameters normally expected by the ActionLink onProcess method. For ActionLink that means the Ajax request must include it's href parameters while a Form would require all it's Field name/value pairs.

Next, the target control AjaxBehaviors are fired. The ClickServlet iterates over the control AjaxBehaviors and for each AjaxBehavior invoke the method: isAjaxTarget. Each AjaxBehavior which isAjaxTarget method returns true, will have their onAction method invoked to handle the Ajax request. The AjaxBehavior's onAction method returns an ActionResult that is rendered to the browser.

Please note: multiple AjaxBehaviors can handle the same Ajax request, however only the first ActionResult returned will be rendered to the browser. If an onAction method returns null, the ActionResult returned by the next AjaxBehavior's onAction method will be used. If all onAction methods returns null, no response is rendered.

Next the ActionResult is rendered to the browser.

The final step in this sequence is invoking each control's onDestroy() method and lastly invoke the Page onDestroy() method.

4.4. First Ajax Example

In this first example we demonstrate how to handle Ajax requests with a DefaultAjaxBehavior. DefaultAjaxBehavior is the default implementation of the AjaxBehavior interface. Below is the Page class, AjaxBehaviorPage.java, showing a DefaultAjaxBehavior added to an ActionLink, called link with an HTML ID of link-id. The DefaultAjaxBehavior onAction method will be invoked to handle the Ajax request. The onAction method returns an ActionResult that is rendered to the browser.

public class AjaxBehaviorPage extends BorderPage {

    private static final long serialVersionUID = 1L;

    private ActionLink link = new ActionLink("link", "here");

    public AjaxBehaviorPage() {
        link.setId("link-id"); 1

        addControl(link);

        // Add a DefaultAjaxBehavior to the link. The DefaultAjaxBehavior will be invoked when the
        // link is clicked.
        link.addBehavior(new DefaultAjaxBehavior() { 2

            @Override
            public ActionResult onAction(Control source) { 3

                // Formatted date instance that will be added to the
                String now = format.currentDate("MMM, yyyy dd HH:MM:ss");

                String msg = "AjaxBehavior <tt>onAction()</tt> method invoked at: " + now;

                // Return an action result containing the message
                return new ActionResult(msg, ActionResult.HTML); 4
            }
        });
    }
} 
1

We assign to ActionLink the HTML ID: link-id. The ID will be used to identify the ActionLink as the Ajax target control. The client-side JavaScript code is expected to send this ID as an Ajax request parameter.

2

Next we add the DefaultAjaxBehavior to the ActionLink. Adding a Behavior to a control will also register that control with the ControlRegistry as a potential Ajax target control.

3

We also implement the DefaultAjaxBehavior onAction method in order to handle the Ajax request.

4

Finally we return an ActionResult containing some HTML content that is rendered to the browser.

Below we show the Page template ajax-behavior.htm, containing the client-side JavaScript code that will initiate the Ajax request.

Note: the example below uses the jQuery library, but any other library can be used. Also see the online Click examples for more Ajax demos.

<!-- // $link is a Velocity reference that will render an ActionLink at runtime. -->
Click $link to make an Ajax request to the server.

<div id="result">
    <!-- // Ajax response will be set here -->
</div>

<!-- // JavaScript code below -->

<!-- // Import the jQuery library -->
<script type="text/javascript" src="$context/js/jquery.js"></script>

<!-- // The client-side JavaScript for initiating an Ajax request -->
<script type="text/javascript">
    // This example uses jQuery for making Ajax requests:

    // Register a function that is invoked as soon as the entire DOM has been loaded
    jQuery(document).ready(function() { 1

        // Register a 'click' handler that makes an Ajax request
        jQuery("#link-id").click(function(event){
            // Make ajax request
            makeRequest();

            // Prevent the default browser behavior of navigating to the link
            return false;
        })
    })

    function makeRequest() {
        // Get a reference to the link
        var link = jQuery('#link-id');

        // In order for Click to identify the Ajax target, we pass the link ID
        // attribute as request parameters
        var extraData = link.attr('id') + '=1'; 2

        // The Ajax URL is set to the link 'href' URL which contains all the link default parameters,
        // including it's name/value pair: 'actionLink=link'
        var url = link.attr('href'); 3

        jQuery.get(url, extraData, function(data) {
            // 'data' is the response returned by the server

            // Find the div element with the id "result", and set its content to the server response
            jQuery("#result").html("<p>" + data + "</p>"); 4
        });
    }
</script> 
1

We start off with a jQuery ready function that is executed as soon as the browser DOM has been loaded. This ensures that the function body is executed before the page images are downloaded, which results in a more responsive UI.

2

This is an important step. We need to pass the link's HTML ID attribute as request parameters in order for the server to identify which server-side control is the Ajax target. We use the jQuery attr function to lookup the link's HTML ID attribute. Click doesn't actually use the value of the parameter, it is enough that the name is present. In this example we pass a value of 1, but any other value could be used, or even left out.

3

This is another important step. In addition to the ActionLink HTML ID pararameter, we also need to send the link's href parameters, otherwise the ActionLink onProcess method won't fire the AjaxBehavior onAction event. An easy way to achieve this to set the Ajax URL to the ActionLink href value.

4

Finally we the jQuery html function to update the div content with the server response.

4.4.1. Ajax Trace Log

Looking at the output log we see the following trace:

[Click] [debug] GET http://localhost:8080/mycorp/ajax/ajax-behavior.htm
[Click] [trace]    is Ajax requesttrue
[Click] [trace]    request param: actionLink=link
[Click] [trace]    request param: link-id=1
[Click] [trace]    invoked: AjaxBehaviorPage.<<init>>
[Click] [trace]    invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace]    invoked: AjaxBehaviorPage.onInit()
[Click] [trace]    invoked: 'link' ActionLink.onInit()
[Click] [trace]    the following controls have been registered as potential Ajax targets:
[Click] [trace]       ActionLink: name='link'
[Click] [trace]    invoked: 'link' ActionLink.isAjaxTarget() : true (Ajax target control found)
[Click] [trace]    invoked: 'link' ActionLink.onProcess() : true
[Click] [trace]    processing AjaxBehaviors for control: 'link' ActionLink
[Click] [trace]       invoked: AjaxBehaviorPage.1.isRequestTarget() : true
[Click] [trace]       invoked: AjaxBehaviorPage.1.onAction() : ActionResult (ActionResult will be rendered)
[Click] [info ]    renderActionResult (text/html) - 1 ms
[Click] [trace]    invoked: 'link' ActionLink.onDestroy()
[Click] [trace]    invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest:  /ajax/ajax-behavior.htm - 25 ms

First thing we notice is that the request is recognized as an Ajax request.

We can also see from the log that the Ajax request passed the parameters, link-id=1 and actionLink=link to the server. link-id is the ActionLink HTML ID attribute that will be used to identify the Control as the Ajax request target.

Next, the log prints which controls have been registered as potential Ajax targets. In our example we added an AjaxBehavior to the ActionLink so the ActionLink is registered as an Ajax target.

Next, the ActionLink#isAjaxTarget was invoked and because it returned true, ActionLink will be used as the Ajax target control.

Having found the Ajax target, the ActionLink onProcess is called.

Next, the log shows it found the target AjaxBehavior by invoking the AjaxBehavior#isRequestTarget method, which returned true.

The AjaxBehavior#onAction is invoked which returns an ActionResult.

Finally, the ActionResult is rendered to the browser.

4.4.2. Ajax Trace Log - No Ajax Target Control Found

Below we show a log trace where no Ajax target control is found. The most common reason this can occur is if the JavaScript code that initiates the Ajax request does not send the necessary request parameters to identify the Ajax target control. Another problem is if no Control is registered ith the ControlRegistry, thus there is no potential Ajax target control. This can occur if no Behavior is added to a Control.

[Click] [debug] GET http://localhost:8080/mycorp/ajax/ajax-behavior.htm
[Click] [trace]    is Ajax requesttrue
[Click] [trace]    request param: actionLink=link
[Click] [trace]    invoked: AjaxBehaviorPage.<<init>>
[Click] [trace]    invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace]    invoked: AjaxBehaviorPage.onInit()
[Click] [trace]    invoked: 'link' ActionLink.onInit()
[Click] [trace]    the following controls have been registered as potential Ajax targets:
[Click] [trace]       ActionLink: name='link'
[Click] [trace]    *no* target control was found for the Ajax request
[Click] [trace]    invoked: 'link' ActionLink.onDestroy()
[Click] [trace]    invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest:  /ajax/ajax-behavior.htm - 87 ms

Notice from the log that the only request parameters sent is actionLink=link.

Next, the log prints which controls have been registered as potential Ajax targets. In our example we added an AjaxBehavior to the ActionLink so the ActionLink is registered as an Ajax target.

Finally, we see that *no* Ajax target control was found. This is because the ActionLink ID attribute, link-id, does not match the incoming request parameter, actionLink=link, hence the ActionLink was not identified as the Ajax request target and no response is rendered.

4.4.3. Ajax Trace Log - No Target AjaxBehavior Found

Below we show a log trace where no target AjaxBehavior is found. This can occur if no AjaxBehavior's isRequestTarget returns true.

[Click] [debug] GET http://localhost:9999/mycorp/ajax/ajax-behavior.htm
[Click] [trace]    is Ajax requesttrue
[Click] [trace]    request param: actionLink=link
[Click] [trace]    request param: link-id=1
[Click] [trace]    invoked: AjaxBehaviorPage.<<init>>
[Click] [trace]    invoked: AjaxBehaviorPage.onSecurityCheck() : true
[Click] [trace]    invoked: AjaxBehaviorPage.onInit()
[Click] [trace]    invoked: 'link' ActionLink.onInit()
[Click] [trace]    the following controls have been registered as potential Ajax targets:
[Click] [trace]       ActionLink: name='link'
[Click] [trace]    invoked: 'link' ActionLink.isAjaxTarget() : true (Ajax target control found)
[Click] [trace]    invoked: 'link' ActionLink.onProcess() : true
[Click] [trace]    processing AjaxBehaviors for control: 'link' ActionLink
[Click] [trace]       invoked: AjaxBehaviorPage.1.isRequestTarget() : false
[Click] [trace]    *no* target AjaxBehavior found for 'link' ActionLink - invoking AjaxBehavior.isRequestTarget() returned false for all AjaxBehaviors
[Click] [trace]    invoked: 'link' ActionLink.onDestroy()
[Click] [trace]    invoked: AjaxBehaviorPage.onDestroy()
[Click] [info ] handleRequest:  /ajax/ajax-behavior.htm - 80 ms
      

We can see from the log that the Ajax request sent the parameters, link-id=1 and actionLink=link to the server.

Next we notice that the AjaxBehavior isRequestTarget() returned false.

Finally we see that *no* target AjaxBehavior was found for the Ajax target control, 'link' ActionLink.

4.5. Ajax Page Action

Page Actions are page methods that can be invoked directly from the browser. So instead of handling the Ajax request with a Control, the request is handled with a page method.

Similar to the AjaxBehavior onAction method, page methods returns an ActionResult containing the data to render to the browser.

Page Actions have been covered earlier. Please click here for a detailed overview.

Using a Page Action for handling an Ajax request is no different from the normal HTTP version. To invoke a Page Action, specify the parameter "pageAction" and the name of the page method eg: "onLinkClicked".

Here is an example using the jQuery JavaScript library to make an Ajax request to a Page Action:

jQuery('#some-link-id').click(function() {

  // The ViewCustomerPage url
  var url = '$context/view-customers.htm';

  // Specify the pageAction parameter and page method to invoke: 'onLinkClicked'
  var extraData = 'pageAction=onLinkClicked';

  // Perform the Ajax request
  jQuery.get(url, extraData, function(response) {

    // Update the target element with the server response
    jQuery("#target").html("<p>" + response + "</p>");
  });

}); 

The JavaScript snippet above will send a request to the ViewCustomerPage method "onLinkClicked", which returns an ActionResult instance:

public class ViewCustomerPage extends Page {

    ...

    public ActionResult onLinkClicked() {
        // Formatted date instance that will be returned to the browser
        String now = format.currentDate("MMM, yyyy dd HH:MM:ss");

        String msg = "PageAction method <tt>onLinkClicked()</tt> invoked at: " + now;

        // Return an action result containing the message
        return new ActionResult(msg, ActionResult.HTML);
    }
} 

The ActionResult contains the data that is rendered to the client browser. In the example above, the action result is an HTML snippet containing todays date.

4.6. Ajax Response Types

The most common server response types are:

  • HTML

  • XML

  • JSON

Click Controls render themselves as XHTML markup so can be used in either XML or HTML responses.

Here is an example showing how to return different types of responses:

public class HelloWorldPage extends Page {

    ...

    public void onInit() {
        Behavior htmlBehavior = new DefaultAjaxBehavior() {
            public ActionResult onAction() {
                String html = "<h1>Hello world</h1>";

                // Return an HTML snippet
                return new ActionResult(html, ActionResult.HTML);
            }
        };
        htmlLink.addBehavior(htmlBehavior);

        ...

        Behavior xmlBehavior = new DefaultAjaxBehavior() {
            public ActionResult onAction() {
                String xml = "<payload>Hello world</payload>";

                // Return an XML snippet
                return new ActionResult(xml, ActionResult.XML);
            }
        };
        xmlLink.addBehavior(xmlBehavior);

        ...

        Behavior jsonBehavior = new DefaultAjaxBehavior() {
            public ActionResult onAction() {
                String json = "{\"value\": \"Hello world\"}";

                // Return an JSON snippet
                return new ActionResult(json, ActionResult.JSON);
            }
        };
        jsonLink.addBehavior(jsonBehavior);
    }
} 

4.7. Ajax Error Handling

If an exception occurs while processing an Ajax request and the application is in development mode, the exception stackTrace is returned to the browser.

If an exception occurs while processing an Ajax request and the application is in production mode, a simple error message is returned.

Chapter 5. Configuration

This section discusses how to setup and configure an Apache Click web application.

The Click configuration files include:

  • WEB-INF/ click.xml   -   Application Configuration ( required)

  • WEB-INF/ web.xml   -   Servlet Configuration ( required)

5.1. Servlet Configuration

For a Click web application to function the ClickServlet must be configured in the web application's /WEB-INF/web.xml file. A basic web application which maps all *.htm requests to a ClickServlet is provided below.

<web-app>

  <servlet>
    <servlet-name>ClickServlet</servlet-name>
    <servlet-class>org.apache.click.ClickServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>ClickServlet</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

</web-app>

5.1.1. Servlet Mapping

By convention all Click page templates should have an .htm extension, and the ClickServlet should be mapped to process all *.htm URL requests. With this convention you have all the static HTML pages use an .html extension and they will not be processed as Click pages.

5.1.2. Load On Startup

Note you should always set load-on-startup element to be 0 so the servlet is initialized when the server is started. This will prevent any delay for the first client which uses the application.

The ClickServlet performs as much work as possible at startup to improve performance later on. The Click start up and caching strategy is configured with the Click application mode element in the "click.xml" config file, covered next.

5.1.3. Type Converter Class

The ClickServlet uses the OGNL library for type coercion when binding request parameters to bindable variables. The default type converter class used is RequestTypeConverter. To specify your own type converter configure a type-converter-class init parameter with the ClickServlet. For example:

  <servlet>
    <servlet-name>ClickServlet</servlet-name>
    <servlet-class>org.apache.click.ClickServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
    <init-param>
	    <param-name>type-converter-class</param-name>
	    <param-value>com.mycorp.util.CustomTypeConverter</param-value>
    </init-param>
  </servlet>

5.1.4. Config Service Class

Click uses a single application configuration service which is instantiated by the ClickServlet at startup. This service defines the application configuration and is used by the ClickServlet to map requests to pages amongst other things.

Once the ConfigService has been initialized it is stored in the ServletContext using the key ConfigService The default ConfigService is XmlConfigService, which configuration is discussed in detail in the next section.

To use an alternative configuration service specify a config-service-class context parameter. For example:

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
   version="2.4">

 ...

     <context-param>
         <param-name>config-service-class</param-name>
          <param-value>com.mycorp.service.CustomConfigSerivce</param-value>
      </context-param>

  ...

 </web-app>

5.2. Application Configuration

The heart of a Click application is the click.xml configuration file. This file specifies the application pages, headers, the format object and the applications mode.

By default the ClickServlet will attempt to load the application configuration file using the path:   /WEB-INF/click.xml

If this file is not found under the WEB-INF directory, then ClickServlet will attempt to load it from the classpath as /click.xml.

See Click DTD for the click-app XML definition.

A complete Click configuration example is available here which can be used as a quick reference when configuring Click.

A basic Click app config file is provided below:

<click-app>

  <!-- Specify the Java package where Page classes can be found -->
  <pages package="com.mycorp.page"/>

  <mode value="profile"/>

</click-app>

An advanced config file would look like this:

<click-app charset="UTF-8" locale="de">

  <!-- To aid Click's automapping, specify the Java package where Page classes can be found -->
  <pages package="com.mycorp.banking.page">
  <!-- We have to manually define the mapping between the Home page class and index.htm template
  because this page doesn't follow the automatic mapping convention of naming the page class and
  template the same-->
    <page path="index.htm" classname="com.mycorp.banking.page.Home"/>
  </pages>

  <!-- Specify a second Java package where Page classes can be found -->
  <pages package="com.mycorp.common.page"/>

  <format classname="com.mycorp.util.Format"/>

  <mode value="profile"/>

  <log-service classname="org.apache.click.extras.service.Log4JLogService"/>

</click-app>

The take away point is that there is not much to configure, even for advanced uses.

5.2.1. Click App

The root click-app element defines two application localization attributes charset and locale.

<!ELEMENT click-app (pages*, headers?, format?, mode?, controls?,
                         file-upload-service?, log-service?, messages-map-service?, resource-service?, template-service?, page-interceptor*)>
  <!ATTLIST click-app charset CDATA #IMPLIED>
  <!ATTLIST click-app locale CDATA #IMPLIED>

The charset attribute defines the character encoding set for:

  • Velocity templates

  • HttpServletRequest character encoding

  • Page Content-Type charset, see Page getContentType()

The locale attribute defines the default application Locale. If this value is defined it will override Locale returned by the request. Please see the Context getLocale() for details. For example the following configuration sets the application character set to UTF-8 and the default Locale as German (de):

<click-app charset="UTF-8" locale="de">
  ..
</click-app>

5.2.2. Pages

The first child element of the click-app is the mandatory pages element which defines the list of Click pages.

<!ELEMENT pages (page*)>
   <!ATTLIST pages package CDATA #IMPLIED>
   <!ATTLIST pages automapping (true|false) "true">
   <!ATTLIST pages autobinding (default|annotation|none) "default">

The pages element can specify a base package that Click should use for mapping page templates to page classes.

The pages element also defines the automapping and autobinding attributes which is discussed in the Page Automapping and Page Autobinding sections respectively.

5.2.2.1. Multiple Pages Packages

Click can support multiple pages elements to enable the automapping of multiple packages.

<click-app>

  <pages package="com.mycorp.banking.page"/>

  <pages package="com.mycorp.common.page"/>

</click-app>

With multiple pages elements, pages are loaded in the order of the page elements, with manual page elements being loaded before automapped pages. Once a page template has been mapped to a Page class it will not be replaced by a subsequent potential match. So pages elements at the top take priority over lower pages elements.

5.2.3. Page

The page element defines the Click application pages.

<!ELEMENT page(header*)>
   <!ATTLIST page path CDATA #REQUIRED>
   <!ATTLIST page classname CDATA #REQUIRED>

Each page path must be unique, as the Click application maps HTTP requests to the page paths.

The Click application will create a new Page instance for the given request using the configured page classname. All pages must subclass Page and provide a public no arguments constructor, so they can be instantiated.

Pages can also define header values which are discussed in the next topic.

When the Click application starts up it will check all the page definitions. If there is a critical configuration error the ClickSerlvet will log an ERROR message and throw an UnavailableException. If this occurs the click application will be permanently unavailable until the error is fixed and the web app is restarted.

5.2.3.1. Page Automapping

Page automapping will automatically configure application pages using a simple set of rules. This enables you to greatly streamline your configuration file as you only need to define pages which don't fit the automapping rules.

Automapping will attempt to associate each page template (*.htm) and JSP file in the web application (excluding those under WEB-INF) to a Page class. Automapped pages are loaded after the manually defined pages are loaded, and manually defined pages takes preference. When automapping is enabled the page mappings will be logged if Click is running in debug or trace mode.

For example, given the following page path to class mapping:

index.htm                     => com.mycorp.page.Home
search.htm                    => com.mycorp.page.Search
contacts/contacts.htm         => com.mycorp.page.contacts.Contacts
security/login.htm            => com.mycorp.page.security.Login
security/logout.htm           => com.mycorp.page.security.Logout
security/change-password.htm  => com.mycorp.page.security.ChangePassword

The above mapping could be configured manually by setting the automapping attribute to false, for example:

<click-app>
  <pages automapping="false">
    <page path="index.htm"                    classname="com.mycorp.page.Home"/>
    <page path="search.htm"                   classname="com.mycorp.page.Search"/>
    <page path="contacts/contacts.htm"        classname="com.mycorp.page.contacts.Contacts"/>
    <page path="security/login.htm"           classname="com.mycorp.page.security.Login"/>
    <page path="security/logout.htm"          classname="com.mycorp.page.security.Logout"/>
    <page path="security/change-password.htm" classname="com.mycorp.page.security.ChangePassword"/>
  </pages>
</click-app>

For an application with many pages, it is cumbersome to manually map each page template to its associated class. This is where automapping comes in.

By setting automapping to true, Click will automatically map page templates to page classes. To map a template to a page class, Click converts the template path to the Page classname. In the example above, Click will convert the template search.htm to the class Search by capitilizing the template name and removing the .htm extension. Of course this is not enough to map the template to the class. what is missing is the class package, com.mycorp.page. To help Click map the page, you can set the base package attribute as shown in the next example.

Below is the full configuration to automatically map the templates to pages (except for index.htm which doesn't automatically map to Home page and has to be mapped manually):

<click-app>
  <pages package="com.mycorp.page" automapping="true">
    <page path="index.htm" classname="com.mycorp.page.Home"/>
  </pages>
</click-app>

Note: automapping is true by default, so it could be omitted.

If a page template is placed in a sub folder of the root web folder, it's associated page class must be placed in an equivalently named sub package of the base package in order for the page to be mapped automatically. In the mapping above the page template security/change-password.htm is located in the security folder under the web root. In order for Click to correctly map the page template to it's class, the class must be located in the security package of the base package com.mycorp.page. The absolute page classname is thus: com.mycorp.page.security.ChangePassword.

The page template name to classname convention is:

change-password.htm  =>  ChangePassword
change_password.htm  =>  ChangePassword
changePassword.htm   =>  ChangePassword
ChangePassword.htm   =>  ChangePassword

During automapping, if a page class cannot be found, Click will add the 'Page' suffix to the classname (if not already present) and attempt to map the page template to this modified classname. For example:

customer.htm         =>  CustomerPage
change-password.htm  =>  ChangePasswordPage

5.2.3.2. Automapping Excludes

With Page automapping there can be resources where you don't want automapping applied. For example when using a JavaScript library with lots of .htm files, you don't want automapping to try and find Page class for each of these files. In these situations you can use the pages excludes element.

<!ELEMENT excludes (#PCDATA)>
   <!ATTLIST excludes pattern CDATA #REQUIRED>

For example if our application uses the TinyMCE JavaScript library we could configure our pages automapping to exclude all .htm files under the /tiny_mce directory.

<click-app>
  <pages package="com.mycorp.page">
    <excludes pattern="/tiny_mce/*"/>
  </pages>
</click-app>

The excludes pattern can specify multiple directories or files using a comma separated notation. For example:

<click-app>
  <pages package="com.mycorp.page">
    <excludes pattern="/dhtml/*, /tiny_mce/*, banner.htm, about.htm"/>
  </pages>
</click-app>

HTM files excluded from Page automapping are handled by an internal Page class with caching headers enabled.

5.2.3.3. Page Autobinding

Autobinding is a feature that allows certain page variables to be handled in a special way by the ClickServlet. The autobinding attribute can be configured with one of the following values:

  • default: bindable variables include both public page variables and variables annotated with the @Bindable annotation

  • annotation: bindable variables are variables annotated with the @Bindable annotation

  • none: disables the autobinding feature

By default all pages have autobinding enabled in default mode.

Please note: we recommend using autobinding only for binding request parameters, not for Controls. It generally leads to code that is difficult to maintain. In a future release we will replace autobinding with a simpler implementation.

With autobinding the ClickServlet will automatically:

  • add all bindable controls to the page, after the page constructor has been invoked

  • if a bindable control name is not defined, the control name will be set to the value of its variable name (note, if the control name is already defined its name will not be changed)

  • bind all request parameters to bindable page variables, after the page constructor has been invoked. See ClickServlet.processPageRequestParams(Page) for more details

  • add all bindable page variables to the page model (this step occurs just before the page is rendered)

For example:

public class EmployeePage extends Page {

    public String employeeDescription;

    // Form does not have a name defined
    public Form employeeForm = new Form();

    // Table defines its own name
    public Table employeeTable = new Table("table");

}

Note in the example above that the employeeDescription variable and the employeeForm and employeeTable controls are not added to the page. Also note that Form name is not defined.

When autobinding is enabled, ClickServlet will create a new Page and add the bindable variables and controls to the page. Following the example above the employeeDescription, employeeForm and employeeTable will be added to the page, which is equivalent to the following statements: addModel("employeeDescription", employeeDescription), addControl(employeeForm) and addControl(employeeTable).

Furthermore, controls that do not have a name defined will have their name set to their instance variable name. In this case the Form name will be set to employeeForm while the Table name won't be altered since it already has a name defined.

The above example is a shorthand way of writing the following:

public class EmployeePage extends Page {

    private String employeeDescription;

    private Form employeeForm = new Form();

    private Table employeeTable = new Table("table");

    public void onInit() {
        employeeForm.setName("employeeForm");
        addControl(employeeForm);

        addControl(myTable);
    }
}

Note that we did not show where employeeDescription is added to the page model. The reason for that is because autobinding handles non controls slightly differently. Non control variables are added to the model just before the page response is written. This allows the value of the variable to be set anywhere in the page. For example:

public class EmployeePage extends Page {

    private String employeeDescription;

    private Form employeeForm = new Form();

    private Table employeeTable = new Table("table");

    ...

    public boolean onSaveClick {
        if (employeeForm.isValid()) {
            // employeeDescription is added to the page model just before the
            // response is written
            employeeDescription = employee.getDescription();
        }
    }
} 

employeeDescription will be added to the page model and can be referenced in the page template as $employeeDescription.

Autobinding can be turned off by setting the autobinding attribute to none as shown below:

<click-app>
  <pages package="com.mycorp.page" autobinding="none"/>
</click-app>

5.2.3.4. Page Autobinding - Using Annotations

Click provides the Bindable annotation which enables autobinding of Page variables The Bindable annotation can bind private, protected and public Page variables.

By default, Click's autobinding feature operates on both public and @Bindable variables. To instruct Click to operate only on @Bindable annotated variables, you can set the autobinding attribute to annotation, for example:

<click-app>
  <pages package="com.mycorp.page" autobinding="annotation"/>
</click-app>

Click won't autobind public variables anymore.

Below is an example using the @Bindable annotation:

public class EmployeePage extends Page {

    @Bindable protected Form employeeForm = new Form();

    @Bindable protected Table myTable = new Table();

}

5.2.4. Headers

The optional headers element defines a list of header elements which are applied to all pages.

<!ELEMENT headers (header*)>

The header element defines header name and value pairs which are applied to the HttpServletResponse.

<!ELEMENT header (#PCDATA)>
   <!ATTLIST header name CDATA #REQUIRED>
   <!ATTLIST header value CDATA #REQUIRED>
   <!ATTLIST header type (String|Integer|Date) "String">

Page headers are set after the Page has been constructed and before onInit() is called. Pages can then modify their headers property using the setHeader() method.

5.2.4.1. Browser Caching

Headers are typically used to switch off browser caching. By default Click will use the following no caching header values if you don't define a headers element in your application:

<click-app>
  <pages>
     ..
  </pages>
  <headers>
    <header name="Pragma" value="no-cache"/>
    <header name="Cache-Control"
            value="no-store, no-cache, must-revalidate, post-check=0, pre-check=0"/>
    <header name="Expires" value="1" type="Date"/>
  </headers>
</click-app>

Alternatively you can define your headers individually in pages or for all application pages by setting header values. For example, to switch off caching in the Login page, set the following page cache control headers:

<pages package="com.mycorp.page">
  <page path="login.htm" classname="com.mycorp.page.Login">
    <header name="Pragma" value="no-cache"/>
    <header name="Expires" value="1" type="Date"/>
  </page>
</pages>

Note: the value for a Date type should be a long number value.

If you wanted to enable caching for a particular page you could set the following page cache control header. This will mark the page as cachable for a period of 1 hour after which it should be reloaded.

<pages package="com.mycorp.page">
  <page path="home.htm" classname="com.mycorp.page.Home">
    <header name="Cache-Control" value="max-age=3600, public, must-revalidate"/>
  </page>
</pages>

To apply header values globally define header values in the headers element. For example:

<click-app>
  <pages>
     ..
  </pages>
  <headers>
    <header name="Pragma" value="no-cache"/>
    <header name="Cache-Control"
               value="no-store, no-cache, must-revalidate, post-check=0, pre-check=0"/>
    <header name="Expires" value="1" type="Date"/>
  </headers>
</click-app>

5.2.5. Format

The optional format element defines the Format object classname which is applied to all pages.

<!ELEMENT format (#PCDATA)>
    <ATTLIST format classname CDATA "org.apache.click.util.Format">

By default all Click pages are configured with a org.apache.click.util.Format object. The format object is made available in the Velocity page templates using the name $format.

To specify a custom format class configure a format element in the click-app descriptor. For example:

<click-app>
  ..
  <format classname="com.mycorp.util.CustomFormat"/>
</click-app>

5.2.6. Mode

The optional mode element defines the application logging and caching mode.

<!ELEMENT mode (#PCDATA)>
    <ATTLIST mode value (production|profile|development|debug|trace) "development">

By default Click applications run in development mode, which switches off page template caching, and the logging level is set to INFO.

To change the default application mode configure a mode element in the click-app descriptor. For example to specify production mode you would add the following mode element:

<click-app>
  ..
  <mode value="production">
</click-app>

The application mode configuration can be overridden by setting the system property "click.mode". This can be use in the scenario of debugging a problem on a production system, where you change the mode to trace by setting the following system property and restarting the application.

-Dclick.mode=trace

The Click Application modes and their settings for Page auto loading, template caching and logging levels are:

Application mode

Page auto loading

Template caching

Click log level

Velocity log level

production

No

Yes

WARN

ERROR

profile

No

Yes

INFO

ERROR

development

Yes

No

INFO

ERROR

debug

Yes

No

DEBUG

ERROR

trace

Yes

No

TRACE

WARN

5.2.6.1. Page Auto Loading

When Page Auto Loading is enabled any new page templates and classes will be automatically loaded at runtime. These pages are loaded using the Page Automapping rules.

Page auto loading is a very handy feature for rapid development as you do not have to restart you application server to pick up new pages.

5.2.6.2. Click and Velocity Logging

The Click and Velocity runtimes use LogService for logging messages. The default LogService implementation is ConsoleLogService which will send messages to the console [System.out]. For example the following logging output is for a HomePage request when the application mode is trace:

[Click] [debug] GET http://localhost:8080/quickstart/home.htm
[Click] [trace]    invoked: HomePage.<<init>>
[Click] [trace]    invoked: HomePage.onSecurityCheck() : true
[Click] [trace]    invoked: HomePage.onInit()
[Click] [trace]    invoked: HomePage.onGet()
[Click] [trace]    invoked: HomePage.onRender()
[Click] [info ]    renderTemplate: /home.htm - 6 ms
[Click] [trace]    invoked: HomePage.onDestroy()
[Click] [info ] handleRequest:  /home.htm - 24 ms

Any unhandled Throwable errors are logged by the ClickServlet.

Note that Click Extras also provide log adaptors for Log4J and the JDK Logging API.

When an application is not in production mode the error page displays detailed debugging information. When the application mode is production no debug information is displayed to prevent sensitive information being revealed. This behaviour can be changed by modifying the deployed click/error.htm page template.

5.2.7. Controls

The optional controls element defines a list of control elements which will be deployed on application startup.

<!ELEMENT controls (control*)>

The control registers Control classes which will have their onDeploy() method invoked when the click application starts.

<!ELEMENT control (#PCDATA)>
   <!ATTLIST control classname CDATA #REQUIRED>

For example to have a CustomField control deploy its resources on application startup, you would add the following elements to your click.xml file:

<click-app>
   ..

   <controls>
     <control classname="com.mycorp.control.CustomField"/>
   </controls>
</click-app>

5.3. Auto Deployed Files

To make pre-configured resources (templates, stylesheets, etc.) available to web applications, Click automatically deploys configured classpath resources to the /click directory at startup (if not already present).

You can modify these support files and Click will not overwrite them. These files include:

  • click/error.htm   -   the Page Error Handling template

  • click/control.css   -   the Controls cascading stylesheet

  • click/control.js   -   the Controls JavaScript library

  • click/not-found.htm   -   the Page Not Found template

For example to customize the control styles you can place a customized copy (or even a brand new version) of control.css under the /click folder in your web project:

/webapp/click/control.css

When Click starts up it will not override your copy of control.css with its own default version.

Different controls might deploy different stylesheet, javascript or image files, however the above principle still applies. By placing a customized copy of the stylesheet, javascript or image under the /click folder, you will override the default resource.

Be aware that some of the more complex controls (checklist, colorpicker, tree), deploys resources to subfolders under /click, for example /click/checklist/*.

A control's Javadoc will normally indicate what resources are deployed for that control.

5.3.1. Deploying resources in a restricted environment

Some environments place restrictions on the file system and Click won't be able to deploy its resources. WebLogic and Google App Engine are examples of such environments. (Note that WebLogic has a property to allow access to the file system. From the Admin Console go to the Server node > Web Applications tab and check the Archived Real Path Enabled parameter.)

If Click cannot deploy its resources because of limited file system access or permissions, warning messages will be logged.

Note: if your application is running on a Servlet 3.0 compliant server, there is no need to deploy resources. Servlet 3.0 specifies that if the server cannot find a resource in the root directory of the webapp, it will look for the resource under 'META-INF/resources', and if found, serve it up. Click is Servlet 3.0 compliant and packages its resources under 'META-INF/resources'.

Click provides a number of options to make resources available in restricted environments which is covered below:

  • The first option (which will work in all environments) is to deploy the resources at build time. Click ships with an Ant Task called DeployTask that deploys Click static resources to a web application. With this option Click's static resources can be copied to the root directory of your webapp, where you can customize the resources further if needed. The DeployTask can easily be incorporated into your build script.

    Currently the DeployTask is part of the click-dev-tools-xxx.jar that can be found in your Click distribution under the lib folder.

    Here is a basic example:

    <target name="deploy" description="Deploy static resources">
        <taskdef name="deploy"
                 classname="org.apache.click.tools.deploy.DeployTask"
                 classpath="<click-distribution>/lib/click-dev-tasks-1.1.jar"/> 1
    
        <deploy dir="<webapp-root>/WEB-INF"
                todir="<webapp-root>"/> 2
    </target> 
    1

    <click-distribution> is the location where Click is installed on your machine, for example: C:\software\click-2.1.0\.

    2

    <webapp-root> is the root directory of your webapp, for example: C:\dev\my-webapp\.

    We use the <deploy> Ant Task and specify the attributes dir and todir.

    dir specifies the source directory to scan for JARs and folders containing static resources, while todir specifies the target directory where the resources should be copied to.

    dir should point to your web application's WEB-INF folder, since that is where Click's JARs will be located. todir should point to your web application's root directory, since that is where Click's resources will be served from.

    The DeployTask also supports nested FileSets if you need to deploy resources from multiple source locations. For example:

    <target name="deploy" description="Deploy static resources">
        <taskdef name="deploy"
                 classname="org.apache.click.tools.deploy.DeployTask"
                 classpath="<click-distribution>/lib/click-dev-tasks-1.1.jar"/>
    
        <deploy todir="${dir.webapp}">
            <fileset dir="<webapp-root>/WEB-INF">
                <include name="**/classes"/>
                <include name="**/*.jar"/>
            </fileset>
            <fileset dir="/some/folder/with/jars">
                <include name="lib-with-resources.jar"/>
                <include name="another-lib-with-resources.jar"/>
            </fileset>
        </deploy>
    </target> 

    The DeployTask also generates an HTML report in the same folder where the build script is executed from. The report will indicate which resources was deployed successfully and which resources in your webapp root directory is outdated. (An outdated resource means that the resource in the click-xxx.jar differs from the resource currently present in your webapp root directory. This can happen when upgrading to a new version of Click)

  • Another option is to add a mapping in web.xml to inform ClickServlet to serve static resources. This feature is made available through the ResourceService interface and its default implementation, ClickResourceService. Below is an example:

    <servlet>
      <servlet-name>ClickServlet</servlet-name>
      <servlet-class>org.apache.click.ClickServlet</servlet-class>
      <load-on-startup>0</load-on-startup>
    </servlet>
    
    <servlet-mapping>
      <servlet-name>ClickServlet</servlet-name>
      <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    
     <!-- Inform ClickServlet to serve static resources contained under the /click/*
           directory directly from Click's JAR files. -->
    <servlet-mapping>
      <servlet-name>ClickServlet</servlet-name>
      <url-pattern>/click/*</url-pattern>
    </servlet-mapping>

    With this setup, ClickServlet will serve all static /click/* resources directly from Click's JAR files.

    One restriction of ClickResourceService is it only serves resources from the /click/* folder. So if you use third-party Click libraries that serve their resources from a different folder e.g. /clickclick/*, this option won't work out-of-the-box.

    Also note that with this option Click's resources are served directly from the JAR files, you won't be able to customize the resources, if for example you want change the default styling through CSS.

  • Another option is to manually deploy the resources. Click resources are packaged in JARs under the directory META-INF/resources. You can use your IDE to navigate into the JARs and copy all the resources from META-INF/resources to your webapp root directory.

    For example, to deploy the resources from click-core.jar, copy the /click folder and its contents to your web application root folder.

  • And finally you can access Click's resources by deploying your application on a development machine where there are no file system restrictions and the WAR/EAR can be unpacked. You can then copy the deployed resources to your webapp root directory.

5.3.2. Deploying Custom Resources

Click supports two ways of deploying pre-configured resources (templates, stylesheets, JavaScript etc.) from a Jar to a web application. (This assumes that the environment Click is running in supports having write access to the file system and that the WAR is unpacked.)

  1. Through a Control's onDeploy() event handler. See the Controls section above.

  2. By packaging the resources (stylesheets, JavaScript, Images etc.) into a special folder called 'META-INF/resources'.

As option #1 was already discussed above in section Controls, lets look at option #2.

When Click starts up, it scans each Jar and folder on the classpath for specially marked entries starting with 'META-INF/resources/'. (Please note that even though Click will scan the entire classpath it is strongly recommended to host your Jar files under your WAR lib folder e.g. WEB-INF/lib. Sharing Jars on the classpath can lead to class loading issues.)

Click will then copy all files found under 'META-INF/resources/' to the root directory of the webapp.

For example, given a Jar file with the following entries:

  • META-INF/resources/mycorp/edit_customer.js

  • META-INF/resources/mycorp/edit_customer.css

  • mycorp/pages/EditCustomerPage.class

Click will copy the files '/mycorp/edit_customer.js' and '/mycorp/edit_customer.css' to the web application folder.

Thus if the web application is called 'webapp', the files will be deployed as 'webapp/mycorp/edit_customer.js' and 'webapp/mycorp/edit_customer.css'.

Option #2 is the recommended approach for deploying your own resources since it makes the managing and maintenance of resources much easier.

Chapter 6. Best Practices

This chapter discusses Best Practices for designing and building Apache Click applications.

6.1. Security

For application security it is highly recommended that you use the declarative JEE Servlet path role based security model. While Click pages provide an onSecurityCheck() method for rolling your own programmatic security model, the declarative JEE model provides numerous advantages.

These advantages include:

  • Its an industry standard pattern making development and maintenance easier.

  • Application servers generally provide numerous ways of integration with an organisations security infrastructure, including LDAP directories and relational databases.

  • Servlet security model support users bookmarking pages. When users go to access these pages later, the container will automatically authenticate them before allowing them to access the resource.

  • Using this security model you can keep your Page code free of security concerns. This makes you code more reusable, or at least easier to write.

If your application has very fine grained or complex security requirements you may need to combine both the JEE declarative security model and a programmatic security model to meet your needs. In these cases its recommended you use declarative security for course grained access and programmatic security for finner grained access control.

6.1.1. Declarative Security

The declarative JEE Servlet security model requires users to be authenticated and in the right roles before they can access secure resources. Relative to many of the JEE specifications the Servlet security model is surprisingly simple.

For example to secure admin pages, you add a security constraint in your web.xml file. This requires users to be in the admin role before they can access to any resources under the admin directory:

<security-constraint>
   <web-resource-collection>
      <web-resource-name>admin</web-resource-name>
      <url-pattern>/admin/*</url-pattern>
   </web-resource-collection>
   <auth-constraint>
      <role-name>admin</role-name>
   </auth-constraint>
</security-constraint>

The application user roles are defined in the web.xml file as security-role elements:

<security-role>
   <role-name>admin</role-name>
</security-role>

The Servlet security model supports three different authentication method:

  • BASIC - only recommended for internal applications where security is not important. This is the easiest authentication method, which simply displays a dialog box to users requiring them to authenticate before accessing secure resources. The BASIC method is relatively insecure as the username and password are posted to the server as a Base64 encoded string.

  • DIGEST - recommended for internal applications with a moderate level of security. As with BASIC authentication, this method simply displays a dialog box to users requiring them to authenticate before accessing secure resources. Not all application servers support DIGEST authentication, with only more recent versions of Apache Tomcat supporting this method.

  • FORM - recommended applications for where you need a customised login page. For applications requiring a high level of security it is recommended that you use the FORM method over HTTPS.

The authentication method is specified in the <login-method> element. For example to use the BASIC authentication method you would specify:

<login-config>
   <auth-method>BASIC</auth-method>
   <realm-name>Admin Realm</realm-name>
</login-config>

To use the FORM method you also need to specify the path to the login page and the login error page:

<login-config>
   <auth-method>FORM</auth-method>
   <realm-name>Secure Realm</realm-name>
   <form-login-config>
      <form-login-page>/login.htm</form-login-page>
      <form-error-page>/login.htm?auth-error=true</form-error-page>
   </form-login-config>
</login-config>

In your Click login.htm page you need to include a special j_security_check form which includes the input fields j_username and j_password. For example:

#if ($request.getParameter("auth-error"))
<div style="margin-bottom:1em;margin-top:1em;color:red;">
  Invalid User Name or Password, please try again.<br/>
  Please ensure Caps Lock is off.
</div>
#end

<form method="POST" action="j_security_check" name="form">
<table border="0" style="margin-left:0.25em;">
 <tr>
   <td><label>User Name</label><font color="red">*</font></td>
   <td><input type="text" name="j_username" maxlength="20" style="width:150px;"/></td>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <td><label>User Password</label><font color="red">*</font></td>
   <td><input type="password" name="j_password" maxlength="20" style="width:150px;"/></td>
   <td><input type="image" src="$context/images/login.png" title="Click to Login"/></td>
  </tr>
</table>
</form>

<script type="text/javascript">
  document.form.j_username.focus();
</script>

When using FORM based authentication do NOT put application logic in a Click Login Page class, as the role of this page is to simply render the login form. If you attempt to put navigation logic in your Login Page class, the JEE Container may simply ignore it or throw errors.

Putting this all together below is a web.xml snippet which features security constraints for pages under the admin path and the user path. This configuration uses the FORM method for authentication, and will also redirect unauthorized (403) requests to the /not-authorized.htm page.

<web-app>

    ..

    <error-page>
        <error-code>403</error-code>
        <location>/not-authorized.htm</location>
    </error-page>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>user</web-resource-name>
            <url-pattern>/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>Secure Zone</realm-name>
        <form-login-config>
            <form-login-page>/login.htm</form-login-page>
            <form-error-page>/login.htm?auth-error=true</form-error-page>
        </form-login-config>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>

    <security-role>
        <role-name>user</role-name>
    </security-role>

</web-app>

6.1.2. Alternative Security solutions

There are also alternative security solutions that provide extra features not available in JEE, such as RememberMe functionality, better resource mapping and Post Logon Page support. (Post Logon Page support allows one to specify a default URL where the user will be forwarded after successful login. This feature allows one to embed a login form in all non-secure pages and after successful authentication the user will be forwarded to their home page.)

Below are some of the alternative security solutions available:

6.1.3. Resources

For more information on using security see the resources below:

6.2. Packages and Classes

An excellent way to design your project package structure is to classify packages initially by technology. So in a Click application all of our pages would be contained under a page package. This also works very well with the Page automapping feature.

All the projects domain entity classes would be contained under a entity package, and service classes would be contained under a service directory. Note alternative names for the entity package include domain or model. We also typically have a util package for any stray classes which don't quite fit into the other packages.

In Java, package names are singular by convention, so we have a util package rather than a utils package.

An example project structure for a MyCorp web application is illustrated below:

Example project structure

Figure 6.1. Example project structure


In this example application we use declarative role and path based security. All the pages in the admin package and directory require the "admin" role to be access, while all the pages in the user package and directory require the "user" role to be accessed.

6.2.1. Page Classes

A best practice when developing application Page classes is to place common methods in a base page class. This is typically used for providing access methods to application services and logger objects.

For example the BasePage below provides access to Spring configured service objects and a Log4J logger object:

public class BasePage extends Page implements ApplicationContextAware {

    /** The Spring application context. */
    protected ApplicationContext applicationContext;

    /** The page Logger instance. */
    protected Logger logger;

    /**
     * Return the Spring configured Customer service.
     *
     * @return the Spring configured Customer service
     */
    public CustomerService getCustomerService() {
        return (CustomerService) getBean("customerService");
    }

    /**
     * Return the Spring configured User service.
     *
     * @return the Spring configured User service
     */
    public UserService getUserService() {
        return (UserService) getBean("userService");
    }

    /**
     * Return the page Logger instance.
     *
     * @return the page Logger instance
     */
    public Logger getLogger() {
        if (logger == null) {
            logger = Logger.getLogger(getClass());
        }
        return logger;
    }

    /**
     * @see ApplicationContextAware#setApplicationContext(ApplicationContext)
     */
    public void setApplicationContext(ApplicationContext applicationContext)  {
        this.applicationContext = applicationContext;
    }

    /**
     * Return the configured Spring Bean for the given name.
     *
     * @param beanName the configured name of the Java Bean
     * @return the configured Spring Bean for the given name
     */
    public Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

}

Applications typically use a border template and have a BorderPage which extends BasePage and defines the template. For example:

public class BorderPage extends BasePage {

    /** The root Menu item. */
    public Menu rootMenu = new Menu();

    /**
     * @see Page#getTemplate()
     */
    public String getTemplate() {
        return "/border-template.htm";
    }
}

Most application pages subclass BorderPage, except AJAX pages which have no need for a HTML border template and typically extend BasePage. The BorderPage class should not include common logic, other than that required for rendering the border template. Common page logic should be defined in the BasePage class.

To prevent these base Page classes being auto mapped, and becoming directly acessible web pages, ensure that there are no page templates which could match their class name. For example the BorderPage class above will not be auto mapped to border-template.htm.

6.3. Page Auto Mapping

You should use the Click page automapping configuration feature. See the Page Automapping topic for details.

Automapping will save you from having to manually configure URL path to Page class mappings in your click.xml file. If you follow this convention it is very easy to maintain and refactor applications.

You can also quickly determine what the corresponding Page class is for a page HTML template and visa versa, and if you use the ClickIDE Eclipse plugin you can switch between a page's class and template by pressing Ctrl+Alt+S.

An example click.xml automapping configuration is provided below (automapping is enabled by default):

<click-app>
  <pages package="com.mycorp.dashboard.page"/>
</click-app>

To see how the page templates are mapped to Page classes set the application mode to debug and at startup the mappings will be listed out. An example Click startup listing is provided below:

[Click] [debug] automapped pages:
[Click] [debug] /category-tree.htm -> com.mycorp.dashboard.page.CategoryTree
[Click] [debug] /process-list.htm -> com.mycorp.dashboard.page.ProcessList
[Click] [debug] /user-list.htm -> com.mycorp.dashboard.page.UserList

6.4. Navigation

When navigating between Pages using forwards and redirects, you should refer to the target page using the Page class rather than using path. This provides you compile time checking and will save you from having to update path strings in Java code if you move pages about.

To forward to another page using the Page class:

public class CustomerListPage extends Page {

    public ActionLink customerLink = new ActionLink(this,"onCustomerClick");

    ..

    public boolean onCustomerClick() {
        Integer id = customerLink.getValueInteger();
        Customer customer = getCustomerService().getCustomer(id);

        CustomerDetailPage customerDetailPage = (CustomerDetailPage)
            getContext().createPage(CustomerDetailPage.class);

        customerDetailPage.setCustomer(customer);
        setForward(customerDetailPage);

        return false;
    }
}

To redirect to another page using the Page class you can obtain the pages path from the Context. In the example below we are passing through the customer id as a request parameter to the target page.

public class CustomerListPage extends Page {

    public ActionLink customerLink = new ActionLink(this, "onCustomerClick");

    ..

    public boolean onCustomerClick() {
        String id = customerLink.getValueInteger();

        String path = getContext().getPagePath(CustomerDetailPage.class);
        setRedirect(path + "?id=" + id);

        return false;
    }
}

A quick way of redirecting to another page is to simply refer to the target class. The example below logs a user out, by invalidating their session, and then redirects them to the application home page.

public boolean onLogoutClick() {
    getContext().getSession().invalidate();

    setRedirect(HomePage.class);

    return false;
}

6.5. Templating

Use Page templating it is highly recommended. Page templates provide numerous advantages including:

  • greatly reduce the amount of HTML you need to maintain

  • ensure you have a common look and feel across your application

  • make global application changes very easy

To see how to use templates see the Page Templating topic. Also see the Click Examples use of page templating.

6.6. Menus

For many applications using the Menu control to centralize application navigation is very useful. Menus are defined in a WEB-INF/menu.xml file which is very easy to change.

A menu is typically defined in the a page border template so they are available through out the application. The Menu control does not support HTML rendering, so you need to define a Velocity macro to programmatically render the menu. You would call the macro in your border template with code like this:

#writeMenu($rootMenu)

An advantage of using a macro to render your menu is that you can reuse the code across different applications, and to modify an applications menu you simply need to edit the WEB-INF/menu.xml file. A good place to define your macros is in the webroot /macro.vm file as it is automatically included by Click.

Using macros you can create dynamic menu behaviour such as only rendering menu items a user is authorized to access with isUserInRoles().

#if ($menu.isUserInRoles())
   ..
#end

You can also use JavaScript to add dynamic behaviour such as drop down menus, for example see the Menu page in Click Examples.

6.7. Logging

For application logging you should use one of the well established logging libraries such as Java Util Logging (JUL) or Log4J.

The library you use will largely depend upon the application server you are targeting. For Apache Tomcat or RedHat JBoss the Log4j library is a good choice. While for the IBM WebSphere or Oracle WebLogic application servers Java Util Logging is better choice as this library is better supported.

If you have to target multiple application servers you should consider using the SLF4J library which uses compile time bindings to an underlying logging implementation.

As a general principle you should probably avoid Commons Logging because of the class loading issues associated with it. If you are using Commons Logging please make sure you have the latest version.

It is a best place to define a logger method in a common base page, for example:

public class BasePage extends Page {

    protected Logger logger;

    public Logger getLogger() {
        if (logger == null) {
            logger = Logger.getLogger(getClass());
        }
        return logger;
    }
}

Using this pattern all your application bases should extend BasePage so they can use the getLogger() method.

public class CustomerListPage extends BasePage {

    public void onGet() {
        try {
            ..
        } catch (Exception e) {
            getLogger().error(e);
        }
    }
}

If you have some very heavy debug statement you should possibly use an isDebugEnabled switch so it is not invoked if debug is not required.

public class CustomerListPage extends BasePage {

    public void onGet() {
        if (getLogger().isDebugEnabled()) {
            String msg = ..

            getLogger().debug(msg);
        }

        ..
    }
}

Please note the Click logging facility is not designed for application use, and is for Click internal use only. When Click is running in production mode it will not produce any logging output. By default Click logs to the console using ConsoleLogService.

If you need to configure Click to log to an alternative destination please configure a LogService such as JdkLogService, Log4JLogService or Slf4jLogService.

6.8. Error Handling

In Click unhandled errors are directed to the ErrorPage for display. If applications require additional error handling they can create and register a custom error page in WEB-INF/click.xml. For example:

<pages package="com.mycorp.page" autobinding="annotation"/>
  <page path="click/error.htm" classname="com.mycorp.page.ErrorPage"/>
</pages>

Generally applications handle transactional errors using service layer code or via a servlet Filter and would not need to include error handling logic in an error page.

Potential uses for a custom error page include custom logging. For example if an application requires unhandled errors to be logged to an application log (rather than System.out) then a custom ErrorPage could be configured. An example ErrorPage error logging page is provided below:

package com.mycorp.page.ErrorPage;
..

public class ErrorPage extends org.apache.click.util.ErrorPage {

    public void onDestroy() {
        Logger.getLogger(getClass()).error(getError());
    }
}

6.9. Performance

Yahoo published a list of best practices for improving web application performance.

Click Framework provides a PerformanceFilter which caters for some of these rules. However not all rules can be easily automated.

This section outlines how to apply some important rules which are not covered by the PerformanceFilter namely, Minimize HTTP Requests (by combining files) and Minify JavaScript and CSS.

The Rule, Minimize HTTP Requests, also mentions CSS Sprites, a method for combining multiple images into a single master image. CSS Sprites can boost performance when your application has many images, however it is harder to create and maintain. Note that CSS Sprites is not covered here.

It is worth pointing out that its not necessary to optimize every page in your application. Instead concentrate on popular pages, for example a web site's Home Page would be a good candidate.

There are a couple of tools that are useful in applying the rules "Minimize HTTP Requests" and "Minify JavaScript and CSS":

  • YUICompressor - minifies and compresses JavaScript and CSS files so less bytes have to be transferred across the wire.

  • Ant Task for YUICompressor - an Ant task that uses YUICompressor to compress JavaScript and CSS files.

  • JSMin - similar to YUICompressor but only minifies (remove whitespace and newlines) JavaScript files and does no compression at all. An advantage of JSMin over YUICompressor is that its faster and can be used at runtime to minify JavaScript, while YUICompressor is most often used at build time.

Below are some articles outlining how to use YUICompressor and Ant to concatenate and compress JavaScript and CSS files:

  • Article explaining how to use Ant and YUICompressor for compression.

  • Article outlining how to use a special YUICompressor Ant Task for compression.

Using one of the approaches above you can concatenate and compress all JavaScript and CSS for your Pages into two separate files, for example home-page.css and home-page.js. Note that the two files must include all the JavaScript and CSS that is generated by the Page and its Controls. Then you can instruct Click to only include the two compressed files, home-page.css and home-page.js.

The Click Page class exposes the property includeControlHeadElements that indicates whether Controls have their CSS and JavaScript resources included or not.

To optimize Page loading one can override Page.getHeadElements(), and import the JavaScript and CSS files and then set the property includeControlHeadElements to false, indicating that Controls won't contribute their own JavaScript and CSS resources.

Here is an example:

public class HomePage extends Page {

    private Form form = new Form("form");

    public void onInit() {
        // Indicate that Controls should not import their head elements
        setIncludeControlHeadElements(false);

        form.add(new EmailField("email");
        addControl(form);
    }

    public List getHeadElements() {
        if (headElements == null) {
            headElements = super.getHeadElements();

            headElements.add(new CssImport("/assets/css/home-page.css"));
            headElements.add(new JsImport("/assets/js/home-page.js"));
        }
        return headElements;
    }
}

Using the following border-template.htm:

<html>
  <head>
    <title>Click Examples</title>
    ${headElements}
  </head>
  <body>

  ...

  ${jsElements}
  </body>
</html>

the rendered HTML will include one CSS and one JavaScript import:

<html>
  <head>
    <title>Click Examples</title>
    <link type="text/css" rel="stylesheet"
          href="/click-examples/assets/css/home-page.css" title="Style"/>
  </head>
  <body>

  ...

  <script type="text/javascript" src="/click-examples/assets/js/home-page.js"></script>
  </body>
</html>

A live demo is available here