3
votes

My problem in brief is that I have a composite component with a componentType specified. When I use the composite component alone on a page, it works fine. When I nest it inside another composite component (also inside a ui:repeat), I get an exception trying to find the properties I've defined on componentType class inside a UINamingContainer--which is the wrong class. I have found I can solve that problem by replacing cc.whatever with component.parent.parent.parent.whatever but I find that a bit of a gross hack. I assume I'm doing something stupid that's confusing JSF, but I'm not sure what it is or what I should do differently.

Now, in excruciating detail, I have the following composite component:

<?xml version='1.0' encoding='UTF-8' ?>
<ui:component xmlns:ui="http://java.sun.com/jsf/facelets"
              xmlns:cc="http://java.sun.com/jsf/composite"
              xmlns:ice="http://www.icesoft.com/icefaces/component"
              xmlns:f="http://java.sun.com/jsf/core">
  <cc:interface componentType="edu.nrao.sss.tools.resrccat.ui.support.LinearVelocityUIComponent">
    <cc:attribute name             = "value"
                  class            = "edu.nrao.sss.measure.LinearVelocity"
                  required         = "true"
                  shortDescription = "The LinearVelocity value to show or edit."/>

    <cc:attribute name             = "separate-units"
                  type             = "boolean"
                  default          = "true"
                  shortDescription = "If true, provide a drop-box to edit the units separately from the value."/>
  </cc:interface>
  <cc:implementation>
    <ice:inputText id="velocity" value="#{cc.attrs.value}" rendered="#{not cc.attrs['separate-units']}"/>

    <ui:fragment rendered="#{cc.attrs['separate-units']}">
      <ice:inputText id="velocityDecimal" value="#{cc.velocityDecimal}"/>
      <ice:selectOneMenu id="velocityUnits" value="#{cc.velocityUnits}">
        <f:selectItems value="#{siFactory.linearVelocityUnits}"/>
      </ice:selectOneMenu>
    </ui:fragment>
  </cc:implementation>
</ui:component>

The backing component is this:

@FacesComponent("edu.nrao.sss.tools.resrccat.ui.support.LinearVelocityUIComponent")
public class LinearVelocityUIComponent extends UIInput implements NamingContainer {

  @Override public String getFamily() {
    return UINamingContainer.COMPONENT_FAMILY;
  }

  public LinearVelocity getVelocity() {
    return (LinearVelocity) getValueExpression("value").getValue(FacesContext.getCurrentInstance().getELContext());
  }

  public void setVelocity(LinearVelocity newVelocity) {
    getValueExpression("value").setValue(FacesContext.getCurrentInstance().getELContext(), newVelocity);
  }

  public BigDecimal getVelocityDecimal() {
    return getVelocity().getValue();
  }

  public LinearVelocityUnits getVelocityUnits() {
    return getVelocity().getUnits();
  }

  public void setVelocityDecimal(BigDecimal newVelocity) {
    setVelocity(new LinearVelocity(newVelocity, getVelocityUnits()));
  }

  public void setVelocityUnits(LinearVelocityUnits units) {
    setVelocity(new LinearVelocity(getVelocityDecimal(), units));
  }
}

When I use this component alone on a page, everything is fine:

<p>Your linear velocity is <rct:linear-velocity value="#{test.velocity}"/></p>

When I nest it inside another composite component like this:

<?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">
<ui:component xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ice="http://www.icesoft.com/icefaces/component"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:cc="http://java.sun.com/jsf/composite"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:rct="http://java.sun.com/jsf/composite/components/rct">
  <cc:interface>
    <cc:attribute name="specs"        required="true"/>
    <cc:attribute name="type"         default="line"/>
  </cc:interface>
  <cc:implementation>
    <table class="iceDatTbl">
      <thead>
        <tr>
          ...
          <th class="iceDatTblColHdr2">Minimum<br/>Span</th> 
          ...
        </tr>
      </thead>
      <tbody>
        <ui:repeat var="spec" value="#{cc.attrs.specs}" varStatus="stat">
          <tr class="#{stat.even ? 'iceDatTblRow1' : 'iceDatTblRow2'}">
            ...
            <td class="iceDatTblCol2">
              Req: <rct:linear-velocity value="#{spec.minimumSpan}"/><br/>
              Sug: #{spec.suggestedMinimumSpan}
            </td>
            ...
          </tr>
        </ui:repeat>
      </tbody>
    </table>
  </cc:implementation>
</ui:component>

I get this exception:

Nov 19, 2012 4:10:37 PM org.icefaces.impl.application.ExtendedExceptionHandler handle
WARNING: queued exception
javax.el.PropertyNotFoundException: /resources/components/rct/linear-velocity.xhtml @20,113 value="#{cc.velocityDecimal}": Property 'velocityDecimal' not found on type javax.faces.component.UINamingContainer
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
    at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
    at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
    at javax.faces.component.UIOutput.getValue(UIOutput.java:169)
    at com.sun.faces.facelets.component.UIRepeat$SavedState.populate(UIRepeat.java:823)
    at com.sun.faces.facelets.component.UIRepeat.saveChildState(UIRepeat.java:369)
    at com.sun.faces.facelets.component.UIRepeat.saveChildState(UIRepeat.java:375)
    at com.sun.faces.facelets.component.UIRepeat.saveChildState(UIRepeat.java:375)
    at com.sun.faces.facelets.component.UIRepeat.saveChildState(UIRepeat.java:355)
    at com.sun.faces.facelets.component.UIRepeat.setIndex(UIRepeat.java:440)
    at com.sun.faces.facelets.component.UIRepeat.visitTree(UIRepeat.java:613)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UINamingContainer.visitTree(UINamingContainer.java:163)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at org.icefaces.impl.component.UISeriesBase.visitRows(UISeriesBase.java:1174)
    at org.icefaces.impl.component.UISeriesBase.visitTree(UISeriesBase.java:1065)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UIForm.visitTree(UIForm.java:344)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1601)
    at org.icefaces.impl.event.RestoreResourceDependencies.processEvent(RestoreResourceDependencies.java:24)
    at javax.faces.event.SystemEvent.processListener(SystemEvent.java:106)
    at com.sun.faces.application.ApplicationImpl.processListeners(ApplicationImpl.java:2168)
    at com.sun.faces.application.ApplicationImpl.invokeListenersFor(ApplicationImpl.java:2144)
    at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:302)
    at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:246)
    at javax.faces.component.UIComponentBase.encodeBegin(UIComponentBase.java:812)
    at javax.faces.component.UIViewRoot.encodeBegin(UIViewRoot.java:962)
    at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1755)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:402)
    at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
    at com.ocpsoft.pretty.faces.application.PrettyViewHandler.renderView(PrettyViewHandler.java:163)
    at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:145)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:487)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:412)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339)
    at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:137)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at edu.nrao.sss.webapp.LookupUserFilter.doFilter(LookupUserFilter.java:57)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.jasig.cas.client.util.HttpServletRequestWrapperFilter.doFilter(HttpServletRequestWrapperFilter.java:50)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.jasig.cas.client.authentication.AuthenticationFilter.doFilter(AuthenticationFilter.java:111)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)

The replacement I can do that enables it to work is this:

<ui:fragment rendered="#{cc.attrs['separate-units']}">
  <ice:inputText id="velocityDecimal" value="#{component.parent.parent.parent.velocityDecimal}"/>
  <ice:selectOneMenu id="velocityUnits" value="#{component.parent.parent.parent.velocityUnits}">
    <f:selectItems value="#{siFactory.linearVelocityUnits}"/>
  </ice:selectOneMenu>
</ui:fragment>

That feels really wrong and historically whenever I try to make JSF work through a hack like this a few months later it ruins my life. I would greatly appreciate any help! I love JSF and I'm really enjoying using composite components to structure things, but I really need to be able to nest composite components.

Edit: I've discovered that in Glassfish, the component doesn't render at all in isolation. It produces the same error (UINamingContainer doesn't have this property LinearVelocityUIComponent has) nested in the other component.

2

2 Answers

2
votes

I was able to reproduce your problem with Mojarra 2.1.14 on Tomcat 7.0.30 (and using standard JSF <h:xxx> equivalents of those ICEfaces components). This is blamed to broken state saving mechanism of nested <ui:repeat> in Mojarra. I'm honestly not sure which one of the Mojarra bug reports on "nested ui:repeat" covers your particular problem, there are too many reports and your particular one is new to me.

Exactly the same code works fine for me in MyFaces 2.1.9. It also works fine when I replace at least the outer <ui:repeat> by <h:dataTable> or <p:dataList>.

So you have 2 options to solve this problem:

  1. Replace (at least the outer) <ui:repeat> by a fullworthy UIData implementation, such as <h:dataTable> or <p:dataList> or any ICEfaces equivalent which I can't tell from top of head. This might not generate the HTML you want, but it works and with CSS you can do a lot.

  2. Replace Mojarra by MyFaces. Don't forget to thoroughly (unit)test your entire webapp. MyFaces has its own set of quirks and bugs as well.

1
votes

If it helps anyone, I solved a similar problem where I had nested a composite component (cc) within another composite component. One instance was ok, two instances in a page was problematic, trying to find class fields that belonged to the nested cc but was looking for them in the host cc. The problem was that I used the same name variables for both ccs. So, the EL expression "cc.attrs.bean" was confusing for JSF to handle... I solved it by keeping different parameter names among CCs. Took me a while to find this out.