Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the JSF resource library for and how should it be used?

The JSF <h:outputStylesheet>, <h:outputScript> and <h:graphicImage> components have a library attribute. What is this and how should this be used? There are a lot of examples on the web which use it as follows with the common content/file type css, js and img (or image) as library name depending on the tag used:

<h:outputStylesheet library="css" name="style.css" />
<h:outputScript library="js" name="script.js" />
<h:graphicImage library="img" name="logo.png" />

How is it useful? The library value in those examples seems to be just repeating whatever is already been represented by the tag name. For a <h:outputStylesheet> it's based on the tag name already obvious that it represents a "CSS library". What's the difference with the following which also just works the same way?

<h:outputStylesheet name="css/style.css" />
<h:outputScript name="js/script.js" />
<h:graphicImage name="img/logo.png" />

Also, the generated HTML output is a bit different. Given a context path of /contextname and FacesServlet mapping on an URL pattern of *.xhtml, the former generates the following HTML with the library name as request parameter:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/style.css.xhtml?ln=css" />
<script type="text/javascript" src="/contextname/javax.faces.resource/script.js.xhtml?ln=js"></script>
<img src="/contextname/javax.faces.resource/logo.png.xhtml?ln=img" alt="" />

While the latter generates the following HTML with the library name just in the path of the URI:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml" alt="" />

The latter approach makes in hindsight also more sense than the former approach. How exactly is the library attribute then useful?

like image 564
BalusC Avatar asked Oct 06 '22 20:10

BalusC


1 Answers

Actually, all of those examples on the web wherein the common content/file type like "js", "css", "img", etc is been used as library name are misleading.

Real world examples

To start, let's look at how existing JSF implementations like Mojarra and MyFaces and JSF component libraries like PrimeFaces and OmniFaces use it. No one of them use resource libraries this way. They use it (under the covers, by @ResourceDependency or UIViewRoot#addComponentResource()) the following way:

<h:outputScript library="javax.faces" name="jsf.js" />
<h:outputScript library="primefaces" name="jquery/jquery.js" />
<h:outputScript library="omnifaces" name="omnifaces.js" />
<h:outputScript library="omnifaces" name="fixviewstate.js" />
<h:outputScript library="omnifaces.combined" name="[dynamicname].js" />
<h:outputStylesheet library="primefaces" name="primefaces.css" />
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
<h:outputStylesheet library="primefaces-vader" name="theme.css" />

It should become clear that it basically represents the common library/module/theme name where all of those resources commonly belong to.

Easier identifying

This way it's so much easier to specify and distinguish where those resources belong to and/or are coming from. Imagine that you happen to have a primefaces.css resource in your own webapp wherein you're overriding/finetuning some default CSS of PrimeFaces; if PrimeFaces didn't use a library name for its own primefaces.css, then the PrimeFaces own one wouldn't be loaded, but instead the webapp-supplied one, which would break the look'n'feel.

Also, when you're using a custom ResourceHandler, you can also apply more finer grained control over resources coming from a specific library when library is used the right way. If all component libraries would have used "js" for all their JS files, how would the ResourceHandler ever distinguish if it's coming from a specific component library? Examples are OmniFaces CombinedResourceHandler and GraphicResourceHandler; check the createResource() method wherein the library is checked before delegating to next resource handler in chain. This way they know when to create CombinedResource or GraphicResource for the purpose.

Noted should be that RichFaces did it wrong. It didn't use any library at all and homebrewed another resource handling layer over it and it's therefore impossible to programmatically identify RichFaces resources. That's exactly the reason why OmniFaces CombinedResourceHander had to introduce a reflection-based hack in order to get it to work anyway with RichFaces resources.

Your own webapp

Your own webapp does not necessarily need a resource library. You'd best just omit it.

<h:outputStylesheet name="css/style.css" />
<h:outputScript name="js/script.js" />
<h:graphicImage name="img/logo.png" />

Or, if you really need to have one, you can just give it a more sensible common name, like "default" or some company name.

<h:outputStylesheet library="default" name="css/style.css" />
<h:outputScript library="default" name="js/script.js" />
<h:graphicImage library="default" name="img/logo.png" />

Or, when the resources are specific to some master Facelets template, you could also give it the name of the template, so that it's easier to relate each other. In other words, it's more for self-documentary purposes. E.g. in a /WEB-INF/templates/layout.xhtml template file:

<h:outputStylesheet library="layout" name="css/style.css" />
<h:outputScript library="layout" name="js/script.js" />

And a /WEB-INF/templates/admin.xhtml template file:

<h:outputStylesheet library="admin" name="css/style.css" />
<h:outputScript library="admin" name="js/script.js" />

For a real world example, check the OmniFaces showcase source code.

Or, when you'd like to share the same resources over multiple webapps and have created a "common" project for that based on the same example as in this answer which is in turn embedded as JAR in webapp's /WEB-INF/lib, then also reference it as library (name is free to your choice; component libraries like OmniFaces and PrimeFaces also work that way):

<h:outputStylesheet library="common" name="css/style.css" />
<h:outputScript library="common" name="js/script.js" />
<h:graphicImage library="common" name="img/logo.png" />

Library versioning

Another main advantage is that you can apply resource library versioning the right way on resources provided by your own webapp (this doesn't work for resources embedded in a JAR). You can create a direct child subfolder in the library folder with a name in the \d+(_\d+)* pattern to denote the resource library version.

WebContent
 |-- resources
 |    `-- default
 |         `-- 1_0
 |              |-- css
 |              |    `-- style.css
 |              |-- img
 |              |    `-- logo.png
 |              `-- js
 |                   `-- script.js
 :

When using this markup:

<h:outputStylesheet library="default" name="css/style.css" />
<h:outputScript library="default" name="js/script.js" />
<h:graphicImage library="default" name="img/logo.png" />

This will generate the following HTML with the library version as v parameter:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml?ln=default&amp;v=1_0" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&amp;v=1_0"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&amp;v=1_0" alt="" />

So, if you have edited/updated some resource, then all you need to do is to copy or rename the version folder into a new value. If you have multiple version folders, then the JSF ResourceHandler will automatically serve the resource from the highest version number, according to numerical ordering rules.

So, when copying/renaming resources/default/1_0/* folder into resources/default/1_1/* like follows:

WebContent
 |-- resources
 |    `-- default
 |         |-- 1_0
 |         |    :
 |         |
 |         `-- 1_1
 |              |-- css
 |              |    `-- style.css
 |              |-- img
 |              |    `-- logo.png
 |              `-- js
 |                   `-- script.js
 :

Then the last markup example would generate the following HTML:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml?ln=default&amp;v=1_1" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&amp;v=1_1"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&amp;v=1_1" alt="" />

This will force the webbrowser to request the resource straight from the server instead of showing the one with the same name from the cache, when the URL with the changed parameter is been requested for the first time. This way the endusers aren't required to do a hard refresh (Ctrl+F5 and so on) when they need to retrieve the updated CSS/JS resource.

Please note that library versioning is not possible for resources enclosed in a JAR file. You'd need a custom ResourceHandler. See also How to use JSF versioning for resources in jar.

See also:

  • JSF resource versioning
  • JSF2 Static resource caching
  • Structure for multiple JSF projects with shared code
  • JSF 2.0 specification - Chapter 2.6 Resource Handling
like image 78
BalusC Avatar answered Oct 20 '22 16:10

BalusC