4

JSF spec 2.2 (2013-03-20) says in item 10.3.3.1 (Declaring a composite component library for use in a Facelet page):

If a facelet taglibrary is declared in an XHTML page with a namespace starting with the string “http://java.sun.com/jsf/composite/” (without the quotes), the remainder of the namespace declaration is taken as the name of a resource library [...]

If the substring following “http://java.sun.com/jsf/composite/” contains a “/” character, or any characters not legal for a library name the following action must be taken. If application.getProjectStage() is Development an informative error message must be placed in the page and also logged. Otherwise the message must be logged only.

So that means it's illegal to have the following folder structure:

resources
    components
        system
        something_specific
        something_even_more_specific

and refer to the library name “http://java.sun.com/jsf/composite/components/something_specific”? Is this correct?

That seems like a weird restriction. I want my sources structured, not mushed together in an enormous lump.

Such hierarchical library actually works in Wildfly 8.0.0.CR1, but I'm not sure if it's wise to rely on this behavior.

A "best practices" kind of answers are welcome.

Vsevolod Golovanov
  • 4,068
  • 3
  • 31
  • 65
  • Stumbled upon the following in the `javax.faces.application.ResourceHandler.createResource(String)` javadoc: `For historical reasons, this method operate correctly when the argument resourceName is of the form libraryName/resourceName, even when resourceName contains '/' characters.` – Vsevolod Golovanov Apr 01 '14 at 14:56
  • https://stackoverflow.com/q/39537911/1341535 – Vsevolod Golovanov May 26 '17 at 11:13

2 Answers2

3

I'll summarize my findings.

Sources: JSF spec issue 740, discussion preceding issue 740, another discussion, JSF spec issue 1141, discussion preceding issue 1141.

Slash in a library name is disallowed. Slash in a resource name is allowed.

In practice on Mojarra 2.2.5 composite component library just works both in a XHTML namespace declaration, and in taglib's <composite-library-name>components/system</composite-library-name>. I expect that could still break in future Mojarra and/or JSF spec versions. If you use this, you're at JSF spec/impl developers' mercy. Linked issues and discussions have shown them to be willing to preserve backwards compatibility even for unintended features.

In MyFaces there is a special setting, org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME (MyFaces issue 3454). I expect that relying on a resource library with slashes in its name, using this setting, could break some functionality such as JSF resource versioning (how can it know what part is a library name and what part belongs to resource name?).

I think composite component library hierarchy may be implemented by importing components in a taglib one by one:

<tag>
    <tag-name>test</tag-name>
    <component>
      <resource-id>
          components/system/test.xhtml
      </resource-id>
    </component>
</tag>

Thus the library name effectively becomes "components" and the resource name becomes "system/test.xhtml".

Update: you need a hack to make this work with Mojarra.

/**
 * Allows nesting composite components deeper. E.g.:
 * <resource-id>system/components/package/anotherpackage/test.xhtml</resource-id>
 *
 * To structure your sources better. To document your components and their attributes in
 * taglib.xml for IDE content assist.
 *
 * So it purposefully violates the resource lookup algorithm described in JSF 2.2,
 * 2.6.1.3 Resource Identifiers.
 */
public class NestedCCResourceHandler extends ResourceHandlerWrapper {

    public NestedCCResourceHandler(final ResourceHandler resourceHandler) {
        super(resourceHandler);
    }

    @Override
    public Resource createResourceFromId(String resourceId) {
        // just treat the whole thing as resourceName, no libraries
        Resource resource = super.createResource(resourceId);
        // com.sun.faces.component.CompositeComponentStackManager#findCompositeComponentUsingLocation
        // expects to find libraryName in the resource path, so set it to empty string
        resource.setLibraryName("");
        return resource;
    }

}

And register it in faces-config.xml:

<application>
    <resource-handler>fully.qualified.name.NestedCCResourceHandler</resource-handler>
</application>

This could break libraries for CCs, maybe version contracts? Not sure, made this a long time ago. Definitely works with Mojarra 2.3.3.SP1.

Vsevolod Golovanov
  • 4,068
  • 3
  • 31
  • 65
  • `I think composite component library hierarchy may be implemented by importing components in a taglib one by one` - did I test this? Doesn't work with Mojarra 2.2.12 - it takes `test.xhtlm` to be the resource name and `system` to be the library name, throwing away `components` completely. See `com.sun.faces.facelets.tag.AbstractTagLibrary.CompositeComponentTagFactory.createHandler(TagConfig)`. This could be fixed with a custom TagLibrary or a ResourceHandler. – Vsevolod Golovanov Apr 27 '17 at 11:38
0

after some experiment, i think that these statements should be read:

  • the remainder of the namespace declaration is taken as the name of a resource library http://java.sun.com/jsf/composite/components/system must match /resources/components/system

  • any characters not legal for a library name

    if you declare http://java.sun.com/jsf/composite/components/system2 and /resources/components/system2 does not exists, take action

    Warning: This page calls for XML namespace http://java.sun.com/jsf/composite/components/system2 declared with prefix y but no taglibrary exists for that namespace.

so it is absolutely legal to declare

xmlns:x="http://java.sun.com/jsf/composite/components/system"

and any other folder structure that exists under /resources

Community
  • 1
  • 1
Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73
  • Hm, I don't get the gist of your answer. Firstly, you suggest decomposing folder structure? That's exactly what I don't want to do. In the "resources" folder there are static resources too, like images, styles, scripts. So I want folders at the first level under "resources" to be "components" and "static". And there certainly will be more than 2 levels of folders under "resources". For example, "components/system/dialogs". I don't want to decompose this structure and create all folders on one level, like "components-system-dialogs". – Vsevolod Golovanov Feb 07 '14 at 09:28
  • Secondly, I don't understand what importing in a taglib changes. The composite component library name with slashes is still illegal, regardless of whether it is used in a XHTML page or in a taglib definition. – Vsevolod Golovanov Feb 07 '14 at 09:29
  • Thank you for sticking to it. So you're saying that the slash restriction applies only when using composite components namespace directly in XHTML via `http://java.sun.com/jsf/composite/`? And a minor question remains: why should I not use `/resources/components` folder? The `components` part is not some special reserved name, it's equivalent to your `my-cc`. – Vsevolod Golovanov Feb 07 '14 at 10:35
  • The 1st paragraph of your answer is confusing and not understandable if one doesn't have read the 1st version of the answer. Try to write answers that they clearly stand on their own, without taking other/previous answers or even comments into account. – BalusC Feb 09 '14 at 13:49
  • @this in my previous answer i thought that the quote means you have to follow the same pattern of declaring composite components inside a jar. reading it with more attention, it's not what it means. – Michele Mariotti Feb 09 '14 at 14:09
  • Now you've basically restated the premise of my question. Yes, in practice that's how things work. But the spec's statement `"If the substring following “http://java.sun.com/jsf/composite/” contains a “/” character..."` leaves little to no room for interpretation. It doesn't say "if the substring contains a “/” and no corresponding folder structure exists...". – Vsevolod Golovanov Feb 10 '14 at 11:01