users@javaserverfaces-spec-public.java.net

[jsr344-experts mirror] [jsr344-experts] Re: Thoughts about FaceletCache, ResourceResolver, ResourceHandler and Multitemplating (FaceletCache interface does not handle "relativeLocations" logic from DefaultFaceletFactory)

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Fri, 29 Jun 2012 17:39:04 +0200

Hi

I hope somebody is reading specially this mail. This is important feedback.

Thinking more about, I think the trick with this is just allow
facelets to get templates directly from ResourceHandler. But the
current implementation adding only createViewResource(String
resourceName) is incomplete.

Some time ago, in MyFaces (thanks to Michael Kurz) we found this issue:

https://issues.apache.org/jira/browse/MYFACES-3308

Allow localized composite components

Here is the comment:

"... I tried to make a localized composite components for dynamic
localization of content on my pages (that goes beyond resource
bundles). The basic idea is to be able to create composite components
with static text and/or components (links, images...) for different
languages. As a composite component basically is a resource I thought
something like this should be possible:

/resources/fragments/fragment01.xhtml
/resources/de/fragments/fragment01.xhtml

IMO the spec is a bit unclear on this but I would say it should work.
I tried it - it did not work. The problem is, that
CompositeComponentResourceTagHandler gets a resource in the
constructor that will be used till the death of the webapp. No chance
to switch locales. ...."

I added this feature in MyFaces, because it comply with the spec. The
idea is you have always to provide a default implementation of the
composite component and then you can just create a localized copy.
Then inside the code, each time the component is created when
buildView() is called (inside CompositeComponentResourceTagHandler), I
do something like this:

            Resource resourceForCurrentView =
faceletContext.getFacesContext().getApplication().

getResourceHandler().createResource(_resource.getResourceName(),
_resource.getLibraryName());

So, I have already one instance from compile time, and on build view
time the idea is retrieve it the "localized" resource instance, right?
It does not matter if in compile time I get a localized one, because
I'm using it only to check if the composite component exists.

There is only one theorical pitfall with this strategy: if you change
the locale inside a POST, and you have a localized composite
component, there is a chance that the view could not be restored
correctly, because you change a part of the component tree. So, if you
do a change in the locale, just do a PRG or a GET call, and things
will work as expected.

... déjà vu .... doesn't that sound too familiar?. I think it also
goes into the same direction as the current javadoc in
ResourceHandler, right?

Try to imagine this. As with UIViewRoot.getLocale(), you have a
property called UIViewRoot.getMultiTemplateLibrary(), that can be
assigned to a ValueExpression in some way, for example:

<f:view multiTemplateLibrary="#{someBean.someOption}">

or maybe in WEB-INF/faces-config.xml , in the same way as is possible
to define a skin in trinidad-config.xml

<mult-template-library-name>some.library.location</mult-template-library-name>

Or maybe in some way setup multiple locations, or something like that,
but I hope you get the idea.

Then, the idea could be add some methods in ResourceHandler to load
facelets templates, just like with composite components. One idea
could be add the code in DefaultFaceletFactory.resolveURL(), but it
could be better just to use ResourceHandler API directly from the
related TagHandler, get the right Resource instance and then the URL.

The idea of multiTemplateLibrary is "... deal with this library as a
valid location to load views, templates or whatever facelet resource
as it was located inside root webapp folder ...". In this way, you
could have multiple jar files with a multi template library in each
one, or just one jar file with many multi templates. Note this should
be done carefully, to prevent load a resource in a multi-template
library, but maybe we could just create another directory specially
for that. Instead store in META-INF/resource, let's use something like
META-INF/multi-templates? Maybe it is a good idea to set a special
folder in the webapp to deal with multi templates (for example,
instead /resources use /multi-templates)

The nice part about do it in this way is you can change skins on the
fly (with GET or PRG, but not in a POST, unless we can force discard
and rebuild the view if the multi template (or the locale) has
changed). If you are using maven jetty plugin or something like that
you can also edit them and see the results just with a refresh on the
browser, without recompile. There is no need to do any big changes
into facelets compiler, just fix some spots to use the new API in
ResourceHandler and that's it.

I hope this helps.

regards,

Leonardo Uribe

2012/6/27 Leonardo Uribe <lu4242_at_gmail.com>:
> Hi
>
> Today this issue has been created in MyFaces issue tracker:
>
> https://issues.apache.org/jira/browse/MYFACES-3575
>
> Inside the discussion, I realized a flaw it has FaceletCache API.
>
> The original code from facelets 1.1.x has a logic to cache the
> "conversion" between logical identifiers to find a specific facelet
> and its related URL.
>
> So inside DefaultFaceletFactory there is a map like this:
>
>    private Map<String, URL> _relativeLocations;
>
> Checking the code in deep, I notice this cache store also the values
> of ResourceResolver.
>
> Now suppose an scenario where you has a custom FaceletCache
> implementation, and by some coincidence there is some mechanism to
> load / unload some facelets by whatever reason. Once a facelet is
> called, relativeLocations will hold the same key/value pair, so if a
> facelet is unloaded and then another one is loaded and it has the same
> association, it will fail.
>
> A realistic scenario that will be part of JSF 2.2 spec is if you have
> a jar with some composite components and facelet files. For two
> different skins, there will be facelets with the same "logical
> identifiers", but with different URL. This issue prevents change the
> skins on the fly, because you can't clean that map.
>
> Who should hold that map? It should be hold by FaceletCache, not by
> FaceletFactory, because FaceletCache is the responsible to load/unload
> and hold facelets into memory.
>
> But things get more confusing when you think about the changes done
> into javax.faces.application.ResourceHandler. In the latest javadoc
> draft there is one method:
>
> Resource createViewResource(String resourceName)
>
> Again, the "resourceName" is in practice the same "logical identifier"
> that is missing in the spec. For an specific view is the physical
> viewId, but if the view is composed by other facelets bundled in the
> same jar, we need that concept too in the spec, and a way to deal with
> that logic too.
>
> I hope you can see the relationship between FaceletCache and
> ResourceHandler. The intention is use ResourceHandler to load facelet
> definitions on the fly, but there is a logic involved in compile them
> store in memory, and finally dispose them that maybe is being ignored,
> right?
>
> I know this is a topic still under discussion, but I just wanted to
> warn you guys about the problem.
>
> regards,
>
> Leonardo Uribe