Ryan has posted 7 posts at DZone. View Full User Profile

JSF 2.0 New Feature Preview Series (Part 4) Resource Re-location

10.15.2008
| 10519 views |
  • submit to reddit

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.

  1. The Facelets runtime will create the component
  2. 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).
  3. The @ResourceDependency is found. A new UIOutput component instance is created.
  4. 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.
  5. 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.
  6. 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.

References
Published at DZone with permission of its author, Ryan Lubke. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Otengi Miloskov replied on Wed, 2008/10/15 - 11:20am

Alright the spec is going great, I liked that the EG added the annotation behavior of @ResourceDependency for resources, nice job.

Ashok Jayadar replied on Mon, 2008/10/20 - 6:29am

I am basically oracle dba and java developer

i am also working in asp.nt2.0 and php 5

working with php and oracle database is very good but .et is good control having less coding

jav  prefered for hardware programming

plz tell me if i am going through oracle and java-> jsp,jsf,servlet development

but i am new for jsf plz. help me.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.