RSS feed
<< May 2004 | Home | July 2004 >>

Living la vita JSP.

I have finally seen the JSP light! I think I actually like them and I will have to stop pouring scorn upon them (and all who use them)! So maybe 2004 is a bit late to be blogging about the wonders of JSPs, but the agent of my conversion has been the new JSP 2.0 combined with the latest 2.4 servlet specification. Together with a bit of careful thought, the new features result in a capable MVC web framework directly on a raw servlet container without the need for heavy weight framework like struts.

Living "la vita open source" as I do, I have recently moved to a telecommuting village on the Italian riviera: Colletta di Castelbianco. I undertook to redevelop the village's website, which was a rushed job that needed an internationalised wiki wrapped around a booking engine. As there are too many web frameworks out there to evaluate, I decided to try to do without.

My basic complaint against JSPs has always been that they do not really separate concerns very well. In fact JSPs originally munged concerns into a single file of unreadable HTML and java code together. The later addition of custom tags improved the principal of JSPs, but the practise was still a mess of descriptors, classes and way too much <% } %> going on.

JSP2.0 and 2.4 Servlets have changed all that. They finally provide the features needed to develop parameterized HTML in modules that are well separated from the business and implementation logic. The key new features are:

    Filters: Added in 2.3 and refined in 2.4, Filters allow a form of aspect oriented programming to be used within a webapp. I have used separate filters to capture each of the major concerns of the Colletta site: Internationalisation, Authentication, Navigation, generic POST handling and specific business logic filters derived from the generic POST filter. Filters provide an easy way of adding logic to a webapp, without the need for a Controller servlet and additional configuration mappings. Why have yet another xml descriptor file mapping controllers to URLs when web.xml can now do that for you!
    Expressions: At first glance, JSP expressions look like a bit of a ${novelty + act}. But they provide a vital link between logic and presentation that avoids much boiler plate syntax. Expressions provide a search mechanism that looks up requests, parameters, sessions, context scopes and hides all the null checking, bean methods, reflection and map lookups that may otherwise end up in the JSP. Expressions finally provide an elegant way of parameterising both HTML fragments and calls to custom tags.
    Tag Files: Modularity, encapsulation and reuse can now be had in the presentation layer without the hassle of descriptors, helper classes and bizzare over complex APIs. No more is a tag developer forced to use java for generating reusable HTML. The simplicity of tag files encourages the creation of a tag almost a soon as a single line of HTML is repeated. Development of a set of custom form input widgets is now trivial and the JSP expressions allow excellent parameterisation of the tags.

The Colletta site has been internationalised with a 167 line LocaleFilter. It's initial job is to look at requests and to determine the locale for them, either from the session, cookies set previously or from HTTP accept headers. Once the locale is determined for the request, the input and output encodings are handled and the filter can forward the request to a localised resource. Thus a request for logo.gif may be forwarded to logo_it.gif or logo_en.gif. More importantly, because 2.4 filters can be applied to request dispatches, this filter is in the path of a <jsp:include page="text.jsp"/> and can include text_it.jsp instead. Thus layout JSPs can simply include localised content.

But localisation cannot be done simply by bulk includes of content. Thus the PageFilter uses the locale and the requested resource to select a localised property file for the request (eg page_it.properties in the same directory as the requested resource). The resulting property files is combined with those from all ancestor directories, cached and set as the "prop" attribute on the request. The presentation JSPs can thus access localised properties via JSP expressions like ${prop.name}. This allows simple localisation of complex layouts and error messages without making the HTML unreadable.

As a final example, I will show you the JSP code that renders the discount field in the booking form:

  <td ...>
    ${prop.discount}:
  </td><td ...>
    <tag:curInput 
      name="discount"
      value="${reservation.discount}" 
      readonly="${!user.managerOf[reservation.aptId]}"/>
  </td>
This input field is preceded with a localised label and then calls the tag file for currency Input to render the actual HTML input field. It is passed the current value of the discount from the reservation option that the BookingFilter has put in the session. Also passed is the readonly flag that is true if the current user (placed in the request by the UserFilter) is not the manager of the apartment. The power here, is not in the tag file (whose details I leave to your imagination), but in the way that JSP expressions allow complex data derived in separate logic to be combined and passed to parameterise modules of HTML.

Web frame works - who needs them!

WADI Web Application Distribution Infrastructure.

The problem with the current breed of HTTP servlet session distribution mechanisms is that they do not scale well. I have just tried the first release of WADI 'Web Application Distribution Infrastructure', and it shows great promise as a next-generation session distribution mechanism that can be used for simple load balancing, as well as huge multi redundant clusters.

Web applications are put into clusters with one or more of the following aims:
Scalability: to use N nodes to increase throughput almost N times.
Availability: to use N nodes to ensure that after N-1 failures, request will find at least 1 node running the application.
Fault Tolerance: to use N nodes to ensure that after N-1 failures, the results of previous requests are correctly available to the application.

Unfortunately, these aims are not always mutually compatible, nor completely understood by those who want to implement a cluster. Also, the components that are used to make a cluster (load balancers, session replicators, EJB replications, distributed databases) all have different focuses with respect to these aims. Thus it is important to remember why you want a cluster and not to make the aims of a single component dominate your own.

The current crop of load balancers (eg. mod_jk, Pound and IP routers) are very good at scalability and quickly distribute the HTTP requests to a node in a cluster. But they are not so good at fault tolerance as once a failure occurs, the mechanisms for selecting the correct node for a request are replaced by a "pick a node, any node" approach.

The currently available HTTP session distribution mechanisms have mostly been written with fault tolerance in mind. When a node receives some session data, it immediately attempts to replicate it somewhere else. Thus the session is broadcast on the network or persisted in a database, and significant extra load is generated. Because load balancers cannot be trusted to select the correct node, the session is often distributed to all nodes. Thus in an N node cluster, every node must store and broadcast it's own sessions while receiving and storing the session from N-1 other nodes. This is great for fault tolerance but a disaster for scalability. Rarely is 1+1>1 or even 1+1+1+1>1 and throughput is inversely proportional to cluster size. Worse still, the complexity of the mechanism often results in more failures and less availability.

It is difficult to achieve scalability and/or availability with a session distribution mechanism that has been designed for fault tolerance. Thus, many clusters do not use session distribution. Unfortunately this has its own problems, as load balancers are not perfect at sticking a session to a node in a cluster. For example, if IP stickiness is used and the client is on AOL, then their source IP can change mid session and the load balancers will route the requests to the wrong node. To handle the imperfections of load balancers, a subset of session distribution is needed - namely session migration.

Session migration is the initial focus of WADI, so that it can provide scalability and availability to match the available load balancers. However, WADIs extensible architecture has also been designed for fault tolerance, so eventually it will be able to handle all the concerns of clustering Servlet sessions.

WADI has been simply integrated with the Jetty and Tomcat Servlet containers. For normal operation, it adds very little overhead to session access. Only the cost of synchronizing requests to the session and of some AOP interceptors. Expensive mechanisms such as migration or persistence are only used when a node has been shutdown or a load balancer has directed the request to a different node.

When a WADI node receives a request for which it has no session data, it can ask the cluster for the location of the session. This can either be on another node or in persistant storage (after a timeout or graceful node shutdown). The request can then be directed to the correct node or the session can be migrated to the current node.

WADI is still alpha software, but is actively being developed by Julian Gosnell of Core Developers Network and should soon be included with Jetty 5 and Geronimo.