<!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"/> | |
<link rel="stylesheet" type="text/css" href="../syntax-highlighter.css"/> | |
<script type="text/javascript" src="../syntax-highlighter.js"></script> | |
</head> | |
<!--Activate syntax highlighting--> | |
<body onload="prettyPrint();"> | |
<h1>Best Practices</h1> | |
This section discusses Best Practices for designing and building Click application. | |
The following topics are covered: | |
<ol> | |
<li><a href="#security">Security</a> - use JEE role based security | |
</li> | |
<li><a href="#packages-classes">Packages and Classes</a> - project package structures and page classes | |
</li> | |
<li><a href="#automapping">Page Auto Mapping</a> - use Convention over Configuration | |
</li> | |
<li><a href="#navigation">Navigation</a> - use Page classes to forward and redirect | |
</li> | |
<li><a href="#templating">Templating</a> - to standardize your web application | |
</li> | |
<li><a href="#menus">Menus</a> - centralize your page navigation | |
</li> | |
<li><a href="#logging">Logging</a> - use Log4j in a base page | |
</li> | |
<li><a href="#error-handling">Error Handling</a> - use custom error page | |
</li> | |
<li><a href="#performance">Performance</a> - enhancing page performance | |
</li> | |
</ol> | |
<p> </p> | |
<a name="security" class="heading"></a><h2>1. Security</h2> | |
For application security it is highly recommended that you use the declarative | |
JEE Servlet path role based security model. While Click pages provide an | |
<tt>onSecurityCheck()</tt> method for rolling your own programatic security model, | |
the declarative JEE model provides numerous advantages. | |
<p/> | |
These advantages include: | |
<ul> | |
<li> | |
Its an industry standard pattern making development and maintenance easier. | |
</li> | |
<li> | |
Application servers generally provide numerous ways of integration with an | |
organisations security infrastructure, including LDAP directories and | |
relational databases. | |
</li> | |
<li> | |
Servlet security model support users bookmarking pages. When users go to access | |
these pages later, the container will automatically authenticate them before | |
allowing them to access the resource. | |
</li> | |
<li> | |
Using this security model you can keep your Page code free of security | |
concerns. This makes you code more reusable, or at least easier to write. | |
</li> | |
</ul> | |
If your application has very fine grained or complex security requirements you | |
may need to combine both the JEE declarative security model and a programmatic | |
security model to meet your needs. In these cases its recommended you use declarative | |
security for course grained access and programmatic security for finner grained | |
access control. | |
<h4>Declarative Security</h4> | |
The declarative JEE Servlet security model requires users to be authenticated and | |
in the right roles before they can access secure resources. Relative to many of | |
the JEE specifications the Servlet security model is surprisingly simple. | |
<p/> | |
For example to secure admin pages, you add a security | |
constraint in your <tt>web.xml</tt> file. This requires users to be in the | |
<span class="blue">admin</span> role before they can access to any resources | |
under the <span class="red">admin</span> directory: | |
<pre class="codeConfig"> | |
<security-constraint> | |
<web-resource-collection> | |
<web-resource-name>admin</web-resource-name> | |
<url-pattern><span class="red">/admin/*</span></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><span class="blue">admin</span></role-name> | |
</auth-constraint> | |
</security-constraint> </pre> | |
The application user roles are defined in the <tt>web.xml</tt> file as | |
<tt>security-role</tt> elements: | |
<pre class="codeConfig"> | |
<security-role> | |
<role-name><span class="blue">admin</span></role-name> | |
</security-role> </pre> | |
The Servlet security model supports three different authentication method: | |
<ul> | |
<li> | |
<tt>BASIC</tt> - only recommended for internal applications where security | |
is not important. This is the easiest authentication method, which simply | |
displays a dialog box to users requiring them to authenticate before | |
accessing secure resources. The BASIC method is relatively unsecure as the | |
username and password are posted to the server as a Base64 encoded string. | |
</li> | |
<li> | |
<tt>DIGEST</tt> - recommended for internal applications with a moderate level | |
of security. As with BASIC authentication, this method simply displays a | |
dialog box to users requiring them to authenticate before accessing | |
secure resources. Not all application servers support DIGEST authentication, | |
with only more recent versions of Apache Tomcat supporting this method. | |
</li> | |
<li> | |
<tt>FORM</tt> - recommended applications for where you need a customised | |
login page. For applications requiring a high level of security it is | |
recommended that you use the FORM method over HTTPS. | |
</li> | |
</ul> | |
The authentication method is specified in the <login-method> element. For | |
example to use the BASIC authentication method you would specify: | |
<pre class="codeConfig"> | |
<login-config> | |
<auth-method><span class="blue">BASIC</span></auth-method> | |
<realm-name>Admin Realm</realm-name> | |
</login-config> </pre> | |
To use the FORM method you also need to specify the path to the login page and | |
the login error page: | |
<pre class="codeConfig"> | |
<login-config> | |
<auth-method><span class="blue">FORM</span></auth-method> | |
<realm-name>Secure Realm</realm-name> | |
<form-login-config> | |
<form-login-page><span class="red">/login.htm</span></form-login-page> | |
<form-error-page><span class="red">/login.htm?auth-error=true</span></form-error-page> | |
</form-login-config> | |
</login-config> </pre> | |
In your Click <tt>login.htm</tt> page you need to include a special | |
<span class="blue">j_security_check</span> form which includes the input | |
fields <span class="blue">j_username</span> and <span class="blue">j_password</span>. | |
For example: | |
<pre class="codeHtml"> | |
<span class="kw">#if</span> ($request.getParameter(<span class="red">"auth-error"</span>)) | |
<div style="margin-bottom:1em;margin-top:1em;color:red;"> | |
Invalid User Name or Password, please try again.<br/> | |
Please ensure Caps Lock is off. | |
</div> | |
<span class="kw">#end</span> | |
<form method="POST" action="<span class="blue">j_security_check</span>" name="form"> | |
<table border="0" style="margin-left:0.25em;"> | |
<tr> | |
<td><label>User Name</label><font color="red">*</font></td> | |
<td><input type="text" name="<span class="blue">j_username</span>" maxlength="20" style="width:150px;"/></td> | |
<td>&nbsp;</td> | |
</tr> | |
<tr> | |
<td><label>User Password</label><font color="red">*</font></td> | |
<td><input type="password" name="<span class="blue">j_password</span>" maxlength="20" style="width:150px;"/></td> | |
<td><input type="image" src="$context/images/login.png" title="Click to Login"/></td> | |
</tr> | |
</table> | |
</form> | |
<script type="text/javascript"> | |
document.form.j_username.focus(); | |
</script> </pre> | |
When using FORM based authentication do <b>NOT</b> put application logic in a | |
Click Login Page class, as the role of this page is to simply render the login | |
form. If you attempt to put navigation logic in your Login Page class, the | |
JEE Container may simply ignore it or throw errors. | |
<p/> | |
Putting this all together below is a <tt>web.xml</tt> snippet which features | |
security constraints for pages under the admin path and the user path. This | |
configuration uses the FORM method for authentication, and will also redirect | |
unauthorized (403) requests to the <tt>/not-authorized.htm</tt> page. | |
<pre class="codeConfig"> | |
<web-app> | |
.. | |
<error-page> | |
<error-code>403</error-code> | |
<location><span class="green">/not-authorized.htm</span></location> | |
</error-page> | |
<security-constraint> | |
<web-resource-collection> | |
<web-resource-name>admin</web-resource-name> | |
<url-pattern><span class="blue">/admin/*</span></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><span class="red">admin</span></role-name> | |
</auth-constraint> | |
</security-constraint> | |
<security-constraint> | |
<web-resource-collection> | |
<web-resource-name>user</web-resource-name> | |
<url-pattern><span class="blue">/user/*</span></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><span class="red">admin</span></role-name> | |
<role-name><span class="red">user</span></role-name> | |
</auth-constraint> | |
</security-constraint> | |
<login-config> | |
<auth-method><span class="green">FORM</span></auth-method> | |
<realm-name>Secure Zone</realm-name> | |
<form-login-config> | |
<form-login-page><span class="green">/login.htm</span></form-login-page> | |
<form-error-page><span class="green">/login.htm?auth-error=true</span></form-error-page> | |
</form-login-config> | |
</login-config> | |
<security-role> | |
<role-name><span class="red">admin</span></role-name> | |
</security-role> | |
<security-role> | |
<role-name><span class="red">user</span></role-name> | |
</security-role> | |
</web-app> | |
</pre> | |
<h4>Alternative Security solutions</h4> | |
There are also alternative security solutions that provide extra features not | |
available in JEE, such as RememberMe functionality, better resource mapping | |
and <tt>Post Logon Page</tt> support. (<tt>Post Logon Page</tt> support allows one to | |
specify a default URL where the user will be forwarded after successful login. | |
This feature allows one to embed a login form in all non-secure pages and after | |
successful authentication the user will be forwarded to their home page.) | |
<p/> | |
Below are some of the alternative security solutions available: | |
<ul> | |
<li> | |
<a class="external" target="_blank" href="http://static.springframework.org/spring-security/site/index.html">Spring Security</a> | |
</li> | |
<li> | |
<a class="external" target="_blank" href="http://securityfilter.sourceforge.net/">SecurityFilter</a> | |
</li> | |
<li> | |
<a class="external" target="_blank" href="http://www.jsecurity.org/">JSecurity</a> | |
</li> | |
</ul> | |
<h4>Resources</h4> | |
For more information on using security see the resources below: | |
<ul> | |
<li><a href="http://stc.cis.brown.edu/~stc/Projects/Shibboleth/Version-3/Checklist/Tomcat-Authn/FormBasedAuthentication.pdf">Form Based Authentication</a> by Louis E. Mauget | |
</li> | |
<li><a href="http://java.sun.com/products/servlet/download.html">Servlet Specification</a> by Sun Microsystems | |
</li> | |
<li><a href="http://en.wikipedia.org/wiki/Basic_authentication_scheme">Basic authentication scheme</a> | |
</li> | |
<li><a href="http://en.wikipedia.org/wiki/Digest_access_authentication">Digest authentication scheme</a> | |
</li> | |
<li><a href="http://en.wikipedia.org/wiki/Https">Https URI scheme</a> | |
</li> | |
</ul> | |
<a name="packages-classes" class="heading"></a><h2>2. Packages and Classes</h2> | |
An excellent way to design your project package structure is the classify packages | |
initially by technology. So in a Click application all of our pages would be contained | |
under a <tt>page</tt> package. This also works very well with the Page automapping feature. | |
<p/> | |
All the projects domain entity classes would be contained under a <tt>entity</tt> package, | |
and service classes would be contained under a <tt>service</tt> directory. Note alternative | |
names for the <tt>entity</tt> package include domain or model. We also typically have a | |
<tt>util</tt> package for any stray classes which don't quite fit into the other packages. | |
<p/> | |
In Java package names are singular by convention, so we have a util package rather than | |
an utils package. | |
<p/> | |
An example project structure for a MyCorp web application is illustrated below: | |
<blockquote> | |
<img alt="Example Project Structure" src="../images/packages-classes.png"/> | |
</blockquote> | |
In this example application we use declarative role and path based security. | |
All the pages in the <tt>admin</tt> package and directory require the <tt>"admin"</tt> | |
role to be access, while all the pages in the <tt>user</tt> package and directory | |
require the <tt>"user"</tt> role to be accessed. | |
<h4>Page Classes</h4> | |
A best practice when developing application Page classes is to place common | |
methods in a base page class. This is typically used for providing access methods | |
to application services and logger objects. | |
<p/> | |
For example the BasePage below provides access to Spring configured service objects | |
and a Log4J logger object: | |
<pre class="codeJava"> | |
<span class="kw">public class</span> BasePage <span class="kw">extends</span> Page <span class="kw">implements</span> ApplicationContextAware { | |
<span class="cm">/** The Spring application context. */</span> | |
<span class="kw">protected</span> ApplicationContext applicationContext; | |
<span class="cm">/** The page Logger instance. */</span> | |
<span class="kw">protected</span> Logger logger; | |
<span class="cm">/** | |
* Return the Spring configured Customer service. | |
* | |
* @return the Spring configured Customer service | |
*/</span> | |
<span class="kw">public</span> CustomerService getCustomerService() { | |
<span class="kw">return</span> (CustomerService) getBean(<span class="st">"customerService"</span>); | |
} | |
<span class="cm">/** | |
* Return the Spring configured User service. | |
* | |
* @return the Spring configured User service | |
*/</span> | |
<span class="kw">public</span> UserService getUserService() { | |
<span class="kw">return</span> (UserService) getBean(<span class="st">"userService"</span>); | |
} | |
<span class="cm">/** | |
* Return the page Logger instance. | |
* | |
* @return the page Logger instance | |
*/</span> | |
<span class="kw">public</span> Logger getLogger() { | |
<span class="kw">if</span> (logger == <span class="kw">null</span>) { | |
logger = Logger.getLogger(getClass()); | |
} | |
<span class="kw">return</span> logger; | |
} | |
<span class="cm">/** | |
* @see ApplicationContextAware#setApplicationContext(ApplicationContext) | |
*/</span> | |
<span class="kw">public void</span> setApplicationContext(ApplicationContext applicationContext) { | |
this.applicationContext = applicationContext; | |
} | |
<span class="cm">/** | |
* Return the configured Spring Bean for the given name. | |
* | |
* @param beanName the configured name of the Java Bean | |
* @return the configured Spring Bean for the given name | |
*/</span> | |
<span class="kw">public</span> Object getBean(String beanName) { | |
return applicationContext.getBean(beanName); | |
} | |
} </pre> | |
Applications typically use a border template and have a <tt>BorderPage</tt> which | |
extends <tt>BasePage</tt> and defines the template. For example: | |
<pre class="codeJava"> | |
<span class="kw">public class</span> BorderPage <span class="kw">extends</span> BasePage { | |
<span class="cm">/** The root Menu item. */</span> | |
<span class="kw">public</span> Menu rootMenu = <span class="kw">new</span> Menu(); | |
<span class="cm">/** | |
* @see Page#getTemplate() | |
*/</span> | |
<span class="kw">public</span> String getTemplate() { | |
<span class="kw">return</span> <span class="st">"/border-template.htm"</span>; | |
} | |
} </pre> | |
Most application pages subclass <tt>BorderPage</tt>, except AJAX pages which | |
have no need for a HTML border template and typically extend <tt>BasePage</tt>. | |
The <tt>BorderPage</tt> class should not include common logic, other than that | |
required for rendering the border template. Common page logic should be defined | |
in the <tt>BasePage</tt> class. | |
<p/> | |
To prevent these base Page classes being auto mapped, and becoming directly | |
acessible web pages, ensure that there are no page templates which could match | |
their class name. For example the <tt>BorderPage</tt> class above will not be | |
auto mapped to <span class="st">border-template.htm</span>. | |
<a name="automapping" class="heading"></a><h2>3. Page Auto Mapping</h2> | |
You should use the Click page automapping configuration feature. See | |
the <a href="configuration.html#application-automapping">Page Automapping</a> | |
topic for details. | |
<p/> | |
Automapping will save you from having to manually configure URL path to Page class | |
mappings in your <tt>click.xml</tt> file. If you follow this convention it is very | |
easy to maintain and refactor applications. | |
<p/> | |
You can also quickly determine what the corresponding Page class is for a page | |
HTML template and visa versa, and if you use the ClickIDE Eclipse plugin you can | |
switch between a page's class and template by pressing Ctrl+Alt+S. | |
<p/> | |
An example <tt>click.xml</tt> automapping configuration is provided below | |
(automapping is enabled by default): | |
<pre class="codeConfig"> | |
<click-app> | |
<pages <span class="blue">package</span>="<span class="red">com.mycorp.dashboard.page</span>"/> | |
</click-app> </pre> | |
To see how the page templates are mapped to Page classes set the application | |
<a href="configuration.html#application-mode">mode</a> to <tt>debug</tt> and | |
at startup the mappings will be listed out. An example Click startup listing is | |
provided below: | |
<pre class="codeConfig"> | |
[Click] [debug] automapped pages: | |
[Click] [debug] /category-tree.htm -> com.mycorp.dashboard.page.CategoryTree | |
[Click] [debug] /process-list.htm -> com.mycorp.dashboard.page.ProcessList | |
[Click] [debug] /user-list.htm -> com.mycorp.dashboard.page.UserList </pre> | |
<a name="navigation" class="heading"></a><h2>4. Navigation</h2> | |
When navigating between Pages using forwards and redirects, you should refer | |
to the target page using the Page class rather than using path. | |
This provides you compile time checking and will save you from having to update | |
path strings in Java code if you move pages about. | |
<p/> | |
To forward to another page using the Page class: | |
<pre class="codeJava"> | |
<span class="kw">public class</span> CustomerListPage <span class="kw">extends</span> Page { | |
<span class="kw">public</span> ActionLink customerLink = <span class="kw">new</span> ActionLink(<span class="kw">this</span>, <span class="st">"onCustomerClick"</span>); | |
.. | |
<span class="kw">public boolean</span> onCustomerClick() { | |
Integer id = customerLink.getValueInteger(); | |
Customer customer = getCustomerService().getCustomer(id); | |
CustomerDetailPage customerDetailPage = (CustomerDetailPage) | |
getContext().createPage(CustomerDetailPage.<span class="kw">class</span>); | |
customerDetailPage.setCustomer(customer); | |
setForward(customerDetailPage); | |
<span class="kw">return false</span>; | |
} | |
} </pre> | |
To redirect to another page using the Page class you can obtain the pages path | |
from the <tt>Context</tt>. In the example below we are passing through the | |
customer id as a request parameter to the target page. | |
<pre class="codeJava"> | |
<span class="kw">public class</span> CustomerListPage <span class="kw">extends</span> Page { | |
<span class="kw">public</span> ActionLink customerLink = <span class="kw">new</span> ActionLink(<span class="kw">this</span>, <span class="st">"onCustomerClick"</span>); | |
.. | |
<span class="kw">public boolean</span> onCustomerClick() { | |
String id = customerLink.getValueInteger(); | |
String path = getContext().getPagePath(CustomerDetailPage.<span class="kw">class</span>); | |
setRedirect(path + <span class="st">"?id="</span> + id); | |
<span class="kw">return false</span>; | |
} | |
} </pre> | |
A quick way of redirecting to another page is to simply refer to the target class. | |
The example below logs a user out, by invalidating their session, and then redirects | |
them to the applications home page. | |
<pre class="codeJava"> | |
<span class="kw">public boolean</span> onLogoutClick() { | |
getContext().getSession().invalidate(); | |
setRedirect(HomePage.<span class="kw">class</span>); | |
<span class="kw">return false</span>; | |
} | |
</pre> | |
<a name="templating" class="heading"></a><h2>5. Templating</h2> | |
Use Page templating it is highly recommended. Page templates provide numerous | |
advantages including: | |
<ul> | |
<li>greatly reduce the amount of HTML you need to maintain</li> | |
<li>ensure you have a common look and feel across your application</li> | |
<li>make global application changes very easy</li> | |
</ul> | |
To see how to use templates see the <a href="pages.html#page-templating">Page Templating</a> topic. | |
Also see the Click <a href="examples.html">Examples</a> use of page templating. | |
<a name="menus" class="heading"></a><h2>6. Menus</h2> | |
For many applications using the <a href="extras-api/org/apache/click/extras/control/Menu.html">Menu</a> | |
control to centralize application navigation is very useful. | |
Menus are defined in a <tt>WEB-INF/menu.xml</tt> file which is very easy to change. | |
<p/> | |
An menu is typically defined in the a page border template so they are available | |
through out the application. The Menu control does not support HTML rendering, | |
so you need to define a Velocity macro to programmatically render the menu. | |
You would call the macro in your border template with code like this: | |
<pre class="codeHtml"> | |
<span class="red">#</span><span class="blue">writeMenu</span>(<span class="red">$</span>rootMenu) </pre> | |
An advantage of using a macro to render your menu is that you can reuse the code | |
across different applications, and to modify an applications menu you simply need | |
to edit the <tt>WEB-INF/menu.xml</tt> file. | |
A good place to define your macros is in the webroot <tt>/macro.vm</tt> file | |
as it is automatically included by Click. | |
<p/> | |
Using macros you can create dynamic menu behaviour such as only rendering menu items | |
a user is authorized to access with <a href="extras-api/org/apache/click/extras/control/Menu.html#isUserInRoles()">isUserInRoles()</a>. | |
<pre class="codeHtml"> | |
<span class="kw">#if</span> (<span class="red">$</span>menu.isUserInRoles()) | |
.. | |
<span class="kw">#end</span> </pre> | |
You can also use JavaScript to add dynamic behaviour such as drop down menus, | |
for example see the Menu page in Click <a href="examples.html">Examples</a>. | |
<a name="logging" class="heading"></a><h2>7. Logging</h2> | |
For page logging you should use <a href="http://logging.apache.org/log4j/docs/">Log4j</a> | |
library. An alternative library is the | |
<a href="http://jakarta.apache.org/commons/logging/">Commons Logging</a>. | |
If you are using Commons Logging please be aware that there have been class loader | |
issues with this library on some application servers. If you are using | |
Commons Logging please make sure you have the latest version. | |
<p/> | |
The best place to define your logger is in a common base page, for example: | |
<pre class="codeJava"> | |
<span class="kw">public class</span> BasePage <span class="kw">extends</span> Page { | |
<span class="kw">protected</span> Logger logger; | |
<span class="kw">public</span> Logger getLogger() { | |
<span class="kw">if</span> (logger == <span class="kw">null</span>) { | |
logger = Logger.getLogger(getClass()); | |
} | |
<span class="kw">return</span> logger; | |
} | |
} </pre> | |
Using this pattern all your application bases should extend <tt>BasePage</tt> | |
so they can use the <tt>getLogger()</tt> method. | |
<pre class="codeJava"> | |
<span class="kw">public class</span> CustomerListPage <span class="kw">extends</span> BasePage { | |
<span class="kw">public void</span> onGet() { | |
<span class="kw">try</span> { | |
.. | |
} <span class="kw">catch</span> (Exception e) { | |
getLogger().error(e); | |
} | |
} | |
} </pre> | |
If you have some very heavy debug statement you should possibly use an | |
<tt>isDebugEnabled</tt> switch so it is not invoked if debug is not required. | |
<pre class="codeJava"> | |
<span class="kw">public class</span> CustomerListPage <span class="kw">extends</span> BasePage { | |
<span class="kw">public void</span> onGet() { | |
<span class="kw">if</span> (getLogger().isDebugEnabled()) { | |
String msg = .. | |
getLogger().debug(msg); | |
} | |
.. | |
} | |
} </pre> | |
<p/> | |
Please note the Click logging facility is not designed for application use, and is | |
for Click internal use only. When Click is running in <tt>production</tt> mode | |
it will not produce any logging output. | |
<a name="error-handling" class="heading"></a><h2>8. Error Handling</h2> | |
In Click unhandled errors are directed to the <a href="click-api/org/apache/click/util/ErrorPage.html">ErrorPage</a> | |
for display. If applications require additional error handling they can create and | |
register a custom error page in <tt>WEB-INF/click.xml</tt>. For example: | |
<pre class="codeConfig"> | |
<pages package="<span class="red">com.mycorp.page</span>" automapping="true"/> | |
<page path="<span class="blue">click/error.htm</span>" classname="<span class="red">ErrorPage</span>"/> | |
</pages> </pre> | |
Generally application handle transactional errors using service layer code or via a | |
servlet <a class="external" target="_blank" href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/Filter.html">Filter</a> and would not | |
need to include error handling logic in an error page. | |
<p/> | |
Potential uses for a custom error page include custom logging. | |
<p/> | |
For example if an application requires unhandled errors to be logged to an | |
application log (rather than System.out) then a custom | |
<a href="click-api/org/apache/click/util/ErrorPage.html">ErrorPage</a> could be | |
configured. An example <tt>ErrorPage</tt> error logging page is provided | |
below: | |
<pre class="codeJava"> | |
<span class="kw">package</span> com.mycorp.page.ErrorPage; | |
.. | |
<span class="kw">public class</span> ErrorPage <span class="kw">extends</span> org.apache.click.util.ErrorPage { | |
<span class="kw">public void</span> onDestory() { | |
Logger.getLogger(getClass()).error(getError()); | |
} | |
} </pre> | |
<a name="performance" class="heading"></a><h2>9. Performance</h2> | |
Yahoo published a list of <a target="_blank" class="external" href="http://developer.yahoo.com/performance/rules.html">best practices</a> | |
for improving web application performance. | |
<p/> | |
Click Framework provides a <a href="extras-api/org/apache/click/extras/filter/PerformanceFilter.html">PerformanceFilter</a> | |
which caters for some of these rules. However not all rules can be easily automated. | |
<p/> | |
This section will outline ways to apply rules which are not covered by the PerformanceFilter namely, <a target="_blank" class="external" href="http://developer.yahoo.com/performance/rules.html#num_http">#1 - Minimize HTTP Requests (by combining files)</a> | |
and <a target="_blank" class="external" href="http://developer.yahoo.com/performance/rules.html#minify">#10 - Minify Javascript and CSS</a>. | |
<p/> | |
Rule #1 also mentions <a target="_blank" class="external" href="http://alistapart.com/articles/sprites">CSS Sprites</a>, | |
a method for combining multiple images into a single master image. CSS Sprites | |
is not covered here. | |
<p/> | |
It is worth pointing out that its not necessary to blindly optimize every page in your application. | |
Instead concentrate on popular pages, for example a web site's <em>Home Page</em> | |
would be a good candidate. | |
<p/> | |
There are a couple of tools that are useful in applying Rule #1 and #10: | |
<ul> | |
<li> | |
<a target="_blank" class="external" href="http://developer.yahoo.com/yui/compressor/">YUICompressor</a> | |
- minifies and compresses JavaScript and CSS files so less | |
bytes have to be transferred across the wire. | |
</li> | |
<li> | |
<a target="_blank" class="external" href="http://code.google.com/p/javaflight-code/">Ant Task for YUICompressor</a> | |
- an Ant task that uses YUICompressor to compress JavaScript and CSS files. | |
</li> | |
<li> | |
<a target="_blank" class="external" href="http://www.crockford.com/javascript/jsmin.html">JSMin</a> | |
- similar to YUICompressor but only minifies (remove whitespace and newlines) JavaScript files and | |
does no compression at all. An advantage of JSMin over YUICompressor | |
is that its faster and can be used at runtime to minify JavaScript, while YUICompressor | |
is most often used at build time. | |
</li> | |
</ul> | |
Below are some articles outlining how to use YUICompressor and Ant to concatenate and | |
compress JavaScript and CSS files: | |
<ul> | |
<li> | |
<a target="_blank" class="external" href="http://www.julienlecomte.net/blog/2007/09/16/">Article</a> explaining | |
how to use Ant and YUICompressor for compression. | |
</li> | |
<li> | |
<a target="_blank" class="external" href="http://javaflight.blogspot.com/2008/01/introducing-yui-compressor-ant-task.html">Article</a> | |
outlining how to use a special YUICompressor Ant Task for compression. | |
</li> | |
</ul> | |
<p/> | |
Using one of the approaches above you can concatenate and compress all JavaScript and CSS | |
for your Pages into two separate files, for example <tt>home-page.css</tt> and <tt>home-page.js</tt>. | |
Note that the two files must include all the JavaScript and CSS that is generated | |
by the Page and its Controls. Then you can instruct Click to <em>only</em> | |
include the two compressed files, home-page.css and home-page.js. | |
<p/> | |
Click makes use of the utility class <a href="click-api/org/apache/click/util/PageImports.html">PageImports</a> | |
to include the CSS and JavaScript. PageImports exposes the method | |
<a href="click-api/org/apache/click/util/PageImports.html#setInitialized(boolean)">setInitialized(boolean)</a>, | |
which controls when PageImports are fully initialized. Once PageImports have been | |
initialized, no other CSS and JavaScript will be included. | |
<p/> | |
Knowing this one can override <a href="click-api/org/apache/click/Page.html#getPageImports()">Page.getPageImports()</a>, | |
and import the necessary JavaScript and CSS files and then set PageImports to <tt>initialized</tt>, | |
forcing PageImports to skip other CSS and JavaScript files. | |
<p/> | |
Here is an example: | |
<pre class="prettyprint"> public class HomePage extends Page { | |
private Form form = new Form("form"); | |
public void onInit() { | |
form.add(new EmailField("email"); | |
addControl(form); | |
} | |
public void getPageImports () { | |
PageImports pageImports = super.getPageImports(); | |
String contextPath = getContext().getRequest().getContextPath(); | |
String cssInclude = contextPath + "/assets/css/home-page.css"; | |
pageImports.addImport("<link type=\"text/javascript\" href=\"" + cssInclude + "\"/>"); | |
String jsInclude = contextPath + "/assets/js/home-page.js"; | |
pageImports.addImport("<script type=\"text/javascript\" src=\"" + jsInclude + "\"></script>"); | |
// Set pageImports to initialized so that no other CSS and JavaScript files will be included. | |
pageImports.setInitialized(true); | |
} | |
} </pre> | |
Using the following <tt>border-template.htm</tt>: | |
<pre class="prettyprint"> | |
<html> | |
<head> | |
<title>Click Examples</title> | |
${cssImports} | |
</head> | |
<body> | |
... | |
${jsImports} | |
</body> | |
</html> | |
</pre> | |
the rendered HTML will include one CSS and one JavaScript import: | |
<pre class="prettyprint"> | |
<html> | |
<head> | |
<title>Click Examples</title> | |
<link type="text/css" rel="stylesheet" href="/click-examples/assets/css/home-page.css" title="Style"/> | |
</head> | |
<body> | |
... | |
<script type="text/javascript" src="/click-examples/assets/js/home-page.js"></script> | |
</body> | |
</html> | |
</pre> | |
<p/> | |
A live demo is available <a target="_blank" class="external" href="http://www.avoka.com/click-examples/general/page-imports-example.htm">here</a> | |
<p/> | |
</body> | |
</html> |