1

Working on a jsf application someone else wrote I must be missing something very basic. I stripped it down to this, which is not working:

page.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<!doctype html>
<html>
<body>
    <f:view>
        <h:messages id="error" globalOnly="true"/>
        <h:outputText value="#{testBean.msg}"/>
    </f:view>
</body>
</html>

testBean.java:

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;

public class testBean {

    public String getMsg() {
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "getMsg"), null));
        return "getMsg";
    }
}

That code does not display the error message generated in testBean#getMsg. If I move the <h:messages> to after the <h:outputText> line, the message is displayed. Why is that? Is this supposed to work?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
mplwork
  • 1,120
  • 10
  • 21

1 Answers1

2

You're doing the business job in a getter method which is in this particular example only invoked during producing the HTML output during render response phase. It's in this particular construct too late to add a faces message if the HTML representation of the messages component has already been produced beforehand. You cannot take back the already written bytes from the response.

In fact, you should never do business logic in getters. You didn't clearly state a concrete functional requirement anywhere, so it's hard to propose the right solution, but in this particular example, one of the solutions would be to perform the job in a pre render view listener method instead.

<f:view>
    <f:event type="preRenderView" listener="#{testBean.init}" />
    <h:messages id="error" globalOnly="true"/>
    <h:outputText value="#{testBean.msg}"/>
</f:view>

with

private String msg;

public void init() { 
    msg = "getMsg";
    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "getMsg"), null));
}

public String getMsg() { // Keep getter untouched! Don't do business logic in there!
    return msg;
}

Update the above applies to JSF2 Facelets only. On legacy and deprecated JSP there's no similar way. Your best bet is then a getter returning an empty string and referencing it in top of the JSP.

<f:view>
    <h:outputText value="#{testBean.initHack}" />
    <h:messages id="error" globalOnly="true"/>
    <h:outputText value="#{testBean.msg}"/>
</f:view>

with

private String msg;

public String getInitHack() {
    msg = "getMsg";
    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "getMsg"), null));
    return "";
}

public String getMsg() { // Keep getter untouched! Don't do business logic in there!
    return msg;
}
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks. I'll have to check what really is intended. I still don't understand the code but this should get me a bit further. – mplwork Apr 09 '13 at 13:57
  • JSF produces HTML code during render response. JSF traverses the component tree hierarchy for that. First, JSF encounters ``, collects messages and produces HTML for that in a list (there are no messages at this point!). Then, JSF encounters ``, invokes the getter for the value and produces HTML with that value (the message is been added at that point!). However, the faces message which is added in the getter cannot be displayed as the `` has already been produced beforehand. The pre render listener method is invoked *before* JSF starts producing HTML. – BalusC Apr 09 '13 at 14:00
  • Thanks again. I sort of figured that. Now what really should happen is: on page load some stuff should happen, the output of stuff should be displayed and depending on it a form is filled. The load phase is all coded in the getter I showed. In the real code it actually doesn't output anything. I suppose all of that has to go into the constructor of the bean, right? – mplwork Apr 09 '13 at 14:38
  • It would in your particular example be still too late if the bean is constructed for the first time when the output text's value is referenced. – BalusC Apr 09 '13 at 14:40
  • Yep. So when the page is called I somehow have to fill the bean with data based the request parameters and _then_ show the results. Is the constructor not the right place for this? – mplwork Apr 09 '13 at 14:51
  • As said, it would in **your particular example** be still too late if the bean is constructed for the first time when the output text's value is referenced. – BalusC Apr 09 '13 at 14:52
  • Ok. So how would you go about to achieve what I need? But we should probably stop here as it's a different question. Btw, my faces here doesn't know `f:event`. – mplwork Apr 09 '13 at 15:24
  • Oh right, I overlooked the not unimportant fact that you're still using the deprecated JSP view technology for some reason. If you're already on JSF2, first step would be to drop it altogether and migrate to its successor Facelets, so that you can use the new JSF2 awesomeness to full extent. Sorry, there's no alternative way in JSP other than referencing the bean in the very top of the page. A common hack is a getter returning an empty string so that nothing would be printed anyway, see updated answer. – BalusC Apr 09 '13 at 15:36
  • Seems like the guy who wrote it did just that. Now I understand it. Thanks. And not much intention here to move this ~10 year old application to newer technology at the moment. – mplwork Apr 09 '13 at 15:45