<?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-best-practices" remap="h1"> | |
<title>Best Practices</title> | |
<para>This chapter discusses Best Practices for designing and building Apache | |
Click applications. | |
</para> | |
<sect1 id="security" remap="h2"> | |
<title>Security</title> | |
<para>For application security it is highly recommended that you use the | |
declarative JEE Servlet path role based security model. While Click pages | |
provide an <methodname>onSecurityCheck()</methodname> method for rolling your own | |
programmatic security model, the declarative JEE model provides numerous | |
advantages. | |
</para> | |
<para>These advantages include: | |
</para> | |
<itemizedlist> | |
<listitem> | |
<para> Its an industry standard pattern making development and maintenance | |
easier. | |
</para> | |
</listitem> | |
<listitem> | |
<para> Application servers generally provide numerous ways of integration | |
with an organisations security infrastructure, including LDAP directories | |
and relational databases. | |
</para> | |
</listitem> | |
<listitem> | |
<para> 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. | |
</para> | |
</listitem> | |
<listitem> | |
<para> 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. | |
</para> | |
</listitem> | |
</itemizedlist> | |
<para>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. | |
</para> | |
<sect2 id="declarative-security" remap="h4"> | |
<title>Declarative Security</title> | |
<para> 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. | |
</para> | |
<para> | |
For example to secure admin pages, you add a security | |
constraint in your <filename>web.xml</filename> file. This requires users | |
to be in the <varname>admin</varname> role before they can access to any | |
resources under the <symbol>admin</symbol> directory: | |
</para> | |
<programlisting language="xml"><security-constraint> | |
<web-resource-collection> | |
<web-resource-name>admin</web-resource-name> | |
<url-pattern><symbol>/admin/*</symbol></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><varname>admin</varname></role-name> | |
</auth-constraint> | |
</security-constraint></programlisting> | |
<para>The application user roles are defined in the <filename>web.xml</filename> | |
file as <literal>security-role</literal> elements: | |
</para> | |
<programlisting language="xml"><security-role> | |
<role-name><varname>admin</varname></role-name> | |
</security-role></programlisting> | |
<para>The Servlet security model supports three different authentication method:</para> | |
<itemizedlist> | |
<listitem> | |
<para> | |
<literal>BASIC</literal> - 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 insecure as the username and password | |
are posted to the server as a Base64 encoded string. | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<literal>DIGEST</literal> - 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. | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<literal>FORM</literal> - 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. | |
</para> | |
</listitem> | |
</itemizedlist> | |
<para>The authentication method is specified in the <login-method> element. | |
For example to use the BASIC authentication method you would specify: | |
</para> | |
<programlisting language="xml"><login-config> | |
<auth-method><varname>BASIC</varname></auth-method> | |
<realm-name>Admin Realm</realm-name> | |
</login-config></programlisting> | |
<para>To use the FORM method you also need to specify the path to the login | |
page and the login error page: | |
</para> | |
<programlisting language="xml"><login-config> | |
<auth-method><varname>FORM</varname></auth-method> | |
<realm-name>Secure Realm</realm-name> | |
<form-login-config> | |
<form-login-page><symbol>/login.htm</symbol></form-login-page> | |
<form-error-page><symbol>/login.htm?auth-error=true</symbol></form-error-page> | |
</form-login-config> | |
</login-config></programlisting> | |
<para>In your Click <filename>login.htm</filename> page you need to include a | |
special <varname>j_security_check</varname> form which includes the input | |
fields <varname>j_username</varname> and <varname>j_password</varname>. | |
For example: | |
</para> | |
<programlisting language="xml"><command>#if</command> ($request.getParameter("<symbol>auth-error</symbol>")) | |
<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> | |
<command>#end</command> | |
<form method="POST" action="<varname>j_security_check</varname>" 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="<varname>j_username</varname>" 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="<varname>j_password</varname>" 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></programlisting> | |
<para>When using FORM based authentication do <emphasis role="bold">NOT</emphasis> | |
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. | |
</para> | |
<para>Putting this all together below is a <filename>web.xml</filename> | |
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 | |
<filename>/not-authorized.htm</filename> page. | |
</para> | |
<programlisting language="xml"><web-app> | |
.. | |
<error-page> | |
<error-code>403</error-code> | |
<location><varname>/not-authorized.htm</varname></location> | |
</error-page> | |
<security-constraint> | |
<web-resource-collection> | |
<web-resource-name>admin</web-resource-name> | |
<url-pattern><varname>/admin/*</varname></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><symbol>admin</symbol></role-name> | |
</auth-constraint> | |
</security-constraint> | |
<security-constraint> | |
<web-resource-collection> | |
<web-resource-name>user</web-resource-name> | |
<url-pattern><varname>/user/*</varname></url-pattern> | |
</web-resource-collection> | |
<auth-constraint> | |
<role-name><symbol>admin</symbol></role-name> | |
<role-name><symbol>user</symbol></role-name> | |
</auth-constraint> | |
</security-constraint> | |
<login-config> | |
<auth-method><varname>FORM</varname></auth-method> | |
<realm-name>Secure Zone</realm-name> | |
<form-login-config> | |
<form-login-page><varname>/login.htm</varname></form-login-page> | |
<form-error-page><varname>/login.htm?auth-error=true</varname></form-error-page> | |
</form-login-config> | |
</login-config> | |
<security-role> | |
<role-name><symbol>admin</symbol></role-name> | |
</security-role> | |
<security-role> | |
<role-name><symbol>user</symbol></role-name> | |
</security-role> | |
</web-app></programlisting> | |
</sect2> | |
<sect2 id="alternatve-security-solutions" remap="h4"> | |
<title>Alternative Security solutions</title> | |
<para> There are also alternative security solutions that provide extra | |
features not available in JEE, such as RememberMe functionality, better | |
resource mapping and <literal>Post Logon Page</literal> support. | |
(<literal>Post Logon Page</literal> 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.) | |
</para> | |
<para>Below are some of the alternative security solutions available: | |
</para> | |
<itemizedlist> | |
<listitem> | |
<para> | |
<ulink url="http://static.springframework.org/spring-security/site/index.html">Spring Security</ulink> | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://securityfilter.sourceforge.net/">SecurityFilter</ulink> | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://incubator.apache.org/shiro/">Apache Shiro</ulink> | |
</para> | |
</listitem> | |
</itemizedlist> | |
</sect2> | |
<sect2 id="resources" remap="h4"> | |
<title>Resources</title> | |
<para>For more information on using security see the resources below:</para> | |
<itemizedlist> | |
<listitem> | |
<para> | |
<ulink url="http://www.scribd.com/doc/34531398/Form-Based-Authentication"> | |
Form Based Authentication | |
</ulink> by Louis E. Mauget | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://java.sun.com/products/servlet/download.html">Servlet Specification</ulink> | |
by Sun Microsystems | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://en.wikipedia.org/wiki/Basic_authentication_scheme"> | |
Basic authentication scheme | |
</ulink> | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://en.wikipedia.org/wiki/Digest_access_authentication"> | |
Digest authentication scheme | |
</ulink> | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://en.wikipedia.org/wiki/Https">Https URI scheme</ulink> | |
</para> | |
</listitem> | |
</itemizedlist> | |
</sect2> | |
</sect1> | |
<sect1 id="packages-classes" remap="h2"> | |
<title>Packages and Classes</title> | |
<para> An excellent way to design your project package structure is to | |
classify packages initially by technology. So in a Click application all | |
of our pages would be contained under a <package>page</package> package. | |
This also works very well with the Page automapping feature. | |
</para> | |
<para> | |
All the projects domain entity classes would be contained under a | |
<package>entity</package> package, and service classes would be contained | |
under a <package>service</package> directory. Note alternative names for the | |
<package>entity</package> package include domain or model. We also typically | |
have a <package>util</package> package for any stray classes which don't quite | |
fit into the other packages. | |
</para> | |
<para>In Java, package names are singular by convention, so we have a util | |
package rather than a utils package. | |
</para> | |
<para>An example project structure for a MyCorp web application is illustrated | |
below: | |
</para> | |
<figure id="example-project-structure"> | |
<title>Example project structure</title> | |
<inlinemediaobject> | |
<imageobject> | |
<imagedata fileref="images/best-practices/packages-classes.png" format="PNG" scale="65"/> | |
</imageobject> | |
</inlinemediaobject> | |
</figure> | |
<para>In this example application we use declarative role and path based security. | |
All the pages in the <package>admin</package> package and directory require the | |
<literal>"admin"</literal> role to be access, while all the pages in the | |
<package>user</package> package and directory require the <literal>"user"</literal> | |
role to be accessed. | |
</para> | |
<sect2 id="page-classes" remap="h4"> | |
<title>Page Classes</title> | |
<para>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. | |
</para> | |
<para>For example the BasePage below provides access to Spring configured | |
service objects and a Log4J logger object: | |
</para> | |
<programlisting language="java">public class BasePage extends Page implements ApplicationContextAware { | |
/** The Spring application context. */ | |
protected ApplicationContext applicationContext; | |
/** The page Logger instance. */ | |
protected Logger logger; | |
/** | |
* Return the Spring configured Customer service. | |
* | |
* @return the Spring configured Customer service | |
*/ | |
public CustomerService getCustomerService() { | |
return (CustomerService) getBean("customerService"); | |
} | |
/** | |
* Return the Spring configured User service. | |
* | |
* @return the Spring configured User service | |
*/ | |
public UserService getUserService() { | |
return (UserService) getBean("userService"); | |
} | |
/** | |
* Return the page Logger instance. | |
* | |
* @return the page Logger instance | |
*/ | |
public Logger getLogger() { | |
if (logger == null) { | |
logger = Logger.getLogger(getClass()); | |
} | |
return logger; | |
} | |
/** | |
* @see ApplicationContextAware#setApplicationContext(ApplicationContext) | |
*/ | |
public void setApplicationContext(ApplicationContext applicationContext) { | |
this.applicationContext = applicationContext; | |
} | |
/** | |
* 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 | |
*/ | |
public Object getBean(String beanName) { | |
return applicationContext.getBean(beanName); | |
} | |
}</programlisting> | |
<para>Applications typically use a border template and have a | |
<classname>BorderPage</classname> which extends <classname>BasePage</classname> | |
and defines the template. For example: | |
</para> | |
<programlisting language="java">public class BorderPage extends BasePage { | |
/** The root Menu item. */ | |
public Menu rootMenu = new Menu(); | |
/** | |
* @see Page#getTemplate() | |
*/ | |
public String getTemplate() { | |
return "/border-template.htm"; | |
} | |
}</programlisting> | |
<para>Most application pages subclass <classname>BorderPage</classname>, except | |
AJAX pages which have no need for a HTML border template and typically extend | |
<classname>BasePage</classname>. The <classname>BorderPage</classname> class | |
should not include common logic, other than that required for rendering the | |
border template. Common page logic should be defined in the | |
<classname>BasePage</classname> class. | |
</para> | |
<para>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 <classname>BorderPage</classname> | |
class above will not be auto mapped to <filename>border-template.htm</filename>. | |
</para> | |
</sect2> | |
</sect1> | |
<sect1 id="automapping" remap="h2"> | |
<title>Page Auto Mapping</title> | |
<para>You should use the Click page automapping configuration feature. | |
See the <link linkend="application-automapping">Page Automapping</link> | |
topic for details. | |
</para> | |
<para>Automapping will save you from having to manually configure URL path to | |
Page class mappings in your <filename>click.xml</filename> file. If you follow | |
this convention it is very easy to maintain and refactor applications. | |
</para> | |
<para>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. | |
</para> | |
<para>An example <filename>click.xml</filename> automapping configuration is | |
provided below (automapping is enabled by default): | |
</para> | |
<programlisting language="xml"><click-app> | |
<pages package="com.mycorp.dashboard.page"/> | |
</click-app></programlisting> | |
<para>To see how the page templates are mapped to Page classes set the application | |
<link linkend="application-mode">mode</link> to | |
<literal>debug</literal> and at startup the mappings will be listed out. An | |
example Click startup listing is provided below: | |
</para> | |
<literallayout>[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</literallayout> | |
</sect1> | |
<sect1 id="navigation" remap="h2"> | |
<title>Navigation</title> | |
<para> 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. | |
</para> | |
<para>To forward to another page using the Page class: | |
</para> | |
<programlisting language="java">public class CustomerListPage extends Page { | |
public ActionLink customerLink = new ActionLink(this,"onCustomerClick"); | |
.. | |
public boolean onCustomerClick() { | |
Integer id = customerLink.getValueInteger(); | |
Customer customer = getCustomerService().getCustomer(id); | |
CustomerDetailPage customerDetailPage = (CustomerDetailPage) | |
getContext().createPage(CustomerDetailPage.class); | |
customerDetailPage.setCustomer(customer); | |
setForward(customerDetailPage); | |
return false; | |
} | |
}</programlisting> | |
<para>To redirect to another page using the Page class you can obtain the pages | |
path from the <classname>Context</classname>. In the example below we are passing | |
through the customer id as a request parameter to the target page. | |
</para> | |
<programlisting language="java">public class CustomerListPage extends Page { | |
public ActionLink customerLink = new ActionLink(this, "onCustomerClick"); | |
.. | |
public boolean onCustomerClick() { | |
String id = customerLink.getValueInteger(); | |
String path = getContext().getPagePath(CustomerDetailPage.class); | |
setRedirect(path + "?id=" + id); | |
return false; | |
} | |
}</programlisting> | |
<para>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 application home page. | |
</para> | |
<programlisting language="java">public boolean onLogoutClick() { | |
getContext().getSession().invalidate(); | |
setRedirect(HomePage.class); | |
return false; | |
}</programlisting> | |
</sect1> | |
<sect1 id="templating" remap="h2"> | |
<title>Templating</title> | |
<para>Use Page templating it is highly recommended. Page templates provide | |
numerous advantages including: | |
</para> | |
<itemizedlist> | |
<listitem> | |
<para>greatly reduce the amount of HTML you need to maintain</para> | |
</listitem> | |
<listitem> | |
<para>ensure you have a common look and feel across your application</para> | |
</listitem> | |
<listitem> | |
<para>make global application changes very easy</para> | |
</listitem> | |
</itemizedlist> | |
<para>To see how to use templates see the | |
<link linkend="page-templating">Page Templating</link> topic. Also see the | |
Click <ulink url="../../examples.html">Examples</ulink> use of page templating. | |
</para> | |
</sect1> | |
<sect1 id="menus" remap="h2"> | |
<title>Menus</title> | |
<para> For many applications using the | |
<ulink url="../../extras-api/org/apache/click/extras/control/Menu.html">Menu</ulink> | |
control to centralize application navigation is very useful. Menus are | |
defined in a <filename>WEB-INF/menu.xml</filename> file which is very easy to | |
change. | |
</para> | |
<para>A 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: | |
</para> | |
<literallayout><symbol>#</symbol><varname>writeMenu</varname>(<symbol>$</symbol>rootMenu)</literallayout> | |
<para>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 <filename>WEB-INF/menu.xml</filename> file. A good | |
place to define your macros is in the webroot <filename>/macro.vm</filename> | |
file as it is automatically included by Click. | |
</para> | |
<para>Using macros you can create dynamic menu behaviour such as only rendering | |
menu items a user is authorized to access with | |
<ulink url="../../extras-api/org/apache/click/extras/control/Menu.html#isUserInRoles()">isUserInRoles()</ulink>. | |
</para> | |
<literallayout><command>#if</command> (<symbol>$</symbol>menu.isUserInRoles()) | |
.. | |
<command>#end</command></literallayout> | |
<para>You can also use JavaScript to add dynamic behaviour such as drop down menus, | |
for example see the Menu page in Click | |
<ulink url="../../examples.html">Examples</ulink>. | |
</para> | |
</sect1> | |
<sect1 id="logging" remap="h2"> | |
<title>Logging</title> | |
<para> | |
For application logging you should use one of the well established | |
logging libraries such as Java Util Logging (JUL) or Log4J. | |
</para> | |
<para> | |
The library you use will largely depend upon the application server you are targeting. | |
For Apache Tomcat or RedHat JBoss the | |
<ulink url="http://logging.apache.org/log4j/">Log4j</ulink> library is a good | |
choice. While for the IBM WebSphere or Oracle WebLogic application servers Java Util | |
Logging is better choice as this library is better supported. | |
</para> | |
<para> | |
If you have to target multiple application servers you should consider using the | |
<ulink url="http://www.slf4j.org/">SLF4J</ulink> library which uses compile time bindings | |
to an underlying logging implementation. | |
</para> | |
<para> | |
As a general principle you should | |
probably avoid <ulink url="http://jakarta.apache.org/commons/logging/">Commons Logging</ulink> | |
because of the class loading issues associated with it. If you are using | |
Commons Logging please make sure you have the latest version. | |
</para> | |
<para> | |
It is a best place to define a logger method in a common base page, for example: | |
</para> | |
<programlisting language="java">public class BasePage extends Page { | |
protected Logger logger; | |
public Logger getLogger() { | |
if (logger == null) { | |
logger = Logger.getLogger(getClass()); | |
} | |
return logger; | |
} | |
}</programlisting> | |
<para>Using this pattern all your application bases should extend | |
<classname>BasePage</classname> so they can use the | |
<methodname>getLogger()</methodname> method. | |
</para> | |
<programlisting language="java">public class CustomerListPage extends BasePage { | |
public void onGet() { | |
try { | |
.. | |
} catch (Exception e) { | |
getLogger().error(e); | |
} | |
} | |
}</programlisting> | |
<para>If you have some very heavy debug statement you should possibly use an | |
<methodname>isDebugEnabled</methodname> switch so it is not invoked if debug is | |
not required. | |
</para> | |
<programlisting language="java">public class CustomerListPage extends BasePage { | |
public void onGet() { | |
if (getLogger().isDebugEnabled()) { | |
String msg = .. | |
getLogger().debug(msg); | |
} | |
.. | |
} | |
}</programlisting> | |
<para> | |
Please note the Click logging facility is not designed for application use, | |
and is for Click internal use only. When Click is running in | |
<literal>production</literal> mode it will not produce any logging output. | |
By default Click logs to the console using | |
<ulink url="../../click-api/org/apache/click/service/ConsoleLogService.html">ConsoleLogService</ulink>. | |
</para> | |
<para> | |
If you need to configure Click to log to an alternative destination please configure a LogService such as | |
<ulink url="../../extras-api/org/apache/click/extras/service/JdkLogService.html">JdkLogService</ulink>, | |
<ulink url="../../extras-api/org/apache/click/extras/service/Log4JLogService.html">Log4JLogService</ulink> or | |
<ulink url="../../extras-api/org/apache/click/extras/service/Slf4jLogService.html">Slf4jLogService</ulink>. | |
</para> | |
</sect1> | |
<sect1 id="error-handling" remap="h2"> | |
<title>Error Handling</title> | |
<para> In Click unhandled errors are directed to the | |
<ulink url="../../click-api/org/apache/click/util/ErrorPage.html">ErrorPage</ulink> | |
for display. If applications require additional error handling they can create | |
and register a custom error page in <filename>WEB-INF/click.xml</filename>. | |
For example: | |
</para> | |
<programlisting language="xml"><pages package="com.mycorp.page" autobinding="annotation"/> | |
<page path="click/error.htm" classname="com.mycorp.page.ErrorPage"/> | |
</pages></programlisting> | |
<para>Generally applications handle transactional errors using service layer code | |
or via a servlet | |
<ulink url="http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/Filter.html">Filter</ulink> | |
and would not need to include error handling logic in an error page. | |
</para> | |
<para>Potential uses for a custom error page include custom logging. For example | |
if an application requires unhandled errors to be logged to an application | |
log (rather than System.out) then a custom | |
<ulink url="../../click-api/org/apache/click/util/ErrorPage.html">ErrorPage</ulink> | |
could be configured. An example <classname>ErrorPage</classname> error logging | |
page is provided below: | |
</para> | |
<programlisting language="java">package com.mycorp.page.ErrorPage; | |
.. | |
public class ErrorPage extends org.apache.click.util.ErrorPage { | |
public void onDestroy() { | |
Logger.getLogger(getClass()).error(getError()); | |
} | |
}</programlisting> | |
</sect1> | |
<sect1 id="performance" remap="h2"> | |
<title>Performance</title> | |
<para>Yahoo published a list of | |
<ulink url="http://developer.yahoo.com/performance/rules.html">best practices</ulink> | |
for improving web application performance. | |
</para> | |
<para>Click Framework provides a | |
<ulink url="../../extras-api/org/apache/click/extras/filter/PerformanceFilter.html">PerformanceFilter</ulink> | |
which caters for some of these rules. However not all rules can be easily automated. | |
</para> | |
<para>This section outlines how to apply some important rules which are not | |
covered by the PerformanceFilter namely, | |
<ulink url="http://developer.yahoo.com/performance/rules.html#num_http">Minimize HTTP Requests (by combining files)</ulink> | |
and <ulink url="http://developer.yahoo.com/performance/rules.html#minify">Minify JavaScript and CSS</ulink>. | |
</para> | |
<para>The Rule, <ulink url="http://developer.yahoo.com/performance/rules.html#num_http">Minimize HTTP Requests</ulink>, | |
also mentions <ulink url="http://alistapart.com/articles/sprites">CSS Sprites</ulink>, | |
a method for combining multiple images into a single master image. CSS Sprites | |
can boost performance when your application has many images, however it is | |
harder to create and maintain. Note that CSS Sprites is not covered here. | |
</para> | |
<para>It is worth pointing out that its not necessary to optimize | |
every page in your application. Instead concentrate on popular pages, for | |
example a web site's <emphasis>Home Page</emphasis> would be a good | |
candidate. | |
</para> | |
<para>There are a couple of tools that are useful in applying the rules | |
"Minimize HTTP Requests" and "Minify JavaScript and CSS": | |
</para> | |
<itemizedlist> | |
<listitem> | |
<para> | |
<ulink url="http://developer.yahoo.com/yui/compressor/">YUICompressor</ulink> | |
- minifies and compresses JavaScript and CSS files so less bytes have to | |
be transferred across the wire. | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://code.google.com/p/javaflight-code/">Ant Task for YUICompressor</ulink> | |
- an Ant task that uses YUICompressor to compress JavaScript and CSS files. | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://www.crockford.com/javascript/jsmin.html">JSMin</ulink> | |
- 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. | |
</para> | |
</listitem> | |
</itemizedlist> | |
<para>Below are some articles outlining how to use YUICompressor and Ant to | |
concatenate and compress JavaScript and CSS files: | |
</para> | |
<itemizedlist> | |
<listitem> | |
<para> | |
<ulink url="http://www.julienlecomte.net/blog/2007/09/16/">Article</ulink> | |
explaining how to use Ant and YUICompressor for compression. | |
</para> | |
</listitem> | |
<listitem> | |
<para> | |
<ulink url="http://javaflight.blogspot.com/2008/01/introducing-yui-compressor-ant-task.html">Article</ulink> | |
outlining how to use a special YUICompressor Ant Task for compression. | |
</para> | |
</listitem> | |
</itemizedlist> | |
<para>Using one of the approaches above you can concatenate and compress all | |
JavaScript and CSS for your Pages into two separate files, for example | |
<filename>home-page.css</filename> | |
and <filename>home-page.js</filename>. 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 <emphasis>only</emphasis> include the two | |
compressed files, home-page.css and home-page.js. | |
</para> | |
<para>The Click Page class exposes the property | |
<ulink url="../../click-api/org/apache/click/Page.html#setIncludeControlHeadElements(boolean)">includeControlHeadElements</ulink> | |
that indicates whether Controls have their CSS and JavaScript resources | |
included or not. | |
</para> | |
<para>To optimize Page loading one can override | |
<ulink url="../../click-api/org/apache/click/Page.html#getHeadElements()">Page.getHeadElements()</ulink>, | |
and import the JavaScript and CSS files and then set the property | |
<ulink url="../../click-api/org/apache/click/Page.html#setIncludeControlHeadElements(boolean)">includeControlHeadElements</ulink> | |
to <literal>false</literal>, indicating that Controls won't contribute | |
their own JavaScript and CSS resources. | |
</para> | |
<para>Here is an example:</para> | |
<programlisting language="java">public class HomePage extends Page { | |
private Form form = new Form("form"); | |
public void onInit() { | |
// Indicate that Controls should not import their head elements | |
setIncludeControlHeadElements(false); | |
form.add(new EmailField("email"); | |
addControl(form); | |
} | |
public List getHeadElements() { | |
if (headElements == null) { | |
headElements = super.getHeadElements(); | |
headElements.add(new CssImport("/assets/css/home-page.css")); | |
headElements.add(new JsImport("/assets/js/home-page.js")); | |
} | |
return headElements; | |
} | |
}</programlisting> | |
<para>Using the following <filename>border-template.htm</filename>: | |
</para> | |
<programlisting language="xml"><html> | |
<head> | |
<title>Click Examples</title> | |
${headElements} | |
</head> | |
<body> | |
... | |
${jsElements} | |
</body> | |
</html></programlisting> | |
<para>the rendered HTML will include one CSS and one JavaScript import:</para> | |
<programlisting language="xml"><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></programlisting> | |
<para>A live demo is available | |
<ulink url="http://click.avoka.com/click-examples/general/page-imports-example.htm">here</ulink> | |
</para> | |
</sect1> | |
</chapter> |