We are working on our first real JSF app and we have been doing pretty well so far, until we started encountering this one problem.
We have a first page, call it "Page1" which has 2 input text fields and a submit button. This page is backed by a managed-bean, call it "Page1FormBean". Now, when the submit call is made, an action listener method in a separate session scoped managed-bean (the view controller), is called which prints out a logger statement, and then continues to populate some other fields in that Page1FormBean with data from the database which corresponds with the user input from Page1.
A navigation rule for "success" is set up to get to "Page2". Once on Page2, the data is retrieved from the Page1FormBean and is displayed to the user. The data is in the form of a collection, and each row is displayed as a h:commandLink. At this point, I am COMPLETELY done using the Page1FormBean. The commandlinks have hard coded actions which are setup in the faces-config file, and they each call to another action listener method in the view controller. When any of the links on Page2 are clicked, the method in the view controller prints a logger message and then populates a managed bean, call it "Page3DisplayBean", with the data from the selection made by the user, which will be used on the next page, "Page3".
Here is the tricky part...
When Page1FormBean is in session scope (in the faces-config.xml file), all of this works perfectly fine. The end result is that we are on Page3 displaying the correct data from the Page3DisplayBean.
However, when Page1FormBean is in request scope, and one of the links on Page2 is clicked, the page simply refreshes and all data elements of the page are blank. The actionListener method which normally prints logger statement is NOT called at all...
Now, I understand why when this page refreshes all of the data is gone, that is simple... The scope of the managed bean limits its life so when the page is refreshed/reloaded a new null one is created and the data is gone... however, this doesn't explain why we are not navigated to Page3. Instead, we are indefinately stuck on Page2.
The problem, as far as we can tell from experimenting and researching the web, is that request scoping should not be used from the JSP level because of the postback nature of JSF... Since the JSF Framework refreshes the view underneath the hood both when the page is loaded, and when it tries to leave, the request scoped bean will be there for the initial load, but not for the post back view refresh...
The solution we have seen up to this point is just to "make everything session scoped". This approach works, and the pages work correctly, but it leaves the door open for old data to remain in memory. This means we have to write bits of rather messy code to make sure that all beans we no longer need are cleaned out or reset.
Finally the Question
Since the problem seems to be that the request scope's lifecycle is barely short of the length needed for this, a scope of 1.5 requests or 2 requests would seem like a logical way to keep the data around long enough to use it, yet still get rid of it in a timely fashion after use.
One possible suggestion was that the beans could be scoped "session" as far as JSF is concerned, but that we could write some code to manage our own sense of a scope... For example, a superclass to all of our beans might have optional constructors which accept an int parameter specifying how many requests that bean should live. Default behavior would remain the same. Then a servlet filter could be used to clean out and remove the beans which have lived through as many requests as they were supposed to...
The missing link is the code needed to increment this internal request cycle counter... I need to find a way to increment this counter... Perhaps there is a given call made on every object in session during each request cycle, or maybe there is a piece of the framework code that could be changed to increment that counter manually during the refresh view cycle, etc...
The other possible solutions are to have something on each of the JSP pages, or at the very least on the main menu page for the application that calls resets on each of these beans... If this is the only way possible, it would be interesting to find out if the JSF framework supports any type of "resetter" components instead of calling value binding methods with hidden components...
Has anyone dealt with anything like this, and could point me in the right direction, or has anyone come up with a solution to this request + 1 scoping problem?
Thanks in advance...
P.S. Sorry for the length... [ March 14, 2005: Message edited by: Garrett Reinard ]
Joined: May 30, 2002
Originally posted by Garrett Reinard:
The missing link is the code needed to increment this internal request cycle counter... I need to find a way to increment this counter...
I guess you need to jump inside the JSF implementation for this. Now when you are mapping your fields of page2 with page1 bean : 1. **something** must be locating the bean - page1 in session and 2. after locating, it must be passing it's refrence to the page2. Now you just put one setter method call (between step 1 & 2) on the session bean to increase the counter. And thereafter you can use filter to clear out all the beans who have reached their counter.
But isn't clean right? ... But If your flow is simple, I think this will work. You can also look into PhaseListener. Whenever a new view (page) is loaded/reloaded, this listener will listen to callback events. Hence if you know after two page, your bean won't be used -- u can use this. Another solution could be you can create your bean yourself rather than depending on JSF , Create a new instance everytime even if bean is in session scope. The best solution (or rather the least worse) would actually depend upon the complexity of your application.