<!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> </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"> | |
<page path="<span class="blue">search.htm</span>" classname="<span class="red">com.mycorp.page.Search</span>"/> | |
</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"> | |
<page path="<span class="blue">search.jsp</span>" classname="<span class="red">com.mycorp.page.Search</span>"/> | |
</pre> | |
<a name="page-class" class="heading"></a><h2>1. 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. 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. 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. 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 - | |
the Servlet context path, e.g. <span class="">/mycorp</span> | |
</li> | |
<li>cssImports - | |
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 - | |
the <a href="../click-api/org/apache/click/util/Format.html">Format</a> | |
object for formatting the display of objects. | |
</li> | |
<li>imports - | |
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 - | |
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 - | |
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 - the <a href="click-api/org/apache/click/Page.html#path">path</a> | |
of the page template to render | |
</li> | |
<li>request - | |
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 - | |
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 - | |
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. 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. 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"> | |
<html> | |
<body> | |
Customer ID: <span class="red">$</span><span class="blue">customerId</span> | |
</body> | |
</html> </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 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. 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 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 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 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 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. 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 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 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"> | |
<html> | |
<head> | |
<title>Customer Details</title> | |
</head> | |
<body> | |
<h1>Customer Details</h1> | |
<pre> | |
Full Name: <span class="st">$customer.fullName</span> | |
Email: <span class="st">$customer.email</span> | |
Telephone: <span class="st">$customer.telephone</span> | |
</pre> | |
</body> | |
</html> | |
</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 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 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 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 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 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. 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"> | |
<html> | |
<head> | |
<title><span class="blue">$title</span></title> | |
<link rel="stylesheet" type="text/css" href="style.css" title="Style"/> | |
</head> | |
<body> | |
<h2 class="title"><span class="blue">$title</span></h2> | |
<span class="red">#parse</span>(<span class="blue">$path</span>) | |
</body> | |
</html> </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"> | |
<page path="<span class="blue">home.htm</span>" classname="Home"/> | |
</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"> | |
<b>Welcome</b> 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"> | |
<html> | |
<head> | |
<title>Home</title> | |
<link rel="stylesheet" type="text/css" href="style.css" title="Style"/> | |
</head> | |
<body> | |
<h2 class="title">Home</h2> | |
<b>Welcome</b> to Home page your application starting point. | |
</body> | |
</html> </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. 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. 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 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 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 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. 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"> | |
<page path="<span class="blue">click/error.htm</span>" classname="<span class="red">org.apache.click.util.ErrorPage</span>"/> | |
</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"> | |
<page path="<span class="blue">click/error.htm</span>" classname="<span class="red">com.mycorp.page.ErrorPage</span>"/> | |
</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. 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"> | |
<page path="<span class="blue">click/not-found.htm</span>" classname="<span class="red">org.apache.click.Page</span>"/> | |
</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. 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"> | |
<h1> <span class="red">$</span><span class="blue">messages.title</span> </h1> </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> |