Sunday, June 6, 2010

The benefits and pitfalls of @ViewScoped

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:

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.

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.

Update: this issue is fixed in Mojarra 2.1.18. See also @ViewScoped calls @PostConstruct on every postback request.

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

48 comments:

Robert Whane said...

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 said...

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...

BalusC said...

@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.

Gimby said...

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 ;)

armen said...

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,

armen said...

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?

Lucio Ribeiro said...

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?

Thang Pham said...

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 Pham said...
This comment has been removed by the author.
BalusC said...

@Thang: it's not needed as long as if you're using the same list reference which the datamodel has wrapped. Try it yourself.

Unknown said...

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?

Seven said...

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?

armen said...

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??

Unknown said...

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?

Unknown said...

@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

Kawu said...

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.

Kawu said...
This comment has been removed by the author.
Kawu said...

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!

BalusC said...

@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.

Brian Reilly said...

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.

BalusC said...

@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.

Ubaid Raja said...
This comment has been removed by the author.
Ubaid Raja said...

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

Unknown said...

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.

Unknown said...

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.

Unknown said...

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.

Prajeesh Kumar said...

Hi Balu,

I have a scenario where in a button action changes the value of some parameter x ( which is passed through url to the first page . I use viewParam for that )and moves to a new page.

The first page bean myBean1 is in viewscope and second page bean myBean2 is also a viewscope bean.

Now how I can pass the new value of x to the second bean myBean2 from myBean1

Thanks in advance

Rathihears said...

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

Neelam Singh said...

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

Olivier2831 said...

First of all, thank you very much for this very informative example !

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 said...

1. The properties are been set on the Item instance itself. E.g. bean.getItem().setValue(newValue).

2. It has the same effect as returning null.

Tarqaron said...

Link for the issue mentioned for binding and @ViewScoped

http://java.net/jira/browse/JAVASERVERFACES-1492

Anonymous said...

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?

Thanks

Twilite said...

Hello, I have a really dumb question. (I am brand new to JSF/Beans/etc.) :)

I ran you example then change the bean scope to SessionScoped and it worked exactly the same (at least as far as I could tell).

Can you explain to me why the behavior is the same?

Thanks!

BalusC said...

@Twittle: open the same page in multiple browser windows/tabs in the same session. You'll notice that the behavior is not the same and that they interfere all with each other.

In order to learn how to choose the right bean scope, read Communication in JSF 2.0 - Managed Bean Scopes.

Haseeb Mirza said...

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.

Please help me in this regard, will be highly appreciated.

Unknown said...

Hi, i have the same issue that had Lucio : i have a view scoped bean with takes out some values out from the flash memory in a PostConstruct annotated method.

Strangely, on page refresh the view scoped bean gets re-constructed with null values, since the flash memory lost his data after the refresh.

How can i solve this issue?

Mahesh said...

Hello guys i am new to JSF,
How should i start about JSF, any reference books or blogs that i can refer,
What are the best practices for JSF

Help Me....

sagsaw said...

Bauke, sorry to post this here didnt know how to get in tch

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 ?

Unknown said...

Hi, I tried a work around for the Jsf view bean issue for both Jsf 2.1 & Jsf 2.2. Try the code in below link http://stackoverflow.com/questions/12182844/memory-leak-with-viewscoped-bean

senthinil said...

Hi BaluC

We are using CDI with apache CODI,with ConversationScoped annotation in the Backing beans, please lemme know how to use apache orchestra framework.

Regards
Senthil

Siva_ms said...

Hi Balu,
I am facing difficulties in validation.
If validation is failed to retain the row(i.e editable mode) but in my code getting refreshed the datatable.
Row Editing















validate method

public void validiD(FacesContext context, UIComponent component, Object value) throws ValidatorException { if (value >=1 || value<=7) { FacesMessage msg = new FacesMessage("Provide valid values 1-7"); throw new ValidatorException(msg); } }

Unknown said...

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.

Surface Detail said...

I know that you wrote this sometime back, but I just wanted to thank you for getting me started on some basic JSF/JPA CRUD screens.

Binh Nguyen said...

Thanks, nice post

Unknown said...
This comment has been removed by the author.
Unknown said...

hi,

thanks for the article. what is instead dao i'm using entityManager and hibernate ? Can I do the same thing(almost) ?

Thanks again

Unknown said...

I was trying to adapt your sample recently, everything except for the "save" seems to work. I am not exactly sure why it doesn't

I have the code

https://github.com/trajano/jee/blob/master/jee-web/src/main/webapp/crud.xhtml

and the respective bean

https://github.com/trajano/jee/blob/master/jee-web/src/main/java/net/trajano/jee/web/ParticipantBean.java

It seems that any commandButtons in the edit form is not being fired (there's nothing on system out logs)