blob: ab3185ebff28dd13bf35abfb33eb2391535d9364 [file] [log] [blame]
<?xml version='1.0' encoding='UTF-8'?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<chapter id="chapter-pages" remap="h1">
<title>Pages</title>
<para>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.
</para>
<para>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
<link linkend="application-configuration">click.xml</link>
file:
</para>
<literallayout>&lt;page path="<varname>search.htm</varname>" classname="<token>com.mycorp.page.Search</token>"/&gt;</literallayout>
<para>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.
</para>
<para>The template path should have an <varname>.htm</varname> extension which
is specified in <link linkend="servlet-configuration">web.xml</link> to route
*.htm requests to the <classname>ClickServlet</classname>.
</para>
<para>Please note if you want Click to process templates with a different
extension e.g. <varname>.xml</varname>, you need to implement the method
<ulink url="../../click-api/org/apache/click/service/ConfigService.html#isTemplate(java.lang.String)">isTemplate(String path)</ulink>
and specify the extra extensions. The simplest way is to subclass
<classname>XmlConfigService</classname> and override the default implementation
as described <ulink url="../../click-api/org/apache/click/service/XmlConfigService.html#isTemplate(java.lang.String)">here</ulink>.
Also remember to map the new extensions in <filename>web.xml</filename>.
</para>
<para>If you use JSP pages for rendering, the <varname>.jsp</varname> extension
must be used. For example:
</para>
<literallayout>&lt;page path="<varname>search.jsp</varname>" classname="<token>com.mycorp.page.Search</token>"/&gt;</literallayout>
<para>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
<filename>*.jsp</filename> requests in <filename>web.xml</filename>. Instead
<varname>.jsp</varname> templates are accessed with a <varname>.htm</varname>
extension. At runtime Click will convert the page path from
<varname>.jsp</varname> to <varname>.htm</varname> and back.
</para>
<sect1 id="classes" remap="h2">
<title>Classes</title>
<para> All custom Click pages must subclass the
<ulink url="../../click-api/org/apache/click/Page.html">Page</ulink> base class.
The Page class and its associated companion classes, Context and Control,
are depicted in the figure below.
</para>
<figure id="page-class-diagram">
<title>Page Class Diagram
</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/pages/click-class-diagram.png" format="PNG" scale="65"/>
</imageobject>
</inlinemediaobject>
</figure>
<para>The Page class provides a
<ulink url="../../click-api/org/apache/click/Page.html#model">model</ulink>
attribute which is used to hold all the objects that are rendered in the
page Velocity template. The model may also contain
<ulink url="../../click-api/org/apache/click/Control.html">Control</ulink>
objects, which provide user interface controls on the Page.
</para>
<para>Pages also provides access to the
<ulink url="../../click-api/org/apache/click/Context.html">Context</ulink>
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.
</para>
</sect1>
<sect1 id="execution" remap="h2">
<title>Execution</title>
<para>The Page class provide a number of empty handler methods which
subclasses can override to provide functionality:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onSecurityCheck()">onSecurityCheck()</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onInit()">onInit()</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onGet()">onGet()</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onPost()">onPost()</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onRender()">onRender()</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="../../click-api/org/apache/click/Page.html#onDestroy()">onDestroy()</ulink>
</para>
</listitem>
</itemizedlist>
<para>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.
</para>
<figure id="get-sequence-diagram">
<title>GET Request Sequence Diagram
</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/pages/get-sequence-diagram.png" format="PNG" scale="65"/>
</imageobject>
</inlinemediaobject>
</figure>
<para>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.
</para>
<para>Then the <methodname>onSecurityCheck()</methodname> 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.
</para>
<para>The next method invoked is <methodname>onInit()</methodname>, this is
where you place any post constructor initialization code.
<methodname>onInit()</methodname> is the ideal place to create controls such
as Forms, Fields and Tables. As illustrated by the diagram, after a Page's
<methodname>onInit()</methodname> is called, each Control, available at that
stage, will have their <methodname>onInit()</methodname> method called.
</para>
<para>
The next step is the processing of the Page's
<ulink url="../../click-api/org/apache/click/Page.html#controls">controls</ulink>.
The ClickServlet gets the list of Controls from the page and then iterates
through the list calling <methodname>onProcess()</methodname>. If any of the
Control's <methodname>onProcess()</methodname> methods return false,
processing of subsequent controls and the Page's <methodname>onGet()</methodname>
method is aborted.
</para>
<para>If everything is executing normally the Page's
<methodname>onGet()</methodname> method is now called.
</para>
<para>The next step is rendering the page template to generate the displayed
HTML. The ClickServlet gets the model (<classname>Map</classname>) from the
Page then adds the following objects to the model:
</para>
<itemizedlist>
<listitem>
<para>any public Page variable using the variable name</para>
</listitem>
<listitem>
<para>context &nbsp;-&nbsp; the Servlet context path, e.g. /mycorp</para>
</listitem>
<listitem>
<para>format &nbsp;-&nbsp; the
<ulink url="../../click-api/org/apache/click/util/Format.html">Format</ulink>
object for formatting the display of objects.
</para>
</listitem>
<listitem>
<para>headElements &nbsp;-&nbsp; the HEAD <ulink url="../../click-api/org/apache/click/element/Element.html">elements</ulink>,
excluding JavaScript, to include in the page header. Please see
<ulink url="../../click-api/org/apache/click/util/PageImports.html">PageImports</ulink>
for more details.
</para>
</listitem>
<listitem>
<para>jsElements &nbsp;-&nbsp; the JavaScript imports and script blocks to
include in the pages footer. Please see
<ulink url="../../click-api/org/apache/click/util/PageImports.html">PageImports</ulink>
for more details.
</para>
</listitem>
<listitem>
<para>messages &nbsp;-&nbsp; the
<ulink url="../../click-api/org/apache/click/util/MessagesMap.html">MessagesMap</ulink>
adaptor for the Page
<ulink url="../../click-api/org/apache/click/Page.html#getMessage(java.lang.String)">getMessage()</ulink>
method
</para>
</listitem>
<listitem>
<para>path &nbsp;-&nbsp; the
<ulink url="../../click-api/org/apache/click/Page.html#path">path</ulink> of
the page template to render
</para>
</listitem>
<listitem>
<para>request &nbsp;-&nbsp; the pages
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</ulink>
object
</para>
</listitem>
<listitem>
<para>response &nbsp;-&nbsp; the pages
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletResponse.html">HttpServletResponse</ulink>
object
</para>
</listitem>
<listitem>
<para>session &nbsp;-&nbsp; the
<ulink url="../../click-api/org/apache/click/util/SessionMap.html">SessionMap</ulink>
adaptor for the users
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSession.html">HttpSession</ulink>
</para>
</listitem>
</itemizedlist>
<para>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
<methodname>toString()</methodname> method.
</para>
<para>The final step in this sequence is invoking each control's
<methodname>onDestroy()</methodname> method and lastly invoke the Page's
<methodname>onDestroy()</methodname> method. This method can be used to
clean up resource associated with the Control or Page before it is garbage
collected. The <methodname>onDestroy()</methodname> method is guaranteed to
be called even if an exception occurs in the previous steps.
</para>
<para>The execution sequence for POST requests is almost identical, except
the <methodname>onPost()</methodname> method is invoked instead on
<methodname>onGet()</methodname>. See the
<ulink url="../../../images/post-sequence-diagram.png">POST Request Sequence Diagram</ulink>.
</para>
<para>Another view on the execution flow of Pages is illustrated in the
Activity diagram below.
</para>
<figure id="activity-diagram">
<title>Page Execution Activity Diagram
</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/pages/activity-diagram-small.png" format="PNG" scale="65"/>
</imageobject>
</inlinemediaobject>
</figure>
</sect1>
<sect1 id="request-param-auto-binding" remap="h2">
<title>Request Parameter Auto Binding</title>
<para>Click will automatically bind any request parameter values to public
Page variable with the same name. You can also use the
<ulink url="../../click-api/org/apache/click/util/Bindable.html">Bindable</ulink>
annotation to bind private and protected Page variables. When binding these
values Click will also attempt to convert them to the correct type.
</para>
<para>The best way to understand this is to walk through an example. Our
application receives a GET request:
</para>
<literallayout>http://localhost:8080/mycorp/customer-details.htm?<varname>customerId</varname>=<symbol>7203</symbol></literallayout>
<para>This request is automatically handled by our
<classname>CustomerDetails</classname> page:
</para>
<programlisting language="java">package com.mycorp.page;
public class CustomerDetails extends Page {
@Bindable protected Integer <varname>customerId</varname>;
}</programlisting>
<para>After the CustomerDetails page has been created the
"<varname>customerId</varname>" request parameter value "<symbol>7023</symbol>"
will be converted into an Integer and assigned to the public page variable
<varname>customerId</varname>.
</para>
<para>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 <varname>customerId</varname> variable will be added to the Page
model and will be available for rendering in the page template.
</para>
<para>Our customer-details.htm page template contains:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;body&gt;
Customer ID: <symbol>$</symbol><varname>customerId</varname>
&lt;/body&gt;
&lt;/html&gt;</programlisting>
<para>After processing the request our page would be rendered as:
</para>
<literallayout>Customer ID: 7203</literallayout>
<sect2 id="customizing-auto-binding" remap="h3">
<title>Customizing Auto Binding</title>
<para>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.
</para>
<para>By default type conversion is performed by the
<ulink url="../../click-api/org/apache/click/util/RequestTypeConverter.html">RequestTypeConverter</ulink>
class which is used by the ClickServlet method
<ulink url="../../click-api/org/apache/click/ClickServlet.html#getTypeConverter()">getTypeConverter()</ulink>.
</para>
<para>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.
</para>
<para>For example if you wanted to automatically load a
<classname>Customer</classname> object from the database when a customer
id request parameter is specified, you could write your own type converter:
</para>
<programlisting language="java">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);
}
}
}</programlisting>
<para>This type converter would handle the following request:
</para>
<literallayout>http://localhost:8080/mycorp/customer-details.htm?<varname>customer</varname>=<symbol>7203</symbol></literallayout>
<para>This request will load the <varname>customer</varname> object from
the database using "<symbol>7203</symbol>" as the customer id value. The
ClickServlet would then assign this <varname>customer</varname> object to
the matching page variable:
</para>
<programlisting language="java">package com.mycorp.page;
public class CustomerDetails extends Page {
@Bindable protected Customer <varname>customer</varname>;
}</programlisting>
<para>To make your custom type converter available you will need to
add an init parameter to ClickServlet in <filename>web.xml</filename>. For example:
</para>
<programlisting language="xml">&lt;web-app&gt;
...
&lt;servlet&gt;
&lt;servlet-name&gt;ClickServlet&lt;/servlet-name&gt;
&lt;servlet-class&gt;org.apache.click.ClickServlet&lt;/servlet-class&gt;
&lt;init-param&gt;
&lt;param-name&gt;type-converter-class&lt;/param-name&gt;
&lt;param-value&gt;com.mycorp.util.CustomTypeConverter&lt;/param-value&gt;
&lt;/init-param&gt;
&lt;load-on-startup&gt;0&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;ClickServlet&lt;/servlet-name&gt;
&lt;url-pattern&gt;*.htm&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
...
&lt;/web-app&gt;</programlisting>
</sect2>
</sect1>
<sect1 id="page-security" remap="h2">
<title>Security</title>
<para>Pages provide an
<ulink url="../../click-api/org/apache/click/Page.html#onSecurityCheck()">onSecurityCheck</ulink>
event handler which application pages can override to implement a
programmatic security model.
</para>
<para>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 <link linkend="security">Security</link> topic for more details.
</para>
<sect2 id="applications-authentication" remap="h3">
<title>Application Authentication</title>
<para>Applications can use the <methodname>onSecurityCheck()</methodname>
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.
</para>
<programlisting language="java">public class Secure extends Page {
/**
* @see Page#onSecurityCheck()
*/
public boolean onSecurityCheck() {
if (getContext().hasSession()) {
return true;
} else {
setRedirect(LoginPage.class);
return false;
}
}
}</programlisting>
</sect2>
<sect2 id="container-authentication" remap="h3">
<title>Container Authentication</title>
<para>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:
</para>
<programlisting language="java">public class Secure extends Page {
/**
* @see Page#onSecurityCheck()
*/
public boolean onSecurityCheck() {
if (getContext().getRequest().<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html#getRemoteUser()"><varname>getRemoteUser</varname></ulink>() != null) {
return true;
} else {
setRedirect(LoginPage.class);
return false;
}
}
}</programlisting>
</sect2>
<sect2 id="container-access-control" remap="h3">
<title>Container Access Control</title>
<para>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.
</para>
<programlisting language="java">public class AdminPage extends Page {
/**
* @see Page#onSecurityCheck()
*/
public boolean onSecurityCheck() {
if (getContext().getRequest().<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)"><varname>isUserInRole</varname></ulink>("admin")) {
return true;
} else {
setRedirect(LoginPage.class);
return false;
}
}
}</programlisting>
</sect2>
<sect2 id="logging-out" remap="h3">
<title>Logging Out</title>
<para>To logout using the application or container based security models
you would simply invalidate the session.
</para>
<programlisting language="java">public class Logout extends Page {
/**
* @see Page#onInit()
*/
public void onInit() {
getContext().getSession().<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSession.html#invalidate()"><varname>invalidate</varname></ulink>();
}
}</programlisting>
</sect2>
</sect1>
<sect1 id="page-navigation" remap="h2">
<title>Page Navigation</title>
<para> Navigation between pages is achieved by using forwards, redirects
and by setting the page template path.
</para>
<sect2 id="forward" remap="h3">
<title>Forward</title>
<para> To forward to another page using the servlet
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/RequestDispatcher.html">RequestDispatcher</ulink>,
set the Page's forward property. For example to forward to a page with a
path <varname>index.htm</varname>:
</para>
<programlisting language="java">/**
* @see Page#onPost()
*/
public void onPost() {
// Process form post
..
setForward("index.htm");
}</programlisting>
<para>This will invoke a new Page class instance mapped to the path
<varname>index.htm</varname>.
</para>
<para><emphasis role="bold">Please note</emphasis> 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.
</para>
<sect3 id="forward-parameter-passing" remap="h4">
<title>Forward Parameter Passing</title>
<para>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.
</para>
<programlisting language="java">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;
}</programlisting>
<para>The snippet above forwards to the page template
<varname>view-customer.htm</varname>:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Customer Details&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Customer Details&lt;/h1&gt;
&lt;pre&gt;
Full Name: <varname>$customer.fullName</varname>
Email: <varname>$customer.email</varname>
Telephone: <varname>$customer.telephone</varname>
&lt;/pre&gt;
&lt;/body&gt;
&lt;/html&gt;</programlisting>
<para>Request attributes are automatically added to the Velocity Context
object so are available in the page template.
</para>
</sect3>
<sect3 id="page-forwarding" remap="h4">
<title>Page Forwarding</title>
<para>Page forwarding is another way of passing information between
pages. In this case you create the page to be forwarded to using the
Context <ulink url="../../click-api/org/apache/click/Context.html#createPage(java.lang.String)">createPage(String)</ulink>
method and then set properties directly on the Page. Finally set this
page as the page to forward the request to. For example:
</para>
<programlisting language="java">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;
}</programlisting>
<para>When creating a page with the <methodname>createPage()</methodname>
method, ensure you prefix the page path with the <varname>"/"</varname>
character.
</para>
<para>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:
</para>
<programlisting language="java">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;
}</programlisting>
<para>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
<methodname>createPage()</methodname> methods to allow Click to inject
Page dependencies.
</para>
<para>Although uncommon it is possible to map more than one path to the
same class. In these cases invoking Context
<methodname>createPage(Class)</methodname> will throw an exception, because
Click will not be able to determine which path to use for the Page.
</para>
</sect3>
</sect2>
<sect2 id="template-path" remap="h3">
<title>Template Path</title>
<para>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:
</para>
<programlisting language="java">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;
}</programlisting>
<para>Note how the <varname>customer</varname> 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
"<ulink url="../../click-api/org/apache/click/Page.html#onDestroy()">destroyed</ulink>"
before the second Page object is created and any model values would be lost.
</para>
</sect2>
<sect2 id="redirect" remap="h3">
<title>Redirect</title>
<para>Redirects are another very useful way to navigate between pages.
See HttpServletResponse.
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletResponse.html#sendRedirect(java.lang.String)">sendRedirect</ulink>
(location) for details.
</para>
<para>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.
</para>
<para>An example of a redirect to a <varname>logout.htm</varname> page is
provided below:
</para>
<programlisting language="java">public boolean onLogoutClick() {
setRedirect("/logout.htm");
return false;
}</programlisting>
<para>If the redirect location begins with a <symbol>"/"</symbol> character
the redirect location will be prefixed with the web applications context
path. For example if an application is deployed to the context
<varname>"mycorp"</varname> calling
<methodname>setRedirect(<varname>"/customer/details.htm"</varname>)</methodname>
will redirect the request to: <varname>"/mycorp/customer/details.htm"</varname>.
</para>
<para>You can also obtain the redirect path via the target Page's class.
For example:
</para>
<programlisting language="java">public boolean onLogoutClick() {
String path = getContext().getPagePath(Logout.class);
setRedirect(path);
return false;
}</programlisting>
<para>Note when using this redirect method, the target Page class must have
a unique path.
</para>
<para>A short hand way of redirecting is to simply specify the target Page
class in the redirect method. For example:
</para>
<programlisting language="java">public boolean onLogoutClick() {
setRedirect(Logout.class);
return false;
}</programlisting>
<sect3 id="redirect-parameter-passing" remap="h4">
<title>Redirect Parameter Passing</title>
<para>You can pass information between redirected pages using URL
request parameters. The ClickServlet will encode the URL for you using
HttpServletResponse.<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletResponse.html#encodeRedirectURL(java.lang.String)">encodeRedirectURL</ulink>
(url).
</para>
<para>In the example below a user will click on an OK button to confirm
a payment. The <methodname>onOkClick()</methodname> button handler
processes the payment, gets the payment transaction id, and then
redirects to the <varname>trans-complete.htm</varname> page with the
transaction id encoded in the URL.
</para>
<programlisting language="java">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;
}
}</programlisting>
<para>The Page class for the trans-complete.htm page can then get the
transaction id through the request parameter <varname>"transId"</varname>:
</para>
<programlisting language="java">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);
}
}
}
}</programlisting>
</sect3>
<sect3 id="post-redirect" remap="h4">
<title>Post Redirect</title>
<para>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.
</para>
</sect3>
</sect2>
</sect1>
<sect1 id="page-templating" remap="h2">
<title>Page Templating</title>
<para>Click supports page templating (a.k.a. <emphasis>Tiles</emphasis> 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.
</para>
<para>To implement templating define a border template base Page which
content Pages should extend. The template base Page class overrides the Page
<ulink url="../../click-api/org/apache/click/Page.html#getTemplate()">getTemplate()</ulink>
method, returning the path of the border template to render. For example:
</para>
<programlisting language="java">package com.mycorp.page;
public class BorderedPage extends Page {
/**
* @see Page#getTemplate()
*/
public String getTemplate() {
return "/border.htm";
}
}</programlisting>
<para>The BorderedPage template <varname>border.htm</varname>:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;<varname>$title</varname>&lt;/title&gt;
&lt;link rel="stylesheet" type="text/css" href="style.css" title="Style"/&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2 class="title"&gt;<varname>$title</varname>&lt;/h2&gt;
<command>#parse</command>(<varname>$path</varname>)
&lt;/body&gt;
&lt;/html&gt;
</programlisting>
<para>Other pages insert their content into this template using the Velocity
<ulink url="../../velocity/vtl-reference-guide.html#parse">#parse</ulink>
directive, passing it their contents pages
<ulink url="../../click-api/org/apache/click/Page.html#path">path</ulink>. The
<varname>$path</varname> value is automatically added to the VelocityContext
by the ClickServlet.
</para>
<para>An example bordered Home page is provided below:
</para>
<literallayout>&lt;page path="<varname>home.htm</varname>" classname="<token>com.mycorp.page.Home</token>"/&gt;</literallayout>
<programlisting language="java">package com.mycorp.page;
public class Home extends BorderedPage {
public String title = "Home";
}</programlisting>
<para>The Home page's content <varname>home.htm</varname>:
</para>
<literallayout>&lt;b&gt;Welcome&lt;/b&gt; to Home page your starting point for the application.</literallayout>
<?dbfo-need height="1.2in" ?>
<para>When a request is made for the Home page (home.htm) Velocity will
merge the <varname>border.htm</varname> page and <varname>home.htm</varname>
page together returning:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Home&lt;/title&gt;
&lt;link rel="stylesheet" type="text/css" href="style.css" title="Style"/&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2 class="title"&gt;Home&lt;/h2&gt;
&lt;b&gt;Welcome&lt;/b&gt; to Home page your application starting point.
&lt;/body&gt;
&lt;/html&gt;</programlisting>
<para>Which may be rendered as:
</para>
<figure id="home-page-screenshot">
<title>Home Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/pages/home-page-screenshot.png" format="PNG" scale="65"/>
</imageobject>
</mediaobject>
</figure>
<para>Note how the Home page class defines a <varname>title</varname> model
value which is referenced in the <varname>border.htm</varname> template as
<varname>$title</varname>. Each bordered page can define their own title
which is rendered in this template.
</para>
<para>Templating with JSP pages is also supported using the same pattern.
Please see the Click Examples application for a demonstration.
</para>
</sect1>
<sect1 id="page-actions" remap="h2">
<title>Page Actions</title>
<para>Page Action is a feature to directly invoke a <literal>Page method</literal>
from the browser. The Page Action method returns an
<ulink url="../../click-api/org/apache/click/ActionResult.html">ActionResult</ulink>
object that is rendered directly to the browser. In other words the Page template will
not be rendered.
</para>
<para>To invoke a Page Action, specify the parameter <varname>"pageAction"</varname>
and the name of the page method, for example: <symbol>"onRenderImage"</symbol>.
</para>
<para>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 <literal>&lt;img&gt;</literal>
element which <literal>src</literal> attribute specifies the Page Action
that will return the image data.
</para>
<para>First we create our template:
</para>
<programlisting language="xml">&lt;img src="$context/mycorp/image.htm?<varname>pageAction</varname>=<symbol>onRenderImage</symbol>"/&gt;
</programlisting>
<para>Next we create our ImagePage with a Page Action method called
<symbol>onRenderImage</symbol> that returns an <token>ActionResult</token>
instance:
</para>
<programlisting language="java">public class ImagePage extends Page {
public <token>ActionResult</token> <symbol>onRenderImage()</symbol> {
byte[] imageData = getImageAsBytes();
String contentType = ClickUtils.getMimeType("png");
return new <token>ActionResult</token>(imageData, contentType);
}
} </programlisting>
<para>A Page Action is a normal Page method with the following signature:
a <symbol>public no-arg</symbol> method returning an <token>ActionResult</token>
instance:
</para>
<programlisting language="java">
// The Page Action method is public, doesn't accept any arguments and returns an ActionResult
public <token>ActionResult</token> <symbol>onRenderImage()</symbol> {
byte[] imageData = getImageAsBytes();
String contentType = ClickUtils.getMimeType("png");
return new <token>ActionResult</token>(imageData, contentType);
} </programlisting>
<para>The <token>ActionResult</token> 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: <literal>"images/png"</literal>.
</para>
<sect2 id="page-action-execution" remap="h3">
<title>Page Action Execution</title>
<para>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.
</para>
<figure id="page-action-sequence-diagram">
<title>Page Action Request Sequence Diagram
</title>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/pages/page-action-sequence-diagram.png" format="PNG" scale="65"/>
</imageobject>
</inlinemediaobject>
</figure>
<para>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.
</para>
<para>Then the <methodname>onSecurityCheck()</methodname> 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
<methodname>onSecurityCheck()</methodname> 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
<methodname>onSecurityCheck()</methodname> event, since other Page events
are not executed. Please see
<ulink url="http://click.avoka.com/click-examples/ajax/ajax-secure.htm">this</ulink>
example for some strategies on implementing
<methodname>onSecurityCheck</methodname> to handle ajax requests.
</para>
<para>Next the target <methodname>page method</methodname> is invoked
which returns an <classname>ActionResult</classname> that is rendered to
the client.</para>
<para>If the page method returns <literal>null</literal> no response is
rendered to the browser.
</para>
</sect2>
<sect2 id="page-action-result" remap="h3">
<title>ActionResult</title>
<para>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.
</para>
</sect2>
<sect2 id="page-action-example" remap="h3">
<title>Page Action Example</title>
<para>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:
</para>
<programlisting language="java">public ImagePage extends Page {
public ActionResult getImageData() {
byte[] imageData = loadImageData();
String contentType = ClickUtils.getContentType("png");
return new ActionResult(imageData, contentType);
}
} </programlisting>
<para>Next we have the page template image.htm:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;body&gt;
&lt;img src="/mycorp/image.htm?<varname>pageAction</varname>=<symbol>getImageData</symbol>"/&gt;
&lt;/body&gt;
&lt;/html&gt; </programlisting>
<para>The browser renders the <literal>&lt;img&gt;</literal> element and
requests the image src url. Click invokes the page method <symbol>getImageData</symbol>
and renders the result to the browser.
</para>
<para>Looking at the output log we see the following trace:
</para>
<literallayout>[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.&lt;&lt;init&gt;&gt;
[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</literallayout>
</sect2>
<sect2 id="page-action-accessing-request-parameters" remap="h3">
<title>Accessing Request Parameters</title>
<para>Request parameters can be accessed through the <classname>Context</classname>
as shown below:
</para>
<programlisting language="java">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);
}
} </programlisting>
</sect2>
<sect2 id="page-action-set-response-headers" remap="h3">
<title>Set response headers and status code</title>
<para>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,
<classname>HttpServetlResponse</classname> which can be accessed
through the <classname>Context</classname>.
</para>
<para>For example:
</para>
<programlisting language="java">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);
...
}
} </programlisting>
</sect2>
</sect1>
<sect1 id="page-direct-rendering" remap="h2">
<title>Direct Rendering</title>
<para>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:
</para>
<itemizedlist>
<listitem>
<para> get the servlet response object</para>
</listitem>
<listitem>
<para> set the content type on the response</para>
</listitem>
<listitem>
<para> get the response output stream</para>
</listitem>
<listitem>
<para> write to the output stream</para>
</listitem>
<listitem>
<para> close the output stream</para>
</listitem>
<listitem>
<para> set the page path to null to inform the ClickServlet that
rendering has been completed</para>
</listitem>
</itemizedlist>
<?dbfo-need height="1.2in" ?>
<para>A direct rendering example is provided below.
</para>
<programlisting language="java">/**
* 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);
}
}</programlisting>
</sect1>
<sect1 id="stateful-pages" remap="h2">
<title>Stateful Pages</title>
<para><symbol>PLEASE NOTE:</symbol> 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.
</para>
<para>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:
</para>
<itemizedlist>
<listitem>
<para>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.
</para>
</listitem>
<listitem>
<para>Complex pages with multiple forms and or tables which need to
maintain their state between interactions.
</para>
</listitem>
</itemizedlist>
<para>To make a page stateful you simply need to set the page
<ulink url="../../click-api/org/apache/click/Page.html#stateful">stateful</ulink>
property to true, have the page implement the <classname>Serializable</classname>
interface and set the <literal>serialVersionUID</literal> indicator.
For example:
</para>
<programlisting language="java">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);
..
}
}</programlisting>
<para>Stateful page instances are stored in the user's
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSession.html">HttpSession</ulink>
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:
<classname>com.mycorp.page.SearchPage</classname>
</para>
<sect2 id="page-creation" remap="h3">
<title>Page Creation</title>
<para>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 <methodname>onInit()</methodname> method.
</para>
<para>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
<methodname>onInit()</methodname> method which will be invoked with each
request.
</para>
<para>If you have dynamic control creation code you would typically place
this in the <methodname>onInit()</methodname> method, but you will need to
take care that controls and or models are not already present in the page.
</para>
</sect2>
<sect2 id="page-execution" remap="h3">
<title>Page Execution</title>
<para>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.
</para>
</sect2>
<sect2 id="page-destruction" remap="h3">
<title>Page Destruction</title>
<para>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 <classname>HttpSession</classname> so
care needs to be take not to store too many objects in stateful page
instances which may cause memory and performance issues.
</para>
<para>When pages have completed their execution, all the Page's controls
<methodname>onDestroy()</methodname> methods are invoked, and then the
Page's <methodname>onDestroy()</methodname> 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
<ulink url="../../click-api/org/apache/click/control/Table.html#onDestroy()">onDestory()</ulink>
method.
</para>
</sect2>
</sect1>
<sect1 id="page-error-handling" remap="h2">
<title>Error Handling</title>
<para>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
<ulink url="../../click-api/org/apache/click/util/ErrorPage.html">ErrorPage</ulink>,
which is automatically configured as:
</para>
<literallayout>&lt;page path="<varname>click/error.htm</varname>" classname="<symbol>org.apache.click.util.ErrorPage</symbol>"/&gt;</literallayout>
<para>To register an alternative error handler you must subclass ErrorPage
and define your page using the path <varname>"click/error.htm"</varname>.
For example:
</para>
<literallayout>&lt;page path="<varname>click/error.htm</varname>" classname="<symbol>com.mycorp.page.ErrorPage</symbol>"/&gt;</literallayout>
<para>When the ClickServlet starts up it checks to see whether the
<varname>error.htm</varname> template exists in the <varname>click</varname>
web sub directory. If it cannot find the page the ClickServlet will
automatically deploy one. You can tailor the <varname>click/error.htm</varname>
template to suite you own tastes, and the ClickServlet will
not overwrite it.
</para>
<para>The default error template will display extensive debug information
when the application is in <literal>development</literal> or
<literal>debug</literal> mode. Example error page displays include:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="../../error-npe.html">NullPointerException</ulink> - in a page
method
</para>
</listitem>
<listitem>
<para>
<ulink url="../../error-parsing.html">ParseErrorException</ulink> - in a
page template
</para>
</listitem>
</itemizedlist>
<para>When the application is in <literal>production</literal> mode only
a simple error message is displayed. See
<link linkend="application-mode">Configuration</link> for details on how to
set the application mode.
</para>
<para>Please also see the <ulink url="../../examples.html">Examples</ulink> web
app Exception Demo for demonstrations of Clicks error handling.
</para>
</sect1>
<sect1 id="page-not-found" remap="h2">
<title>Page Not Found</title>
<para>If the ClickServlet cannot find a requested page in the
<literal>click.xml</literal> config file it will use the registered
<ulink url="../../not-found.html">not-found.htm</ulink> page.
</para>
<para>The Click <literal>not found page</literal> is automatically configured
as:
</para>
<literallayout>&lt;page path="<varname>click/not-found.htm</varname>" classname="<symbol>org.apache.click.Page</symbol>"/&gt;</literallayout>
<para>You can override the default configuration and specify your own class,
but you cannot change the path.
</para>
<para>When the ClickServlet starts up it checks to see whether the
<varname>not-found.htm</varname> template exists in the <varname>click</varname>
web sub directory. If it cannot find the page the ClickServlet will
automatically deploy one.
</para>
<para>You can tailor the <varname>click/not-found.htm</varname> template to
suite you own needs. This page template has access to the usual Click objects.
</para>
</sect1>
<sect1 id="page-message-properties" remap="h2">
<title>Page Message Properties</title>
<para>The Page class provides a
<ulink url="../../click-api/org/apache/click/Page.html#messages">messages</ulink>
property which is a
<ulink url="../../click-api/org/apache/click/util/MessagesMap.html">MessagesMap</ulink>
of localized messages for the page. These messages are made available in the
VelocityContext when the page is rendered under the key
<literal>messages</literal>. So for example if you had a page title message
you would access it in your page template as:
</para>
<literallayout>&lt;h1&gt; <symbol>$</symbol><varname>messages.title</varname> &lt;/h1&gt;</literallayout>
<para>This messages map is loaded from the page class property bundle. For
example if you had a page class <classname>com.mycorp.page.CustomerList</classname>
you could have an associated property file containing the pages localized
messages:
</para>
<literallayout>/com/mycorp/page/CustomerList.properties</literallayout>
<para>You can also defined a application global page messages properties file:
</para>
<literallayout>/click-page.properties</literallayout>
<para>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.
</para>
<para>Page messages can also be used to override Control messages, see the
Controls <link linkend="control-message-properties">Message Properties</link>
topic for more details.
</para>
</sect1>
<sect1 id="page-head-elements" remap="h2">
<title>Page HEAD Elements</title>
<para>The Page class provides the method
<ulink url="../../click-api/org/apache/click/Page.html#getHeadElements()">getHeadElements()</ulink>
for contributing HEAD <ulink url="../../click-api/org/apache/click/element/Element.html">elements</ulink>
such as <ulink url="../../click-api/org/apache/click/element/JsImport.html">JsImport</ulink>,
<ulink url="../../click-api/org/apache/click/element/JsScript.html">JsScript</ulink>,
<ulink url="../../click-api/org/apache/click/element/CssImport.html">CssImport</ulink>
and <ulink url="../../click-api/org/apache/click/element/CssStyle.html">CssStyle</ulink>.
</para>
<para>Here is an example of adding HEAD elements to the
<classname>MyPage</classname> class:
</para>
<programlisting language="java">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; }"));
}
...
} </programlisting>
<para>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 <literal>onInit</literal>, <literal>onGet</literal>,
<literal>onPost</literal>, <literal>onRender</literal> etc.
Please see <ulink url="../../click-api/org/apache/click/Page.html#getHeadElements()">getHeadElements()</ulink>
for more details.
</para>
<para>Below is the <filename>/my-page.htm</filename> template:
</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
<varname>$headElements</varname>
&lt;/head&gt;
&lt;body&gt;
...
<varname>$jsElements</varname>
&lt;/body&gt;
&lt;/html&gt; </programlisting>
<para>The two variables, <varname>$headElements</varname> and
<varname>$jsElements</varname>, are automatically made available to the Page
template. These variables references the JavaScript and Css HEAD elements
specified in <classname>MyPage</classname>.
</para>
<para>The following HTML will be rendered (assuming the application context
is "/myapp"):
</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
&lt;link rel="stylesheet" type="text/css" href="<symbol>/myapp/mypage.css</symbol>"&gt;&lt;/link&gt;
&lt;style rel="stylesheet" type="text/css"&gt;
<symbol>body { font-family: Verdana; }</symbol>
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
...
&lt;script type="text/javascript" src="<symbol>/myapp/mypage.js</symbol>"/&gt;
&lt;script type="text/javascript"&gt;
<symbol>alert('Welcome to MyPage');</symbol>
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt; </programlisting>
<para>A live demo showing how to add HEAD elements to a Page can be seen
<ulink url="http://click.avoka.com/click-examples/general/page-head-demo.htm">here</ulink>.
</para>
</sect1>
</chapter>