12

I want to make a redirect in my @PostConstruct in 4 of my backing beans. As I've learned from the follwoing question: JSF PostConstruct Exception Handling - Redirect I know that I'm supposed to use:

    @PostConstruct
    public void init() {    
       if (shouldRedirect) {
          try { 
             FacesContext.getCurrentInstance().getExternalContext().redirect("bolagsSok_company.xhtml");
             return;
          } catch (IOException e) {
             //do nothing
          }
        }
        ....
     }

This works great for 2 of my Backing beans... but for the other two, the non-redirected-xhtml file is still making calls to the backing bean and doesn't redirect. I've confirmed (with debug) that the backing beans indeed calls both FacesContext.getCurrentInstance().getExternalContext().redirect("bolagsSok_company.xhtml"); and return; statements.

Any clues what could be wrong?

Community
  • 1
  • 1
su99-bsa
  • 427
  • 1
  • 5
  • 11

1 Answers1

28

Redirecting in a @PostConstruct might be too late if the response is already committed. I.e. when the first few bytes of the response are already been sent to the client. This is a point of no return. That can in your case happen when the backing bean is referenced (and thus constructed) for the first time relatively late in the view, maybe about halfway or in the end.

You could solve this in one of the following ways:

  1. Reference the bean for the first time as early as possible in the view.

  2. Use <f:event type="preRenderView"> instead of @PostConstruct. This will invoke the method right before the render response starts (thus, before any bit is been sent to the response). Or, when you're on JSF 2.2 already, use the <f:viewAction>. Additional advantage is that the <f:viewAction> can return a navigation case outcome like return bolagsSok_company?faces-redirect=true" without the need to fiddle with ExternalContext#redirect().

  3. Increase the default Facelets buffer size by javax.faces.FACELETS_BUFFER_SIZE context param in web.xml to about the size of the largest HTML response.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I tried your second solution with preRenderView with somewhat humoristic results. Now the two pages that didnt work... do work! But not the two that worked with @PostConstruct. I placed the – su99-bsa Aug 29 '12 at 14:27
  • 2
    The `` must be placed in the top level view (the template client), not in the template file (the master template). See also [tag documentation](http://docs.oracle.com/javaee/6/javaserverfaces/2.1/docs/vdldocs/facelets/f/metadata.html). However, the `` doesn't necessarily need to be placed in a ``. Just place it outside and get rid of the `` (note that I have nowhere in my answer mentioned to use ``...). See also http://stackoverflow.com/questions/7343220/jsf-does-it-matter-whether-place-fevent-inside-fmetadata-or-not – BalusC Aug 29 '12 at 14:31
  • I really appreciate your help BalusC =) I've now changed according to tag documentation and without the now. But still getting the same results. What I notice however is that for the pages that dont work... they have bindings to UIInput. When I remove the parts of the xhtml-file that have bindings to UIInput... it suddenly works for all pages. Since I'm just a JSF beginner, I'm guessing that there is a design flaw in the code im working with. Or is it a shortcoming of JSF? – su99-bsa Aug 30 '12 at 19:24
  • I've investigated further. When I have bindings (input bindings) on my xhtml-file... then PostConstruct gets called before the preRenderViewEvent. And when I dont have any bindings... then preRenderViewEvent gets called before the PostConstruct. Is this supposed to happen or is it a bug? – su99-bsa Sep 01 '12 at 17:36
  • 1
    When you bind components of the view to a view scoped bean, then a new instance of the bean will be created to resolve those bindings instead of that an existing one, stored in the view scope, is been reused. The simple reason is that the input bindings are resolved during building of the view and that view scoped beans can only be retained once the entire view is built/restored. You need to bind components to a request scoped bean instead (or to look for alternate approaches for the particular functional requirement for which you thought that binding the components is the right solution). – BalusC Sep 01 '12 at 17:50
  • For starters... thanx again for your time =) For reply... only 1 of the 2 views that executes the PostConstruct before the preRenderView uses view scooped beans. The other one uses only request based scope beans. It seems that the only difference between those views that work and those that doesn't is the use of input bindings. Thanx again for your time. Much appreciated! =) – su99-bsa Sep 01 '12 at 19:16