blob: c4bbd05ab4e929a4f40f70e8e8fcb76226bc168b [file] [log] [blame]
<?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">&lt;security-constraint&gt;
&lt;web-resource-collection&gt;
&lt;web-resource-name&gt;admin&lt;/web-resource-name&gt;
&lt;url-pattern&gt;<symbol>/admin/*</symbol>&lt;/url-pattern&gt;
&lt;/web-resource-collection&gt;
&lt;auth-constraint&gt;
&lt;role-name&gt;<varname>admin</varname>&lt;/role-name&gt;
&lt;/auth-constraint&gt;
&lt;/security-constraint&gt;</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">&lt;security-role&gt;
&lt;role-name&gt;<varname>admin</varname>&lt;/role-name&gt;
&lt;/security-role&gt;</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 &lt;login-method&gt; element.
For example to use the BASIC authentication method you would specify:
</para>
<programlisting language="xml">&lt;login-config&gt;
&lt;auth-method&gt;<varname>BASIC</varname>&lt;/auth-method&gt;
&lt;realm-name&gt;Admin Realm&lt;/realm-name&gt;
&lt;/login-config&gt;</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">&lt;login-config&gt;
&lt;auth-method&gt;<varname>FORM</varname>&lt;/auth-method&gt;
&lt;realm-name&gt;Secure Realm&lt;/realm-name&gt;
&lt;form-login-config&gt;
&lt;form-login-page&gt;<symbol>/login.htm</symbol>&lt;/form-login-page&gt;
&lt;form-error-page&gt;<symbol>/login.htm?auth-error=true</symbol>&lt;/form-error-page&gt;
&lt;/form-login-config&gt;
&lt;/login-config&gt;</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>"))
&lt;div style="margin-bottom:1em;margin-top:1em;color:red;"&gt;
Invalid User Name or Password, please try again.&lt;br/&gt;
Please ensure Caps Lock is off.
&lt;/div&gt;
<command>#end</command>
&lt;form method="POST" action="<varname>j_security_check</varname>" name="form"&gt;
&lt;table border="0" style="margin-left:0.25em;"&gt;
&lt;tr&gt;
&lt;td&gt;&lt;label&gt;User Name&lt;/label&gt;&lt;font color="red"&gt;*&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;input type="text" name="<varname>j_username</varname>" maxlength="20" style="width:150px;"/&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;label&gt;User Password&lt;/label&gt;&lt;font color="red"&gt;*&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;input type="password" name="<varname>j_password</varname>" maxlength="20" style="width:150px;"/&gt;&lt;/td&gt;
&lt;td&gt;&lt;input type="image" src="$context/images/login.png" title="Click to Login"/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/form&gt;
&lt;script type="text/javascript"&gt;
document.form.j_username.focus();
&lt;/script&gt;</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">&lt;web-app&gt;
..
&lt;error-page&gt;
&lt;error-code&gt;403&lt;/error-code&gt;
&lt;location&gt;<varname>/not-authorized.htm</varname>&lt;/location&gt;
&lt;/error-page&gt;
&lt;security-constraint&gt;
&lt;web-resource-collection&gt;
&lt;web-resource-name&gt;admin&lt;/web-resource-name&gt;
&lt;url-pattern&gt;<varname>/admin/*</varname>&lt;/url-pattern&gt;
&lt;/web-resource-collection&gt;
&lt;auth-constraint&gt;
&lt;role-name&gt;<symbol>admin</symbol>&lt;/role-name&gt;
&lt;/auth-constraint&gt;
&lt;/security-constraint&gt;
&lt;security-constraint&gt;
&lt;web-resource-collection&gt;
&lt;web-resource-name&gt;user&lt;/web-resource-name&gt;
&lt;url-pattern&gt;<varname>/user/*</varname>&lt;/url-pattern&gt;
&lt;/web-resource-collection&gt;
&lt;auth-constraint&gt;
&lt;role-name&gt;<symbol>admin</symbol>&lt;/role-name&gt;
&lt;role-name&gt;<symbol>user</symbol>&lt;/role-name&gt;
&lt;/auth-constraint&gt;
&lt;/security-constraint&gt;
&lt;login-config&gt;
&lt;auth-method&gt;<varname>FORM</varname>&lt;/auth-method&gt;
&lt;realm-name&gt;Secure Zone&lt;/realm-name&gt;
&lt;form-login-config&gt;
&lt;form-login-page&gt;<varname>/login.htm</varname>&lt;/form-login-page&gt;
&lt;form-error-page&gt;<varname>/login.htm?auth-error=true</varname>&lt;/form-error-page&gt;
&lt;/form-login-config&gt;
&lt;/login-config&gt;
&lt;security-role&gt;
&lt;role-name&gt;<symbol>admin</symbol>&lt;/role-name&gt;
&lt;/security-role&gt;
&lt;security-role&gt;
&lt;role-name&gt;<symbol>user</symbol>&lt;/role-name&gt;
&lt;/security-role&gt;
&lt;/web-app&gt;</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">&lt;click-app&gt;
&lt;pages package="com.mycorp.dashboard.page"/&gt;
&lt;/click-app&gt;</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 -&gt; com.mycorp.dashboard.page.CategoryTree
[Click] [debug] /process-list.htm -&gt; com.mycorp.dashboard.page.ProcessList
[Click] [debug] /user-list.htm -&gt; 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">&lt;pages package="com.mycorp.page" autobinding="annotation"/&gt;
&lt;page path="click/error.htm" classname="com.mycorp.page.ErrorPage"/&gt;
&lt;/pages&gt;</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">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Click Examples&lt;/title&gt;
${headElements}
&lt;/head&gt;
&lt;body&gt;
...
${jsElements}
&lt;/body&gt;
&lt;/html&gt;</programlisting>
<para>the rendered HTML will include one CSS and one JavaScript import:</para>
<programlisting language="xml">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Click Examples&lt;/title&gt;
&lt;link type="text/css" rel="stylesheet"
href="/click-examples/assets/css/home-page.css" title="Style"/&gt;
&lt;/head&gt;
&lt;body&gt;
...
&lt;script type="text/javascript" src="/click-examples/assets/js/home-page.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</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>