Practical example of the JSF 2.0 event system

When I blogged about the new event system coming with JSF 2.0, there was a comment asking for a concrete example of how to use it. I mentioned the GuessNumber 2.0, included in the EDR1 bundle, demonstrated one aspect of its use, but I've got another, simpler, example to share.

As part of (but not related to) the work that has been on going to make JSF component authoring easier, an implementation specific PhaseListener was created that would, when the ProjectStage was Development (see the entry concerning ProjectStage if you're not aware of this feature), add a UIMessages component to the view if one hadn't already been added. This is useful for those developers just starting with JSF, testing their application, and finding out that an undisplayed message is preventing them from navigating to a new view.

The problem with the PhaseListener approach is that outside of the Development phase, having this listener installed would have a small impact on the runtime. So we needed to come up with a way to only install this listener if the current ProjectStage was Development.

We decided to do this using a custom SystemEventListener listening for a new, standard, event called ConfigurationCompleteEvent1. This event will be fired after the configuration process has processed all of the configuration files.

While the following examples are specific the Mojarra, the concept can be applied to any logic that a developer wants executed after the JSF application has been initialized, especially since ServletContextListeners, which have traditionally ben used for this kind of task with JSF, aren't guaranteed to be invoked in order unless they are explicitly listed in the desired order in the web.xml (order can be problematic for ServletContextListeners defined in a TLD).

So first, I created a new SystemEventListener:

public class ProjectStagePhaseListenerInstallationListener
      implements SystemEventListener {


     // ---------------------------------------- Methods from SystemEventListener


     public void processEvent(SystemEvent event)
     throws AbortProcessingException {

         PhaseListener pl = new ProjectStagePhaseListener();
         LifecycleFactory factory = (LifecycleFactory)
         FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
         for (Iterator i = factory.getLifecycleIds(); i.hasNext();) {
             Lifecycle l = factory.getLifecycle((String) i.next());
             l.addPhaseListener(pl);
         }

         // uninstall the listener - no need to be included during usual
         // runtime processing.
         Application application = (Application) event.getSource();
         application.unsubscribeFromEvent(ConfigurationCompleteEvent.class,
                                                                    this);


     }


     public boolean isListenerForSource(Object source) {

         if (source instanceof Application) {
             Application application = (Application) source;
             return (application.getProjectStage() == ProjectStage.Development);
         }
         return false;

     }

The logic is pretty straight forward. The isListenerForSource method will be invoked when the configuration process calls Application.publishEvent(ConfigurationCompleteEvent.class, applicationInstance) and will return true if the current ProjectStage is development. By returning true, we ensure that processEvent() will be called.

The body of the processEvent() method is staight forward. It obtains all of the Lifecycle instances associated with the application and installs the ProjectStagePhaseListener to each of them (exactly what would happen if the PhaseListener is referenced in the faces-config.xml file). At the end of processEvent() the listener uninstalls itself from the application. There's no need to keep the listener during the standard request processing lifecycle as it's a waste of cycles.

Next, the phase-listener reference in the mojarra configuration file was removed and instead a system-event-listener
element was added:

<application>
   .
   .
   .
<system-event-listener>
<system-event-listener-class>
       com.sun.faces.config.listeners.ProjectStagePhaseListenerInstallationListener

     </system-event-listener-class>
     <system-event-class>
       javax.faces.event.ConfigurationCompleteEvent
     </system-event-class>
   </system-event-listener>
</application>


And that's it! We now have a PhaseListener that's conditionally installed depending on the current ProjectStage.

Given all of this, it would be nice to be able to optionally install JSF artifacts depending on the ProjectStage without having to write code. Perhaps an optional attribute on an element where one can define the stage that the artifact is to be used with. I'll be forwarding a couple of ideas to the spec leads about this.


[1] The name for this event hasn't been finalized by the EG


Comments:

Post a Comment:
Comments are closed for this entry.
About

user12615560

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today