I’ve been talking about JSF 2.2 new features out on the conference trail for quite a while now. I usually talk about the big three: Flows, Resource Library Contracts, and HTML5 Friendly Markup. This blog entry talks about another, mostly behind-the-scenes, feature:
ClientWindow. I introduce the concept of
ClientWindow and give a simple example illustrating one solution to a common web browser problem: the browser’s "open in new tab” or "open in new window” feature.
First, some background on the
ClientWindow feature. In the name of clean design, this feature is disabled by default. Because the Faces Flows feature entirely depends on
ClientWindow, the usage of Faces Flows will automatically enable
ClientWindow if it is not enabled already. To explicitly enable
ClientWindow, you can set a
<context-param> with a
javax.faces.CLIENT_WINDOW_MODE and a
url in your
web.xml. For example
The JSF specification allows other values for this parameter, but other values would be implementation specific.
ClientWindow is enabled, it will add an extra name=value pair onto every navigation component rendered by JSF. This extra name=value pair is the rendered representation of an instance of the
javax.faces.lifecycle.ClientWindow class. The JavaDoc for that class states:
This class represents a client window, which may be a browser tab, browser window, browser pop-up, portlet, or anything else that can display a UIComponent hierarchy rooted at a UIViewRoot.
The specification provides for additional logic that will cause an instance of
ClientWindow to be automatically created if one is not already present for the currently rendered view. Now that we’ve defined the feature, let’s explain how to trip it up, and how to fix it.
“Open in new tab” or “Open in new window”
If a JSF 2.2 app has the
ClientWindow feature enabled, every view that can be reached from the landing page for that app will have the extra name=value pair appended. In Oracle Mojarra, this parameter looks like this:
jfwid=518a78c52da3c025ab7a31cf4d50:6. Components that render as hyperlinks offer the user a browser context menu that usually includes two options: “Open in new tab” and “Open in new window”. Selecting either of these two options would effectively cause two tabs to share the same
ClientWindow. This is a violation of the concept and any data that is client window specific (such as flow scoped data) would now be shared across the two tabs (or windows). The easiest way to work around this is to simply prevent the context menu on any link components. This can simply be accomplished using an HTML5 Friendly Markup feature in JSF 2.2: passthrough attributes.
Declare a an XML namespace on your facelet page with the following namespace URI:
http://xmlns.jcp.org/jsf/passthrough. Once declared, any attribute from that namespace will automatically be rendered straight to the markup without any server side interpretation. This can be used with the handy
<p><h:link value="callB via GET" outcome="callB"
Granted, this is a pretty brute force approach, but it can be targeted at individual components. I’ve opened JAVASERVERFACES_SPEC_PUBLIC-1227 to create a more nuanced approach. Please vote for it if you’re interested and maybe we’ll get to it for JSF 2.3.