Search code examples
jsf-2composite-component

Composite component folder structure


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.


Solution

  • 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.