Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF calls methods when managed bean constructor sends 404 ERROR CODE

Tags:

jsf

jsf-2

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 ?

like image 443
Welyab Paula Avatar asked Sep 03 '14 04:09

Welyab Paula


1 Answers

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>.

like image 157
BalusC Avatar answered Oct 11 '22 12:10

BalusC