Introduction
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.
package com.example; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Bean implements Serializable { // ... }
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.
Back to top
Really simple CRUD
Here's a quick'n'dirty example in flavor of a really simple CRUD on a single page how you could take benefit of it in combination with a datatable.
package com.example; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; @ManagedBean @ViewScoped public class Bean implements Serializable { private List<Item> list; private transient DataModel<Item> model; private Item item = new Item(); private boolean edit; @PostConstruct public void init() { // list = dao.list(); // Actually, you should retrieve the list from DAO. This is just for demo. list = new ArrayList<Item>(); list.add(new Item(1L, "item1")); list.add(new Item(2L, "item2")); list.add(new Item(3L, "item3")); } public void add() { // dao.create(item); // Actually, the DAO should already have set the ID from DB. This is just for demo. item.setId(list.isEmpty() ? 1 : list.get(list.size() - 1).getId() + 1); list.add(item); item = new Item(); // Reset placeholder. } public void edit() { item = model.getRowData(); edit = true; } public void save() { // dao.update(item); item = new Item(); // Reset placeholder. edit = false; } public void delete() { // dao.delete(item); list.remove(model.getRowData()); } public List<Item> getList() { return list; } public DataModel<Item> getModel() { if (model == null) { model = new ListDataModel<Item>(list); } return model; } public Item getItem() { return item; } public boolean isEdit() { return edit; } // Other getters/setters are actually unnecessary. Feel free to add them though. }
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.
And now the view, it's Facelets, save it as crud.xhtml:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Really simple CRUD</title> </h:head> <h:body> <h3>List items</h3> <h:form rendered="#{not empty bean.list}"> <h:dataTable value="#{bean.model}" var="item"> <h:column><f:facet name="header">ID</f:facet>#{item.id}</h:column> <h:column><f:facet name="header">Value</f:facet>#{item.value}</h:column> <h:column><h:commandButton value="edit" action="#{bean.edit}" /></h:column> <h:column><h:commandButton value="delete" action="#{bean.delete}" /></h:column> </h:dataTable> </h:form> <h:panelGroup rendered="#{empty bean.list}"> <p>Table is empty! Please add new items.</p> </h:panelGroup> <h:panelGroup rendered="#{!bean.edit}"> <h3>Add item</h3> <h:form> <p>Value: <h:inputText value="#{bean.item.value}" /></p> <p><h:commandButton value="add" action="#{bean.add}" /></p> </h:form> </h:panelGroup> <h:panelGroup rendered="#{bean.edit}"> <h3>Edit item #{bean.item.id}</h3> <h:form> <p>Value: <h:inputText value="#{bean.item.value}" /></p> <p><h:commandButton value="save" action="#{bean.save}" /></p> </h:form> </h:panelGroup> </h:body> </html>
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.
Really simple CRUD, now without DataModel!
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.
package com.example; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Bean implements Serializable { private List<Item> list; private Item item = new Item(); private boolean edit; @PostConstruct public void init() { // list = dao.list(); // Actually, you should retrieve the list from DAO. This is just for demo. list = new ArrayList<Item>(); list.add(new Item(1L, "item1")); list.add(new Item(2L, "item2")); list.add(new Item(3L, "item3")); } public void add() { // dao.create(item); // Actually, the DAO should already have set the ID from DB. This is just for demo. item.setId(list.isEmpty() ? 1 : list.get(list.size() - 1).getId() + 1); list.add(item); item = new Item(); // Reset placeholder. } public void edit(Item item) { this.item = item; edit = true; } public void save() { // dao.update(item); item = new Item(); // Reset placeholder. edit = false; } public void delete(Item item) { // dao.delete(item); list.remove(item); } public List<Item> getList() { return list; } public Item getItem() { return item; } public boolean isEdit() { return edit; } // Other getters/setters are actually unnecessary. Feel free to add them though. }
And now the view:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Really simple CRUD</title> </h:head> <h:body> <h3>List items</h3> <h:form rendered="#{not empty bean.list}"> <h:dataTable value="#{bean.list}" var="item"> <h:column><f:facet name="header">ID</f:facet>#{item.id}</h:column> <h:column><f:facet name="header">Value</f:facet>#{item.value}</h:column> <h:column><h:commandButton value="edit" action="#{bean.edit(item)}" /></h:column> <h:column><h:commandButton value="delete" action="#{bean.delete(item)}" /></h:column> </h:dataTable> </h:form> <h:panelGroup rendered="#{empty bean.list}"> <p>Table is empty! Please add new items.</p> </h:panelGroup> <h:panelGroup rendered="#{!bean.edit}"> <h3>Add item</h3> <h:form> <p>Value: <h:inputText value="#{bean.item.value}" /></p> <p><h:commandButton value="add" action="#{bean.add}" /></p> </h:form> </h:panelGroup> <h:panelGroup rendered="#{bean.edit}"> <h3>Edit item #{bean.item.id}</h3> <h:form> <p>Value: <h:inputText value="#{bean.item.value}" /></p> <p><h:commandButton value="save" action="#{bean.save}" /></p> </h:form> </h:panelGroup> </h:body> </html>
Note the action="#{bean.edit(item)}" and action="#{bean.delete(item)}". The current item is simply been passed as method argument! This allows us to get rid from DataModel altogether.
Back to top
Hey, there's "pitfalls" in the title?
Yes, well spotted. Check the following two questions on Stackoverflow.com:
- Why does @PostConstruct callback fire every time even though bean is @ViewScoped?
- JSTL c:forEach causes @ViewScoped bean to invoke @PostConstruct on every request
In a nutshell: the @ViewScoped breaks when any UIComponent 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.
This is related to JSF 2.0 issue 1492. Here's an extract of relevance:
This is a chicken/egg issue with partial state saving. The view is executed to populate the view *before* delta state is applied, so we see the behavior you've described.
At this point, I don't see a clear way to resolve this use case.
The workaround, if you must use view-scoped bindings would be setting javax.faces.PARTIAL_STATE_SAVING to false.
Back to top
Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair.
(C) June 2010, BalusC

26 reacties:
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.
Good to see you're still alive dude - I was almost starting to get worried when you didn't show up on forums.sun.com anymore ;) (gimbal2)
Seeing your other blog: very nice house! My spanking new house in the Netherlands doesn't even come close to that...
@Robert: Thank you! Yes, that's JSR299, but you're dependent on the servletcontainer/appserver whether it's supported. There's however a nice blog about it: http://thatjavathing.blogspot.com/2010/05/conversation-scope-cdi-jsr-299-vs-seam.html
@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.
hey, another blog (thatjavathing) that has topics on JBoss stuff :) And there I thought I was doing something original...
Well keep writing articles about JSF so I can link to them in the Sun forums ;)
Hello, days ago I started new site based on JSF 1.2 http://lastpubs.com
please 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,
Hello, I have one question.
is it real write code in java/jsf for chechking site rank..... like in this sites?
http://popuri.us/
http://www.googlepagerankchecker.com/
Who know any api or examples in java?
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?
Hi BalusC.
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
@Thang: it's not needed as long as if you're using the same list reference which the datamodel has wrapped. Try it yourself.
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?
Very nice tutorial. I surprised by the Edit part, how DataModel determines which item is selected by the user? Where can I learn more about such features in JSF?
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??
Hi BalusC! Your tips help me a lot, thanks man!
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?
@Davi I'm using GlassFish 3.1.1 within Mojarra 2.1.x and this solution works once time, but you try again to open page.xhtml and throw a NPE from com.sun.faces.application.StateManagerImpl.saveView()
I don´t know if the exception is throws from another issue in my app, but I decide to rollback this property, until another idea appears.
Thanks @BalusC your article are very useful and helpful
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.
Oh and if you're using RichFaces 4 right now you'll not want to set PARTIAL_STATE_SAVING to false for multiple reasons: https://issues.jboss.org/browse/RF-11434, https://issues.jboss.org/browse/RF-10967, ... probably more. It's fun!
@Kawu: you could try MyFaces, it has a different view state handling mechanism which is told to be more robust than the Mojarra's one at the moment.
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
hi BaluC,
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.
thanks in advance
Sowmiya.
hi BaluC,
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.
thanks in advance
Sowmiya.
hi BaluC,
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.
thanks in advance
Sowmiya.
Post a Comment