0xdeadbeef
0xdeadbeef

Reputation: 33

FacesMessage added from a ViewScoped bean not rendered

I have a ViewScoped bean with the following method:

public Item getItem()  
{
     try {
          itemID = Integer.parseInt(
             FacesContext.getCurrentInstance().getExternalContext()
                         .getRequestParameterMap().get("itemID"));  
     } catch(NumberFormatException ex) {
          FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
                       FacesMessage.SEVERITY_ERROR, "Item lookup error", null));
          return null;  
     }  
     return itemsDAO.findByID(itemID);
}

The method takes the ViewID param and look up the Object using the itemsDAO. I wanted to display a FacesMessage if the itemID is not integer by catching the NumberFormatException and adding the message in there. This however does not work. The FacesMessage in the above code snippet does not render inside the following view tag h:messages globalOnly="true" infoClass="info" errorClass="error"

I'm suspecting that the FacesContext is not available by the time the method is called, but I have no idea how to go about handling this exception.

Update 1:

About the suggestion to use viewParam to make use of built-in conversion and validation. I don't know how this is going to work in this case. The itemID is being passed from an "index" view where all items are listed as shown below. Each item title is a link to the viewItem page with the itemID being passed through URL. How can I use the viewParam to pass the itemID to the viewItem view in this case given that the index view has many items?

index.xhtml

<code>
 <ui:repeat var="item" value="#{ItemViewBean.items}">
       <div id="item">
        <h:link value="#{item.title}" outcome="viewItem">
        <f:param name="itemID" value="#{item.id}"></f:param>
        </h:link>
        <p><h:outputText value="#{item.description}"></h:outputText> <br />
        Address: <h:outputText value="#{item.address}"></h:outputText></p>
        <span class="itemFooter">
        Submitted By: <h:outputText value="#{item.user.username}"></h:outputText> On
        <h:outputText value="#{item.postDate}"></h:outputText>
        </span>
       <hr />
       </div>
       </ui:repeat>
</code>

Update 2: I came up with one workaround for this issue. If an exception occurs, I put the error FacesMessage in the Flash object and redirect to some error page. I still don't understand why the FacesMessage can't be simply added to the FacesContext and displayed on the view.

Upvotes: 3

Views: 3640

Answers (1)

BalusC
BalusC

Reputation: 1109635

Given that you're traversing ExternalContext#getRequestParameterMap(), you seem to be intercepting on a GET request. Given that it takes place in a getter method (bad!!), it seems to be executed only when the component which references the item property is to be rendered.

So, assuming that the above assumptions are true, then this problem will manifest when the <h:messages> component is declared in the view before the component which references the item property. It's then namely too late to add a new message to the faces context. The <h:messages> is at that point namely already rendered.

I can at least not think of other reasonable causes matching this specific problem symptoms. There are basically 2 solutions to the problem:

  1. Move the <h:messages> to after the component which references the item property, so that it's rendered after the faces message is been added.

  2. Perform the business job during pre render view event by <f:event>, preferably in combination with <f:viewParam> to set the request parameter as a bean property. You can even use alone a <f:viewParam> in combination with a converter given that the sole job of your bad getter approach is converting the value.

Needless to say that option 2 is preferred given that you're currently basically abusing/workarounding some basic JSF concepts. Among others that business logic doesn't belong in getters.

E.g.

<f:metadata>
    <f:viewParam id="itemID" name="itemID" value="#{bean.item}"
        required="true" requiredMessage="Invalid page access. Please use a link from within the system."
        converter="itemConverter" converterMessage="Unknown item ID."
    />
</f:metadata>
<h:message for="itemID" />

With

private Item item; // +getter +setter

The itemConverter is just a Converter implementation wherein the request parameter is converted to an Item instance in getAsObject() method.

See also:

Upvotes: 3

Related Questions