blob: bf6f899ab2cf628fc14ce1c9a455f1cbbb580395 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<!--
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.
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<meta name="Author" content="Malcolm Edgar"/>
<meta name="description" lang="en" content="Apache Click Java web application framework"/>
<meta name="keywords" lang="en" content="Apache Click, Click Framework, Java, JEE, J2EE, web application framework, open source"/>
<title>Apache Click</title>
<link rel="stylesheet" type="text/css" href="../help.css"/>
</head>
<body>
<h1>Pages</h1>
Pages are the heart of web applications. In Click, Pages encapsulate the processing of
HTML requests and the rendering of HTML responses. The section discusses Click pages
and covers to following topics:
<ol>
<li><a href="#page-class">Classes</a> - page Java classes
</li>
<li><a href="#page-execution">Execution</a> - page execution sequence
</li>
<li><a href="#page-param-auto-binding">Request Param Auto Binding</a> - request parameter to page field auto binding
</li>
<li><a href="#page-security">Security</a> - page security model
</li>
<li><a href="#page-navigation">Navigation</a> - navigation between pages
</li>
<li><a href="#page-templating">Page Templating</a> - templating common page content
</li>
<li><a href="#page-direct-rendering">Direct Rendering</a> - page direct rendering
</li>
<li><a href="#page-stateful">Stateful</a> - stateful pages
</li>
<li><a href="#page-error-handling">Error Handling</a> - page error handling
</li>
<li><a href="#page-not-found">Page Not Found</a> - page not found handling
</li>
<li><a href="#page-messages">Message Properties</a> - pages message properties
</li>
</ol>
<p>&nbsp;</p>
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
<a target="topic" href="configuration.html#application-configuration">click.xml</a> file.
<pre class="codeConfig">
&lt;page path="<span class="blue">search.htm</span>" classname="<span class="red">com.mycorp.page.Search</span>"/&gt;
</pre>
The path attribute specifies the location of the page Velocity template, and the classname
attribute specifies the page Java class name.
<p/>
If you use an alternative template engine such as Freemarker, the setup above is the same.
<p/>
Alternatively you can also configure Click to use JSP pages for rendering.
<pre class="codeConfig">
&lt;page path="<span class="blue">search.jsp</span>" classname="<span class="red">com.mycorp.page.Search</span>"/&gt;
</pre>
<a name="page-class" class="heading"></a><h2>1.&nbsp; Classes</h2>
All custom Click pages must subclass the
<a target="topic" href="click-api/org/apache/click/Page.html">Page</a> base class.
The Page class and its associated companion classes, Context and Control, are depicted
below in Figure 1.
<p/>
<img src="../images/click-class-diagram.png"/>
<p class="diagram">
<b>Figure 1. &nbsp; Page Class Diagram</b>
- <span class="sparx">created with Enterprise Architect courtesy <a target="blank" href="http://www.sparxsystems.com.au">Sparx Systems</a></span>
</p>
The Page class provides a <a href="click-api/org/apache/click/Page.html#model">model</a>
attribute which is used to hold all the objects that are rendered in the page's
Velocity template. The model may also contain
<a target="topic" href="click-api/org/apache/click/Control.html">Control</a> objects, which
provide user interface controls on the Page.
<p/>
Pages also have an associated
<a target="topic" href="click-api/org/apache/click/Context.html">Context</a> 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.
<p/>
<a name="page-execution" class="heading"></a><h2>2.&nbsp; Execution</h2>
The Page class provide a number of empty handler methods which subclasses can override to
provide functionality:
<ul>
<li><a href="click-api/org/apache/click/Page.html#onSecurityCheck()">onSecurityCheck()</a></li>
<li><a href="click-api/org/apache/click/Page.html#onInit()">onInit()</a></li>
<li><a href="click-api/org/apache/click/Page.html#onGet()">onGet()</a></li>
<li><a href="click-api/org/apache/click/Page.html#onPost()">onPost()</a></li>
<li><a href="click-api/org/apache/click/Page.html#onRender()">onRender()</a></li>
<li><a href="click-api/org/apache/click/Page.html#onDestroy()">onDestroy()</a></li>
</ul>
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.
<p/>
<img src="../images/get-sequence-diagram.png"/>
<p class="diagram">
<b>Figure 2. &nbsp; GET Request Sequence Diagram</b>
- <span class="sparx">created with Enterprise Architect courtesy <a target="blank" href="http://www.sparxsystems.com.au">Sparx Systems</a></span>
</p>
Stepping through this GET request sequence, a new Page instance is created and the
attributes for the Page are set (context, format, headers, path). Next, request
parameter values are bound to any matching public Page fields.
<p/>
Then the <tt>onSecurityCheck()</tt> 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.
<p/>
The next method invoked is <tt>onInit()</tt>, this is where you place any
post constructor initialization code. <tt>onInit()</tt> is the ideal place to
create controls such as Forms, Fields and Tables. As illustrated by the diagram,
after a Page's <tt>onInit()</tt> is called, each Control, available at that stage,
will have their <tt>onInit()</tt> method called.
<p/>
The next step is the processing of the Page's <a href="click-api/org/apache/click/Page.html#controls">controls</a>.
The ClickSerlvet gets the list of
Controls from the page and then iterates through the list calling <tt>onProcess()</tt>.
If any of the Control's <tt>onProcess()</tt> methods return false, processing of
subsequent controls and the Page's <tt>onGet()</tt> method is aborted.
<p/>
If everything is executing normally the Page's <tt>onGet()</tt> method is now called.
<p/>
The next step is rendering the page template to generate the displayed HTML. The
ClickServlet gets the model (<tt>Map</tt>) from the Page then adds the following
objects to the model:
<ul>
<li>
any public Page fields using the fields name
</li>
<li>context &nbsp; - &nbsp;
the Servlet context path, e.g. <span class="">/mycorp</span>
</li>
<li>cssImports &nbsp; - &nbsp;
the CSS imports and style blocks to include in the pages header.
Please see <a href="../click-api/org/apache/click/util/PageImports.html">PageImports</a>
for more details.
</li>
<li>format &nbsp; - &nbsp;
the <a href="../click-api/org/apache/click/util/Format.html">Format</a>
object for formatting the display of objects.
</li>
<li>imports &nbsp; - &nbsp;
the CSS and JavaScript imports to include in the pages header.
Please see <a href="../click-api/org/apache/click/util/PageImports.html">PageImports</a>
for more details.
</li>
<li>jsImports &nbsp; - &nbsp;
the JavaScript imports and script blocks to include in the pages footer.
Please see <a href="../click-api/org/apache/click/util/PageImports.html">PageImports</a>
for more details.
</li>
<li>messages &nbsp; - &nbsp;
the <a target="topic" href="click-api/org/apache/click/util/MessagesMap.html">MessagesMap</a> adaptor
for the Page <a href="click-api/org/apache/click/Page.html#getMessage(java.lang.String)">getMessage()</a>
method
</li>
<li>path &nbsp; - &nbsp; the <a href="click-api/org/apache/click/Page.html#path">path</a>
of the page template to render
</li>
<li>request &nbsp; - &nbsp;
the pages <a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</a>
object
</li>
<li>response &nbsp; - &nbsp;
the pages <a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletResponse.html">HttpServletResponse</a>
object
</li>
<li>session &nbsp; - &nbsp;
the <a href="click-api/org/apache/click/util/SessionMap.html">SessionMap</a> adaptor
for the users <a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpSession.html">HttpSession</a>
</li>
</ul>
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 <tt>toString()</tt> method.
<p/>
The final step in this sequence is invoking each control's <tt>onDestroy()</tt> method
and lastly invoke the Page's <tt>onDestroy()</tt> method. This
method can be used to clean up resource associated with the Control or Page before
it is garbage collected. The <tt>onDestroy()</tt> method is guaranteed to be called
even if an exception occurs in the previous steps.
<p/>
The execution sequence for POST requests is almost identical, except the <tt>onPost()</tt>
method is invoked instead on <tt>onGet()</tt>. See the
<a href="../images/post-sequence-diagram.png">POST Request Sequence Diagram</a>.
<p/>
Another view on the execution flow of Pages is illustrated in the Activity diagram below.
<p/>
<a name="activity-diagram" class="heading"><img src="../images/activity-diagram-small.png" style="margin-left:4em;"/></a>
<p class="diagram">
<b>Figure 3. &nbsp; Page Execution Activity Diagram</b>
- <span class="sparx">created with Enterprise Architect courtesy <a target="blank" href="http://www.sparxsystems.com.au">Sparx Systems</a></span>
</p>
<a name="page-param-auto-binding" class="heading"></a><h2>3.&nbsp; Request Param Auto Binding</h2>
Click will automatically bind any request parameter values to public Page fields
with the same name. When binding these values it will also attempt to convert them
to the correct type.
<p/>
The best way to understand this is to walk through an example. Our application
recieves a GET request:
<pre class="codeConfig">
http://localhost:8080/mycorp/customer-details.htm?<span class="blue">customerId</span>=<span class="red">7203</span>
</pre>
This request is automatically handled by our <tt>CustomerDetails</tt> page:
<pre class="codeJava">
<span class="kw">package</span> com.mycorp.page;
<span class="kw">public class</span> CustomerDetails <span class="kw">extends</span> Page {
<span class="kw">public</span> Integer <span class="blue">customerId</span>;
} </pre>
After the CustomerDetails page has been created the "customerId" request parameter
value "7023" will be converted into an Integer and assigned to the public page field
<tt>customerId</tt>.
<p/>
Another feature of Click is that any public Page fields 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 <tt>customerId</tt> field
will be added to the Page model and will be available for rendering in the
page template:
<p/>
Our customer-details.htm page template contains:
<pre class="codeHtml">
&lt;html&gt;
&lt;body&gt;
Customer ID: <span class="red">$</span><span class="blue">customerId</span>
&lt;/body&gt;
&lt;/html&gt; </pre>
After processing the request our page would be rendered as:
<table class="htmlExample" cellspacing="12" width="100%">
<tr>
<td>Customer ID: 7203</td>
</tr>
</table>
<h3>3.1&nbsp; Customizing Auto Binding</h3>
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.
<p/>
By default type conversion is performed by the
<a target="topic" href="click-api/org/apache/click/util/RequestTypeConverter.html">RequestTypeConverter</a> class
which is used by the ClickServlet method
<a target="topic" href="click-api/org/apache/click/ClickServlet.html#getTypeConverter()">getTypeConverter()</a>.
<p/>
If you need to add support for additional types, you would write your own type
converter class and subclass the ClickSerlvet to use your custom converter.
<p/>
For example if we wanted to automatically load a <tt>Customer</tt> object from the database
when a customer id request parameter is specified, you could write your own type converter:
<pre class="codeJava">
<span class="kw">public class</span> CustomTypeConverter <span class="kw">extends</span> RequestTypeConverter {
<span class="kw">private</span> CustomerService customerService = <span class="kw">new</span> CustomerService();
<span class="cm">/**
* @see RequestTypeConverter#convertValue(Object, Class)
*/</span>
<span class="kw">protected</span> Object convertValue(Object value, Class toType) {
<span class="kw">if</span> (toType == Customer.<span class="kw">class</span>) {
<span class="kw">return</span> customerService.getCustomerForId(value);
} <span class="kw">else</span> {
<span class="kw">return super</span>.convertValue(value, toType);
}
}
} </pre>
This type converter would handle the following request:
<pre class="codeConfig">
http://localhost:8080/mycorp/customer-details.htm?<span class="blue">customer</span>=<span class="red">7203</span>
</pre>
This request will load the customer object from the database using "7203" as the customer id value.
The ClickServlet would then assign this customer object to the matching page field:
<pre class="codeJava">
<span class="kw">package</span> com.mycorp.page;
<span class="kw">public class</span> CustomerDetails <span class="kw">extends</span> Page {
<span class="kw">public</span> Customer <span class="blue">customer</span>;
} </pre>
To make your custom type converter available you will need to subclass ClickServlet and override the
<tt>getTypeConverter()</tt> method. For example:
<pre class="codeJava">
<span class="kw">public class</span> CustomClickServlet <span class="kw">extends</span> ClickServlet {
<span class="cm">/**
* @see ClickServlet#getTypeConverter()
*/</span>
<span class="kw">protected</span> TypeConverter getTypeConverter() {
<span class="kw">if</span> (typeConverter == <span class="kw">null</span>) {
typeConverter = <span class="kw">new</span> CustomTypeConverter();
}
<span class="kw">return</span> typeConverter;
}
} </pre>
<a name="page-security" class="heading"></a><h2>4.&nbsp; Security</h2>
Pages provide an
<a target="topic" href="click-api/org/apache/click/Page.html#onSecurityCheck()">onSecurityCheck</a>
event handler which application pages can override to implement a programmatic security
model.
<p/>
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
<a href="best-practices.html#security">Security</a> topic for more details.
<h3>4.1&nbsp; Application Authentication</h3>
Applications can use the <tt>onSecurityCheck()</tt> 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.
<pre class="codeJava">
<span class="kw">public class</span> Secure <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#onSecurityCheck()
*/</span>
<span class="kw">public boolean</span> onSecurityCheck() {
<span class="kw">if</span> (getContext().hasSession()) {
<span class="kw">return true</span>;
} <span class="kw">else</span> {
setRedirect(LoginPage.<span class="kw">class</span>);
<span class="kw">return false</span>;
}
}
}
</pre>
<h3>4.2&nbsp; Container Authentication</h3>
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:
<pre class="codeJava">
<span class="kw">public class</span> Secure <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#onSecurityCheck()
*/</span>
<span class="kw">public boolean</span> onSecurityCheck() {
<span class="kw">if</span> (getContext().getRequest().<a target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletRequest.html#getRemoteUser()">getRemoteUser</a>() != <span class="kw">null</span>) {
<span class="kw">return true</span>;
} <span class="kw">else</span> {
setRedirect(LoginPage.<span class="kw">class</span>);
<span class="kw">return false</span>;
}
}
}
</pre>
<h3>4.3&nbsp; Container Access Control</h3>
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.
<pre class="codeJava">
<span class="kw">public class</span> AdminPage <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#onSecurityCheck()
*/</span>
<span class="kw">public boolean</span> onSecurityCheck() {
<span class="kw">if</span> (getContext().getRequest().<a target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)">isUserInRole</a>(<span class="st">"admin"</span>)) {
<span class="kw">return true</span>;
} <span class="kw">else</span> {
setRedirect(LoginPage.<span class="kw">class</span>);
<span class="kw">return false</span>;
}
}
}
</pre>
<h3>4.4&nbsp; Logging Out</h3>
To logout using the application or container based security models you would simply
invalidate the session.
<pre class="codeJava">
<span class="kw">public class</span> Logout <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#onInit()
*/</span>
<span class="kw">public void</span> onInit() {
getContext().getSession().<a target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpSession.html#invalidate()">invalidate</a>();
}
}
</pre>
<a name="page-navigation" class="heading"></a><h2>5.&nbsp; Navigation</h2>
Navigation between pages is achieved by using forwards, redirects and by setting the
page template path.
<a name="page-foward" class="heading"></a><h3>5.1&nbsp; Forward</h3>
To forward to another page using the servlet
<a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/RequestDispatcher.html">RequestDispatcher</a>,
set the Page's forward property. For example to forward to a page with a path
<span class="html">index.htm</span>:
<pre class="codeJava">
<span class="jd">/**
* @see Page#onPost()
*/</span>
<span class="kw">public void</span> onPost() {
<span class="cm">// Process form post</span>
..
setForward(<span class="st">"index.htm"</span>);
}
</pre>
This will invoke a new Page class instance mapped to the path
<span class="html">index.htm</span>.
<p/>
<b>Please note</b> 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.
<h4>5.1.1&nbsp; Forward Parameter Passing</h4>
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.
<pre class="codeJava">
<span class="kw">public boolean</span> onViewClick() {
Long id = viewLink.getValueLong();
Customer customer = CustomerDAO.findByPK(id);
<span class="cm">// Set the customer object as a request parameter</span>
getContext().setRequestAttribute(<span class="st">"customer"</span>, customer);
setForward(<span class="st">"view-customer.htm"</span>);
<span class="kw">return false</span>;
}
</pre>
The snippet above forwards to the page template <span class="html">view-customer.htm</span>:
<pre class="codeHtml">
&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: <span class="st">$customer.fullName</span>
Email: <span class="st">$customer.email</span>
Telephone: <span class="st">$customer.telephone</span>
&lt;/pre&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
Request attributes are automatically added to the Velocity Context object so are
available in the page template.
<a name="page-forwarding" class="heading"></a><h4>5.1.2&nbsp; Page Forwarding</h4>
Page forwarding is another way of passing information between pages. In this case
you create the page to be forwarded to using the Context
<a href="click-api/org/apache/click/Context.html#createPage(java.lang.String)">createPage()</a>
method and then set properties directly on the Page.
Finally set this page as the page to forward the request to. For example:
<pre class="codeJava">
<span class="kw">public boolean</span> onEditClick() {
Long id = viewLink.getValueLong();
Customer customer = CustomerDAO.findByPK(id);
<span class="cm">// Create a new EditPage instance based on the specified path</span>
EditPage editPage = (EditPage) getContext().createPage(<span class="st">"/edit-customer.htm"</span>);
editPage.setCustomer(customer);
setForward(editPage);
<span class="kw">return false</span>;
}
</pre>
When creating a page with the <tt>createPage()</tt> method ensure you prefix
the page path with the <tt class="st">"/"</tt> character.
<p/>
You can also specify the target page using its class as long as the Page has
a unique path. (Although uncommon it is possible to map more than one path to the
same class. In these cases invoking <span class="st">Context.createPage</span> will
throw an exception, because Click will not be able to determine which path to use).
<p/>
Using this technique the above code becomes:
<pre class="codeJava">
<span class="kw">public boolean</span> onEditClick() {
Long id = viewLink.getValueLong();
Customer customer = CustomerDAO.findByPK(id);
<span class="cm">// Create a new EditPage instance based on its class</span>
EditPage editPage = (EditPage) getContext().createPage(EditPage.<span class="kw">class</span>);
editPage.setCustomer(customer);
setForward(editPage);
<span class="kw">return false</span>;
}
</pre>
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.
<p/>
Please always use the Context <tt>createPage()</tt> methods to allow Click to
inject Page dependencies.
<a name="page-template-path" class="heading"></a><h3>5.2&nbsp; Template Path</h3>
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:
<pre class="codeJava">
<span class="kw">public boolean</span> onViewClick() {
Long id = viewLink.getValueLong();
Customer customer = CustomerDAO.findByPK(id);
addModel(<span class="st">"customer"</span>, customer);
<span class="cm">// Set the Page's path to a new value</span>
setPath(<span class="st">"view-customer.htm"</span>);
<span class="kw">return false</span>;
}
</pre>
Note how the <font color="red">customer</font> 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
"<a target="topic" href="click-api/org/apache/click/Page.html#onDestroy()">destroyed</a>"
before the second Page object is created and any model values would be lost.
<a name="page-redirect" class="heading"></a><h3>5.3&nbsp; Redirect</h3>
Redirects are another very useful way to navigate between pages. See
HttpServletResponse.<a target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletResponse.html#sendRedirect(java.lang.String)">sendRedirect</a>(location)
for details.
<p/>
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.
<p/>
An example of a redirect to a <span class="html">logout.htm</span>
page is provided below:
<pre class="codeJava">
<span class="kw">public boolean</span> onLogoutClick() {
setRedirect(<span class="st">"/logout.htm"</span>);
<span class="kw">return false</span>;
}
</pre>
If the redirect location begins with a <span class="wr">"/"</span>
character the redirect location will be prefixed with the web applications
context path.
<p/>
For example if an application is deployed to the context
<span class="wr">"mycorp"</span> calling
<tt>setRedirect(<span class="navy">"/customer/details.htm"</span>)</tt>
will redirect the request to:
<span class="wr">"/mycorp/customer/details.htm"</span>
<p/>
You can also obtain the redirect path via the target Page's class. For example:
<pre class="codeJava">
<span class="kw">public boolean</span> onLogoutClick() {
String path = getContext().getPagePath(Logout.<span class="kw">class</span>);
setRedirect(path);
<span class="kw">return false</span>;
}
</pre>
Note when using this redirect method, the target Page class must have a unique path.
<p/>
A short hand way of redirecting is to simply specify the target Page class in
the redirect method. For example:
<pre class="codeJava">
<span class="kw">public boolean</span> onLogoutClick() {
setRedirect(Logout.<span class="kw">class</span>);
<span class="kw">return false</span>;
}
</pre>
<h4>5.3.1&nbsp; Redirect Parameter Passing</h4>
You can pass information between redirected pages using URL request parameters.
The ClickServlet will encode the URL for you using
HttpServletResponse.<a target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletResponse.html#encodeRedirectURL(java.lang.String)">encodeRedirectURL</a>(url).
<p/>
In the example below a user will click on an OK button to confirm a payment. The
<tt>onOkClick()</tt> button handler processes the payment, gets the payment transaction id, and then
redirects to the <span class="html">trans-complete.htm</span> page with the
transaction id encoded in the URL.
<pre class="codeJava">
<span class="kw">public class</span> Payment <span class="kw">extends</span> Page {
..
<span class="kw">public boolean</span> onOkClick() {
<span class="kw">if</span> (form.isValid()) {
<span class="cm">// Process payment</span>
..
<span class="cm">// Get transaction id</span>
Long transId = OrderDAO.purchase(order);
setRedirect(<span class="st">"trans-complete.htm?transId="</span> + transId);
<span class="kw">return false</span>;
}
<span class="kw">return true</span>;
}
}
</pre>
The Page class for the <span clas="html">trans-complete.htm</span> page can
then get the transaction id through the request parameter
<tt>"<span color="red">transId</span>"</tt>:
<pre class="codeJava">
<span class="kw">public class</span> TransComplete <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#onInit()
*/</span>
<span class="kw">public void</span> onInit() {
String transId = getContext().getRequest().getParameter(<span class="st">"transId"</span>);
<span class="kw">if</span> (transId != <span class="kw">null</span>) {
<span class="cm">// Get order details</span>
Order order = OrderDAO.findOrderByPK(<span class="kw">new</span> Long(transId));
<span class="kw">if</span> (order != <span class="kw">null</span>) {
addModel(<span class="st">"order"</span>, order);
}
}
}
}
</pre>
<h4>5.3.2&nbsp; Post Redirect</h4>
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.
<a name="page-templating" class="heading"></a><h2>6.&nbsp; Page Templating</h2>
Click supports page templating (a.k.a. <i>Tiles</i> 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.
<p/>
To implement templating define a border template base Page
which content Pages should extend. The template base Page class overrides the
Page <a href="click-api/org/apache/click/Page.html#getTemplate()">getTemplate()</a>
method, returning the path of the border template to render. For example:
<pre class="codeJava"><span class="kw">public class</span> BorderedPage <span class="kw">extends</span> Page {
<span class="jd">/**
* @see Page#getTemplate()
*/</span>
<span class="kw">public</span> String getTemplate() {
<span class="kw">return</span> <span class="st">"/border.htm"</span>;
}
} </pre>
<p/>
The BorderedPage template <span class="blue">border.htm</span>:
<pre class="codeHtml">
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;<span class="blue">$title</span>&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;<span class="blue">$title</span>&lt;/h2&gt;
<span class="red">#parse</span>(<span class="blue">$path</span>)
&lt;/body&gt;
&lt;/html&gt; </pre>
Other pages insert their content into this template using the Velocity
<a href="velocity/vtl-reference-guide.html#parse">#parse</a> directive,
passing it their contents pages <a href="click-api/org/apache/click/Page.html#path">path</a>.
The <span class="blue">$path</span> value is automatically added to
the VelocityContext by the ClickServlet.
<p/>
An example bordered Home page is provided below:
<pre class="codeConfig">
&lt;page path="<span class="blue">home.htm</span>" classname="Home"/&gt;
</pre>
<pre class="codeJava"><span class="kw">public class</span> Home <span class="kw">extends</span> BorderedPage {
<span class="kw">public</span> String title = <span class="st">"Home"</span>;
} </pre>
The Home page's content <span class="blue">home.htm</span>:
<pre class="codeHtml">
&lt;b&gt;Welcome&lt;/b&gt; to Home page your starting point for the application.
</pre>
When a request is made for the Home page (home.htm) Velocity will
merge the <span class="blue">border.htm</span> page and
<span class="blue">home.htm</span> page together returning:
<pre class="codeHtml">
&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; </pre>
Which may be rendered as:
<table class="htmlExample" cellspacing="12">
<tr>
<td>
<h2 style="color: white; background-color: navy; padding: 0.25em; margin-top: 0em;">
Home
</h2>
<p>
<b>Welcome</b> to Home page your application starting point.
<p/>
</td>
</tr>
</table>
Note how the Home page class defines a <span class="blue">title</span> model value
which is referenced in the <span class="blue">border.htm</span> template as <span class="blue">$title</span>.
Each bordered page can define their own title which is rendered in this template.
<p/>
Templating with JSP pages is also supported using the same pattern. Please
see the Click Examples application for a demonstration.
<a name="page-direct-rendering" class="heading"></a><h2>7.&nbsp; Direct Rendering</h2>
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:
<ul>
<li>get the servlet response object</li>
<li>set the content type on the response</li>
<li>get the response output stream</li>
<li>write to the output stream</li>
<li>close the output stream</li>
<li>set the page path to null to inform the ClickServlet that rendering has been completed</li>
</ul>
A direct rendering example is provided below.
<pre class="codeJava">
<span class="cm">/**
* Render the Java source file as "text/plain".
*
* @see Page#onGet()
*/</span>
<span class="kw">public void</span> onGet() {
String filename = ..
HttpServletResponse response = getContext().getResponse();
response.setContentType(<span class="st">"text/plain"</span>);
response.setHeader(<span class="st">"Pragma"</span>, <span class="st">"no-cache"</span>);
ServletContext context = getContext().getServletContext();
InputStream inputStream = <span class="kw">null</span>;
<span class="kw">try</span> {
inputStream = context.getResourceAsStream(filename);
PrintWriter writer = response.getWriter();
BufferedReader reader =
<span class="kw">new</span> BufferedReader(new InputStreamReader(inputStream));
String line = reader.readLine();
<span class="kw">while</span> (line != <span class="kw">null</span>) {
writer.println(line);
line = reader.readLine();
}
setPath(<span class="kw">null</span>);
} <span class="kw">catch</span> (IOException ioe) {
ioe.printStackTrace();
} <span class="kw">finally</span> {
ClickUtils.close(inputStream);
}
} </pre>
<a name="page-stateful" class="heading"></a><h2>8.&nbsp; Stateful Pages</h2>
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:
<ul>
<li>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.
</li>
<li>Complex pages with multiple forms and or tables which need to maintain their
state between interactions.
</li>
</ul>
To make a page stateful you simply need to set the page
<a target="topic" href="click-api/org/apache/click/Page.html#stateful">stateful</a>
property to true, have the page implement the <tt>Serializable</tt> interface
and set the <tt>serialVersionUID</tt> indicator. For example:
<pre class="codeJava">
<span class="kw">package</span> com.mycorp.page;
<span class="kw">import</span> java.io.Serializable;
<span class="kw">import</span> org.apache.click.Page;
<span class="kw">public class</span> SearchPage <span class="kw">extends</span> Page <span class="kw">implements</span> Serializable {
<span class="kw">private static final long</span> serialVersionUID = 1L;
<span class="kw">public</span> SearchPage() {
setStateful(<span class="kw">true</span>);
..
}
}
</pre>
Stateful page instances are stored in the user's
<a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpSession.html">HttpSession</a>
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: <tt>com.mycorp.page.SearchPage</tt>
<h3>8.1&nbsp; Page Creation</h3>
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 <tt>onInit()</tt> method.
<p/>
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
<tt>onInit()</tt> method which will be invoked with each request.
<p/>
If you have dynamic control creation code you would typically place this in the <tt>onInit()</tt> method,
but you will need to take care that controls and or models are not already present in the page.
<h3>8.2&nbsp; Page Execution</h3>
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.
<h3>8.3&nbsp; Page Destruction</h3>
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
<tt>HttpSession</tt> so care needs to be take not to store too many objects in stateful
page instances which may cause memory and performance issues.
<p/>
When pages have completed their execution, all the Page's controls <tt>onDestroy()</tt>
methods are invoked, and then the Page's <tt>onDestroy()</tt> 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
<a target="topic" href="click-api/org/apache/click/control/Table.html#onDestroy()">onDestory()</a>
method.
<a name="page-error-handling" class="heading"></a><h2>9.&nbsp; Error Handling</h2>
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
<a target="topic" href="click-api/org/apache/click/util/ErrorPage.html">ErrorPage</a>,
which is automatically configured as:
<pre class="codeConfig">
&lt;page path="<span class="blue">click/error.htm</span>" classname="<span class="red">org.apache.click.util.ErrorPage</span>"/&gt;
</pre>
To register an alternative error handler you must subclass ErrorPage and define
your page using the path "<span class="html">click/error.htm</span>". For example:
<pre class="codeConfig">
&lt;page path="<span class="blue">click/error.htm</span>" classname="<span class="red">com.mycorp.page.ErrorPage</span>"/&gt;
</pre>
When the ClickSevlet starts up it checks to see whether the <span class="html">error.htm</span> template exists in the
<span class="html">click</span> web sub directory. If it cannot find the page the ClickServlet will automatically deploy one.
You can tailor the <span class="html">click/error.htm</span> template to suite you own tastes, and the ClickServlet
will not overwrite it.
<p/>
The default error template will display extensive debug information when the application is in <tt>development</tt>
or <tt>debug</tt> mode. Example error page displays include:
<ul>
<li><a href="error-npe.html">NullPointerException</a> - in a page method</li>
<li><a href="error-parsing.html">ParseErrorException</a> - in a page template </li>
</ul>
When the application is in <tt>production</tt> mode only a simple
error message is displayed. See <a target="topic" href="configuration.html#application-mode">Configuration</a>
for details on how to set the application mode.
<p/>
Please also see the <a href="examples.html">Examples</a> web app Exception Demo for demonstrations of Clicks error
handling.
<a name="page-not-found" class="heading"></a><h2>10.&nbsp; Page Not Found</h2>
If the ClickServlet cannot find a requested page in the <tt>click.xml</tt> config file
it will use the registered <a href="not-found.html">not-found.htm</a> page.
<p/>
The Click not found page is automatically configured as:
<pre class="codeConfig">
&lt;page path="<span class="blue">click/not-found.htm</span>" classname="<span class="red">org.apache.click.Page</span>"/&gt;
</pre>
You can override the default configuration and specify your own class, but you cannot change the path.
<p/>
When the ClickSevlet starts up it checks to see whether the <span class="html">not-found.htm</span> template exists in the
<span class="html">click</span> web sub directory. If it cannot find the page the ClickServlet will automatically deploy one.
<p/>
You can tailor the <span class="html">click/not-found.htm</span> template to suite you own needs. This page template
has access to the usual Click objects.
<a name="page-messages" class="heading"></a><h2>11.&nbsp; Message Properties</h2>
The Page class provides a
<a href="click-api/org/apache/click/Page.html#messages">messages</a> property which
is a <a href="click-api/org/apache/click/util/MessagesMap.html">MessagesMap</a>
of localized messages for the page. These messages are made available
in the VelocityContext when the page is rendered under the key <tt>messages</tt>.
So for example if you had a page title message you would access it in your page template as:
<pre class="codeHtml">
&lt;h1&gt; <span class="red">$</span><span class="blue">messages.title</span> &lt;/h1&gt; </pre>
This messages map is loaded from the page class property bundle. For example
if you had a page class <tt>com.mycorp.page.CustomerList</tt> you could have
an associated property file containing the pages localized messages:
<pre class="codeConfig">
/com/mycorp/page/CustomerList.properties </pre>
You can also defined a application global page messages properties file:
<pre class="codeConfig">
/click-page.properties </pre>
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.
<p/>
Page messages can also be used to override Control messages, see the Controls
<a href="controls.html#message-properties">Message Properties</a> topic for
more details.
</body>
</html>