Monday Aug 11, 2008
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
Friday Jul 25, 2008
This seventh entry in the JSF 2.0 New Feature Preview Series wraps up the content included in EDR1 (yes, I know I'm a bit behind). The last entry covered resource re-location. This entry will include information on the miscellaneous bits that were added as a side effect of adding the larger feature sets.
View Scope
This new scope allows attributes to be associated with a particular view. Attributes within the view scope persist until the user navigates to a new view at which they will be cleared out.
The view scope, like request or session, can be accessed via the EL using the identifier viewScope (e.g. #{viewScope.attrName}). The view scope can also be accessed from code by calling UIViewRoot.getViewMap().
To have parity with the Servlet specification, there are two events associated with the lifecycle of the view scope: ViewMapCreatedEvent and ViewMapDestroyedEvent. ViewMapCreatedEvent is fairly straight forward. ViewMapDestroyedEvent is a bit different in that it will be fired when the view map is cleared (i.e. Map.clear() is invoked).
I'm sure most of us have been bit by the case where one is trying to use a request scoped attribute for the rendered or readonly attribute only to find it doesn't really affect the rendering of the component (due to the lifecycle of the request attribute). You're then forced to use session scope or perform some other actions within code to get around it. Using the view scope, you're able to achieve the same result as a session scoped value, but without it remaining in the session and as said previously, the values are cleared as soon as the user navigates to a new view.
Component Implicit EL Object
The component implicit EL object allows developers to access the component associated with the current tag via the EL. So #{component.attributes.bar} would access the component's attributes map and return the value associated with the key, bar. Developers can also access the component that is currently being processed (i.e. during decodes, validation, or rendering) from code by calling UIComponent.getCurrentComponent().
FacesContext Attributes Map
Because we are always trying to improve performance (even in the smallest ways), this addition is close to my heart. To support resource injection for managed beans that are request scoped, we use (I believe MyFaces does as well) a ServletRequestListener and a ServletRequestAttributeListener so we can be notified when a ServletRequest goes out of scope and when attribute values are changed or removed. When these events are triggered we can take the appropriate action on the request scoped managed bean. However, each time one of these events is triggered, a new Event object is created by the container.
Mojarra, and I'm sure most component libraries, use the request scope for private per-request state, however doing so triggers the aforementioned events. This might not be so bad, but consider when a component library or an application adds request listeners of their own. Two or more events are being created per action.
Because of the potential for Event creation to be a burden on the server, we added the FacesContext attributes map (FacesContext.getAttributes()). This API is really there for JSF implementors or component set authors to use so that they can avoid the request scope and firing events for private state. If utilized this leaves the request scope for the developer's application and keeps the private runtime/components state out of the mix.
Thursday Jun 26, 2008
This is the sixth entry in the JSF 2.0 New Feature Preview Series. The last entry covered the new event system. For this entry, we'll cover resource re-location. The driving force behind this feature is to simplify development of
pages. A page author shouldn't need to know what resources a
particular component needs. The Tomahawk component set does this by having the user install a Filter that post-processes the response produced by JSF. This solution doesn't scale all that well as the entire response needs to be buffered, parsed, manipulated and then rendered out. Building off the new resource and event systems, we can avoid doing this and allow resources to be placed where they should be (e.g. stylesheet and script references within the head element).
For now, let's start with the page level mechanics and then drill down into how this works. The 2.0 EDR1 specification calls for four new tags.
- h:head - this represents the head element of an HTML page
- h:body - this represents the body element of an HTML page
- h:outputScript - this represents an external javascript file reference
- h:outputStylesheet - this represents an external stylesheet reference
So armed with these new tags, let's create a simple facelet:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head id="head">
<title>resourcereslocation</title>
</h:head>
<h:body id="body">
<h:form id="form">
<h:outputScript name="simple.js" target="#{param.location}"/>
<h:outputText value="Hello"/>
<h:outputStylesheet name="simple.css" target="#{param.location}"/>
</h:form>
</h:body>
</html>
The
head and
body tags are fairly straight forward as to their attributes. Their main use is for knowing where to output relocated resources. Now, notice the
outputScript and
outputStylesheet tags are within the form element. The
name attribute is for the name of the resource to be found by the ResourceHandler (NOTE: there is a
library attribute available as well). The
target attribute specifies where the rendered content should appear. In the sample above, we're using an expression so that we can pass a request parameter to change the behavior easily.
So given all of this, if there is no location request parameter or target isn't defined, so the style sheet reference will be rendered in the head and the script reference will be rendered inline:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
hello
</form>
</body>
</html>
Issue the same request, this time with a request parameter
location=head:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
hello
</form>
</body>
</html>
Then issue the request again this time with the request parameter value equal to
body:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
hello
</form>
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
</body>
</html>
Notice that the style sheet render ignores the
target attribute and always renderes the link within the head - as it should!
Recall how I said that page authors shouldn't have to know what resources a component needs? Well, the above examples only get you so far. It would be tedious if using a third party component set would require you to use different h:outputScript or h:outputStylesheet references depending on which component was being used. So the 2.0 specification has added the
@ResourceDependency annotation to allow component authors to declare the resource(s) the component will need. A simple example might look like:
@ResourceDependency(name="style.css",library="corp")
public class MastHead extends UIComponentBase {
.
.
.
}
a more complex component may look like:
@ResourceDependencies({
@ResourceDependency(name="style.css",library="corp"),
@ResourceDependency(name="menu.js",library="corp",target="head")
})
public class Menu extends UIComponentBase {
.
.
.
}
When the page author uses these components, they'll not need to know anything about any of the stylesheets or scripts. The necessary dependencies will be rendered as needed without further action.
So how does this work under the covers? Glad you asked. Let's base the follow of the component example with the single
@ResourceDependency annotation.
- The Facelets runtime will create the component
- The component will be added as a child to some other component. Before returning from the add() method, the component will be checked for @ResourceDependency annotations (either the singluar or plural version).
- The @ResourceDependency is found. A new UIOutput component instance is created.
- The ResourceHandler is queried for an appropriate Renderer based on the content type of the resource, which in this case is text/css, so the style sheet renderer will be set as the Rendere for this new UIOutput.
- The values of the name, library, and target attributes (library and target are optional) from the annotation are stored in the component's attribute map.
- UIViewRoot.addComponentResource() is called passing in the UIOutput and the value of target attribute from the annotation (if any)
All of this has occurred while building the view. Now when we render the view, the head renderer (it will be the head renderer in this case as the example we're using is a style sheet) we simply encode each of the resources that have been targeted for the head like so:
UIViewRoot viewRoot = context.getViewRoot();
for (UIComponent resource : viewRoot.getComponentResources(context, "head")) {
resource.encodeAll(context);
}
Now I mentioned that the event system comes into play with the implementation of this feature. So I would be remiss if I didn't touch on that, but it requires going back to the example where the
h:outputScript or
h:outputStylesheet are referenced within the view. In this case, there is no component with annotations so when the component is added to the view, the sequence of events described above will not occur and thus
UIViewRoot.addComponentResource() will not have been called. For this case, the style sheet and script renderers have a little extra magic to help obtain the same end result.
The renderers leverage another new annotation,
@ListenerFor. This annotation allows a component to subscribe to particular events with the component itself, in this case the Renderer, as the listener. Since these two renderers will be listeners for these events, they also implement the
ComponentSystemEventListener interface that was mentioned in the
previous entry. So the Renderer with these new bells and whistles looks a little like:
@ListenerFor(systemEventClass=AfterAddToParentEvent.class, sourceClass=UIOutput.class)
public class ScriptRenderer extends Renderer implements ComponentSystemEventListener {
.
.
.
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
UIComponent component = event.getComponent();
FacesContext context = FacesContext.getCurrentInstance();
String target = (String) component.getAttributes().get("target");
if (target != null) {
context.getViewRoot().addComponentResource(context, component, target);
}
}
.
.
.
}
When the Facelets runtime creates the component associated with the h:outputScript, it will obtain the Renderer for this component and interrogate it for any
@ListenerFor annotations (there is a plural form of course,
@ListenersFor). For each annotation found, the Renderer will be added as a component listener for, in the case above, the AfterAddToParent event. So when the Facelets runtime adds the component to the tree, the AfterAddToParent event is invoked and this Renderer's
processEvent() method is invoked which will add the component as a resource to the ViewRoot with the appropriate target.
Well, I think that about covers it. I had initially thought this blog was going to be quick and easy, but it kinda snowballed it seems. I hope this all made sense. If not, please leave comments with questions and I'll do my best to clarify.
Monday Jun 23, 2008
This is the fifth entry in the JSF 2.0 New Feature Preview Series. The last entry wrapped up the new application resource handling. We'll now switch focus to the new publish/subscribe event system.
Several component and application level APIs have been added to enable a robust event system for component and application developers to take advantage of. Please note that this blog is based on EDR1 and as such the signatures of the APIs will change. When this is the case, I'll try to remember to update the blog accordingly.
Let's start with the changes to javax.faces.component.UIComponent.
- public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
ComponentSystemEventListener componentListener)
- public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass,
ComponentSystemEventListener listener)
- public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> systemEventClass)
The signatures are fairly straight forward. The API allows a developer to, on this single component instance, associate/disassocaite a ComponentSystemEventListener with some event type extending SystemEvent. The method, getListenersForEventClass() is leveraged by the Application implementation when an event is published.
The other class central to the event system is javax.faces.application.Application. The following methods have been added:
- public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
Class sourceClass,
SystemEventListener listener)
- public void publishEvent(Class<? extends SystemEvent> systemEventClass,
SystemEventListenerHolder source)
- public void unsubscribeFromEvent(Class<? extends SystemEvent> systemEventClass,
Class sourceClass,
SystemEventListener listener)
The subscripbe/unsubscribe methods are similar to those define on
UIComponent, however, these are application-wide instead of per-component. It should be noted that an optional
sourceClass can be provided so that the listener will only be invoked when the source of the event is of the configured type. I should note, that there is a two argument version of the subscribe and publish methods on Application that do not have
sourceClass in their signature. These merely call through to the three argument version passing null as the
sourceClass.
Now,
publishEvent is where all the work happens. As currently spec'd,
publishEvent will look for any listeners defined within the provided
SystemEventListenerHolder (UIComponent implements this interface, hence the description of the methods on UIComponent) interested in the provided SystemEvent type. Following that,
publishEvent will invoke any listeners interested in both the provided SystemEvent and source type. Lastly,
publishEvent will invoke any listeners interested in received the specified SystemEvent type regardless of the source.
The spec includes several new events:
- AfterAddToParentEvent. This event will be published under the following circumstances:
- When UIComponent.getChildren().add(childToAdd) is called (*)
- When UIComponent.getFacets().put(facetName, facetChildToAdd) is called (*)
- After the the view has been built
- BeforeRenderEvent. This event will be published when UIComponent.encodeBegin() is called.
- ViewMapCreatedEvent. This event will be published when UIViewRoot.getViewMap() or UIViewRoot.getViewMap(true) has been called (this is the basis for the new view scope).
- ViewMapDestroyedEvent. This event will be published by the default NavigationHandler when the view is being transitioned to a new view (i.e. the view IDs are not equal) and the view map has been used by the application
More events may be added by the time the JSF 2.0 specification is completed.
As you can see, the new event system is quite flexible and extensible enough to be adapted for many uses. For example, the jsf-guessNumber2.0 demo application (to be included with the Mojarra 2.0.0 EDR1 release some time this week) leverages the BeforeRenderEvent to perform a hybrid of client/server-side validation. Having this system in place also allows for another new feature which I will be discussing in the next entry, resource relocation. I won't give too many details here, but resource relocation is the facility that allows stylesheets or scripts to be rendered in one of several locations independent of where actual tag was placed within the view definition.
(*) - This event will not be published when restoring the view from a post-back.