Navigation from managed bean constructor in ADF Fa

2019-05-08 03:12发布

问题:

Is it possible to navigate to another page/view from the constructor of the managed bean? I want this redirection if any exception occurred. I have tried many ways:

Try-1:

getFacesContext().responseComplete();
getFacesContext().getApplication().getNavigationHandler().handleNavigation(getFacesContext(), null, "gotoPartError");    
getFacesContext().renderResponse();

Try-2:

getServletResponse().sendRedirect("partError.jspx")

Try-3:

getFacesContext().responseComplete();    
getFacesContext().getExternalContext().redirect(getServletRequest().getContextPath() + "/pages/partError.jspx");

Try-4:

RequestDispatcher dispatcher = getServletRequest().getRequestDispatcher("partError.jspx");
dispatcher.forward(getServletRequest(), getServletResponse());

Try-5:

FacesContext context = getFacesContext();
UIViewRoot newPage = context.getApplication().getViewHandler().createView(context, "/partError.jspx");
context.setViewRoot(newPage);
context.renderResponse();

Try-6:

ControllerContext.getInstance().getCurrentViewPort().setViewId("partError");

Try-7:

Exception Handler in adfc-config.xml

Try-8:

Custom service handler defined in /.adf/META-INF/services/oracle.adf.view.rich.context.Exceptionhandler which extends oracle.adf.view.rich.context.Exceptionhandler

Try-9:

By extending JSF Life Cycle

None of them worked. For all the cases I received

java.lang.IllegalStateException: Cannot forward after response has been committed

Is not really possible in JSF 1.2? As I am using ADF 11.1.1.6.0, which uses JSF 1.2, some of the above "Try" contains ADF Faces ways.

I need anyway, that can be JSF 1.2 or ADF Faces, to navigate to error page. The only way I got success is the use of javascript, executed from backend, to open the error page in the _self window in case of error, but I don't really like it.

Any pointer in this matter would be very helpful.

回答1:

It's easier to solve a problem if the cause of the problem is understood. A good exception tells basically already everything about the cause of the problem.

Look closer:

java.lang.IllegalStateException: Cannot forward after response has been committed

The response has been committed. This is a point of no return. Perhaps you failed to understand what it means that a response has been committed (which has the consequence that you also failed to understand the exception itself).

By default, the HTTP response is written to a buffer which is flushed every ~2KB, depending on server configuration. A flush of the response buffer causes the written bytes being actually sent from server to client. Once that happens for the first time, a response is considered committed. This is a point of no return. The server cannot take the already written bytes back from the client in case the server actually needs to change the response afterwards.

If you have some code which potentially needs to change the response, then you should be invoking it before the response is committed.

In your particular case, the managed bean is apparently constructed in the midst of the JSF render response phase during generating the HTML output. A part of the generated HTML output has already been sent to the client (so, the response is committed). You're apparently referencing the request scoped bean relatively late in the JSF page, or the response buffer is relatively small, or the HTML <head> is relatively large which causes a flush already before the <body> starts, etcetera.

You really need to invoke the code before the render response phase. In JSF 1.2, you can use the <f:view beforePhase> for this.

E.g.

<f:view beforePhase="#{bean.navigate}">

with

public void navigate(PhaseEvent event) {
    if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
        // Do here your job which should run right before the RENDER_RESPONSE.
    }
}

Then your Try-1 and Try-3 will work (you can however leave those responseComplete() and renderResponse() lines away, they are implicitly already taken care of).

Try-2 and Try-4 are poor. You should avoid having javax.servlet.* imports in your backing bean. Try-5 is clumsy. Try-6, Try-7 and Try-8 are beyond my scope. Try-9 is doable, but extremely clumsy.