Resolving componentType of compositeComponent on post request

Till Gräfenberg <[email protected]> Mon, 3 Feb 2025 09:15:37 +0100
Newsgroups gmane.comp.java.openejb.user
Message-ID <c2f3e704-3e7d-4ba7-9cac-f314e0ca652b__40066.8589327083$1738570582$gmane$org@business-logics.de>
Hello together,

I mentioned that TomEE Plume 10 struggles to resolve the component type
of a composite component, when restoring a view with multiple different
composite components.

I created the following play example:

On the page index.xhtml I am using two different composite components
<cc:output1> and <cc:output2> with componentType
"com.example.components.CompositeOutput1" and
"com.example.components.CompositeOutput2" respectively.

There also is a <h:commandButton> whose action method navigates back to
the current page.

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="jakarta.faces.html" xmlns:cc="jakarta.faces.composite/taglib"> <h:body><h:form
id="form"><cc:output1 id="output1"
value="#{valueFace.value}"/><cc:output2 id="output2"
value="#{valueFace.value}"/><h:commandButton value="submit"
action="#{valueFace.increment}"/></h:form></h:body></html>

The composite components consist of a .xhtml file:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="jakarta.faces.html" xmlns:composite="jakarta.faces.composite"> <composite:interface
componentType="com.example.components.CompositeOutput1"><composite:attribute
name="value"
required="true"/></composite:interface><composite:implementation><div id="#{cc.clientId}"> <h:outputText binding="#{cc.output1}"/></div>
</composite:implementation></html>

and a java class:

@FacesComponent("com.example.components.CompositeOutput1")
public class CompositeOutput1extends UIInputimplements NamingContainer {

     private UIOutputoutput1;

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

     @Override public void encodeBegin(FacesContext context)throws IOException {
         Object value = getValue();

         this.output1.setValue(value);
     }

     public UIOutputgetOutput1() {
         return output1;
     }

     public void setOutput1(UIOutput output1) {
         this.output1 = output1;
     }
}

You can find the other required files attached.

*Steps to reproduce:*

1. go to the page index.xhtml

2. click the submit button


*Expected behavior:*

When clicking the submit button, one should stay at the same page. The
output values are incremented


*Observed behavior:*

When clicking the submit button, the following exception is thrown:

jakarta.el.PropertyNotFoundException: Die Eigenschaft [output2] wurde für den Typ [com.example.components.CompositeOutput1] nicht gefunden
	jakarta.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:261)
	jakarta.el.BeanELResolver.property(BeanELResolver.java:330)
	jakarta.el.BeanELResolver.getType(BeanELResolver.java:82)
	com.sun.faces.el.DemuxCompositeELResolver._getType(DemuxCompositeELResolver.java:169)
	com.sun.faces.el.DemuxCompositeELResolver.getType(DemuxCompositeELResolver.java:194)
	org.apache.el.parser.AstValue.setValue(AstValue.java:191)
	org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:199)
	org.apache.webbeans.el22.WrappedValueExpression.setValue(WrappedValueExpression.java:92)
	com.sun.faces.facelets.el.ContextualCompositeValueExpression.setValue(ContextualCompositeValueExpression.java:126)
	com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:95)
	jakarta.faces.component.UIComponent.processEvent(UIComponent.java:1875)
	com.sun.faces.lifecycle.RestoreViewPhase.lambda$deliverPostRestoreStateEvent$0(RestoreViewPhase.java:381)
	com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:125)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1266)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
	jakarta.faces.component.UIForm.visitTree(UIForm.java:344)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
	jakarta.faces.component.UIComponent.visitTree(UIComponent.java:1278)
	com.sun.faces.lifecycle.RestoreViewPhase.deliverPostRestoreStateEvent(RestoreViewPhase.java:379)
	com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:223)
	com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
	com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:96)
	com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:131)
	jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:691)
	jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:449)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	org.apache.openejb.server.httpd.EEFilter.doFilter(EEFilter.java:67)

The same error can be observed, if a composite component is used inside
of another composite component.

This behavior only occurs on TomEE Plume 10.0.0. It doesn't on TomEE
Plus 10.0.0 and Wildfly 35.

Is this a known issue? Is there a workaround? Defining the components in
a taglib file did not solve the problem.

Thank you for your help

Kind regards

Till Gräfenberg
index.xhtml (application/xhtml+xml, 405 B) - not displayed
output1.xhtml (application/xhtml+xml, 449 B) - not displayed
output2.xhtml (application/xhtml+xml, 449 B) - not displayed
ValueFace.java (text/x-java, 461 B)
package com.example.faces;

import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Named;

import java.io.Serializable;

@Named("valueFace")
@SessionScoped
public class ValueFace implements Serializable {

    Integer value = 42;

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public void increment() {
        this.value = this.value + 1;
    }

}
CompositeOutput1.java (text/x-java, 730 B)
package com.example.components;

import jakarta.faces.component.*;
import jakarta.faces.context.FacesContext;

import java.io.IOException;

@FacesComponent("com.example.components.CompositeOutput1")
public class CompositeOutput1 extends UIOutput implements NamingContainer {

    private UIOutput output1;

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

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        Object value = getValue();

        this.output1.setValue(value);
    }

    public UIOutput getOutput1() {
        return output1;
    }

    public void setOutput1(UIOutput output1) {
        this.output1 = output1;
    }
}
CompositeOutput2.java (text/x-java, 728 B)
package com.example.components;

import jakarta.faces.component.*;
import jakarta.faces.context.FacesContext;

import java.io.IOException;

@FacesComponent("com.example.components.CompositeOutput2")
public class CompositeOutput2 extends UIOutput implements NamingContainer {

    private UIOutput output2;

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

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        Object value = getValue();

        this.output2.setValue(value);
    }

    public UIOutput getOutput2() {
        return output2;
    }

    public void setOutput2(UIOutput output) {
        this.output2 = output;
    }
}