5
votes

In a JSF managed bean constructor, I load a entity from database usint a request parameter. Some times, the entity is not in database and I want to show other JSF (.xhtml) page with 404 message.
This is a sample of managed bean:

@ManagedBean(name = "someBean")
@RequestScoped
public class SomeBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private SomeData someData;

    public SomeBean() throws IOException {
        someData = ... loads from database using JPA features
        if(someData == null){
              HttpServletResponse response = (HttpServletResponse) FacesContext
                    .getCurrentInstance().getExternalContext().getResponse();
              response.sendError(404);
        }
    }

    public SomeData getSomeData(){
        return someData;
    }
}

I configured the web.xml file something like that:

<error-page>
   <error-code>404</error-code>
   <location>/404.xhtml</location>
</error-page>

I have a JSF page to handle the entity loaded by managed bean. When the entity exists, I will use it in the page. Like that:

<h1>#{someBean.someEntity.name}</h1>
<h2>#{someBean.someEntity.description}</h2>
<ui:repeat value="#{someBean.someEntity.books}" var="book">
// ..........
</ui:repeat>

The page above works when the managed loads the data successfully.

The Problem

When the entity not exists and I send a 404 ERROR CODE, the JSF still process methods defined in the expression language of the first page.
This behavior makes the managed bean throws a NullPointerException, and a HTTP 500 ERRO CODE.
My 404 error page is not called. I do not know why.

I try send the 404 error even when the entity is found in database and the 404 error page works.

Enyone can explain this JSF behavior to this happiness? Or offer some kind to show the 404 error page without URL change ?

1
What server? And what is url-mapping for FacesServlet? Seems on Wildfly at least it will work when mapped to *.xhtmlJaqen H'ghar
I use Tomcat 7, with Servlet 3.0 API, and JSF 2.2. The problem ocuors in other jsf 2 versions as well.Welyab Paula
I use Tomcat 7, with Servlet 3.0 API, and JSF 2.2. The problem occurs in other jsf 2 versions as well. I use WELD CDI implementation manual configured, but the problem also occurs in tomcat without WELD CDI. I try use the FacesContext.getCurrentInstance().responseComplete(); and nothing. I think that this behavior is corret, by the JSF life cycle, but I cannot understanding it.Welyab Paula
I use usual FacesServlet mapping (/faces/*), with PrettyFaces to mapping URL to .xhtml files; works fine, except by the reported problem. <br/><br/> The problem occurs without PrettyFaces as well.Welyab Paula

1 Answers

5
votes

You're basically trying to perform front controller logic while rendering the view. You should do it before rendering the view. Because, once you start rendering the view, it's already too late to change the view to a different destination, e.g. an error page as in your case. You namely cannot take the already sent response back from the client.

In JSF 2.2 you can use <f:viewAction> for this.

<f:metadata>
    <f:viewAction action="#{bean.init}" />
</f:metadata>
public void init() {
    // ...

    if (someCondition) {
        context.getExternalContext().responseSendError(404, "some message");
        context.responseComplete();
    }
}

(note that whenever you need to import javax.servlet.* classes into your JSF backing bean, you should absolutely stop and look if the functionality isn't already available in ExternalContext or otherwise think twice if you're doing things the right way, e.g. perhaps you needed a servlet filter; also note that you need to explicitly tell JSF that you've completed the response, otherwise it will still attempt to render the view)

In JSF 2.0/2.1 you can use <f:event type="preRenderView"> for this. See also among others What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?

In case you're actually trying to validate a HTTP request parameter and you also happen to use OmniFaces, you may consider using <f:viewParam> with a true JSF validator and control the sendError with OmniFaces <o:viewParamValidationFailed>.