users@javaserverfaces-spec-public.java.net

[jsr344-experts mirror] [jsr344-experts] Loading Facelets via ResourceHandler (Part 1)

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Fri, 1 Feb 2013 16:12:33 -0500

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