Showing posts with label ViewScoped. Show all posts
Showing posts with label ViewScoped. Show all posts

Thursday, February 2, 2017

OmniFaces 2.6 enables saving entire @ViewScoped bean in client side state

OmniFaces 2.6 has been released!

The main new things are a bunch of general purpose converters for strings and collections, a new implicit EL object #{faces} which delegates to Faces utility class and last but not least a new attribute for @ViewScoped which triggers saving the entire physical bean in JSF view state when client side state saving is enabled, hereby making it effectively to be not expirable and thus live forever as long as the associated page lives, even when cached/copied.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 2.6? list in showcase.

Update 12 April 2018: OmniFaces 2.6.9 has been released with the main bugfixes being: 1) Removed accidentally introduced Java 1.8 specific methods causing OmniFaces 2.6 it to not run on Java 1.7 anymore. 2) Fixed several MyFaces incompatibilities with @ViewScoped unload. 3) Fixed the problem of standard JSF converters not being useable anymore when a @FacesConverter is created which extends from a standard JSF converter while having beans.xml with bean-discovery-mode set to all.

Installation

Non-Maven users: download OmniFaces 2.6.9 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.6.9</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.6.9</version>
</dependency>

For CDI-less users, the latest 1.1x version is still 1.14.1.

If you need some arguments to move to CDI:

  • Upcoming JSF 2.3 will require a CDI dependency
  • JSF own @ManagedBean and @XxxScope annotations are marked deprecated in JSF 2.3 (already as of 2.3-m06)
  • Spring 3.x already supports javax.inject API from CDI, so Spring services (which are often used to substitute the lack of EJB support in Tomcat) can seamlessly be injected in CDI managed beans.

@ViewScope(saveInViewState=true)

The @ViewScoped annotation got a new attribute: saveInViewState. When you're using client side state saving by having the javax.faces.STATE_SAVING_METHOD context parameter set to client along with a valid jsf/ClientSideSecretKey in web.xml as below...

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
</context-param>
<env-entry>
    <env-entry-name>jsf/ClientSideSecretKey</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value><!-- See http://stackoverflow.com/q/35102645/157882 --></env-entry-value>
</env-entry>

...and you explicitly want to store the whole view scoped bean instance in the JSF view state instead of in the HTTP session, then set the annotation's saveInViewState attribute to true.

import javax.inject.Named;
import org.omnifaces.cdi.ViewScoped;

@Named
@ViewScoped(saveInViewState=true)
public class OmniCDIViewScopedBean implements Serializable {}

It's very important that you understand that this setting has potentially a major impact on the size of the JSF view state, certainly when the view scoped bean instance holds "too much" data, such as a collection of entities for a data table, and that such beans will in fact never expire as they are stored entirely in the javax.faces.ViewState hidden input field in the HTML page. Moreover, the @PreDestroy annotated method on such bean will explicitly never be invoked, even not on an unload as it's quite possible to save or cache the page source and re-execute it at a (much) later moment.

It's therefore strongly recommended to use this setting only on a view scoped bean instance which is exclusively used to keep track of the dynamically controlled form state, such as disabled, readonly and rendered attributes which are controlled by ajax events.

New converters

A bunch of new converters have been added. It's after all a bit embarrassing why they were not added to OmniFaces much sooner as they are all quite "general purpose" and have been repeated in all our projects for years. Anyway, better late than never ;)

  • omnifaces.TrimConverter: the getAsObject() trims any whitespace from submitted string values. The getAsString() does effectively nothing.
  • omnifaces.ToLowerCaseConverter: the getAsObject() lowercases any character in submitted string values based on current locale. The getAsString() does effectively nothing.
  • omnifaces.ToUpperCaseConverter: the getAsObject() uppercases any character in submitted string values based on current locale. The getAsString() does effectively nothing.
  • omnifaces.ToCollectionConverter: the getAsObject() converts any commaseparated string to a collection, along with trimming away any whitespace around the delimiters. The delimiter is configurable via an attribute. The getAsString() does effectively the reverse, but when using for output only, you could also use the existing #{of:joinXxx()} functions instead.

#{faces}

The well-known Faces utility class got a counterpart in EL with help of FacesELResolver. All methods which start with get or is, and take no parameters, and return either String or boolean, and are not related to response nor to session or flash (for which already implicit EL objects #{session} and #{flash} exist), will be available as properties of the implicit object #{faces}. Examples are:

  • #{faces.development}
  • #{faces.serverInfo}
  • #{faces.ajaxRequest}
  • #{faces.requestBaseURL}
  • #{faces.requestURLWithQueryString}

To find out all of them, check the javadoc whether the method explicitly states This is also available in EL as #{faces.xxx}.

OmniFaces 3.x

The 2.6.x will be the latest OmniFaces release for JSF 2.2. The focus will now be moved to OmniFaces 3.0 for JSF 2.3. JSF 2.3 itself is scheduled to be finalized this quarter. Major change will be the upgrade from Java 7 to —finally— Java 8 with all of its awesomeness. With this new major release, a bunch of OmniFaces artifacts which are already taken over in JSF 2.3 will be deprecated (except of <o:socket> as it still allows room for new ideas and improvements), and the ones already deprecated in OmniFaces 2.x will be removed.

To learn what's new in JSF 2.3, head to this complete overview of new JSF 2.3 features in Arjan Tijms' blog.

Maven download stats

Here are the Maven download stats after previous release:

  • October 2016: 11663
  • November 2016: 12911
  • December 2016: 13439
  • January 2017: 10889
  • February 2017: 12060
  • March 2017: 14669

Friday, April 1, 2016

OmniFaces 2.3 brings JSF and WebSockets together!

OmniFaces 2.3 has been released!

This version brings a fairly important new tag to the world: <o:socket> for WebSocket based server-side push. Not only because it integrates JSR-356 WebSocket API seamlessly into JSF API with help of the CDI API, but also because it is the base for JSF 2.3's upcoming new <f:websocket> tag. Basically, OmniFaces offers you the opportunity to benefit of it long before JSF 2.3 itself is released (not before 2017). Afterwards, any migration should be a piece of cake.

Next to the <o:socket> and a lot of utility methods and functions, there's another new tag <o:skipValidators> and a further improved @ViewScoped which now also really physically destroys the associated JSF view state during unload (and thus not only the view scoped bean itself as it did before).

Note: whilst perhaps confusing, OmniFaces version 2.3 is still targeted at JSF 2.2. All OmniFaces 2.x versions are targeted at JSF 2.2. Only the future OmniFaces 3.x will be targeted at JSF 2.3.

Installation

Non-Maven users: download OmniFaces 2.3 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.3</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.3</version>
</dependency>

For users who don't want to use CDI, there's the CDI-less 1.13 with all 2.3 enhancements and fixes (but no brand new 2.x additions nor the o:socket!).

Easy scoped and user-targeted push

Besides "easy to use", an important design decision of <o:socket> was being able to send push messages from the server side to a specific scope (application, session or view) and/or to a specific user.

Example 1

The below application scoped socket example will receive the same push message from the application in all pages having the same socket channel.

<o:socket channel="top10" onmessage="top10Listener" />
function top10Listener(top10) {
    console.log(top10); // Do your thing with HTML DOM.
}
@Startup @Singleton
@ConcurrencyManagement(BEAN)
public class SomeTop10Manager {

    private List<Some> top10;

    @PersistenceContext
    private EntityManager entityManager;

    @Inject
    private BeanManager beanManager;

    @PostConstruct
    @Schedule(hour="*", minute="*/1", second="0", persistent=false)
    public void load() {
        List<Some> top10 = entityManager.createNamedQuery("Some.top10", Some.class).getResultList();

        if (!top10.equals(this.top10)) {
            this.top10 = top10;
            beanManager.fireEvent(new SomeTop10Event(top10));
        }
    }

    public List<Some> list() {
        return top10;
    }

}
@ApplicationScoped
public class SomeTop10Observer {

    @Inject @Push
    private PushContext top10;

    public void onTop10Change(@Observes SomeTop10Event event) {
        top10.send(event.getTop10());
    }

}

Example 2

The below user-targeted (implicitly session scoped) socket example will receive the same push message in all pages having the same socket channel and user. This example assumes that the logged-in user is available via an EL variable as #{user} which has an id property representing its identifier.

<o:socket channel="chat" user="#{user.id}" onmessage="chatListener" />
function chatListener(message) {
    console.log(message); // Do your thing with HTML DOM.
}
<h:form>
    <p:inputText value="#{someChatBacking.message}" required="true" />
    <p:selectOneMenu value="#{someChatBacking.recipientUser}" required="true">
        <f:selectItems value="#{someSocketObserver.onlineUsers}" var="user" itemLabel="#{user.name}" />
    </p:selectOneMenu>
    <p:commandButton value="send" action="#{someChatBacking.send}" />
</h:form>
@Named
@RequestScoped
public class SomeChatBacking {

    @Inject @Push
    private PushContext chat;

    private String message;
    private User recipientUser;

    public void send() {
        Set<Future<Void>> sent = chat.send(message, recipientUser.getId());

        // TIPS:
        // sent.size() represents amount of open web sockets at send time.
        // future.get() will return null if message is successfully delivered.
        // future.get() will throw ExecutionException if message delivery failed.
        // You can observe @Opened/@Closed websockets with CDI. See o:socket showcase/javadoc.
    }

    // ...
}

Example 3

The below conditionally connected view scoped socket example will receive the push message as result of a user-initiated asynchronous task only in the current view.

<h:form>
    <p:commandButton widgetVar="asyncCommand" value="submit"
        action="#{someTaskBacking.start}" onclick="startAsync()" />
</h:form>
<o:socket channel="async" scope="view" onmessage="asyncListener" connected="false" />
function startAsync() {
    PF("asyncCommand").disable();
    OmniFaces.Push.open("async");
}
function asyncListener(result) {
    OmniFaces.Push.close("async");
    console.log(result); // Do your thing with HTML DOM.
    PF("asyncCommand").enable();
}
@Named
@RequestScoped
public class SomeTaskBacking {

    @Inject
    private SomeTaskService service;

    @Inject @Push
    private PushContext async;

    public void start() {
        service.someLongRunningAsyncTask(result -> async.send(result));
    }

}
@Stateless
public class SomeTaskService {

    @Asynchronous
    public void someLongRunningAsyncTask(Consumer<Object> callback) {
        // ...
        // ... (some long process)
        // ...

        callback.accept(result);
    }

}

Complex UI updates

The only disadvantage from the JSF perspective is that the target JSF view state isn't anywhere available at the moment the push message is being sent. So, no partial rendering based on JSF view could be performed. That's why it isn't possible to perform complex UI updates as easily as with JSF ajax. If you're not that fluent with e.g. jQuery, then you could combine <o:socket> with <o:commandScript> which in turn uses ajax to obtain the desired rendering based on the message.

<o:socket ... onmessage="commandScriptName" />
<o:commandScript name="commandScriptName" action="#{bean.pushed}" render="result" />

If you pass a Map<String, V> or a JavaBean as push message object, then all entries/properties will transparently be available as request parameters in the command script action method.

Session/view expired

Oh, if you ever wanted to immediately warn the user about an expired session, then you could now use onclose of a session scoped <o:socket> for that. If it returns code 1000 while you didn't manually close the socket, then it means that session has been expired on the server side. How useful! This also works on view scoped sockets (but indicates "View expired" instead and should actually be a very rare case if it isn't caused by an expired session).

<o:socket ... scope="session" onclose="sessionCloseListener" />
function sessionCloseListener(code) {
    if (code == 1000) {
        alert("Session has expired! Page will be reloaded.");
        window.location.reload(true); // Or just go to some login/home page.
    }
}

For an elaborate documentation and several live demos, checkout the <o:socket> showcase.

Skip validators

Contributed by Michele Mariotti, <o:skipValidators> allows developers to entirely skip JSF validation (required, <f:validateXxx>, custom validators, etc) when executing a specific UICommand or ClientBehaviorHolder action.

For example, when adding a new row to the data table, you'd like to not immediately validate all empty rows.

<h:form>
    <h:dataTable value="#{bean.items}" var="item">
        <h:column>
            <h:inputText value="#{item.value}" required="true" />
        </h:column>
    </h:dataTable>
    <h:commandButton value="add new row" action="#{bean.add}">
        <o:skipValidators />
    </h:commandButton>
    <h:commandButton value="save all data" action="#{bean.save}" />
    <h:messages />
</h:form>

Under the covers, it will basically during PreValidateEvent remove and remember all validators and re-attach them during the PostValidateEvent.

Only converters will still run. Conversion failures can still be suppressed with help of the existing <o:ignoreValidationFailed>.

Further improved @ViewScoped

The OmniFaces CDI @ViewScoped annotation has been further improved to also physically destroy the associated JSF view state in session during an unload event. I.e. during an unload it does not only destroy view scoped beans in order to eagerly free memory, but it will now also immediately push out unused JSF view states from the session. This is important in order to prevent "older" JSF view states from expiring too soon as they are basically held in a LRU map.

A good use case is described in OmniFaces issue 208:

  1. Set number of physical views to 3 (in Mojarra, use com.sun.faces.numberOfLogicalViews context param and in MyFaces use org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION context param).
  2. Create a page which immediately initializes an OmniFaces @ViewScoped bean during load, and has a form which submits to it. For clarity, add @PostConstruct and @PreDestroy methods which log a debug line.
  3. Open this page in a tab and keep it open all time.
  4. Open the same page in another tab and then immediately close this tab.
  5. Open the same page in another tab and then immediately close this tab.
  6. Open the same page in another tab and then immediately close this tab.
  7. Submit the form in the first tab.

Previously, this would have failed with ViewExpiredException, but since OmniFaces 2.3 not anymore. Currently, a fairly convoluted hack has to be used, as the way to explicitly destroy a physical view is implementation specific. Both MyFaces and Mojarra required a different way. Currently, it only works when partial state saving is kept turned on (as by default). Those who still use full state saving should better ask themselves if it's still fruitful to keep using full state saving in JSF 2.2. During JSF 2.0/2.1 there have been reasons to keep using full state saving, but all of them are workarounds for partial state saving bugs which have long been fixed in JSF 2.2.

While at it I noticed in MyFaces org.apache.myfaces.spi.ViewScopeProvider source code a TODO comment saying that something should be provided to cleanup the session when a view is discarded. This is exactly what we want! It would be even nicer if there was a standard API which explicitly destroys the view state. Perhaps a new removeState or destroyState method in ResponseStateManager. That would make things simpler, also for the JSF implementation itself.

Maven download stats

Here are the Maven download stats after previous release:

  • November 2015: 5906
  • December 2015: 6068
  • January 2016: 6135
  • February 2016: 6635
  • March 2016: 7265

Below is the version pie of March 2016:

Tuesday, November 24, 2015

OmniFaces 2.2 released!

OmniFaces 2.2 has been released!

This version brings a bunch of new utility methods in Faces, Messages and Components classes, a new <o:viewAction> component, a new @ContextParam annotation, a whole FileServlet, and a second life for the @ViewScoped annotation.

As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 2.2. The top three additions are the second life for the @ViewScoped annotation, the new and refactored "BalusC FileServlet", and the new <o:viewAction>.

Installation

Non-Maven users: download OmniFaces 2.2 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.2</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.2</version>
</dependency>

For users who don't want to use CDI, there's the CDI-less 1.12.1 with all 2.2 enhancements and fixes (but no brand new 2.x additions!). UPDATE: FacesViews was broken in 1.12, so a 1.12.1 has been baked with the hotfix. FacesViews continues to work fine in 2.2, so there's no 2.2.1.

Second life for @ViewScoped

In standard JSF 2.0/2.1, the @PreDestroy annotated method on a standard JSF view scoped bean was never invoked when the session expires. This was solved since OmniFaces 1.6 with its new CDI @ViewScoped annotation. However, since JSF 2.2 this problem is solved on JSF's own @ViewScoped beans, hereby basically making the OmniFaces CDI @ViewScoped annotation superflous in JSF 2.2. You could as good just stick to JSF's own @ViewScoped annotation.

Instead being potentially deprecated, the OmniFaces @ViewScoped got a second life: immediately invoke @PreDestroy when the browser unloads the page instead of waiting until the session is expired. In other words, when the user navigates away by a GET link, or closes the browser tab/window, or refreshes the page (by GET), then the OmniFaces @ViewScoped bean will immediately be destroyed. None of the both JSF 2.2 @ViewScoped annotations support this.

The trick is done by a synchronous XHR request during beforeunload event (thus not on the main thread! ;) ) via a helper script omnifaces:unload.js which is automatically included when an OmniFaces @ViewScoped bean is created. The XHR request sends a special request parameter omnifaces.event=unload along with the OmniFaces specific view scope ID and the JSF javax.faces.ViewState ID:

var xhr = new XMLHttpRequest();
xhr.open("POST", window.location.href.split(/[?#;]/)[0], false);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("omnifaces.event=unload&id=" + id + "&" + VIEW_STATE_PARAM + "=" + viewState);

The existing OmniFaces RestorableViewHandler has been extended with below lines:

if (isUnloadRequest(context)) {
    UIViewRoot createdView = createView(context, viewId);
    createdView.restoreViewScopeState(context, getRenderKit(context).getResponseStateManager().getState(context, viewId));
    BeanManager.INSTANCE.getReference(ViewScopeManager.class).preDestroyView();
    responseComplete();
    return createdView;
}

In a nutshell: when the omnifaces.event=unload parameter is present, then only create the view and restore any view scoped beans (and thus don't build the view! which might be time consuming and unnecessarily create new beans) and finally destroy those beans, hereby immediately invoking the @PreDestroy and freeing unused memory space on demand. After that, JSF is instructed to immediately render the response and return the dummy (non-built!) view, preventing potential unnecessary ViewExpiredException.

There's however a small caveat: on slow network and/or poor server hardware, there may be a noticeable lag between the enduser action of unloading the page and the desired result, even though the server side action may take place in only a few milliseconds (in case you're doing expensive tasks inside @PreDestroy method, better delegate it to an @Asynchronous EJB method or so). If the slow network/hardware lag is in your case noticeable and thus undesireable, then better stick to JSF 2.2's own @ViewScoped annotation and accept the postponed destroy.

New FileServlet with advanced HTTP range and caching support

The well known "BalusC FileServlet" from the blog article FileServlet supporting resume and caching has been cleaned, refactored and reworked into an abstract template. Now, instead of copypasting and editing the code from the blog, you can simply extend from org.omnifaces.servlet.FileServlet and implement the getFile() method in its simplest form as below:

@WebServlet("/media/*")
public class MediaFileServlet extends FileServlet {

    @Override
    protected File getFile(HttpServletRequest request) throws IllegalArgumentException {
        return new File("/var/webapp/media", request.getPathInfo());
    }

}

When referenced in e.g. a HTML5 <video> tag like below:

<video src="#{request.contextPath}/media/video.mp4" controls="controls" />

Then the FileServlet will worry about properly streaming the media range requests. I.e. the servlet is able to return specific parts of the media file on a request with a HTTP Range header. For example, only the bytes at exactly the index 1000 until 2000 on a file of 10MB long. This is mandatory for many media players in order to be able to skip a certain range of the media stream quickly enough and/or to improve buffering speed by creating multiple connections which each requests different parts of the file. Also download accelerators will take benefit of this on large files. They will then simply open multiple HTTP connections which each downloads a specific range of the file and afterwards put the pieces together.

Fix unintuitive "if" attribute of <f:viewAction>

The if attribute of the standard <f:viewAction> not really intuitive. It is checked during APPLY_REQUEST_VALUES phase instead of INVOKE_APPLICATION phase. This would make several straightforward use cases to fail.

In below example, the FooConverter may convert a non-null parameter to null without causing a validation or conversion error, and the intent is to redirect the current page to otherpage.xhtml when the converted result is null.

<f:viewParam name="foo" value="#{bean.foo}" converter="fooConverter" />
<f:viewAction action="otherpage" if="#{bean.foo eq null}" />

However, this fails because the if attribute runs before the conversion has taken place, when the component wants to check whether it should queue the action event. This happens during APPLY_REQUEST_VALUES phase. The OmniFaces <o:viewAction> solves this by postponing the evaluation of the if attribute to the INVOKE_APPLICATION phase.

<f:viewParam name="foo" value="#{bean.foo}" converter="fooConverter" />
<o:viewAction action="otherpage" if="#{bean.foo eq null}" />

The implementation of the extended component is really simple. It's nothing more than below:

@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
    if (super.isRendered()) {
        super.broadcast(event);
    }
}

@Override
public boolean isRendered() {
    return !isImmediate() || super.isRendered();
}

It gives a bit of thinking as the if attribute is basically a renamed rendered attribute, and one would wonder why !isImmediate() is checked instead of e.g. the current phase ID or a hardcoded true. But this way the original and intented behavior of immediate attribute is maintained. Actually, the <o:viewAction immediate="true"> behaves exactly the same as a <f:viewAction immediate="true">.

Maven download stats

Here are the Maven download stats after previous release:

  • June 2015: 6949
  • July 2015: 6574
  • August 2015: 5906
  • September 2015: 6561
  • October 2015: 5904

Below is the version pie of October 2015:

Surprising how many 1.8.1 users are still there. To those who can't upgrade to 2.2 or 1.12, I'd like to point out that 1.8.3 contains an important memory leak fix in case you're using UnmappedResourceHandler while having composite components on the pages. So, if you can't upgrade to 2.2 or 1.12, then please at least upgrade to 1.8.3. And those few old RC1/RC2 and M1 users, upgrade to the final version without any RC/M suffix!

Monday, March 5, 2012

Stateless view parameter

The JSF 2.0 <f:viewParam> tag is a very useful tag which allows you to set GET request parameters as model values of a managed bean. You can even convert and validate them before it get set in the model. See also Communication in JSF 2.0 - Processing GET request parameters.

However, in combination with view scoped beans, it has one disadvantage. When you perform a postback on the same view, then the initial GET request parameter get converted and validated again based on the submitted value which was saved in the view state (the initial GET request parameter is namely not available anymore in the postback request). When the conversion is performed through a service/DAO method based on some entity ID as request parameter, then that service/DAO method would be invoked again during the postback, which is completely unnecessary in case of a view scoped bean.

The solution is simple: just make the <f:viewParam> tag stateless as to the submitted value! If it becomes null, then it won't be set in the model anyway. On jdevelopment.nl you can find a more in depth explanation about this. Arjan Tijms has extended the UIViewParameter component to achieve this functionality into a new OmniFaces <o:viewParam> tag (source code here).

The usage is simple, just replace f: tag prefix by o: tag prefix. So, instead of


    <f:metadata>
        <f:viewParam name="id" value="#{users.user}"
            converter="#{userConverter}" converterMessage="Bad request. Unknown user."
            required="true" requiredMessage="Bad request. Please use a link from within the system."
        />
    </f:metadata>

use


    <f:metadata>
        <o:viewParam name="id" value="#{users.user}"
            converter="#{userConverter}" converterMessage="Bad request. Unknown user."
            required="true" requiredMessage="Bad request. Please use a link from within the system."
        />
    </f:metadata>

As a bonus, the label attribute will also default to the name attribute instead of the client ID whenever the label attribute is not specified. The name attribute is a more sane default for this.

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