jsf-2composite-componentwebsphere-8omnifacesicefaces-3

How to dynamically add a component using "Faces.includeCompositeComponent"?. Websphere 8 and ICEFaces 3


I´m trying to dynamically add a composite component to another UIComponent using a small adaptation of OmniFaces Faces.includeCompositeComponent(...). I´ve tried to recreate the behavior of this question but it´s not working.

The code for the adapted method is the following (I took it from OmniFaces v1.6)

/**
   * Create and include the composite component of the given library ane
   * resource name as child of the given UI component parent and return the
   * created composite component. This has the same effect as using
   * <code>&lt;my:resourceName&gt;</code>. The given component ID must be unique
   * relative to the current naming container parent and is mandatory for
   * functioning of input components inside the composite, if any.
   * 
   * @param parent The parent component to include the composite component in.
   * @param libraryName The library name of the composite component.
   * @param resourceName The resource name of the composite component.
   * @param id The component ID of the composite component.
   * @return The created composite component, which can if necessary be further
   *         used to set custom attributes or value expressions on it.
   * @since 1.5
   */
  public static UIComponent includeCompositeComponent(UIComponent parent, String libraryName, String resourceName, String id)
  {
    // Prepare.
    FacesContext context = FacesContext.getCurrentInstance();
    Application application = context.getApplication();
    FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);

    // This basically creates <ui:component> based on <composite:interface>.
    Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);
    UIComponent composite = application.createComponent(context, resource);
    composite.setId(id); // Mandatory for the case composite is part of UIForm!
                         // Otherwise JSF can't find inputs.

    // This basically creates <composite:implementation>.
    UIComponent implementation = application.createComponent(UIPanel.COMPONENT_TYPE);
    implementation.setRendererType("javax.faces.Group");
    composite.getFacets().put(UIComponent.COMPOSITE_FACET_NAME, implementation);

    // Now include the composite component file in the given parent.
    parent.getChildren().add(composite);
    parent.pushComponentToEL(context, composite); // This makes #{cc} available.
    try
    {
      faceletContext.includeFacelet(implementation, resource.getURL());
    }
    catch (IOException e)
    {
      throw new FacesException(e);
    }
    finally
    {
      parent.popComponentFromEL(context);
    }

    return composite;
 }

And this is the way I´m trying to use it

JSFUtils.includeCompositeComponent(panelGroup, "comp", 
   "../inc-templates/test.xhtml", JSFUtils.createUniqueId());

Also, I created the "test.xhtml" file and placed it under WEB-INF/inc-templates/test.xhtml . Here is its content:

test.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:cc="http://java.sun.com/jsf/composite"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui"
      xmlns:composite="http://java.sun.com/jsf/composite">
    <!-- INTERFACE -->
    <cc:interface>

    </cc:interface>

    <!-- IMPLEMENTATION -->
    <cc:implementation>
        <h:outputText value="TEST"/>
    </cc:implementation>
</html>

Last but not least, I´ve added the test.xhtml tag to the taglib.xml, like this:

<tag>
  <tag-name>test</tag-name>
  <source>../inc-templates/test.xhtml</source>
</tag>

The thing is, when I run the code and try to insert the component I get the following exception

Caused by: java.lang.NullPointerException: Argumentfehler: Parameter componentResource ist null
        at com.sun.faces.util.Util.notNull(Util.java:314)
        at com.sun.faces.application.ApplicationImpl.createComponent(ApplicationImpl.java:928)
        at org.my.JSFUtils.includeCompositeComponent(JSFUtils.java:496)

and the line at org.my.JSFUtils.includeCompositeComponent(JSFUtils.java:496) refers exactly to the line of code UIComponent composite = application.createComponent(context, resource);, which in fact reveals that the resource is not being created in the previous line: Resource resource = application.getResourceHandler().createResource(resourceName, libraryName);

Does anyone have any idea why could this be happening?. I´ve even tried changing the location of the resource to inc-templates/test.xhtml and even just test.xhtml when invoking the method, but nothing works... I always get the same error.

By the way, I´m trying to deploy in Websphere 8 and I´m using ICEFaces 3.

Thanks in advance!


Solution

  • It seems that you're mixing composite components with tag files. To see and understand the difference between both head to this answer and the links inside it: When to use <ui:include>, tag files, composite components and/or custom components?

    Your XHTML file clearly represents a composite component. However, you're treating it like a tag file. It's been placed in /WEB-INF folder like a tag file instead of /resources folder like a true composite. It's been registered in a .taglib.xml file like a tag file instead of configurationless like a true composite.

    It's not entirely clear what exactly you want. Do you want to programmatically include an include file, or a tag file, or a composite component? Let's assume that you actually want to programmatically include a composite component (otherwise the whole usage of includeCompositeComponent() is completely pointless). In that case, move the XHTML file to /resources folder like as shown in all composite component tutorials (in such way that it also really works when you use <my:test> in any arbitrary XHTML template client):

    WebContent
     |-- WEB-INF
     |    `-- lib
     |-- resources
     |    `-- mycomponents
     |         `-- test.xhtml   <---
     `-- some.xhtml
    

    Get rid of the unnecessary entry in .taglib.xml and finally include it using library name of null and resource name of /mycomponents/test.xhtml.

    includeCompositeComponent(parent, null, "/mycomponents/test.xhtml", uniqueId);