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)
Related
First of all, sorry for my english. I have a RequestScoped ManagedBean in order to send parameters to other views, without getting the The scope of the object referenced by expression is shorter than the referring managed beans error. I also have in the same RequestScoped view a p:dataTable showing these beans objects, with an update button for each row, that retrieves this bean to another form in the same view to be update with new values.
The problem is, when I hit the submit button to record the new values, another record is created, instead of the older one being updated. Of course, because the bean is killed when the submit button is pressed (RequestScoped), creating a new bean and another record in the DB. How can I fix it in this scope?
I've seen some alternatives using #PostConstruct here, however I'm not entirely sure it would solve my specific problem.
EDIT:
After researching a bit more into this topic, I came to another doubt: I am using the same Bean in both views (in my case, ProjectBean), should I create a new Bean with RequestScoped annotation (something like ProjectIdBean), set the older one to ViewScoped (so I can reproduce updates naturally on my Database), and let this new Bean handle the requests for other views?
Submit button:
<p:commandButton value="Gravar" action="#{projetoBean.gravar}"
process="#form" update="#form :formTabelaProjetos:tabelaProjetos" />
'Gravar' method:
public void gravar() {
System.out.println("Gravando projeto " + this.projeto.getNome());
ProjetoDAO dao = new ProjetoDAO();
if (this.projeto.getId() == null) {
dao.adiciona(this.projeto);
projetosAtivos = this.getProjetosAtivos();
} else {
dao.atualiza(this.projeto);
}
this.projeto = new Projeto();
}
You can use request scoped backing bean for updating entities. The problem is, that the request life cycle ends when your page is rendered. So anything you loaded will get discarded. The submit creates another request, that will try to reload resources, but it is a different request than the previous one and for example request parameters often do not contain what the programmer expects. But this is what you found out already. There are two ways how to deal with the problem:
1) use simple getters and setters to set "String, Integer" and similar variables in your request scoped bean, that you use to reconstruct and modify the entity you want to update. It is not convenient for the programmer but request scoped beans save resources of your server.
2) change the scope of your backing bean. Session scope is not ideal, because it can stay in memory for a really long time. And you might realize you need to clean it up manually. Much better is ViewScoped bean as it allows you to work with the entities you loaded over several steps. It gets wiped out when the user leaves the page.
#javax.faces.bean.ViewScoped
#javax.faces.bean.ManagedBean
public class SomethingBean {
......
}
This question already has answers here:
Invoke JSF managed bean action on page load
(4 answers)
Closed 7 years ago.
I have to call a init method on my bean as first action on the page load, I have tried to simply call #{bean.init} at the very beginning of my page, but I have seen that the <c:if> tests are performed before the init(). I have something like
#{bean.init}
<c:if test="#{bean.conditionsCheck}">...</c:if>
and the conditionsCheck() method is called before the init(),
how can I fix it and call init() as really first thing?
You can use the #PostConstruct annotation to automatically invoke your init method:
#PostConstruct
public void init() {
// do something
}
This method is automatically invoked after construction of the bean.
Your solution looks more like a f:event with type="preRenderView" but this can't be used because the c:if tags are evaluated during view build time, while the f:event (respectively your solution) runs right before the view is rendered during render response phase. Have a look at this question and this question to get details.
Update: As you commented you are using a #SessionScoped bean where #PostConstruct is only called once per session and not on every page load. In this case another solution would be to call your init method as first statement in your conditionsCheck method (nearly the same as your suggestion with fake c:if boolean init). You could also use a custom PhaseListener but I guess that would be somewhat overdosed for this problem.
See also:
Why use #PostConstruct?
This works if you add a JSF Control like CommandButton, and in its value you write it as:
value="#{sessionScopedBean.method()}"
This method will be called whenever this page loads.
for anyone searching ...
We can use preRender of scriptCollector tag, if the used JSF implementation supports it, something like:
<scriptCollector id="scriptCollector1" preRender="#{bean.method}">
...
</scriptCollector>
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.
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)
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.