Saturday, June 5, 2010

The benefits and pitfalls of @ViewScoped

To prepare for a new set of JSF 2.0 targeted articles (have patience, I'd like to wait for Eclipse Helios and Tomcat 7 to be finished), I've played intensively with JSF 2.0 and Facelets the last weeks (to be precise, with Mojarra 2.0.2). The new JSF 2.0 annotations and implicit navigation (the outcome will implicitly go to /outcomevalue.xhtml page) are great and very useful. No hassling with faces-config.xml anymore. It's awesome.

JSF 2.0 also introduces an important new scope and offers the possibility to define your own custom scopes. The new scope is the view scope. It lies between the existing and well-known request and session scopes in. You can put the managed bean in the view scope using the @ViewScoped annotation.

Note that the bean needs to implement Serializable as it will be stored in the view map which is in turn stored in the session. Some servletcontainer configurations will namely store sessions on disk instead of in memory. This is also mandatory when you have configured JSF view state saving method to client instead of (default) server.

You'll probably recognize yourself in abusing the session scope for data which you would like to retain in the subsequent requests to avoid the expensive task of reloading it from the database on every request again and again, such as the datamodel for the <h:dataTable> in order to be able to retrieve the selected row. A major caveat is that the changes are reflected in every opened window/tab in the same session which leads to unintuitive webapplication behaviour and thus bad user experience. Tomahawk was early to introduce a solution for this in flavor of <t:saveState> and <t:dataTable preserveDataModel="true">. The first will store the given model value temporarily in the viewroot and set it back in the model during the restore view phase of the subsequent request. The second does that for the datatable's value. Hereafter JBoss Seam came with the Conversation Scope and Apache MyFaces Orchestra followed shortly. Both saves the bean state in the session among requests, identified by an extra request parameter.

You'll probably also recognize the issue of the <h:commandButton> or <h:commandLink> action not being fired because the rendered attribute of the component or one of its parents returns false during the form submit, while it was true during the initial request. You would need to fall back to the session scope or grab Tomahawk's <t:saveState> to fix it. How annoying!

The new view scope should solve exactly those issues. A @ViewScoped bean will live as long as you're submitting the form to the same view again and again. In other words, as long as when the action method(s) returns null or even void, the bean will be there in the next request. Once you navigate to a different view, then the bean will be trashed.

Note: the outcommented dao.somemethod() lines are what you actually should do as well in real code. Also note that the DataModel is lazily instantiated in the getter, because it doesn't implement Serializable and it would otherwise be null after deserialization.

The Item class is just a simple model object, its code should be straightforward enough. A Serializable Javabean with two properties Long id and String value, a default constructor and a constructor filling both properties, a bunch of appropriate getters/setters, equals() and hashCode() overriden.

Amazingly simple, isn't it? If you know or have read the well known Using Datatables article (it has been almost exactly 4 year ago when I wrote it for first! according to Google Analytics, that page alone has already been viewed almost 150,000 times since 1 September 2007), you'll realize how hacky and verbose it could/would be when doing it in the request scope alone with all of those bound <h:inputHidden> components and reloading the data in the action method or getter.

If you've studied the managed bean code closely, you'll also see that the javax.faces.model.DataModel is finally parameterized in JSF 2.0. No need for nasty casts on getRowData() anymore.

If you're targeting a Servlet 3.0 / EL 2.2 capable container such as Tomcat 7, Glassfish 3, JBoss AS 6, etc, then it can be done even more simple! You can pass method arguments in EL! This allows you for passing the current row just straight into bean's action method. Here's a minor rewrite of the above quick'n'dirty example which utilizes EL 2.2 powers.

In a nutshell: the @ViewScoped breaks when anyUIComponent is bound to the bean using binding attribute or when using JSTL <c:forEach> or <c:if> tags in the view. In both cases the bean will behave like a request scoped one. The first one is in my opinion a pretty major bug, the second one is only an extra excuse to get rid of the whole JSTL stuff in Facelets.

46 comments:

Very nice! JSF 2.0 really seems like a HUGE improvement, although it's a pity there are new pitfalls again.

Regarding the scoping, you may also want to look into the conversation scope that's newly introduced with Java EE 6. This is technically from CDI, but practically it's for JSF. It basically works just like the viewscope and other JSF scoped, in that you annotate your backing back with it.

@Gimby: I'm all fine :) I just got enough from Sun forums. At Stackoverflow you've under each the benefit that you can edit questions to improve its quality (spelling, code formatting, etc) when you've earned enough reputation. Further that site is also simply more robust and userfriendly. I checked, forums.sun.com is right now again down.

Hello, days ago I started new site based on JSF 1.2 http://lastpubs.complease visit:) In this site I used t:saveState for solving this kind of problems, example saving some id.I do not tested viewScoped too long, but I can suggest primefaces optimus libs for do the same without including JSF 2.0. Best regards,

Hi, i have one question.I'm using JSF 2.0 and i've had some problems with ViewScoped. When I press F5 to refresh the page an error occurs because the parameters passed to this page via Flash are losing. Why this happens?Another thing, why when I press F5 to refresh the page my @PostConstruct method is called again? With ViewScoped the class is instantiated again?

You have a DataModel that wrap around a List. However, when u add or delete entry from the List, dont you need to re-update your DataModel, or is DataModel smart enough to know that there are changes in the list

I used your code and it worked great! Although I had a problem on update. I am using versioned entities so after my update, if I attempted to update the object again I would get a merge exception. I fixed this by reloading the list, is that OK or am I doing bad bad things?

Hello , I need help for one problem with JSF, it eat a lot of memory in the server side, how we can solve it finally? I think we can all save state save into db and release it?Have you example?JSF 2.0 must be better, but have you solution (not a standart), to solving memory problem with jsf??

Well I used to have the "@ViewScoped as @RequestScoped" problem and then I changed the PARTIAL_STATE_SAVING property in web.xml, as you mentioned, and voila! Everything is fine now.

But I'm concerned about this property, what are the consequences and penalties that my application will suffer with this property (PARTIAL_STATE_SAVING) set to false? Is this workaround configuration recommended for production environment?

The dilemma about this is when you need to use RichFaces (or similar) tab panels from iterative data, then you either have to use c:forEach or you have to use component bindings. If then the PARTIAL_STATE_SAVING isn't working, you're bummed (until JSF 2.2 is out...). Horrible situation for JSF newcomers - especially when you have a deadline.

I think the fact that "Once you navigate to a different view, then the bean will be trashed" is a fatal flaw with view scope. For example, if you have a commandButton that relies on data in view scope and whose action causes navigation to a different view, it will only work once. Click the button, hit the back button, and then click the button again and the required view data is gone.

Much of JSF seems to want to ignore the fact that the most common client to render to is a web browser. Since web browsers have built-in navigation abilities, server-side state saving is extremely problematic.

View scope has the appearance of solving the problem, but it really doesn't. It's effectively an implicit conversation scope that ends as soon as you navigate to a new view. There are too many subtle consequences of the automatic conversation ending, so I don't think view scope is actually all that useful.

@Brian: this is caused because the page is requested from browser cache instead of from server. There are 2 ways to solve this: 1) set state saving method to client. 2) create a filter which instructs the browser to not cache the JSF pages.

Haey BalusC, I have a problem in my application, I am using Facelets with Rich Faces 4 and JSF2.0. I had this a4j:commandButton in the page and the scope for my backend bean is view scope, but every time i click on this button, the control is transferred to the constructor rather than the method specified in the action attribute. I am helpless here as this button works fine in Firefox but i need to implement it in Internet Explorer.Thanks

Am using primefaces selectonemenu. Am filling the data from mysql table when the page loads. If there is data in table no prob, its simply filling the data to selectonemenu. But if dont have data its throwing null pointer exception. how can i solve this issue. if no data in table it should not throw any error.

Am using primefaces selectonemenu. Am filling the data from mysql table when the page loads. If there is data in table no prob, its simply filling the data to selectonemenu. But if dont have data its throwing null pointer exception. how can i solve this issue. if no data in table it should not throw any error.

Am using primefaces selectonemenu. Am filling the data from mysql table when the page loads. If there is data in table no prob, its simply filling the data to selectonemenu. But if dont have data its throwing null pointer exception. how can i solve this issue. if no data in table it should not throw any error.

Hi Very nice tutorial on data table,i am having a problem in data scroller,the data table doesn't get reset once i updated the value from the database.As such in your code i am having a table and below then i ma having add button and it will render a panel on click and when i click on save button in the add panel,it will render the table with the new row which is updated with the database,after which i my datascroller is not working,its not refreshing the data table.can u tell me a suggestion

Hi.. I am new to JSF. I have been trying to use the function to update two list boxes based on the selection on one list box. The original ManagedBean was RequestScoped, but after adding it gives errors. Changing it to SessionScoped works, but then the page does not refresh and the selected values are always highlighted.If I use ViewScoped and declare the bean Serializable, I get and error when I declare the injected EJB object as Serializable!! Please help

May I ask these questions:1. I can't see any setItem() setter in the code, though 2 input fields are present (in Add and Save panels). I tried this example as shown (ie without setItem) and it works.To me, it should not.What am I missing ?

2. In numerous doc around the net, managed beans methods are supposed to have a signature such as "public String method(Item)". The one used in the example return void.Where does it come from ?

BalusC Is it possible to have two datatables in one jsf page, top and bottom ideal for typical master detail scenario? When I click radio button of top datatable, populate bottom datatable with rows based on master datatable. Could you be kind enough to provide some insight into this?

Hi Balus,Nice explanation indeed! I have been facing little problem, my all @viewscoped beans get initialized on server startup and publishing changes on server in Eclipse in development environment. @PostConstruct method gets called, i have code in @PostConstruct which should be called on view or page render instead of server startup.

case with ViewScoped beans not garbage collected ... even after session expiration and this would cause a memory leak ...

i am using primefaces 3.5 and jsf 2.1.24 ...

imagine people loging in and logging out of the application and leaving behind so many viewscoped beans in memory this could cause havoc ...

wehn i refresh browser new instance of viewscoped bean is created but old one is not garbage collected ... can see in profiler / and also neither finalize method sout is printed nor @PreDestroy sout is printed implicating they are never called ...

so is this problem of @Predestroy/finalize not called or Viewscoped bean not garbage collected and how to fix this ?

Hello BalusC, I am using p:datatable to display value from list. I put this list in session map in one button click event. now i want to update this list from another jsf page which is open using javascript. after changing values I am getting updated list in controller but it is not updated in datatable. So if any idea then please let me know. Here note that i am not using session bean only use session map to share the object with other jsf page.

About

Donate

For the ones who want to express their excessive thanks for my work, I used to have an Amazon wishlist with a list of books, but right now I don't have any interesting books on the list anymore (to anyone who've sent books before: thank you very much, I got 6 books in 6 months). You can always donate something so that I can use it for other stuff, such as Nespresso coffee.