2

The page should be rendered only if user has permissions to see it. I try to achieve that by performing a check in PreRenderView event listener method:

<f:event type="preRenderView" listener="#{theBean.init}" />

And the method itself:

public void init() {
    if (user.havePermission()) {
        // backing bean init
    } else {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        ec.getRequestMap().put("message", no_permissions_message);
        try {
            ec.dispatch(ec.getRequestContextPath()
                + path_to_no_perm_page);
            fc.responseComlete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

When I try to look at denied page I see it, but get an exception in server log:

at org.omnifaces.util.FacesLocal.getRequestMap(FacesLocal.java:945) [omnifaces-1.7.jar:1.7]
at org.omnifaces.util.FacesLocal.getRequestAttribute(FacesLocal.java:953) [omnifaces-1.7.jar:1.7]
at org.omnifaces.util.Faces.getRequestAttribute(Faces.java:1416) [omnifaces-1.7.jar:1.7]
at org.omnifaces.eventlistener.CallbackPhaseListener.getCallbackPhaseListeners(CallbackPhaseListener.java:110) [omnifaces-1.7.jar:1.7]
at org.omnifaces.eventlistener.CallbackPhaseListener.afterPhase(CallbackPhaseListener.java:77) [omnifaces-1.7.jar:1.7]
at com.sun.faces.lifecycle.Phase.handleAfterPhase(Phase.java:189) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:107) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139) [jsf-impl-2.1.7-jbossorg-2.jar:]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:98) [primefaces-4.0.7.jar:4.0.7]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
...

The cause: immediately after dispatch call, FacesContext.getCurrentInstance() returns null. But this don't interrupt current phase. So on the phase end PhaseListener is executed. It tries to get FacesContext instance, but gets null.

Can I avoid PhaseListener execution here? Or may be check for FacesContext current instance is not null should be added to omnifaces?

Thanks.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
12kb
  • 99
  • 3
  • 5
  • Make this line `fc.responseComlete();` the last line in the `try...catch` block. There is a misspelled which won't compile. (OmniFaces has got nothing to do with the exception). – Tiny Apr 17 '15 at 23:10
  • I tried both cases. Nothing changed. Seems both should work fine. With `responseComplete()` we just interrupting JSF lifecycle, but not commiting the response, aren't we? – 12kb Apr 18 '15 at 10:20
  • I corrected it as you say. Thanks. According to the docs this is more right way. Even if both work fine. – 12kb Apr 18 '15 at 10:36
  • You are dispatching/forwarding using `ExternalContext#dispatch("/path/to/resource");` (404 is likely - I did not concentrate much yesterday). If you need to restrict access (when authorization (permission check) fails), you should conditionally redirect to a public resource using `ExternalContext#redirect("/path/to/resource");`. In both the cases, `FacesContext#responseComplete();` is completely unnecessary. Do you somehow need it in your real application? Besides, if you are on JSF 2.2, then `` may better be replaced with ``. – Tiny Apr 18 '15 at 13:03
  • Yes, I need it in real application. I want to use dispatch because I want not to change URL in client's browser. – 12kb Apr 18 '15 at 16:41
  • `FacesContext#responseComplete();` is needed here. I tried to remove it and got an Illegal state exception then. Thanks for viewAction, i'll consider it's usage. – 12kb Apr 18 '15 at 16:48

1 Answers1

3

Do not use ExternalContext#dispatch(), ever. This method has no single sensible use case in a decent JSF web application. It only corrupts the JSF life cycle because it creates another FacesContext within the current request. You should use JSF's own NavigationHandler or use ExternalContext#redirect().

When you're already on JSF 2.2, use <f:viewAction action="#{bean.init}"> which supports a navigation case outcome (like <h:commandButton action>):

public String init() {
    // ...
    return "someViewId";
}

Or when you're still on JSF 2.0/2.1, and can thus only use <f:event type="preRenderView">, and given that you're using OmniFaces use its Faces#navigate() or Faces#redirect():

public void init() {
    // ...
    Faces.navigate("someViewId");
}

In case you aren't using OmniFaces, here's how it's implemented:

public void init() {
    // ...
    FacesContext context = FacesContext.getCurrentInstance();
    context.getApplication().getNavigationHandler().handleNavigation(context, null, "someViewId");
}

Note that this all may still fail when using @PostConstruct instead of f:viewAction or preRenderView. See also a.o. Redirect in @PostConstruct causes IllegalStateException.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555