Issue Details (XML | Word | Printable)

Key: JAVASERVERFACES_SPEC_PUBLIC-867
Type: Improvement Improvement
Status: Resolved Resolved
Resolution: Duplicate
Priority: Major Major
Assignee: sheetalv
Reporter: Jan-Kees van Andel
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
javaserverfaces-spec-public

viewParam+preRenderView not sufficient for GET support

Created: 19/Jul/10 11:32 AM   Updated: 07/Jun/11 05:33 PM   Resolved: 04/Aug/10 11:36 AM
Component/s: Lifecycle
Affects Version/s: 2.0
Fix Version/s: None

Time Tracking:
Not Specified

Environment:

Operating System: All
Platform: All


Issuezilla Id: 867
Tags:
Participants: Jan-Kees van Andel and sheetalv


 Description  « Hide

While working with the new GET support in JSF2, I encountered "strange" behavior. Strange, not from an implementation perspective, but from a
user's/application developer's perspective.

It has to do with the way UIViewParameter is implemented. It is implemented as a child of UIInput, giving the impression that it's capable of containing
validators and converters, but also that it's required attribute may be set to true.

All the attributes are handled fine, but I think the way the lifecycle handles them is strange, especially in combination with preRenderView system
event listeners.

The combination of UIViewParameter and a preRenderView listener is being promoted as the solution for GET support in JSF, but this solution has some
serious limitations, making it not much more useful than using the old fashioned @PostConstruct annotation for page initialization.

Let me explain:

Consider the following page:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<f:metadata>
<f:viewParam name="param1" value="#{testViewParamsBean.param1}" required="true"/>
<!-- <f:convertDateTime pattern="dd-MM-yyyy"/>-->
<!-- </f:viewParam>-->
<f:viewParam name="param2" value="#{testViewParamsBean.param2}"/>
<f:event type="preRenderView" listener="#{testViewParamsBean.init}"/>
</f:metadata>
<h:body>
<h:outputText value="#{testViewParamsBean.param1}"/><br/>
<h:outputText value="#{testViewParamsBean.param2}"/><br/>
</h:body>
</html>

And the corresponding backing bean:

public class TestViewParamsBean {
private String param1, param2;
public void init() { System.out.println("param1 = " + param1); System.out.println("param2 = " + param2); }
// Getters and setters...
}

Nothing special, just a page that should handle two incoming GET parameters.

In JSF2, when I only specify param2 in the querystring, validation fails for "param1" (required), which causes the lifecycle to bypass "Update Model
Values" and proceed with phase 6. In phase 6, the preRenderView event triggers our init() method, which prints two times null to the console.

This is completely according to spec, but from an application developer's perspective, very strange, because all over the web there are blogs telling
everyone that preRenderView is the place to put any page initialization logic. See for example: http://andyschwartz.wordpress.com/2009/07/31/whats-new-
in-jsf-2/#get-prerenderview-event

Compare this to the way action events work. When a form is submitted, but a required field is not entered. With ActionEvents, the action method isn't
invoked (phase 5) and the old page is rendered.

I think the way action events work is much more intuitive, because invalid parameters prevent the method from executing.

This issue also applies to a very common pattern in web applications, passing around an ID in the querystring and using it to load some entity from the
database.

If I want to correctly implement this in JSF2, I do the following:
<html ...>
<f:metadata>
<f:viewParam name="id" value="#{personDetailsBean.id}"/>
<f:event type="preRenderView" listener="#{personDetailsBean.init}"/>
</f:metadata>
<h:body>
<h:outputText value="#{personDetailsBean.person.firstName}"/><br/>
<h:outputText value="#{personDetailsBean.person.lastName}"/><br/>
Etc ...
</h:body>
</html>

public class PersonDetailsBean {
private Long id;
private Person person;
public void init() {
if (id == null) { // kill lifecycle and show error to the user }
person = loadFromDB(id);
}
// Getters and setters...
}

You see? To implement this, I need to write any required-validations inside my init() method. Also, it's pretty vague, because if you pass in an invalid
Long, the value of the "id" property is still null. I would expect a ConverterException here.

Another implementation would be checking for "FacesContext.getCurrentInstance().isValidationFailed()" on the first line of the init() method. While
better than manually checking all parameters, this is still quite cumbersome. Imagine every developer needs to manually write these two lines in every
managed bean...

Also, when you start to consider things like using the PersonDetailsBean for AJAX, it's pretty annoying that the init() method is called for each
request. It's not only inefficient (like doing unneccesary DB queries), but also requires a lot of custom code to get working properly. For example, if
we want to add an AJAX listener to the PersonDetailsBean, we need to check for PartialViewContext.isAjaxRequest() on the first request to make sure the
init() method doesn't get triggered (because there is a big risk on NullPointerExceptions if init() is being invoked without the GET parameters
present).

I think this is a shame because JSF has a very powerful lifecycle which can easily handle this use case.

After a discussion with Jakob Korherr (also MyFaces), we came to a solution which might fix the issues described above.

The solution we came up with, is a new event type. This event is thrown at the same time as preRenderView, but only when:

  • isAjaxRequest() returns false,
  • isValidationFailed() returns false

This would probably fix the issues above, except one. The user needs to see a proper error page when he doesn't supply the correct parameters or when a
ConverterException occurs.

For this second issue, I guess a new Exception type is appropriate, because then it can be handled in a generic way.

Reference back to the original myfaces thread: http://markmail.org/thread/4kehztvgwitjsuuq



Jan-Kees van Andel added a comment - 04/Aug/10 11:36 AM

Closed because it's a duplicate of 758

      • This issue has been marked as a duplicate of 758 ***