Saturday, May 16, 2009

The pain of request scoped JSF

While I like the idea of a component based web framework, JSF never really felt right to me. There are too many pitfalls and the whole programming model (even with the use of Facelets) feels cumbersome and more complex than necessary. I could probably write up a detailed list of reasons for why I think JSF is the least productive and fun web frameworks of those I know, but that is not the purpose of this blog post. Suffice to say this appears to be a shared sentiment. This post addresses only JSF's focus on server state and the associated overloaded POST aspect.

The problem of state
All programs that does anything remotely interesting, needs to keep some kind of state. In web applications, there are really only two places to put state and that is either on the server or on the client. Because HTTP itself is stateless, most frameworks stores state on the server in a session that is allocated the very first time a unique user visits. This is done either through an associated cookie or a sessionId that's passed along at all times. It's a simple approach that works reasonable well, but it also has some problems.

  • Scalability - When you do state, inevitably you start to consume memory that accumulates over time only to be cleared when your session times out. While memory and swap space by itself is relatively cheap, you can only throw hardware at it while your user count remains relatively low. Another problem comes to play when you decide to add more machines to assist in carrying the workload, since then you need to propagate out to a globally shared state between all the machines.

  • Complexity - The moment you do state, you have to think about concurrency and visibilty. Everything is essentially shared so opening several windows in the browser or using asyncronious (Ajax) calls can potentially wreck havoc in the state space for your session. Only with a debugger and non-trivial testing can you gain an overview of this aspect. It does not help that in JSF, there's (to my knowledge) no way to detect a new page load in a session scoped backing bean so you can't use that to do a partial "reset".

  • Unpredictable - Have you ever been working on something online, then picked up a phone call or gone to lunch only to return and receive an error message that says your session has timed out?. In JSF you often receive the more cryptic message "Could not restore view!". This happens because of the stateless nature of HTTP, there's no way for the server to know if a user is still sitting there in the other end, so it relies on a timeout for the session. There's a reverse proportional relationship between the size of this session timeout and the amount of memory consumed by the server, which is why the timeout is often kept down around 30 min.

I'm not totally against state on the server when it's used as a application level caching mechanism or for lightweight session info like language setting etc. Unfortunately it is all too common to see a heavyweight hierarchy from another layer (JPA entities comes to mind) creep into the user session and then it's bye bye to scalability.

JSF favors statefullness
Everything about the JSF programming model is clearly made with session scoped backing beans in mind. For instance, if you try to use various components in request scope, you will find it to be a major problem how events dispatch BEFORE your bean have had its properties filled out from the POST request. So, since JSF does not provide sufficient context, what people often do is to go behind JSF's back and grab the required context from the request parameters.

Example, say that you have a JSF page that displays some info about a customer entity. You would like an easy way to test and interface with legacy apps, so you allow a request parameter called customerId to be passed along as initial context. From within this page, you may perform various CRUD operations on the customer, which means you pass along this context in various ways, typically by including something like this in your POST-back form:


<html:form id="someform">
   <html:commandButton value="Show customer" action="showCustomer"/>
   <html:inputHidden id="customerId" value="#{customerBB.customerId}" binding="#{customerBB.customerIdHidden}"/>
</html:form>


What people do then in the backing bean, is to use lazy detection code to first try to fetch a property directly (if they know the form name) or from bound components. The latter approach would look something like this:


private static final String CUSTOMER_ID_PARAM = "customerId";
private Long customerId;
private HtmlInputHidden customerIdHidden;

public HtmlInputHidden getCustomerIdHidden() {
   
return customerIdHidden;
}

public void setCustomerIdHidden(HtmlInputHidden customerIdHidden) {
   
this.customerIdHidden = customerIdHidden;
}

public Long getCustomerId(){
   
if(customerId == null){
       
String customerIdString = getRequestParameter(CUSTOMER_ID_PARAM, false);
       
if(customerIdString != null && !customerIdString.isEmpty())
           
customerId= Long.parseLong(customerIdString);
       
else if(getCustomerIdHidden() != null){
           
customerIdString = getRequestParameter(getCustomerIdHidden().getClientId(getFacesContext()), false);
           
if(customerIdString != null && !customerIdString.isEmpty())
               
customerId = Long.parseLong(customerIdString);
       
}
   
}
   
   
return customerId;
}


Very verbose and fragile. And while JSF proponents will probably claim this is not the sanctioned way to use JSF, that's the kind of hacks I've seen on numerous occasions. In the following sections I'll describe an approach which is essentially a return to an action based framework where the idea is to separate functionality out into distinct pages and backing beans, with a controlled context flowing between these.

Managed properties
It turns out, you can instruct JSF to automatically initialize a backing bean property from both POST and GET requests. You do this via the managed-property tag in faces-config.xml, like so:


<managed-property>
   <property-name>customerId</property-name>
   <value>#{param['customerId']}</value>
</managed-property>


That still leaves us with the problem that in JSF, input variables are identified on the form [formname]:[inputname], so a <h:inputHidden> would not be injected into the beans customerId field. There's a way around this though. In your form, simply escape back to basic HTML by using the <core:verbatim> tag. This prevents JSF from modifying the tag name and id:


<core:verbatim>
   <input type="hidden" id="customerId" name="custumerId" value="#{customerBB.customerId}"></input>
</core:verbatim>


Overloaded POST's with GET redirects
A strongly associated aspect of the state on the server, is how everything in JSF is done through POST's. That presents a particular problem to JSF authors who wish to write in request scope, since an URL no longer expresses any of the necessary minimum state needed for the website to "live in the know". Hitting refresh in the browser won't work. What I do is to peek over at the RESTfull architectures, and only use POST for actions that causes state to mutate (updating or creation), and GET for the remaining idempotent actions.

For instance, rather than using a <html:commandLink> and navigation rules to go to an edit page for an item in a list, I'll use <html:outputLink> that points to the JSF page itsef along with the necessary context:


<html:outputLink value="customer-edit.jsf">
   <html:outputText value="Edit"></html:outputText>
   <core:param name="customerId" value="#{customerBB.customerId}"/>
</html:outputLink>


Note that this actually has nothing to do with the form, it simply expands to a hyperlink which is why in this case we won't need the <core:verbatim> tag.
The second part to this is to ensure we always emit the new context from the logic in the backingbean, that means for mutating POST operations to forward to a GET. This can be done by manually navigating to a result URL rather than returning a navigation rule. The following could be an example of what to do after a save operation was invoked on the consumer-edit.jsf page.



FacesContext.getCurrentInstance().getExternalContext()
.redirect("customer-view.jsf?customerId=" + getCustomerId());



In conclusion
JSF makes it very hard to avoid state on the server, but I hope this entry will have proven that it is possible to compose simple CRUD pages with this objective in mind. It's arguably bending JSF beyond its intentions, but if you have had your share of problems with JSF as I have, perhaps this classic way of thinking in request parameters will let you utilize some of JSF's nice looking components in a simple way without walking down the dreaded statefull alley. As a side effect, it becomes trivial to document the state space and assert preconditions of your pages.

The approach probably does not scale beyond relatively simple pages although I could claim adherence to the Separation of concerns principle. So if you command your own toolchain, I would urge you to have a look at Wicket (for classic broad-spectred web apps) or GWT (for data-intensive intranet apps) instead.

Post a Comment