<?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><page path="<varname>search.htm</varname>" classname="<token>com.mycorp.page.Search</token>"/></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><page path="<varname>search.jsp</varname>" classname="<token>com.mycorp.page.Search</token>"/></literallayout> | |
<para>At runtime Click automatically converts the page path from | |
<varname>.jsp</varname> to <varname>.htm</varname> and back, so there is no | |
need to map the <varname>.jsp</varname> extension in <filename>web.xml</filename>. | |
</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's 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 have an associated | |
<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 (context, format, headers, | |
path). Next, request parameter values are bound to any matching public | |
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, | |
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 fields using the fields name</para> | |
</listitem> | |
<listitem> | |
<para>context - the Servlet context path, e.g. /mycorp</para> | |
</listitem> | |
<listitem> | |
<para>format - 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 - 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 - 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 - 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 - the | |
<ulink url="../../click-api/org/apache/click/Page.html#path">path</ulink> of | |
the page template to render | |
</para> | |
</listitem> | |
<listitem> | |
<para>request - the pages | |
<ulink url="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletRequest.html">HttpServletRequest</ulink> | |
object | |
</para> | |
</listitem> | |
<listitem> | |
<para>response - the pages | |
<ulink url="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpServletResponse.html">HttpServletResponse</ulink> | |
object | |
</para> | |
</listitem> | |
<listitem> | |
<para>session - the | |
<ulink url="../../click-api/org/apache/click/util/SessionMap.html">SessionMap</ulink> | |
adaptor for the users | |
<ulink url="http://java.sun.com/products/servlet/2.3/javadoc/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 fields 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 fields. 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 recieves 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 field | |
<varname>customerId</varname>. | |
</para> | |
<para>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 <varname>customerId</varname> field 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"><html> | |
<body> | |
Customer ID: <symbol>$</symbol><varname>customerId</varname> | |
</body> | |
</html></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 field: | |
</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"><web-app> | |
... | |
<servlet> | |
<servlet-name>ClickServlet</servlet-name> | |
<servlet-class>org.apache.click.ClickServlet</servlet-class> | |
<init-param> | |
<param-name>type-converter-class</param-name> | |
<param-value>com.mycorp.util.CustomTypeConverter</param-value> | |
</init-param> | |
<load-on-startup>0</load-on-startup> | |
</servlet> | |
<servlet-mapping> | |
<servlet-name>ClickServlet</servlet-name> | |
<url-pattern>*.htm</url-pattern> | |
</servlet-mapping> | |
... | |
</web-app></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://java.sun.com/products/servlet/2.3/javadoc/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://java.sun.com/products/servlet/2.3/javadoc/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://java.sun.com/products/servlet/2.3/javadoc/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://java.sun.com/products/servlet/2.3/javadoc/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"><html> | |
<head> | |
<title>Customer Details</title> | |
</head> | |
<body> | |
<h1>Customer Details</h1> | |
<pre> | |
Full Name: <varname>$customer.fullName</varname> | |
Email: <varname>$customer.email</varname> | |
Telephone: <varname>$customer.telephone</varname> | |
</pre> | |
</body> | |
</html></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://java.sun.com/products/servlet/2.3/javadoc/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://java.sun.com/products/servlet/2.3/javadoc/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">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"><html> | |
<head> | |
<title><varname>$title</varname></title> | |
<link rel="stylesheet" type="text/css" href="style.css" title="Style"/> | |
</head> | |
<body> | |
<h2 class="title"><varname>$title</varname></h2> | |
<command>#parse</command>(<varname>$path</varname>) | |
</body> | |
</html> | |
</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><page path="<varname>home.htm</varname>" classname="<token>Home</token>"/></literallayout> | |
<programlisting language="java">public class Home extends BorderedPage { | |
public String title = "Home"; | |
}</programlisting> | |
<para>The Home page's content <varname>home.htm</varname>: | |
</para> | |
<literallayout><b>Welcome</b> 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"><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></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-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>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://java.sun.com/products/servlet/2.3/javadoc/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><page path="<varname>click/error.htm</varname>" classname="<symbol>org.apache.click.util.ErrorPage</symbol>"/></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><page path="<varname>click/error.htm</varname>" classname="<symbol>com.mycorp.page.ErrorPage</symbol>"/></literallayout> | |
<para>When the ClickSevlet 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><page path="<varname>click/not-found.htm</varname>" classname="<symbol>org.apache.click.Page</symbol>"/></literallayout> | |
<para>You can override the default configuration and specify your own class, | |
but you cannot change the path. | |
</para> | |
<para>When the ClickSevlet 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><h1> <symbol>$</symbol><varname>messages.title</varname> </h1></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"><html> | |
<head> | |
<varname>$headElements</varname> | |
</head> | |
<body> | |
... | |
<varname>$jsElements</varname> | |
</body> | |
</html> </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"><html> | |
<head> | |
<link rel="stylesheet" type="text/css" href="<symbol>/myapp/mypage.css</symbol>"></link> | |
<style rel="stylesheet" type="text/css"> | |
<symbol>body { font-family: Verdana; }</symbol> | |
</style> | |
</head> | |
<body> | |
... | |
<script type="text/javascript" src="<symbol>/myapp/mypage.js</symbol>"/> | |
<script type="text/javascript"> | |
<symbol>alert('Welcome to MyPage');</symbol> | |
</script> | |
</body> | |
</html> </programlisting> | |
<para>A live demo showing how to add HEAD elements to a Page can be seen | |
<ulink url="http://www.avoka.com/click-examples/general/page-head-demo.htm">here</ulink>. | |
</para> | |
</sect1> | |
</chapter> |