blog.smart-java.nl
Ordina J-Technologies – Java Blog



JSF 2.0: Maximum flexibility with System Events

By: Jan-Kees van Andel, 24 January 2010

JSF 2.0 adds a lot of interesting features. Andy Schwartz provides an extensive overview on his blog.

On this blog, I’d like to elaborate a bit further on the interesting features JSF 2.0 has to offer.

JSF 2.0: System Events
JSF 2.0 comes with a useful feature, called System events. System events are predefined events that are published at predefined points in the lifecycle.
System events are either global or component system events.

Global system events
An example of a global system event is PostConstructApplicationEvent, which is published when all configuration is done. It can be seen as a JSF replacement for ServletContextListener.

Listener registration is done by invoking Application.subscribeToEvent(Class<? extends SystemEvent> type, SystemEventListener listener).

Component system events
Component system events only apply to the component to which the listener is attached.

An example of a component system event is PreRenderComponentEvent. This event is published to the appropriate component when it’s about to be rendered.

Listener registration is done by invoking UIComponent.subscribeToEvent(Class<? extends SystemEvent> type, ComponentSystemEventListener listener).

<f:event /> tag
The JSF 2.0 spec also defines the <f:event /> tag, which needs to be nested inside a component tag in the web page. This tag is used to make listener registration easier. Just nest the tag inside a component tag in your Facelet, specify the event to listen to and specify a MethodExpression to your listener method and everything works.

An example
Below is an example of an e-banking login page. It uses a challenge-response authentication mechanism, so it generates a “random” challenge when the page is loaded. Because users can navigate to the login page directly (GET-request), we can’t rely on an action method to be invoked first, so let’s fix this using system events.

First, a bit of history
I’ve always hated to write “the first” page in JSF. Since JSF short circuits the lifecycle in the case of a GET request, you don’t really have an appropriate hook for page initialization, making it difficult to create a dynamic landing page.

One could write “lazy getters”:

public class MyBackingBean {
    private List accounts;

    public List getAccounts() { // Yuck
        if (accounts == null) {
            // Do some expensive database query
            accounts = doSomeWork();
        }
        return accounts;
    }
}

It’s quite obvious this is extremely nasty. You don’t know when the getter is invoked (EL triggers the invocation), and that if-null check is also ugly. Btw. getters with logic are ugly anyway…

JSF 1.2 offered a slightly more elegant option with @PostConstruct:

public class MyBackingBean {
    private List accounts;

    @PostConstruct
    public void init() {
        accounts = doSomeWork();
    }

    public List getAccounts() { // Still not good
        return accounts;
    }
}

This is a bit better, but still not very good. The exact point of invocation within the lifecycle is still not obvious. But at least we now know the init() method won’t be invoked multiple times.

There are other ways to tackle this issue, for example, using a PhaseListener, or using a framework like Seam, but that has issues of its own.

Back to the event example
Below is a snippet of my login page.

<h:form>
  <dl>
    <dt><h:outputLabel value="Customer ID" for="customerId" /></dt>
    <dd><h:inputText id="customerId" value="#{loginBean.customerId}" required="true" /></dd>
    <dt><h:outputLabel value="Challenge" for="challenge" /></dt>
    <dd><h:outputText id="challenge" value="#{loginBean.challenge}">
      <f:event name="preRenderComponent" listener="#{loginBean.generateChallenge}" />
    </h:outputText></dd>
    <dt><h:outputLabel value="Response (always 123456)" for="response" /></dt>
    <dd><h:inputSecret id="response" value="#{loginBean.response}" required="true" /></dd>
    <dt> </dt>
    <dd><h:commandButton value="Login" action="#{loginBean.loginUsingTokenGenerator}" /></dd>
  </dl>
</h:form>

As you can see, the above snippet contains the challenge response login form. Line 7 is the interesting one. It registers a PreRenderComponentEvent listener method on the LoginBean.

The LoginBean is shown next (snippet…).

@Named
@SessionScoped
public class LoginBean implements Serializable {
    private Integer customerId;
    private Integer challenge;
    private Integer response;
    private @Inject LoginService loginService;
    private Customer customer;
    // Getters and setters

    public void generateChallenge(ComponentSystemEvent event) throws AbortProcessingException {
        this.challenge = loginService.generateChallenge();
    }

    public String loginUsingTokenGenerator() {
        customer = loginService.loginUsingTokenGenerator(customerId, challenge, response);
        if (customer == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(
                        "Username and/or password is incorrect or your account has been disabled"));
            return null;
        } else {
            return "/pages/homepage.xhtml?faces-redirect=true";
        }
    }
}

Don’t mind the annotations, they come from CDI (JSR-330 a.k.a. Contexts and Dependency Injection). The only thing to remember from these is that this class is a Session scoped managed bean which gets a LoginService injected. Note that I would also prefer ViewScoped, instead of SessionScoped.

Also, don’t mind the return values in loginUsingTokenGenerator. This is also a new feature in JSF 2.0, called Implicit Navigation.

Note the generateChallenge() method. The throws clause is not mandatory. The argument type must be ComponentSystemEvent though.

Wrapping up
As you can see, System events and the <f:event /> tag provide a convenient way to hook in custom logic on a per-component basis.

And, because we’re back to normal OO programming, instead of getter hacking, unit testing the LoginBean is easy as pie!

Pretty neat huh?! ;-)

Eén reactie op “JSF 2.0: Maximum flexibility with System Events”

  1. Tweets die vermelden blog.smart-java.nl » Blog archief » JSF 2.0: Maximum flexibility with System Events -- Topsy.com zegt:

    [...] Dit blogartikel was vermeld op Twitter door Richard Laksana, Jan-Kees van Andel. Jan-Kees van Andel heeft gezegd: Published article about JSF 2.0 System Events and the <f:event /> tag: http://bit.ly/4tcntO [...]

Laat een reactie achter