JSF applications often use view scope for server-side state. While that is convenient it can be problematic to handle multiple browser tabs. The backend has no way of knowing which tab the user is working with. The session cookie is global for the entire browser and any URL
parameters or hidden parameters will be copied to new tabs. What to do? And even worse, what to do when you have to live with an ancient version of JSF (2.0 in our case)?
My first idea was to use the sessionStorage attribute. Add a script to the top of every page that looks for a window id in the sessionStorage. If it is found, add a hidden field with it to all forms in the loaded page, register a click handler that prevents navigation from links and sets the location manually with the id appended as a query parameter, override
window.open in order to append the id as a query parameter in that case as well and finally register a
ajaxPrefilter handler with jQuery in order to intercept AJAX requests and add the id as a request header. Oh, and rewrite the history in order to remove the id from the browser’s address line as well.
When a new top-level browsing context is created by cloning an existing browsing context, the new browsing context must start with the same session storage areas as the original, but the two sets must from that point on be considered separate, not affecting each other in any way.
The major browsers diverge in how they interpret that. To cut things short my cunning plan failed.
Fortunately there is an attribute that is unique across browser tabs:
window.name! I changed the code to store the unique id in
window.name instead and voila! It worked.
Lesson learned – as usual the browsers can be trusted not to be consistent. Take care with sessionStorage.
Java ServerFaces (JSF) has a configuration option that should be set to Development in development and Production in production:
<context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param>
Unfortunately it is defined in web.xml, so the binary file installed in development must differ from the binary file in production. Find and replace at build time works, but it is cumbersome and prone to errors.
The ideal solution would be to use system properties, but JSF does not support that. However, JSF can use JNDI lookups! Why they would support something as complex as JNDI in place of something as simple and straightforward as system properties beats me, but there you are.
To use a JNDI lookup, add the following to web.xml instead of the context parameter:
<resource-ref> <res-ref-name>jsf/ProjectStage</res-ref-name> <res-type>java.lang.String</res-type> <lookup-name>JsfProjectStage</lookup-name> </resource-ref>
The lookup name can be defined in the application server. If it is missing JSF will warn, but defaults to production mode.