EE6 wishlist part II: JSF

Posted by    |      

This is the second installment of a series. Part I is here:

http://blog.hibernate.org/cgi-bin/blosxom.cgi/2007/03/30#ee6part1

I'm a fan of JSF, not because JSF is by any means perfect, but because I like the overall architecture, and judge it's warts and limitations to be more fixable than those of other Web Framworks I've used. Of course, whatever my feelings about other other frameworks, I would be idiotic to ignore JSF. JSF is easily the fastest growing web framework in the Java space. It is already easily the most popular framework after Struts. By virtue of being an open standard, JSF has fostered a fecund ecosystem of extensions and component libraries. But in case you doubt that JSF provides any compelling technical advantages compared to other approaches, here's why I find the model superior:

  • Application components are plain Java objects (JSF managed beans), bound to the user interface and orchestration logic via EL. It is possible to write pure business logic components, with no dependencies to JSF, and use them directly from your JSF pages, without the need for an intermediate presentation layer written in Java.
  • Managed beans are stateful and contextual. While the context model is extremely limited compared to Seam, this problem is fixable, as demonstrated by Seam.
  • The JSF lifecycle, which some people find complex, is perfect for use with an application framework like Seam where business logic is bound directly to the view. In particular, the phased processing of validation, followed by model update, followed by event processing and finally view rendering is exactly what you need if the model objects might be managed entities. Any other lifecycle simply won't work.
  • User interfaces are usually defined using a markup language. Markup languages, being hierarchical, naturally map to the structure of a user interface. (I've never felt very comfortable defining my hierarchical user interface in procedural code, a la Swing, Wicket or GWT.) However, somewhat uniquely, if you do wish to create or manipulate your user interface using procedural code, this is also possible in JSF.
  • Even better, this markup language is not limited to the set of primitives provided by HTML. HTML is, I suppose, a reasonable language for defining hyperlinked textual documents. But it was never designed to be used for designing todays rich internet applications and is clearly inadequate for that task. JSF provides the means to extend HTML with new language elements for defining rich user interfaces. Some people have argued that the use of JSF-specific markup elements breaks the traditional designer/developer role separation. But in HTML, the correct separation of roles is that developers create semantic HTML, and designers create CSS. Furthermore, this argument presupposes that it is impossible for designers to learn what the JSF markup elements do - which in my opinion deeply underestimates the intelligence of a good web designer.
  • The JSF UI component model solves certain problems that affect plain HTML form processing and many other Web Frameworks. Multiple submit buttons on the same form can easily be bound to different server-side actions. Forms with repeating input elements (think of inputs laid out in a grid) are easily mapped back to server-side model objects.

Nevertheless, there are some things which JSF 1.2 doesn't get right, and this has required the development of non-standard extensions to JSF in products like Facelets, Seam, Shale, Ajax4JSF, ICEFaces, Avatar. Certain people have argued that if you need to use non-standardized extensions, you would be better to avoid the standard altogether. There are three problems with this line of reasoning:

  • The primary audience for the JSF standard is not application developers. The primary audience is developers of rich user interface component libraries. Here, JSF is a roaring success! There is an embarrassment of riches in open source (Richfaces, ICEFaces, Trinidad, Woodstock, RCFaces, etc) along with many commercial offerings. JSF provides the standard platform upon which component developers are building.
  • If your choice is between (a) adhering to a standard in 80% of your code while using some open source extension to the standard in the remaining 20% and (b) using a totally non-standard framework for everything, you're not better off with the non-standard. You're 80% worse off.
  • The people who have developed the extensions to the standard are working hard to bring the extensions back into JSF 2.0.

And that's the reason for this wishlist. As JBoss rep on the JSF EG, these are the things I'll be pushing for JSF 2.0.

Asynchronous partial submits and renders

This is a no-brainer and everyone already agrees that it is needed. While JSF 1.2 was being finallized, the Ajax craze happened. JSF is actually a great component model for rich internet applications and projects such as Ajax4JSF, Avatar and ICEFaces have clearly demonstrated this. Unfortunately, coexistence of Ajaxified JSF component libraries is currently a nightmare. This problem can only be conclusively addressed by defining how partial submits and renders work in the spec.

Annotation-based programming model

Like the rest of the Java web tier, JSF is stuck in 2003. Definition of managed beans requires EJB2/Spring-like levels of XML tedium. There are two ways to solve this problem. One option would be to introduce annotations for defining managed bean names, scopes, and dependency injection. The second option would be to simply defer to the component model being defined by the Web Beans group. As the convener of JSR-299, I'm understandably sympathetic to the second option, but I'm not sure how the rest of the JSF EG will react to the suggestion.

Converters, Validators and UI components should also be definable via annotations.

Enhanced lifecycle for non-faces requests

JSF devotes much love and attention to the faces request lifecycle for JSF form submissions. It also talks briefly about something called a non-faces request. The most interesting kind of non-faces request is a HTTP GET request, which - when you think about it - is actually the most common kind of request. Here, the spec is a great disappointment. It's certainly possible to create bookmarkable JSF pages with request parameters, but you lose a level of abstraction, and end up writing servlet-like code.

Seam solves this problem by providing page actions and page parameters, which are similar to the abstractions provided for faces requests. (They also look a lot like the functionality provided by an action-based web framework such as Struts or WebWork.)

JSF 2.0 should define a lifecycle for non-faces requests that includes:

  • validation and model update for request parameters
  • some mechanism for handling validation failures
  • action invocation
  • some facility to redirect the request to a different URL

In other words, non-faces requests are going to need to provide all the things that faces requests currently provide. The only difference being that the submitted values are not coming from JSF input components, but rather from plain HTTP parameters (in a bookmarkable URL, for example).

The interesting question is exactly where should these actions, the mapping of request parameters to model attributes and validation be declared. The solution provided by Seam today is to declare this in an XML document along with the orchestration logic (navigation). But I'm increasingly favorable to the idea of embedding this in the page definition. For example:

<f:view>
   <f:parameter name="customerName" value="#{customerFinder.name}" required="true">
      <f:validateLength max="100"/>
   </f:parameter>
   <f:onRender action="#{customerFinder.findByName}"/>
   ...
</f:view>

Improved orchestration

JSF navigation rules provide the basic functionality you expect if you've ever used something like Struts or WebWork. You can write an action method which returns a String-valued outcome, and define navigation rules in XML that determine the view to be rendered or redirected to. The following improvements are sorely needed:

  • Outcomes shouldn't need to be Strings - anything with a toString() method should work
  • It's much more transparent and elegant if the action method does not need to return an outcome at all - instead, the navigation rule could specify a value expression to be evaluated
  • Navigation rules which perform redirects should be able to specify a list of request parameters to use in the redirect (where the parameter values are defined using value expressions)
  • Navigation rules should be able to specify a HTTP error code as the result
  • It should be possible to write exception handling rules that are triggered when a particula exception type propagates out of a JSF lifecycle phase

The following kind of thing would be possible:

<navigation-rule>
   <from-action>#{customerFinder.findByName}</from-action>
   <navigation-case>
      <if>#{customerFinder.result!=null}</if>
      <to-view-id>/displayCustomer.xhtml</to-view-id>
      <redirect>
         <parameter-name>customerId</parameter-name>
         <parameter-value>#{customerFinder.result.id}</parameter-value>
      </redirect>
   </navigation-case>
</navigation-rule>

(Seam provides all this functionality today by defining it's own XML-based language for navigation rules.)

Error handling

JSF's lack of facilities for exception handling is a major embarrasment, and totally ignorant of current best practices for exception handling in Java. It is impossible to do any centralized handling of exceptions without writing a servlet filter. Even worse, Unified EL has the totally pathalogical behavior of wrapping all exceptions (even runtime exceptions) that occur during invocation of the managed beans in a totally useless ELEvaluationException. Both of these problems should be fixed.

One very exciting and interesting solution to the first problem would be to redesign the JSF Lifecycle object using a chain-of-responsibility pattern. Each lifecycle phase would be an element of the chain and would be responsible for delegating to the next phase. The application would be able to add in new phases at any point in the chain, which would provide for the possibility of centralized exception handling via around-style interception. We could then deprecate the PhaseListener extension point which is useful but limited and inelegant. This approach would make JSF much more extensible.

Standardize Facelets and simplify development of JSF UI components

JSF needs its own templating language. Facelets is the perfect starting point. Support for JSP should be deprecated.

It's lucky that there are so many JSF component libraries out there, since developing controls yourself is a huge pain. The biggest problem is JSP, so introducing a standard JSF-specific template language should improve the situation dramatically and make JSF UI component development much more accessible to regular users. The template language could also make it easy to define components which are composed of other components, just like Facelets does today.

Alternative stateless UI component lifecycle

JSF's UI component tree is stateful, meaning that the tree of components is maintained across faces requests (form submissions). This is a nice feature that lets JSF handle forms with conditionally rendered and repeated inputs, and forms with components that are manipulated programmatically. However, in the simple (and common) case, statefulness is overkill. Most forms don't have conditionally rendered controls or grids of controls. JSF needs an alternative stateless lifecycle to deal with the common case.

One possible option would be to copy Tapestry. Tapestry distinguishes between simple forms (which have no repeated or conditional controls) and complex forms (which do). For simple forms, Tapestry uses a stateless lifecycle. For complex forms, Tapestry serializes some information about the component tree to and from the client, and uses this when reconstructing the component tree during the rewind phase. (The handling of complex forms in Tapestry is very conceptually similar to the stateful model used in JSF.)

Databinding

Databinding is a problem that is currently under-specified and causes many problems for new users. The JSF DataModel class is kind of a sketch of a solution, but it falls far short of what is really needed. I have an inkling of what a better solution might look like, but a lot more work is needed on that before it becomes a concrete proposal.

Model-based validation

JSR-303 is defining a standard facility for model-based validation for the Java platform. This new facility must be integrated with both JPA and JSF. In Seam, Hibernate Validator provides this functionality today, and the integration that Seam provides between JSF and Hibernate Validator is a possible model for JSF 2.0.

Allow use of EL in messages

JSF's use of EL is a really strong point of the whole architecture. However, one minor thing is missing: messages defined in resource bundles and in FacesMessages should support interpolation of embedded value expressions. (Seam already supports this.)

For example, you could have the following text in your JSF page:

#{messages['myapp.welcome']}

And this in messages_en.properties:

myapp.welcome=Welcome, #{user.firstName} #{user.lastName}!

Session invalidation

Amazing as it seems, JSF provides no API for invalidating the HTTPSession.

Security

JSF doesn't provide any special functionality for authentication or authorization. I'm not sure what we should do here (if anything), but we need to at least discuss the problem.


Back to top