4
votes

Background: I have a JSF 2.0 application using MyFaces. Using this application, the users will create a complex "Widget" by going through a number of different JSF pages. I am storing the managed bean in the session scope, and the properties get filled in as the user goes through each page. Once they are done assembling the "widget", they would then want to create a brand new "widget" and repeat the process. My question is, how do I completely (and safely) remove the managed bean from the session without disrupting the JSF lifecycle?

I have seen other responses for similar questions suggesting that you can invalidate the session, and also you can access the HttpSession through the FacesContext and remove the bean that way. I don't want the user to have to log out or in again, and I don't want to invalidate the entire session. Assuming that I need to go through FacesContext to access the HttpSession and remove the bean, when in the JSF lifecyle is the most appropriate place to do this safely so that problems don't cascade through the rest of the cycle? I want to make sure JSF will have no problems creating a new session bean when the user starts the process of creating the next "widget".

I am also curious to understand why there isn't a mechanism in JSF to make this easier. Is there some other approach that I should be taking which is better in line with the intended JSF pattern? I can't use View Scope here because the managed bean will go through several different pages and views before it is completed.

Thanks in Advance!

4

4 Answers

5
votes

Create a single page wherein you render the multiple wizard steps conditionally.

<h:panelGroup rendered="#{wizard.step == 1}">
   <ui:include src="/WEB-INF/wizard/step1.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{wizard.step == 2}">
   <ui:include src="/WEB-INF/wizard/step2.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{wizard.step == 3}">
   <ui:include src="/WEB-INF/wizard/step3.xhtml" />
</h:panelGroup>

This way you can just use a @ViewScoped managed bean on this single page without much hassle.


Unrelated to the concrete question, PrimeFaces has a <p:wizard> component which does almost exactly like that. You may find it useful in order to save yourself from some painful boilerplate code as to validation and such.

1
votes

Your case can be simplified in case the widget creation data is encapsulated into single bean. I mean

@SessionScoped
@ManagedBean
public class WidgetMaker {
  private Widget widget;

  public void makeWidget(){
   //after creating/saving widget
    widget = new Widget();
  }

  public void resetWidget(){
    //this method can be called with pre render view handler
    //call this method on the first page that starts widget creation
    //in case person leaves the widget creation in between and starts over again
    widget = new Widget();
  }
}

For resetWidget in view (In the first widget creation page only)

<f:metadata>
  <f:event type="preRenderView" listener="#{widgetMaker.resetWidget}"/>
</f:metadata>

You can take pointers and adapt accordingly.

Hope this helps.

0
votes

Although I haven't used it myself yet, i would have a look at CDI's ConversationScope.

0
votes

I agree with BalusC that the view scope would be the better choice in your case. I just looked into my (very old) first JSF project where I had a multipage form in session scope. Later I switched to view state for the well known reasons.

However, I did reset the session scoped bean by simply replacing the instance in the session map:

MyBean newInstance = new MyBean();
FacesContext.getCurrentInstance().getExternalContext()
   .getSessionMap().put(beanName, newInstance);

with beanName as the bean's el name (a String).