Navigation from managed bean constructor in ADF Faces JSF 1.2 - jsf

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.

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.

Related

Why FaceletViewHandlingStrategy.renderView force http session creation [duplicate]

I am running Mojarra 2.2.0.
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
The managed bean action method is-
public void action() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
System.out.println(session.getId()); // not null for stateful views
}
For stateless views session.getId() throws NPE
For views which are not stateless-
Firing a GET request, there is JSESSIONID=340041C96D5AA446D761C3602F54A76D
I read it here that-
For client side state saving mechanism, JSF won't create the session
and will store the view state in a hidden input field with the name
javax.faces.ViewState in the form whenever necessary.
Further, it's mentioned here that
JSF will indeed autocreate the session because the JSF view state has
to be stored over there. If you set the JSF state saving method to
client instead of server, then it won't be stored in session and hence
no session needs to be created
I think the above line is a source for trouble for me.
If you set the JSF state saving method to client instead of server,
then it won't be stored in session // FULLY AGREED
and
hence no session needs to be created. // This confuses because for
client side saving mechanism, a session id gets generated by the
servlet container & hence there is a session associated with the
request.
In reference to the discussion which I had with BalusC in this question,
I created a HttpSessionListener-
#WebListener
public class MyHttpSessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
Thread.dumpStack();
}
public void sessionDestroyed(HttpSessionEvent event) {
}
}
See below attached screenshots(these 2 screenshots are for version 2.0.3, there must have been an old bug due to which the session was getting created)-
Libraby (Mojarra 2.2.0)-
When does JSF creates a session
Eaiest way to naildown this is creating a HttpSessionListener, putting a debug breakpoint on sessionCreated() method and inspecting the call stack who needed to get the session for the first time (and thus implicitly needs to create it).
In below example you will see a chain of getSession() calls in the call stack. You will see that FaceletViewHandlingStrategy.renderView() method is the one calling it for the first time.
After you click on FaceletViewHandlingStrategy.renderView() line in debugger's call stack, you will get into its source code (Maven will load source code automatically, otherwise you need to manually attach it).
You see, when server side state saving is enabled and the view to render is not transient (stateless), then JSF will implicitly create the session, just to ensure it's created on time in order to save the view (if the session was created later, e.g. during render response phase, you would otherwise risk exceptions like this Adding <h:form> causes java.lang.IllegalStateException: Cannot create a session after the response has been committed).
You'll in the source code also immediately see that when the state saving method is set to client, or when the view is stateless as in <f:view transient="true">, then JSF won't anymore implicitly create the session. Older JSF versions may do that as you figured, but this is to be accounted as a bug and ought to be fixed in a newer version.
If you would like to ensure statelessness and avoid accidental/unforeseen session creation, then you could just throw new IllegalStateException() inside sessionCreated() method. When that happens, you just have to look in call stack who's responsible for creating the session and then fix/change the code to not do that anymore.
what does it puts in a session map?
Under the covers, ExternalContext#getSessionMap() delegates to HttpSession#setAttribute()/getAttribute()/removeAttribute(). You can listen on those methods using a HttpSessionAttributeListener.
In below example you will see that ViewScopeContextManager.getContextMap() line calls SessionMap#put() method in order to put something in the session map. When you unfold the event argument, you will see the session attribute name and value, which is com.sun.faces.application.view.activeViewContexts and an empty ConcurrentHashMap respectively.
Indeed, I was using a #Named #ViewScoped which was referenced by an value expression on the particular page (you see EL resolver and Weld resolver further down in call stack). When you click ViewScopeContextManager.getContextMap() line in call stack, you'll see that it was just preparing a map in session scope in order to store view scoped beans.
That's just one example. There are more things which could be stored in the session. Using a debugger this way and inspecting the associated source code will tell a lot about the Why.

javax.faces.FacesException Can't instantiate class [duplicate]

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?
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:
Reference the bean for the first time as early as possible in the view.
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().
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:
Hit a bean method and redirect on a GET request
Is there any easy way to preprocess and redirect GET requests?
How to navigate in JSF? How to make URL reflect current page (and not previous one)

Redirect in #PostConstruct causes IllegalStateException

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?
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:
Reference the bean for the first time as early as possible in the view.
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().
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:
Hit a bean method and redirect on a GET request
Is there any easy way to preprocess and redirect GET requests?
How to navigate in JSF? How to make URL reflect current page (and not previous one)

How to handle multiple submits before response is rendered?

It has been reported from testing that it is occasionally possible to press a button more than once if the response is not rendered fast enough, causing several invocations of the back-end code, which we do not want.
Application is Java EE 6 Web Profile using JSF 2.0 inside Glassfish 3.1.1.
I was wondering how this should be properly dealt with, and have thought of a few scenarios:
Submitting should disable all buttons using javascript while response is being rendered.
A flag in the Session scope saying it is already active, so the sensitive code is skipped and just moves on to the re-rendering of the response for the previous submit.
A synchronized block delaying the processing until the previous request have finished. Then it should be detected that it has been already processed and skipped.
Using one of the "new" scopes like conversion to handle the detection?
My immediate gut feeling is that the best approach is to have sensitive code blocks atomic, but then the problem is with rendering the correct response.
How should I approach this?
Submitting should disable all buttons using javascript while response is being rendered.
This is the easiest to implement in a generic manner. If you happen to use <f:ajax> exclusively, you could use the jsf.ajax.addOnEvent() to perform the job in a generic manner. An alternative JavaScript approach is to create kind of an "Loading" overlay which blocks the UI so that the enduser won't be able to interact with the underlying page anymore. This is basically an absolutely positioned hidden <div> which spans the entire viewport with some opacity (transparency). You could show it on submit and hide it on render. The keyword for this technique is "modal dialog". UI-oriented JSF component libraries have at least such a component already in their assortiment. E.g. PrimeFaces with a <p:dialog modal="true"> inside a <p:ajaxStatus>, or the <p:blockUI>
The only disadvantage is that it won't work if the client has JS disabled or don't use it and it thus won't prevent HTTP clients from double submits.
A flag in the Session scope saying it is already active, so the sensitive code is skipped and just moves on to the re-rendering of the response for the previous submit.
This is more known as "synchronizer token pattern" and has ever been requested for JSF by spec issue 559 which is currently on the ticket targeted for 2.2, but there doesn't seem to be any activity on it. The detection and blocking part is technically easy to implement, but the synchronous response handling part is not easy to implement if you want that the enduser ultimately retrieves the response as generated by the initial request. The asynchronous response handling is easy: just don't specify any components to update, i.e. empty the collection as returned by PartialViewContext#getRenderIds(). After all, this is more robust than using JS to disable the buttons or block the UI.
As far as I know, Seam 2 was the only who offered a reuseable JSF component for this, the <s:token>. I must however admit that this is an interesting idea for a new OmniFaces component. Maybe I will personally take a look at it.
A synchronized block delaying the processing until the previous request have finished. Then it should be detected that it has been already processed and skipped.
This is not easy to implement generically, this would require a change in all action methods to check if the job is already done. It also won't work if the webapp runs on multiple servers. A synchronizer token is easier as it would be performed before the action methods are invoked. A synchronizer token is also less expensive as you don't end up with multiple requests in the queue which would only cost threads/resources.
Using one of the "new" scopes like conversion to handle the detection?
This problem cannot be solved by playing around with managed bean scopes. Managed bean scopes serve a different purpose: the lifetime of the bean instance.
As Adrian mentioned, I would also use BlockUI. There is a BlockUI component from Primefaces. When you submit your forms through ajax, you could also use an overlay during the request. See Primefaces`s Ajax Status for an example.

JSF adding query parameters

I am using JSF and I have a backing bean method which does some processing and sets a
variable 'outcome' which then decides the next page to navigate to depending on the
faces-config.xml navigation rules.
What I want to do is add parameters to the URL (in the backing bean?) when the next page is navigated to.
However in the Handler where the backing bean method is, there is no reference to the
HttpRequest object. This is an existing handler which has been around for a long time, so I
am wondering how I can do
request.setAttribute("name", value);
Is there a different approach available for JSF? Any help much appreciated.
HI BalusC,
I am trying to implement what you explained below, however I am running into a problem.
This is what I have:
StringBuffer url = ( (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getRequestURL();
url.append( "?serialNumber=");
url.append(regBean.getSerialNumber());
try{ FacesContext.getCurrentInstance().getExternalContext().redirect(url.toString());
}catch (Exception ex){
ex.printStackTrace();
}
There is no exception generated however I get a 500 Http error "the server has encountered an unknown error." The log shows a little more detail but not enough to be helpful:
ERROR [lifecycle] JSF1054: (Phase ID: INVOKE_APPLICATION 5, View ID: /registration/productValidation.jsp) Exception thrown during phase execution: javax.faces.event.PhaseEvent[source=com.sun.faces.lifecycle.LifecycleImpl#591dae]
11:19:12,186 ERROR [[Faces Servlet]] Servlet.service() for servlet Faces Servlet threw exception
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:435)
at com.sun.faces.context.ExternalContextImpl.redirect(ExternalContextImpl.java:421)
at com.sun.faces.application.NavigationHandlerImpl.handleNavigation(NavigationHandlerImpl.java:181)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:130)
at javax.faces.component.UICommand.broadcast(UICommand.java:387)
at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
Any ideas at all will be very much appreciated. Thanks!
Ok, thanks for your comments, I changed some stuff around and now I have:
FacesContext.getCurrentInstance().getExternalContext().redirect("mypage.jsp?serialNumber=555555");
Upon debugging I can see that the redirect is working since on mypage.htm I am displaying some headers from a resourcebundle (properties file) so when it tried to get the header to display it is encountering a NullPointer on the line below:
FacesContext context = FacesContext.getCurrentInstance();
context is null, so the log shows NullPointer error but the url of the page is correct I can see the address bar showing http://..../mypage.jsp?serialNum=5555 just as expected!
It appears its having trouble just displaying the contents of the page. So close yet so far ;-(
You need to fire ExternalContext#redirect() in the bean action method yourself.
public void submit() {
String url = "page.jsp?name1=value1&name2=value2";
FacesContext.getCurrentInstance().getExternalContext().redirect(url);
}
If your IDE validator is jerking about the void action method, then you can just ignore it or declare it back to String and put return null; at end of method block.
If you want to set the particular parameters back in some bean in the subsequent request, then you can set them as managed properties in faces-config.xml by #{param.name1} and #{param.name2}.
That said, request attributes should not be confused with request parameters. The request attributes are attached to the current request in the server side only. They are in no way passed to the next request. There you use request parameters for which you can either attach to the redirect URL or include as hidden parameters in a POST form in the response page.
Further, it might be useful to know that you can get a handle of the HttpServletRequest in JSF by ExternalContext#getRequest(). You should however try to avoid to go that far with hauling the "raw" Servlet API from under the JSF hoods. Make use of JSF-provided facilities as many as possible.
JSF 2 added parameters to the navigation handler via the view-param element. From the spec:
If a matching <navigation-case> element was located, and the <redirect/> element was specified in this <navigation-case>, call getRedirectURL() on the ViewHandler, passing the current FacesContext, the <to-view-id>, any name=value parameter pairs specified within <view-param> elements within the element, and the value of the include-view-params attribute of the <redirect /> element if present, false, if not. The return from this method is the value to be sent to the client to which the redirect will occurr. Call getFlash().setRedirect(true) on the current FacesContext. Cause the current response to perform an HTTP redirect to this path, and call responseComplete() on the FacesContext instance for the current request. If the content of <to-view-id> is a value expression, first evaluate it to obtain the value of the view id.

Resources