Hi
Let's start from the beginning. JSF 2.2 spec now allows load facelet views or
templates from these two locations: META-INF/flows/<flow-name> and
META-INF/contracts/<contract-name> . The problem is both locations have a
different semantic and resources in each location needs to be handled in a
different way. Things get problematic when you start to do mixtures. For
example:
A) META-INF/flows/<flow-name>
It is supposed that facelet files in that location are dealt just like if
/<flow-name> were in webapp root folder. So this structure:
webapp/
WEB-INF/
classes/
myfile.jar!META-INF/
flows/
flow-a/
step1.xhtml
step2.xhtml
y.xhtml
is "similar" to this structure:
webapp/
flow-a/
step1.xhtml
step2.xhtml
y.xhtml
This is good, but it raises some questions:
- Is it possible to put additional resources inside a flow? For example:
META-INF/flows/flow-a/
step1.xhtml
step2.xhtml
custom.css
mylogo.png
template.xhtml
Only step1.xhtml and step2.xhtml are real JSF views, but template.xhtml is not
a view. The other resources are not a problem, but a template should be hidden
because is not a view. Maybe it shouldn't be there from start. Now try this:
META-INF/resources/mylib/
custom.css
mylogo.png
template.xhtml
META-INF/flows/flow-a/
step1.xhtml
step2.xhtml
That's better. The images/css are handled by ResourceHandler and the template
is hidden.
The resources can be loaded just doing:
<h:outputStylesheet name="mylib/custom.css"/>
<h:graphicImage name="/mylib/mylogo.png"/>
or its equivalent
<h:outputStylesheet library="mylib" name="custom.css"/>
<h:graphicImage library="mylib" name="/mylogo.png"/>
The controversy here resides in how to load the template. In JSF 2.0 it is
not possible to load templates from META-INF/resources. One option is
update facelets ui tags to allow load templates using libraryName/resourceName
logic. Other option is scan for templates directly in META-INF/resources
as if the resources were located on webapp path, but that option has its
implications, and maybe there is a chance to use the new
createResourceFromId(), we'll see it later.
Anyway, maybe it is not a big deal to allow facelet templates inside a flow.
One way or another, this point must be clarified.
B) META-INF/contracts/<contract-name>
The idea is if the view has an "active" contract, the resources inside a
contract just overlap the webapp folder. For example this structure:
webapp/
WEB-INF/
classes/
myfile.jar!META-INF/
contracts/
contract-a/
template1.xhtml
template2.xhtml
custom.css
mylogo.png
y.xhtml
is "similar" to this structure (read it as "is like if the files were in"):
webapp/
resources/
custom.css
mylogo.png
template1.xhtml
template2.xhtml
y.xhtml
This is good, but it raises one questions:
- It is possible to define views into a resource library contract? Absolutely
no. It is clear the intention of a resource library contract is provide a
well defined set of resources that can be reused across different views,
but to provide a reusable set of views for your application, the right way
to do it is create a flow.
Allow load templates from /resources or META-INF/resources as if they were on
webapp root folder sounds attractive, because it is a way to hide the templates
and prevent them to be loaded as if they were views. Take a look at this
example:
webapp/
resources/
es/mylib/
template1.xhtml
mylib/
template1.xhtml
template2.xhtml
custom.css
mylogo.png
y.xhtml
The idea is doing something like this in y.xhtml:
<ui:decorate template="/mylib/template1.xhtml">
If the locale is "es" load the template from /es/mylib and by default the one
in /mylib. It could be very useful. Now let's go further and take a look at
this example:
webapp/
WEB-INF/
classes/
myfile.jar!META-INF/
contracts/
contract-a/
es/mylib/
template1.xhtml
template2.xhtml
custom.css
mylogo.png
resources/
mylib/
template1.xhtml
y.xhtml
The idea is override mylib/template1.xhtml with a localized version inside a
resource library contract. With this you can override everything, but the side
effect is that resources inside a META-INF/contracts/<contract-name/ must be
in the same way is if they were located in META-INF/resources . The other
alternative is instead call:
createResourceFromId("/mylib/template1.xhtml")
use this (null is for library name):
createResource("/mylib/template1.xhtml", null);
and avoid override libraries in a contract at all, but I can't find a reason
why do that.
In conclusion and to make it short:
- Load a facelet that is a view is different than load a facelet that is
part of a view.
- It is a good idea to load templates from META-INF/resources or from
META-INF/contracts/<contract-name>, but a facelet view should never be there,
only in META-INF/flows/<flow-name>
How to do it? the proposals is:
- Load facelets views from these locations:
* META-INF/flows/<flow-name>/...
* /... in webapp folder, excluding /resources folder.
- Load facelets templates from these locations:
* META-INF/contracts/<contract-name>/...
* META-INF/resources/... and /resources/...
* /... in webapp folder
So we need add these two methods:
- ResourceHandler.createViewResourceFromId(
FacesContext context, String resourceId) : load files from these
locations:
* META-INF/flows/<flow-name>/...
- ResourceHandler.createViewTemplateResourceFromId(
FacesContext context, String resourceId) : load template files from
these locations:
* META-INF/contracts/<contract-name>/...
* META-INF/resources/... and /resources/...
By default it calls createResourceFromId(String resourceId), maybe
a create a variant
createResourceFromId(FacesContext context, String resourceId)
is a good idea.
and the idea is modify:
- ResourceHandler.createResourceFromId(
FacesContext context, String resourceId) : load a resource file from
these locations:
* META-INF/contracts/<contract-name>/...
* META-INF/resources/... and /resources/...
What's the difference between createViewTemplateResourceFromId() and
createResourceFromId() ? the difference is createViewTemplateResourceFromId()
has the ability of filter resources that are not considered templates or
cannot be consumed by the VDL. Maybe it is a good idea to create a method
on the VDL called boolean isViewTemplateResource(String resourceId) and do
the necessary checks for these web config params:
* javax.faces.DEFAULT_SUFFIX
* javax.faces.FACELETS_SUFFIX
* javax.faces.FACELETS_VIEW_MAPPINGS
And other related params according to the VDL.
In theory webapp folder (/...) is special, so the best is exclude it from
ResourceHandler methods.
This proposal enforces the role that a resource library contract is made to
override from images, css to templates or composite components. It also
reuse the concept behind JSF 2.0 Resource Handling, and allow override
resources according to its Locale, giving user a wide flexibility about
how to group resources and mix them in a webapp.
In the second part, the idea is explore how to solve the problem related
to load facelet files in a relative way and define the precedence order
in each case.
regards,
Leonardo Uribe