Pages

Saturday, May 05, 2012

JSF 2 Tip of the Day: Programmatic PhaseListener

I had a use case where I needed to use a PhaseListener programmatically. I had originally tried to use a faces-config.xml to add the listener. I encountered a problem with an ear file which contained a war that contained a space. When Weblogic expanded the ear file it could not find the faces-config.xml since the path from expanded ear file had a space. Unfortunately, the program had too many dependencies to change to remove the space. So I decided to inject it. I was quite surprised to find that there was no annotation for PhaseListener in JSF 2.1.

Undaunted, I decided to figure it out. Admittedly, after I figured it out, I searched on Google, but everyone who had posted a solution did not a good example , or it was not quite as elegant as mine. I believe.

Specifically, my use case was to provide programmatic login where I use a PhaseListener to determine if the user is authenticated before restoring the view. This particular use case means that I must have the PhaseListener injected prior to responding to any requests. Fortunately, the JSF 2 API makes it very easy to do this.

The basic design is as follows:
  1. Use a @ManagedBean(eager=true) annotation. The eager attribute forces the container to load the bean immediately rather than lazy loading.
  2. Use the @ApplicationScoped annotation. This will allow us to set the PhaseListener for the whole application.
  3. Create a method which has a @PostConstruct annotation. This will allow us to set the listener immediately following the constructor.
    Note: You will want to follow this design so that if there are any annotations, they will be processed prior to adding the PhaseListener.
  4. Add the listener in the @PostConstruct method.

The JSF 2 API does a great job of explaining, how to use the LifecycleFactory to get a reference to the Lifecycle object. This is the key to adding the PhaseListener. If you are an API developer, this is a great example of documentation for people who want to use your API.

Code

The code for the project was developed using NetBeans 7.1.2 using Apache Maven. The code can be found here: programmatic-phaselistener.zip

ApplicationBean.java


5 comments:

  1. John,

    the drawback on this approach is to have an application scoped bean living forever thats only purpose is to do stuff upon application startup. JSF 2 offers SystemEvents for this task. So the logic performed in initialize should be moved to the processEvent(...) method of a SystemEventListener.

    ReplyDelete
  2. You are correct that the ApplicationScoped bean does exist for the lifetime of the application. That is the idea.

    SystemEventListener is another approach. Thanks for bringing this up. You can take action based on application initialization, or destruction. However to register the SystemEventListener you must call Application.subscribeToEvent(Class<? extends SystemEvent> systemEventClazz, SystemEventListener listener) which requires you to use a bean which should be ApplicationScoped.

    The defined SystemEvents are PostConstructApplicationEvent which is the same as my example, or PostConstructCustomScopeEvent.

    I do like options, and I should create an example of this. Thanks for your comments. Good stuff.

    ReplyDelete
  3. John,

    How about using Servlet Filter? When the webapp starts up, the servlet container will create an instance of the filter and keep it in memory during webapp's lifetime. The same instance will be reused for every incoming request whose URL matches the filter's URL pattern. The doFilter() method will then be called on every request. Servlet Filter is part of Servlet 2.5 API, so it should be portable to any web/app server out there.

    ReplyDelete
  4. Thang... It seems like you live servlet filters. That is not what this solution is about.

    The idea here is that you can programmatically inject a PhaseListener which is part of JSF. The PhaseListener allows you to control aspects of the JSF lifecycle in a way that a simple ServletFilter can not.

    A filter is not intimate with the JSF lifecycle, and does not allow you to make changes based on results intermediate results in the lifecycle at the extension points in JSF where phase listeners are located.

    ReplyDelete
  5. You are absolutely right about Servlet Filter is not very intimate with JSF life cycle. When I read the line "determine if the user is authenticated before restoring the view", right away I think of Filter since that how I would implement this, but you are right, if the application concern about the JSF life cycle, then PhaseListener would be a great way to go :) Great stuffs, learn something new today.

    ReplyDelete