Tobago aids developers in creating web applications with a consistent look and feel. Therefore it provides the developer with a collection of comfortable high-level components, which can be positioned with a layout manager. The goal is to approximate the appearance of classical desktop applications. Tobago is based on a strict separation of structure and design. The developer describes the structure of a page and which controls it contains, the representation of the page and its style is handled by Tobago. The developer can customize the design and style through themes. Currently Tobago only contains themes for HTML clients. To achieve output independence, the views should be developed without any HTML, CSS, or JavaScript. A Tobago page normally contains only JSF and Tobago tags. Features, styling and design via HTML, CSS, or JavaScript are handled by the theme.
The development of Tobago started in 2002. With the introduction of the JSF standard it was decided to base Tobago on JSF. By 2005, Tobago was released as an open-source project and became a sub project of Apache MyFaces in 2006.
We will be referencing a simple address book application throughout this chapter to demonstrate the various features of Tobago.
Tobago is a constantly maturing project with rich potential to fill a unique niche in the MyFaces family — feel free to sign up on the MyFaces mailing lists[1] or sign up to the JIRA[2] and participate in the process. Additionally, you can also check out the MyFaces project website to find out about future releases.
The Tobago project contains several examples like the demo, the address book and the blank example. The demo example demonstrates many of the important features of Tobago and acts as the online documentation. The address book example is a small self-contained web application. Finally, there is the blank example, which is a minimal Tobago application that can act as a starting point for a new application. The Tobago download page links to the example archive, which contains a precompiled web application archive (WAR) for the blank example and the demo. The source for the blank example can be obtained from the Subversion repository. Checking out the blank example for Tobago is achieved by entering the following command in the shell:
svn checkout \ http://svn.apache.org/repos/asf/myfaces/tobago/trunk/example/blank/ \ blank
For building the blank example Java 5 and Maven 2 (at least 2.0.6) are needed. Running Tobago applications in a Java 1.4 environment is supported, too. The Tobago distribution contains bytecode transformed jars, which were generated with Retrotranslator.
A Tobago application is a standard web application and needs a web
application descriptor (WEB-INF/web.xml). As a JSF application the web.xml
file has to include a servlet definition for the FacesServlet and a
corresponding servlet mapping. During development it is convenient to
serve the resources for Tobago like images, scripts, and style sheets
directly out of the theme jars. To accomplish this, the web.xml file needs
to contain the definition for the Tobago ResourceServlet and the
corresponding servlet mapping. For a production environment it is advised
to extract the theme resources and let the servlet container or web server
serve these resources directly — see the section called “Advanced Assembly”.
The JSF specific configuration for Tobago is contained in the
faces-config.xml file inside the Tobago core jar. To make the
configuration available for JSF the tobago-core.jar has to be placed into
the web application. The Tobago specific configuration is defined in the
WEB-INF/tobago-config.xml file. A minimal configuration should at least
specify the default theme for the Tobago application.
<tobago-config>
<theme-config>
<default-theme>speyside</default-theme>
</theme-config>
</tobago-config>A Tobago web application needs to package many libraries. First of
all, the Tobago core jar and theme jars should be made available in the
application. Some themes depend on each other, for example to be able to
use the Speyside theme the Scarborough theme and Standard theme have to be
included as well. These dependencies are defined in the
META-INF/tobago-theme.xml files inside the theme jars.
Tobago depends on several Jakarta Commons libraries:
commons-beanutils
commons-collections
commons-digester
commons-fileupload
commons-io
commons-lang
commons-logging
Additionally a JSF implementation has to be selected. For MyFaces the following additional libraries have to be included:
myfaces-api
myfaces-impl
commons-codec
commons-el
jstl
Finally, a logging mechanism has to be selected. All Tobago examples use log4j as a default.
To use JSP as a rendering technology Tobago provides two tag libraries which contain the definition of available JSP tags — the Tobago core library and the extension library. The tag library definitions (TLD) for these libraries contain documentation for the tags and define the available required and optional attributes. IDEs can leverage this information to help the developer construct JSP pages.
A basic Tobago page looks like this:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://myfaces.apache.org/tobago/component" prefix="tc" %>
<%@ taglib uri="http://myfaces.apache.org/tobago/extension" prefix="tx" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<f:view>
<tc:page>
<f:facet name="layout">
<tc:gridLayout/>
</f:facet>
<tc:out value="Hello World"/>
</tc:page>
</f:view>The Tobago project leverages Maven for all build management and
Maven is the easiest way to build a web application with Tobago. The
project descriptor (pom.xml) or project object model (POM) of Tobago
defines all necessary dependencies and the Maven repository server and
its mirrors provide the appropriate jars. For more information on Maven
see the Maven web site[3]
and the book Better builds with Maven[4].
The POM for the blank example specifies the packaging of the
project as "war". The dependencies for the web application are managed
by the <dependencies> element in the POM Maven automatically takes
care of the dependencies through its transitivity mechanism. The POM
comprises all necessary information needed to compile and package a web
application archive. Use the following command in the shell:
mvn package
This results in a WAR archive in the target directory, where Maven stores its build artifacts and intermediary files. Moreover Maven can be used to deploy the WAR on an application server as well. The Tobago example POM, which is the parent POM of the blank example, contains the necessary information to deploy a WAR on an embedded Jetty server. By executing
mvn jetty:run-exploded
a Jetty server is started via Maven and the WAR is deployed on
this servlet container. The server listens on the port 8080 and the web
application can accessed under the URL
http://localhost:8080/tobago-example-blank/.
Tobago makes use of the extensibility of JSF to achieve its decoupling from the rendering tier. It provides its own tag library, components and render kit. Besides the JSP tag library, an alternative rendering technology called Facelets can be used, too. The extension folder in the Tobago repository contains the support library to use Facelets instead of JSP.
Because of the strict separation between structure and design, it is possible to use an application with different themes. This allows the developer to execute applications that can render different corporate designs for different portals while keeping the view source unchanged. This mechanism is easier than adapting plain CSS in the sense that various stylesheet themes/skins have to be referenced and loaded based on environmental parameters.
Besides the basic input controls HTML provides, Tobago offers additional high level controls, which a traditional desktop application developers would recognize. These controls will be described in the following sections.
Tobago also contains a deprecated tree control. The API of this tree control does not really fit to the other controls. But the sandbox already contains a version of the future tree control, which will be introduced in Tobago 1.1.
HTML offers a large set of input controls which form the basis of Tobago's controls. This base includes single line text input fields, text areas, labels, radio buttons, check boxes, links, buttons, and so on. The Tobago demo shows these controls in action. A precompiled WAR for the demo is part of the example distribution and the Tobago web site links to a server with a live version. See Figure 1, “Basic controls”:
A single line input control can be rendered with the
<tc:in> tag. The extended version can be accessed with
<tx:in>. In general the extension library, which is usually
referenced by the tx prefix, provides convenience variants of
controls. For the <tc:in> control the extended version
<tx:in> contains boilerplate code for rendering labels. For form
like input views nearly every input control has a corresponding label.
The label is connected to the input control. If the label is clicked
the input control gains focus.
<tx:in
label="#{bundle.name}"
value="#{address.name}"
required="false" readonly="false"
disabled="false" rendered="true" />The label attribute determines the textual description for the
control. It is laid out with a grid layout manager. The theme
specifies the default label width, but it can be overwritten with the
labelWidth attribute. If the label contains an underscore character
the following character will become an access key. This is visualized
by underlining the access key character. If the access key is pressed
together with the Alt key the corresponding input field gains focus.
The value attribute contains the content of the input control. The
required attribute controls validation and allows the theme to render
a special marker for required input. The required feature is rendered
as a small box with a check mark inside the input field to inform the
user to enter information into this field. Read-only controls do not
allow the user to modify the value of the input control. A disabled
control cannot gain focus, the label is rendered in a fashion to
highlight the disabled nature of the input control and the user cannot
copy content from the input control. The rendered attribute manages if
the control is rendered at all. If the control is not rendered the
layout manager can distribute the resulting space to other controls.
For password fields the respective attribute can be set to true. If a
page contains multiple input controls the first control will be
focused by default. This behavior can be overwritten by setting the
focus attribute of an input control to true.
Tobago supports different ways to use commands. The basic
versions are <tc:button> and <tc:link>; others include
toolbars and menus, which are described in later sections.
<tc:button label="Delete" action="#{controller.delete}"
image="image/delete.png" defaultCommand="false">
<f:facet name="confirmation">
<tc:out value="Do you want to delete it?" />
</f:facet>
</tc:button>The label attribute defines the text on the button. The action
attribute points to a method which is executed if the button is
pressed. By means of the image attribute the button can be decorated
with an icon. The image can be placed relatively to the root of the
web application or the resource manager can be used to locate the
image — see the section called “Resource Management”. The <tc:button>
control supports the confirmation facet, which generates a message
dialog. Only if the confirmation question is answered with OK, the
action is executed. If the defaultCommand attribute is set to true the
button will be activated as soon as the Enter key is pressed.
Tobago includes a double request prevention mechanism. After a
button or link is clicked in the client, the page is blocked to avoid
duplicate clicks. If the server request takes longer than expected a
transitioning effect is shown. First the page is faded out and later a
progress animation is presented. To turn this effect off the
transition attribute can be set to false.
A generic <tc:command> control can be used for event
facets for select controls like <tc:selectBooleanCheckbox>,
<tc:selectOneRadio>, <tc:selectManyCheckbox>, and
<tc:selectOneChoice>. This is how the theme changing in the
footer of the address book example is realized. If a new theme is
selected, a change event is triggered, the page is submitted, and the
action of the <tc:command> inside the change facet is called:
<tx:selectOneChoice label="#{bundle.footerTheme}" value="#{controller.theme}">
<f:selectItems value="#{controller.themeItems}" />
<f:facet name="change">
<tc:command action="#{controller.themeChanged}"/>
</f:facet>
</tx:selectOneChoice>Besides the change event select controls also support the click event. The click event is triggered if someone clicks on the control. The actual value does not need to change to trigger the event.
The <tc:sheet> component allows to display tabular data. The
address book uses it to provide an overview of all stored
addresses.
<tc:sheet columns="1*;1*;1*" value="#{controller.currentAddressList}"
var="address" state="#{controller.selectedAddresses}"
sortActionListener="#{controller.sheetSorter}" rows="25"
showRowRange="left" showPageRange="right" showDirectLinks="center">
<tc:column id="firstName" label="#{bundle.listFirstName}" sortable="true"
rendered="#{controller.renderFirstName}">
<tc:out value="#{address.firstName}" />
</tc:column>
<tc:column id="lastName" label="#{bundle.listLastName}" sortable="true"
rendered="#{controller.renderLastName}">
<tc:out value="#{address.lastName}" />
</tc:column>
<tc:column id="dayOfBirth" label="Birthday" sortable="true"
rendered="#{controller.renderDayOfBirth}">
<tc:out value="#{address.dayOfBirth}">
<f:convertDateTime pattern="#{bundle.editorDatePattern}" />
</tc:out>
</tc:column>
</tc:sheet>The value attribute links to a list model in the controller
providing the data for the sheet. The <tc:sheet> contains three
<tc:column> tags which describe the columns of the sheet. The
label of the column is rendered as a header cell. The var attribute
inside <tc:sheet> defines a local variable address, which refers
to a row in the data model and can be used in the definition of the
columns.
In the example, each column uses a <tc:out> tag to render
the data for the sheet cell by accessing the appropriate property of the
row object. Instead of <tc:out> arbitrary input controls can be
used like <tc:in>, <tc:selectBooleanCheckbox>, or
<tc:selectOneChoice>.
The various attributes of the sheet which start with 'show'
configure the navigational elements of the sheet — showDirectLinks for
example allows the user to directly jump to the desired page of the
sheet.
The state attribute refers to a SheetState object. Tobago binds
information about the state of the sheet to this object — like which
rows are selected. This allows the developer to react on the selection
inside the business logic. In the address book example there is a
toolbar above the sheet. The toolbar contains a delete action among
others. This delete action is dependent on the selected rows in the
sheet. The attribute selectable of the sheet controls the selection mode
for the sheet. Possible values are none, single, and multi. The default
value is multi and allows multiple rows to be selected. The following
code fragment show the method bound to the delete button and describes
how to access the selected rows:
public String deleteAddresses() throws AddressDaoException {
List<Integer> selection = selectedAddresses.getSelectedRows();
if (selection.size() < 1) {
FacesMessage error
= new FacesMessage("Please select at least one address.");
FacesContext.getCurrentInstance().addMessage(null, error);
return null;
}
for (int i = selection.size() - 1; i >= 0; i--) {
Address address = currentAddressList.get(selection.get(i));
addressDao.removeAddress(address);
}
// ...
return OUTCOME_LIST;
}The sortable attribute of the <tc:column> activates sorting
for the related column. If the data for the sheet is a List or an array
the data can be sorted implicitly. The sortActionListener attribute
allows implementation of sorting in the business logic. The respective
method binding has to point to a public action listener method which
takes an ActionEvent as a parameter and returns void. The method will
receive a SortActionEvent, which denotes the column triggering the sort
event. However, information about the sort direction is contained in the
SheetState.
The <tc:tabGroup> control renders different content on the
same area of the view via tab panels. The switching behavior of the
panels can be controlled with the switchType attribute. The panels can
be switched on the client or the server. If the switching is performed
on the server, Tobago can be instructed to only partially exchange the
content of the page making use of Ajax. If the switching is done on the
client it is faster, but the content of the page is bigger, because the
rendering information of all tab panels has to be transferred to the
client at once.
<tc:tabGroup switchType="reloadTab" immediate="true">
<tc:tab label="#{bundle.editorTabPersonal}">
<jsp:include page="tab/personal.jsp"/>
</tc:tab>
<tc:tab label="#{bundle.editorTabBusiness}" rendered="#{!controller.simple}">
<jsp:include page="tab/business.jsp"/>
</tc:tab>
<tc:tab label="#{bundle.editorTabMisc}" rendered="#{!controller.simple}">
<jsp:include page="tab/misc.jsp"/>
</tc:tab>
</tc:tabGroup>In the example application, two of the tab panels are only rendered if a certain condition is met in the controller. These particular tab panels are only rendered if the application is in the expert mode.
To mimic the appearance of a desktop application it is common to
place a menu bar at the top of the page. This can be done with the
<tc:menuBar> tag inside the menuBar facet of the <tc:page>.
A menu bar can contain <tc:menu> tags, which can be nested to
produce sub menus. Actions can be bound with method bindings to
<tc:menuItem> tags. A menu item can be disabled and can
encapsulate icons. An underscore in the label marks the following
character as an access key, which can be activated together with the Alt key.
In the address book example the settings menu contains single
selections created by <tx:menuRadio> to choose the current theme
and language. Additionally it demonstrates how to use a check box menu
item <tc:menuCheckbox> to change the mode of the
application.
<tc:menuBar id="menuBar">
<tc:menu label="_File">
<tc:menuItem label="_New" action="#{controller.createAddress}" image
="image/org/tango-project/tango-icon-theme/16x16/actions/contact-new.png"/>
<tc:menuItem label="_Add Dummy Addresses"
action="#{controller.addDummyAddresses}"/>
<tc:menuSeparator/>
<tc:menuItem label="_Logout" image
="image/org/tango-project/tango-icon-theme/16x16/actions/system-log-out.png"/>
</tc:menu>
<tc:menu label="_Settings">
...
<tc:menu label="_Theme">
<tx:menuRadio action="#{controller.themeChanged}"
value="#{controller.theme}">
<f:selectItems value="#{controller.themeItems}"/>
</tx:menuRadio>
</tc:menu>
<tc:menuCheckbox label="Simple _Mode" value="#{controller.simple}"/>
</tc:menu>
...
</tc:menuBar>The <tc:toolBar> tag renders a group of buttons. The tool
bar can be configured to render a textual description of the action or
an icon in different standard sizes. The buttons are created with the
<tc:toolBarCommand> tag, which is a slightly limited version of
the standard button tag.
<tc:toolBar iconSize="big">
<tc:toolBarCommand label="#{bundle.toolbarAddressList}"
action="#{controller.search}" immediate="true" image=
"image/org/tango-project/tango-icon-theme/32x32/mimetypes/x-office-address-book.png"
disabled="#{facesContext.viewRoot.viewId == '/application/list.jsp'}"/>
...
</tc:toolBar>In the address book example the first toolbar button navigates to
the address list. If the address list is already the current view the
button is disabled. There is a naming convention to provide disabled
versions for image resources by adding an additional Disabled before the
file extension. The disabled version has to reside in the same folder as
the original image. In the example the resource manager can find a black
and white icon x-office-address-bookDisabled.png as a disabled version
of the normal address book icon.
A popup is a small modal dialog, which is displayed in the context
of the current page. A general use case for a popup is entering new data
and confirming or canceling the new data. Popups can be activated by
adding a popup facet to commands. Alternatively a popup can be activated
programmatically by setting the rendered attribute to true. In this way
you can use a popup as a message box.
In the address book example the displayed columns for the address list can be controlled via a popup. This popup is bound to a button in the toolbar for the sheet.
<tc:button label="Open Popup">
<f:facet name="popup">
<tc:popup width="300" height="270">
<tc:box label="Popup Title">
...
<tc:panel>
<f:facet name="layout">
<tc:gridLayout columns="*;fixed;fixed"/>
</f:facet>
<tc:cell/>
<tc:button label="OK">
<tc:attribute name="popupClose" value="afterSubmit"/>
</tc:button>
<tc:button label="Cancel">
<tc:attribute name="popupClose" value="immediate"/>
</tc:button>
</tc:panel>
</tc:box>
</tc:popup>
</f:facet>
</tc:button>The typical semantics of the confirmation
and cancel button can be
controlled via a <tc:attribute> with the name popupClose. This
attribute can have two different values: afterSubmit or immediate. Both
buttons close the popup, but only the afterSubmit variant stores the
entered data into the model.
A file select control can be created with <tc:file>. The
uploaded file is stored inside a FileItem object bound to the value
attribute. The class FileItem is provided by commons-fileupload.
The address book allows the user to store photographs associated with the contacts. The user can click on an empty image placeholder to open a popup with the file select control which is created by the following code fragment:
<tc:file value="#{controller.uploadedFile}" required="true">
<tc:validateFileItem contentType="image/*"/>
</tc:file>With the validator <tc:validateFileItem> the content type or
maximum size of the uploaded file can be restricted. For security
reasons the style of an HTML file select control (input field of type
file) can only be customized in a very limited way. Therefore the input
control for the file name and the upload button may not optimally fit to
the theme.
To handle multipart form data requests from the browser either the
TobagoMultipartFormdataFilter servlet filter has to be configured in the
web.xml or the tobago-fileupload.jar has to be added to the classpath.
The address book demo uses the latter approach, which is based on a
FacesContextFactory defined in the included faces-config.xml of the jar.
The context factory can be configured with two different
<env-entry> tags in the web.xml to control maximum upload limit
and the directory for uploaded files. See the Javadoc of
FileUploadFacesContextFactoryImpl for more information.
The placement of components on a page is done with the help of a
layout manager. The functionality of a layout manager is similar to those
found in Swing. The standard layout manager for Tobago is
<tc:gridLayout>. It can be bound to a container tag with a layout
facet.
The grid layout manager divides the area of the page into a
rectangular grid. This grid can be controlled with the rows and columns
attributes of the <tc:gridLayout> tag. The syntax is similar to the
multi-length notation known from HTML[5] and consists of a semicolon
separated list of layout tokens.
A layout token may be an absolute length in pixels like 100px, a
percentage length like 50%, a relative length like 2* or * as a shorthand
for 1*, or the value fixed. Relative lengths are served by the layout manager
from the remaining available space, which is distributed proportionally
based on the number before the *.
If the layout manager has 400 pixels to lay out the columns with the
following layout tokens 2*;*;100px, it first claims 100px for the third
column and distributes the remaining 300 pixels in the ratio 2:1, ending
up in 200 pixel for the first column and 100 pixels for the second column.
The layout token fixed instructs the layout manager to take the required
pixel size of the contained control. A <tx:in> control normally has
a fixed height. To place multiple <tx:in> controls one below the
other without superfluous spacing the layout manager can be instructed
with fixed layout tokens to use exactly the required vertical space for a
<tx:in>. For a more concrete example see the following code fragment
based on the editor view of the address book:
<tc:panel>
<f:facet name="layout">
<tc:gridLayout columns="*" rows="fixed;fixed;fixed;*"/>
</f:facet>
<tf:in ... />
<tf:in ... />
<tf:in rendered="#{! controller.simple}" ... />
<tf:textarea ... />
</tc:panel>The grid consists of one column and four rows. The first three rows use a fixed height which is implied by the theme and contained controls. The fourth row takes the remaining vertical space.
One of the main reasons to use a layout manager is its ability to optimally manage the available space. The layout manager knows, for example, if controls have a fixed height and which can grow if there is enough space.
Since the layout manager is targeted specifically at JSF it can
flexibly react on the rendered attribute of components. If the address
book application is in simple mode some of the components are not
rendered. The layout manager automatically distributes the newly available
space to the remaining dynamic components, which are laid out with
relative or percentage lengths.
If a control should utilize multiple adjacent grid cells, it can be
wrapped with a <tc:cell> tag. With the spanX and spanY attributes of
a <tc:cell> control the layout manager can be instructed to make the
contained control span multiple cells in X and Y direction.
Tobago allows the developer to specify the structure of a page. Additionally, the look of a page is controlled by the selected theme. A theme defines the colors, dimensions, behavior, and graphics of controls. Tobago comes with a selection of themes. These are Scarborough, Speyside, Charlotteville, and Richmond — named after settlements on Tobago. Scarborough is the basic theme which tries to directly rely on the standard features of HTML. Speyside is the main theme, where most development focus is targeted at. You will want to use this theme to start a new web application with. The remaining themes Charlotteville and Richmond are mainly variations of Speyside.
Themes can be used to make an application follow the corporate
design of a company or give the user the ability to change the look and
feel of an application to their preferences. The address book example
demonstrates how to make the theme selectable by the user. A select box
displays the available themes, which are configured in the
tobago-config.xml file, and a button triggers an action in the controller
to set the theme in the Tobago ClientProperties object:
public String themeChanged() {
FacesContext facesContext = FacesContext.getCurrentInstance();
ClientProperties client
= ClientProperties.getInstance(facesContext);
client.setTheme(theme);
return null;
}For Tobago, resources are images, style sheets, scripts, and string resources. These resources can be dependent on the locale, browser, or the theme. The resource manager collects all resource locations. When a resource is requested, the resource manager determines the inclusion order. A resource can be available in different themes and multiple locales. The resource manager first looks under the selected theme with the used browser and locale. If the resource is not found, the fallback locale is used to continue the search. After all fallback locales are examined the fallback for the browser is used and the locales are searched again. After all fallback browsers are searched the fallback for the theme is used and the search starts again with the locales until all fallback themes are processed. The result is cached for later reuse.
For resources such as images, the resource manager stops with the first match. For style sheets and scripts the resource manager returns a list of resources in the order they were found. This establishes a powerful defaulting mechanism.
By relying on the resource manager the developer can provide localized images with different text values for a button. The locale of the view is used to determine the correct language version of the image. The resource manager supports the XML format for property files, easing the use of special characters in string resources. The evaluation of the theme in inclusion order can be used to support different corporate wordings. If each client has its own theme string resources for the different themes can arrange the words in different ways — for example: email, e-mail, or eMail.
In the tobago-config.xml file, additional paths can be specified
for inclusion by the resource manager. In this way you can add your own
resources or even overwrite or extend existing resources. But changing
existing themes this way may result in compatibility issues when
switching to a newer version of Tobago, since the theme files are not
stable yet and do not count as an official external API.
<tobago-config>
<theme-config>
<default-theme>speyside</default-theme>
</theme-config>
<resource-dir>tobago-resource</resource-dir>
</tobago-config>The resource directory denotes a folder inside the WAR relative to the root. Paths for resources have to follow this pattern:
<content-type>/<theme>/<browser>/<directory>/<resource-name>(_<locale>)?.<extension>
Currently only html is supported as a content type, although the
sandbox contains the beginnings for WML support. For the address book
example there are two variants of an empty portrait with instructions
how to upload a real portrait.
tobago-resource/html/standard/standard/image/empty-portrait.png tobago-resource/html/standard/standard/image/empty-portrait_de.png
The first image is the default image with instructions in English.
In the second image the instructions are localized in German. The first
standard in the path denotes the fallback theme and the second standard
represents the fallback browser. The directory part in the pattern
stands for an arbitrary sub-path — in this example for the folder
image.
Several controls like <tc:in>, <tc:panel>, or
<tc:column> support the markup attribute. It allows the developer
to apply logical styles to a control. Which markup values are supported
is defined per theme in the theme-config.xml file. If a theme does not
define supported markup for a renderer, it inherits the markup from the
fallback theme. The Standard theme defines number for <tc:in> and
<tc:out>, and strong and deleted for <tc:out>. The markup
number is usually used to format numbers right aligned, strong is used
to emphasize content, and deleted marks text as no longer available
normally by crossing it out. The visual representation of markup is up
to the theme and therefore it will fit to the overall look and feel of
the theme.
To add new markup you can write your own theme. Choose the theme you want to extend and specify the selected theme as fallback theme. The Example theme extends Speyside and adds markup for columns.
<tobago-theme>
<name>example</name>
<deprecated-name>
org.apache.myfaces.tobago.context.ExampleTheme
</deprecated-name>
<resource-path>org/apache/myfaces/tobago/renderkit</resource-path>
<fallback>speyside</fallback>
<renderers>
<renderer>
<name>Column</name>
<supported-markup>
<markup>new</markup>
</supported-markup>
</renderer>
</renderers>
</tobago-theme>To realize the visualization of the markup Tobago will add certain
CSS style classes into the generated HTML for the marked control. The
theme has to provide the styling information. The style class name
results from the following naming rule "tobago-" +
rendererName.toLower() + "-markup-" + markupName:
.tobago-column-markup-new {
background-color: yellow;
}For the Example theme it was decided to render a new column with a yellow background.
The address book example uses a different way to add markup. In
this case three markup values — ok, warn, and error — are defined for
the <tc:progress> control directly in the tobago-config.xml file.
The administration area contains a progress bar to visualize the memory
utilization of the virtual machine. Depending on the percentage value of
the memory usage the business logic assigns different markup values for
the progress bar to classify the state of the memory utilization.
The resource directory contains style sheets for visualizing the
markup values as different background colors — green for ok, yellow for
warn, and red for error. The Speyside theme for example is extended by
the html/speyside/standard/style/style.css file.
The ideal place to start when you want to create your own Tobago control is the sandbox. To access the sandbox you have to check it out:
svn checkout \
http://svn.apache.org/repos/asf/myfaces/tobago/trunk/sandbox \
sandboxAlternatively if you have checked out the complete Tobago source tree you can find the sandbox directory right under the root directory. In this section we will create an HTML control in the sandbox as an example. The new control will be a slider for entering integer numbers.
This control consists of a slider bar with an attached number input field. If you move the slider the number field is changed accordingly and vice versa. Figure 10, “Slider control for entering numbers” shows a preview of the control. You can find the complete code in the Tobago sandbox.
We are starting with the user interface component of the control
and will name it:
UIInputNumberSlider[6]. Like all JSF
components, Tobago components must extend
UIComponent[7]. Because we want to create an input
component we can extend UIInput[8] which is a (non
direct) subclass of UIComponent. The class UIInput
is already an implementation of
EditableValueHolder[9] which saves us a lot of
work.
The JSF runtime environment needs the component type as an
identifier for creating instances of components. Tobago components need
to store this information in the component itself in the constant
COMPONENT_TYPE. This constant is processed by the Tobago annotation
visitor that is used in the build process to create the faces-config.xml
file.
We also implement some additional properties into our component:
min, max, readonly and disabled. The property min specifies the smallest
and the property max the largest number that can be entered. With the
property readonly set the user cannot modify the value of the slider.
The same is true for the property disabled. But in this case the
complete control appears deactivated. All properties should be value
binding enabled which makes their getters a little bit more complicated.
To ensure that our component state is saved between requests we have to
override the state holder methods of UIInput.
The following code shows some parts of our component class:
public class UIInputNumberSlider extends javax.faces.component.UIInput {
public static final String COMPONENT_TYPE
= "org.apache.myfaces.tobago.InputNumberSlider";
private Boolean readonly;
private Boolean disabled;
private Integer min;
private Integer max;
public Boolean isReadonly() {
if (readonly != null) {
return readonly;
}
ValueBinding vb = getValueBinding(TobagoConstants.ATTR_READONLY);
if (vb == null) {
return false;
} else {
return (Boolean.TRUE.equals(vb.getValue(getFacesContext())));
}
}
public void setReadonly(Boolean readonly) {
this.readonly = readonly;
}
...
public void restoreState(FacesContext context, Object state) {...}
public Object saveState(FacesContext context) {...}
}There are two ways in JSF to render a component. The first one is to implement the encoding and decoding between UI component and the view in the UI component directly. This is called the direct implementation programming model. The second method is to delegate the task to an appropriate renderer which is called the delegating programming model. The delegated programming model keeps your components independent from the view technology and is preferred for Tobago components.
A renderer in JSF must be a subclass of type
Renderer[10]. For Tobago components this is different:
their renderers must also implement the interface
LayoutInformationProvider[11] to get the
renderer to work with the Tobago layout management. With this interface
Tobago renderers provide the layout manager with certain information
about the sizing of the component that is rendered. The required methods
have a default implementation in the class
LayoutableRendererBase[12] which uses properties
that are provided by the theme configuration of the used theme
(tobago-theme-config.properties). We name our renderer
InputNumberSliderRenderer and extend from LayoutableRendererBase. Following the
Tobago naming convention the renderer must end with "Renderer".
When using the LayoutableRendererBase the theme configuration is a good
place to provide the information needed for the layout. The
implementation searches the configuration for properties with the
following pattern: renderer name without "Renderer" + "." + key. All
values are interpreted as pixels. Table 1, “Recognized keys for LayoutableRendererBase” specifies some of the
recognized keys.
Table 1. Recognized keys for LayoutableRendererBase
| Key | Description |
|---|---|
fixedWidth, fixedHeight | This is the preferred and normal size of a control. It is used if the value fixed is specified within the layout properties of a layout manager. |
paddingWidth, paddingHeight | The padding attributes add empty space between the control and its surroundings. |
headerHeight | Some controls like the <tc:box> control use this property to specify extra space for their header. |
If LayoutableRendererBase does not find a value for a renderer it searches
for default properties: "Tobago." + key. We could (but will not)
specify a width padding of five pixels for our control by adding the
property InputNumberSlider.paddingWidth=5 in the theme configuration of
the sandbox theme. Please note that the resource location mechanism as
described in the section called “Resource Management” is used to find a
property.
The Tobago theme configuration is not only used by RendererBase,
but properties of own renderers can be specified there. For example we
define the percentage of the width of the slider of our control in the
tobago-theme-config.xml: InputNumberSlider.sliderWidthPercent=66. This
means the slider gets 66 percent of the width of our control and the
input field 34 percent. The value can be accessed in the renderer with
the following line of code:
int sliderWidthPercent
= ThemeConfig.getValue(facesContext, component, "sliderWidthPercent");The Tobago theming mechanism locates renderers by their location
in the package structure. Normally all Tobago renderers are located
under the root package org.apache.myfaces.tobago.renderkit. Below this,
the location depends on the output technology, the theme and
browser.
We use the sub package html.sandbox.standard.tag because we are
writing an HTML control for the sandbox theme. Package standard means
that all browsers (Mozilla, Internet Explorer etc.) will be served by
this renderer. The last package tag is obligatory for historical
reasons.
The code for encoding/decoding in the renderer is quite long so
only some interesting fragments are shown in code snippet. All encoding
is done in the method encodeEnd. First the properties of the component
are retrieved. Then the response writer is used to generate the HTML
input field of our control. The response writer for Tobago components is
the TobagoResponseWriter[13] which is
created by the Tobago render kit. It provides the component developer
with additional convenience methods and handles escaping. The decoding
is done in the method decode and retrieves the value of the input field
back from the HTTP request.
Example 1. Renderer for the slider bar control
package org.apache.myfaces.tobago.renderkit.html.sandbox.standard.tag;
public class InputNumberSliderRenderer extends LayoutableRendererBase {
public void encodeEnd(FacesContext facesContext, UIComponent component)
throws IOException {
String currentValue = getCurrentValue(facesContext, component);
boolean readonly
= ComponentUtil.getBooleanAttribute(component, ATTR_READONLY);
boolean disabled
= ComponentUtil.getBooleanAttribute(component, ATTR_DISABLED);
TobagoResponseWriter writer
= HtmlRendererUtil.getTobagoResponseWriter(facesContext);
...
writer.startElement(HtmlConstants.INPUT);
String inputIdAndName = getIdForInputField(facesContext, component);
writer.writeNameAttribute(inputIdAndName);
writer.writeIdAttribute(inputIdAndName);
if (currentValue != null) {
writer.writeAttribute(HtmlAttributes.VALUE, currentValue, false);
}
writer.writeAttribute(HtmlAttributes.READONLY, readonly);
writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
writer.endElement(HtmlConstants.INPUT);
...
}
public void decode(FacesContext context, UIComponent component) {
UIInput uiInput = (UIInput) component;
...
String inputId = getIdForInputField(context, component);
Map requestParameterMap
= context.getExternalContext().getRequestParameterMap();
if (requestParameterMap.containsKey(inputId)) {
String newValue = (String) requestParameterMap.get(inputId);
uiInput.setSubmittedValue(newValue);
}
}
private String getIdForInputField(
FacesContext context, UIComponent component) {...}
}When the HTML output in the renderer is generated the question how to size the elements arises. It is not a good idea to hard code static width or height information because the layout manager determines how much space the control should occupy. But how can a renderer know how much space it should use?
Tobago creates a "style map" for each component and adds it to the components attributes. This style map contains values for the width and the height of the control determined by the layout manager. In the renderer this map can be accessed and the values can be taken into account when creating HTML elements. The following source snippet demonstrates the usage of the style map.
HtmlStyleMap style = (HtmlStyleMap) component.getAttributes().get("style");
int width = style.getInt("width");All JSF implementations must support JSP as page description language for JSF pages. This is also standard for writing Tobago pages. A different view technology that does not depend on JSP is also available — Facelets. To allow an author to use our control we need to define a JSP tag — a custom action. This is done by writing one class and one interface: the tag class itself and a tag declaration interface which is Tobago specific.
We start with the tag class. All JSP custom actions in JSF that
correspond to a UI component in the component tree must either subclass
UIComponentTag[14] or
UIComponentBodyTag[15] depending on whether they need
support for body content functionality or not. Our action has no body
content so we want to subclass UIComponentTag. Again there is an
opportunity to save some effort if we extend from
TobagoTag[16] which extends from
UIComponentTag. This class already implements the handling for the
properties readonly and disabled.
Our tag is named
InputNumberSliderTag[17] and
consists of four new properties (min, max, value and
valueChangeListener) with their getter/setters, an implementation of
getComponentType, a release method and a setProperties method.
Tag classes in Tobago are dumb gateways between the view technology (JSP) and the render independent UI component. No output should be generated in the JSP tag directly. This is done only in appropriate renderers. The advantage with this approach is that we can use a different view technology like Facelets without changing our code.
Next thing is the declaration of the tag. Therefore it implements
an interface
InputNumberSliderTagDeclaration[18].
This interface describes our tag and its attributes with annotations.
Tobago's build process uses this declaration to generate a
faces-config.xml and a tag library description (TLD) for our component
with the help of the annotation processing tool (apt). The interface is
annotated with a @Tag annotation with a name attribute. The specified
value "numberSlider" is the name of the JSP tag. The next annotation
@UIComponentTag makes it clear that the tag belongs to a component with
an associated renderer. The attributes rendererType and uiComponent
specify the type of the renderer and the class of the UI component.
Please note that the Tobago render kit will add the suffix "Renderer" to
the renderer type to find a matching renderer class.
After the interface, the properties that are defined for the tag
are annotated. By convention, the setter of the property is used. The
@TagAttribute annotation describes a property that is part of the JSP
tag. The @UIComponentTagAttribute annotation specifies a component
property. Our properties appear in both the tag and the component. With
the additional annotation attribute type we define the type of the
properties.
Tobago has many predefined tag attribute declarations. We make use
of them by extending the needed interfaces like IsReadonly or
HasValue:
Example 2. Tag declaration for the slider bar tag
@Tag(name = "numberSlider")
@UIComponentTag(rendererType = "InputNumberSlider",
uiComponent = "org.apache.myfaces.tobago.component.UIInputNumberSlider")
public interface InputNumberSliderTagDeclaration extends
HasIdBindingAndRendered, IsReadonly, IsDisabled,
HasValue, HasValueChangeListener {
@TagAttribute()
@UIComponentTagAttribute(type="java.lang.Integer", defaultValue="0")
void setMin(String min);
@TagAttribute()
@UIComponentTagAttribute(type="java.lang.Integer", defaultValue="100")
void setMax(String max);
}After performing all four steps that are needed to create a Tobago
control (UI component, renderer, tag and tag declaration) we are ready
to build. This is done with a simple mvn install in the sandbox
directory; the classes are compiled, the faces-config.xml and the tag
library description is generated, everything is packaged and stored in
the target directory. Depending on the Tobago version the package is
called something like tobago-sandbox-i.j.k.jar. When this jar file is
put into the classpath of a web application, the control can be used on
a JSP page. It is important to switch to the Sandbox theme in the Tobago
configuration.
<%@ taglib uri="http://myfaces.apache.org/tobago/sandbox" prefix="tcs" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<f:view>
...
<tcs:numberSlider value="#{controller.value}" min="0" max="200"/>
...
</f:view>We have not talked about the HTML layout of the control. There is a significant amount of work required for layouts when creating new Tobago controls, particularly with regard to browser compatibility and using Ajax. Additionally — there is also individual coding for every new control. Everything covered so far deals with aspects that are common to all Tobago component development.
The Tobago theming mechanism is not the only aspect that locates renderers with a predefined pattern. Almost everything (styles, images, scripts and properties) is organized within a tree structure and located in a similar way.
To enable security for a Tobago application you can either write
your own login mechanism or use the standard way provided by the servlet
specification. The later way has a small drawback because the name
attribute for an HTML input control normally cannot be controlled via JSF
or Tobago. For form based authentication the servlet specification
requires the input fields of a login dialog to have the names j_username
and j_password, respectively. Since we cannot influence the names of
<tc:in> controls directly we have to resort to a hack. We
subsequently change the rendered field names with JavaScript inside the
browser.
<tx:in id="j_username" label="Username"/>
<tx:in id="j_password" password="true" label="Password"/>
...
<tc:script onload="initLoginForm();">
function initLoginForm() {
var user = document.getElementById("page:j_username");
user.name = "j_username";
var pass = document.getElementById("page:j_password");
pass.name = "j_password";
var form = document.getElementById("page::form");
form.action
= "${pageContext.request.contextPath}/j_security_check";
}
</tc:script>The onload attribute instructs Tobago to execute the passed
JavaScript function after the HTML page was loaded.
Tobago provides an extension package tobago-security to secure
method bindings with annotations. Currently it is not sufficient to
include the jar in the classpath, since the order in which the
faces-config.xml files from libraries in the classpath are evaluated is
depending on the JSF implementation. The faces-config.xml file of
tobago-security defines alternatives for command components with security
handling. The easiest way is to copy the component redefinitions into the
faces-config.xml file of the web application.
With the tobago-security package a method can be marked with
@RolesAllowed to designate the necessary roles for a business
functionality. The method binding will only be evaluated if the user has
the appropriate role. Likewise the class can be marked with @RolesAllowed
to secure all methods. In the address book example the Admin toolbar
button points to a method in the AdminController. This method is annotated
with the required role admin:
@RolesAllowed("admin")
public String admin() {
return OUTCOME_ADMIN;
}If the user of the application has not the role admin the button is
disabled and the method binding will not be evaluated. Additionally the
annotations @DenyAll and @PermitAll are supported. These security
annotations are part of the Common Annotations specification
(JSR250.)
The <tc:page> tag acts as a global form. Therefore, for simple
pages without control dependencies no explicit form has to be used. The
<tc:form> control allows nesting of forms and creates dependencies
between controls.
If a form is submitted only the contained model references are
updated, other values are temporarily stored. With <tc:form> partial
validation can be realized, because validation is limited to controls
inside a <tc:form>. As a result sub forms provide an alternative to
immediate for input controls. In the address book example changes to the
theme and the language are isolated to sub forms to avoid conflicts with
validations elsewhere on the page:
<tc:form>
<tx:selectOneChoice label="Theme" value="#{controller.theme}">
<f:selectItems value="#{controller.themeItems}" />
<f:facet name="change">
<tc:command action="#{controller.themeChanged}"/>
</f:facet>
</tx:selectOneChoice>
</tc:form>To avoid the reloading of complete pages, Tobago has a
renderedPartially attribute to update only parts of the page. For
<tc:tabGroup> controls this can be achieved by configuring the
switching type to reloadTab. Tobago also supports a more generic way to
update container controls like <tc:panel>, <tc:box>,
<tc:popup>, or <tc:sheet> using <tc:attribute> with the
name renderedPartially and a value with a comma separated list of
identifier paths of the respective containers. An identifier path is a
colon separated list of ids of nested naming containers. An absolute path
of ids has to begin with a colon character followed by the id of the
<tc:page> control. If the path does not start with a colon it is a
relative path from the current naming container. By using multiple colon
characters at the beginning of the path parent naming containers can be
accessed. The action of the command has to return null as an outcome,
because the current view has to be used again. Only a sub-tree of the view
is updated.
The following code fragment shows a simple example where an input control is enabled depending on the state of a checkbox.
<tc:page id="page">
<tc:box label="Container" id="box">
<tx:selectBooleanCheckbox label="Enable"
value="#{controller.miscEnabled}">
<f:facet name="change">
<tc:command>
<tc:attribute name="renderedPartially" value=":page:box"/>
</tc:command>
</f:facet>
</tx:selectBooleanCheckbox>
<tx:in label="Misc." disabled="#{!controller.miscEnabled}"/>
</tc:box
</tc:page>Instead of reloading the whole page, only the surrounding container
<tc:box> of the <tx:in> control is updated, if the value of
the checkbox changes. The absolute id path of the box, which should be
updated, is set as renderedPartially attribute of the command.
Additionally, <tc:panel> controls can be reloaded on a regular
basis to be able to display changes over time. To accomplish this, the
panel has to be provided with a reload facet. This facet has to contain a
<tc:reload> tag to specify the frequency for the reload in
milliseconds.
<tc:panel>
<f:facet name="reload">
<tc:reload frequency="5000"/>
</f:facet>
...
</tc:panel>The address book uses the reload facility on an administration page to regularly display the memory utilization of the virtual machine.
As described in the the section called “Getting Started”, themes can be served
directly out of the theme jars with the help of a special servlet.
Generally streaming static resources poses a slight overhead, but using
the servlet also provides a simple way to define HTTP expire headers for
the static resources. The expiration period can be specified in seconds as
an init-param for the servlet.
<servlet>
<servlet-name>ResourceServlet</servlet-name>
<servlet-class>
org.apache.myfaces.tobago.servlet.ResourceServlet</servlet-class>
<init-param>
<param-name>expires</param-name>
<param-value>14400</param-value>
</init-param>
</servlet>Instead of streaming the resources with a servlet they can be unpacked and supplied by the servlet container directly. Alternatively a web server like the Apache HTTP server can be set up in front of the servlet container. The web server can intercept the URLs for the static resources and serve them instead of the servlet container. If the themes are unpacked, only the resources should be unpacked not the class files or property files. But the jars still have to be put into the classpath in order to provide the necessary implementation files for the theme.
Besides the MyFaces JSF implementation Tobago also works with other JSF implementations like the reference implementation (RI) from Sun. The POM for the address book example provides three profiles for different JSF implementations. The default is MyFaces. There is an additional profile for the SUN RI. The third profile assumes that the container provides an implementation for JSF.
Tobago allows developing rich web applications with a rich set of controls. The development is easy and independent of the view technology. Nevertheless the web application makes use of technologies like Ajax without making any effort. The ease of development enables Tobago to be used for rapid prototyping, because the views can be designed without the need to program any lines of Java code.
In the near future the tree from the sandbox will become part of the standard Tobago distribution and you can expect more useful controls to follow. With MyFaces focusing on fulfilling the JSF specification 1.2 Tobago will aim for JSF 1.2 compatibility as well. Another goal is to attain a form of integration with other component sets like Tomahawk. Currently adding such controls to a Tobago application works only limited, partly because there is no way to add the necessary layout and theming information for external controls.
[3] See http://maven.apache.org/
[6] See http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UIInputNumberSlider.java?view=markup
[7] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html
[8] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIInput.html
[9] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html
[10] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/render/Renderer.html
[11] See http://myfaces.apache.org/tobago/apidocs/org/apache/myfaces/tobago/renderkit/LayoutInformationProvider.html
[12] See http://myfaces.apache.org/tobago/apidocs/org/apache/myfaces/tobago/renderkit/LayoutableRendererBase.html
[13] See http://myfaces.apache.org/tobago/apidocs/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.html
[14] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/webapp/UIComponentTag.html
[15] See http://java.sun.com/javaee/javaserverfaces/1.1_01/docs/api/javax/faces/webapp/UIComponentBodyTag.html
[16] See http://myfaces.apache.org/tobago/apidocs/org/apache/myfaces/tobago/taglib/component/TobagoTag.html