JSF and RESTful URLs
I have recently started using Java Server Faces (JSF). It alleviates many of the burdens of writing a web user interface, like for instance validation of input data and invocation of actions. But it has a weakness: navigation between pages is very rigid. There is for example no way to have an action bean generate a navigation target in the form of a HTTP GET request with query parameters. For RESTful web services, a GET request with a query parameter is the most natural way to reference a resource that lives in a dynamic database: the query parameter is simply the key that identifies the resource. One could argue that it should be even more natural to have the key in the URL path, but this usually requires more custom code in the server, and it also restricts the key format somewhat.
Consider the following scenario. You have a widget administration application that contains three pages:
Apart from modifying or extending JSF to fix this, I can see two possible workarounds: the first is to short-circuit JSF navigation completely by letting the response page do
I used the following approach for the second method. Two backing beans,
The above solution is not 100% ideal, but it's good enough to satisfy the basic demands of RESTfulness since each URL represents a fairly well-defined resource.
Consider the following scenario. You have a widget administration application that contains three pages:
list.jsp
, details.jsp
, and create.jsp
. The page list.jsp
presents a list of links of the form details.jsp?id=4711
, where 4711 is the key for a particular widget. This is easy to accomplish with the JSF tags <h:dataTable>
and <h:outputLink>
with a nested <f:param>
for the id
parameter. Now for the difficult part. We have a form in create.jsp
for creating new widgets. When the form is submitted, our backing bean creates a new widget, producing a new id value. Let's say the new id value is 4712. We want the response page to be details.jsp?id=4712
. Unfortunately this is not possible, due to the rigid XML-driven navigation system in JSF.Apart from modifying or extending JSF to fix this, I can see two possible workarounds: the first is to short-circuit JSF navigation completely by letting the response page do
<jsp:forward>
to another servlet which sends a redirect to the final URL. I haven't tried this. The second workaround is a compromise: let details.jsp
without the id parameter show the last widget that was created in the current session. This has potential race condition issues if two windows in the same browser session create widgets concurrently, but this is probably a negligible problem.I used the following approach for the second method. Two backing beans,
SessionBean
and RequestBean
. The only purpose of SessionBean
is to keep track of the last created id. RequestBean
has two <managed-property>
declarations: one for the id, with <value>#{param.id}</value>
, and one for the session bean, with <value>#{SessionBean}</value>
. The RequestBean
's id property is normally set by the HTTP GET query parameter, but when invoked without this parameter the bean defaults it from SessionBean
instead. This is done in an initialization method that also picks up the widget data from the database:@PostConstructSince the
private void init() {
if (id == 0) {
id = sessionBean.getId();
}
widget = entityManager.find(Widget.class, id);
}
RequestBean
is initialized from the SessionBean
in this situation, the POST-Redirect-GET pattern can be used for the response with no extra effort. Just put a <redirect/>
in the <navigation-case>
in faces-config.xml
.The above solution is not 100% ideal, but it's good enough to satisfy the basic demands of RESTfulness since each URL represents a fairly well-defined resource.