I'm running a Tomcat 7, JSF 2.2, OpenWebBeans 1.6, Omnifaces 1.8 and Richfaces 3. I recently added the Omnifaces CombinedResourceHandler, and noticed that it causes a datatable's value method to be called despite the method being in an unrendered block, when the form on the page is submitted.

Example:

<h:body styleClass="bdyPage">
    <h:form id="form">
        <h:commandButton value="test button" />
        <h:panelGroup rendered="false">
            <h:outputText value="#{testBean.getTestString()}" />
            <h:dataTable id="testtable22" value="#{testBean.strings}" var="str"
                rowClasses="odd, even">
                <h:column>
                    <f:facet name="header">
                        test
                    </f:facet>
                    <h:outputText value="str" />
                </h:column>
            </h:dataTable>
        </h:panelGroup>
    </h:form>
</h:body>

I have a simple test bean:

@Named("testBean")
@RequestScoped
public class TestBean1 implements Serializable {

  private static final long serialVersionUID = 1L;

    public String getTestString() {
        System.out.println("Test string");
        return "test string";
    }

    public List<String> getStrings() {
        List<String> strings = new ArrayList<String>();
        strings.add("hej");
        strings.add("hejsan");
        strings.add("hej hej!");
        System.out.println("getting strings");
        return strings;
    }

}

Normally, the getStrings() isn't called because the panelGroup is set to not be rendered. However, when I run both Omnifaces CombinedResourceHandler and Richfaces, getStrings() is called when the submit button is pressed more than once. This only seems to affect the value attribute of the h:dataTable component. Other el expressions don't get invoked.

I set up a small sample project with the following maven dependices:

    <dependencies>
    <dependency>
        <groupId>org.richfaces.ui</groupId>
        <artifactId>richfaces-components-ui</artifactId>
        <version>4.3.7.Final</version>
    </dependency>
    <dependency>
        <groupId>org.richfaces.core</groupId>
        <artifactId>richfaces-core-impl</artifactId>
        <version>4.3.7.Final</version>
    </dependency> 
<!--    <dependency>  
    <groupId>org.richfaces</groupId>  
    <artifactId>richfaces</artifactId>  
  <version>5.0.0.Alpha3</version>  
</dependency>  
     -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.0.GA</version>
        <type>jar</type>
        <classifier>sources</classifier>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.0.GA</version>
        <type>jar</type>
    </dependency>
    <dependency>
        <groupId>org.omnifaces</groupId>
        <artifactId>omnifaces</artifactId>
        <version>1.8</version>
    </dependency>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-impl</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-jsf</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-el22</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.openwebbeans</groupId>
        <artifactId>openwebbeans-web</artifactId>
        <version>1.6.2</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.faces</artifactId>
        <version>2.2.6</version>
    </dependency>
</dependencies>

I am not using any richfaces components in the project or on the test page. I am merely including Richfaces as a dependency. I don't have any javascripts that get combined. If I disable the Omnifaces CRH or remove Richfaces from the pom file, it works as expected.

I tried using Richfaces 5, but I get a similar behaviour there (although the getter is called fewer times). I have not tried the latest Omnifaces version, since it isn't compatible with OWB 1.6.

Anoter point of interest might be that if I surround the datatable with a JSTL <c:if>, it works. But I don't want to have to do that, obviously ...

Am I doing something wrong here? Is there a compatibility issue between these versions Omnifaces CRH and Richfaces?

Edit: Call stack from the datatable's getter

TestBean1.getStrings() line: 33 
TestBean1$$OwbNormalScopeProxy0.getStrings() line: not available    
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available   
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available   
Method.invoke(Object, Object...) line: not available    
BeanELResolver.getValue(ELContext, Object, Object) line: 87 
DemuxCompositeELResolver._getValue(int, ELResolver[], ELContext, Object, Object) line: 176  
DemuxCompositeELResolver.getValue(ELContext, Object, Object) line: 203  
AstValue.getValue(EvaluationContext) line: 183  
ValueExpressionImpl.getValue(ELContext) line: 185   
WrappedValueExpression.getValue(ELContext) line: 70 
TagValueExpression.getValue(ELContext) line: 109    
ComponentStateHelper.eval(Serializable, Object) line: 194   
ComponentStateHelper.eval(Serializable) line: 182   
HtmlDataTable(UIData).getValue() line: 732  
HtmlDataTable(UIData).getDataModel() line: 1822 
HtmlDataTable(UIData).setRowIndexWithoutRowStatePreserved(int) line: 484    
HtmlDataTable(UIData).setRowIndex(int) line: 473    
HtmlDataTable(UIData).visitColumnsAndColumnFacets(VisitContext, VisitCallback, boolean) line: 2104  
HtmlDataTable(UIData).visitTree(VisitContext, VisitCallback) line: 1446 
HtmlPanelGroup(UIComponent).visitTree(VisitContext, VisitCallback) line: 1701   
HtmlForm(UIComponent).visitTree(VisitContext, VisitCallback) line: 1701 
HtmlForm(UIForm).visitTree(VisitContext, VisitCallback) line: 371   
HtmlBody(UIComponent).visitTree(VisitContext, VisitCallback) line: 1701 
UIViewRoot(UIComponent).visitTree(VisitContext, VisitCallback) line: 1701   
FaceletViewHandlingStrategy.locateComponentByClientId(FacesContext, String) line: 2082  
FaceletViewHandlingStrategy.reapplyDynamicRemove(FacesContext, ComponentStruct) line: 2174  
FaceletViewHandlingStrategy.reapplyDynamicActions(FacesContext) line: 2116  
FaceletViewHandlingStrategy.buildView(FacesContext, UIViewRoot) line: 966   
RenderResponsePhase.execute(FacesContext) line: 99  
RenderResponsePhase(Phase).doPhase(FacesContext, Lifecycle, ListIterator<PhaseListener>) line: 101  
LifecycleImpl.render(FacesContext) line: 219    
FacesServlet.service(ServletRequest, ServletResponse) line: 647 
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305  
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210  
StandardWrapperValve.invoke(Request, Response) line: 222    
StandardContextValve.invoke(Request, Response) line: 123    
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472    
StandardHostValve.invoke(Request, Response) line: 171   
ErrorReportValve.invoke(Request, Response) line: 99 
AccessLogValve.invoke(Request, Response) line: 936  
StandardEngineValve.invoke(Request, Response) line: 118 
CoyoteAdapter.service(Request, Response) line: 407  
Http11Processor(AbstractHttp11Processor).process(SocketWrapper<S>) line: 1004   
Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler).process(SocketWrapper<S>, SocketStatus) line: 589    
JIoEndpoint$SocketProcessor.run() line: 312 
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: not available 
ThreadPoolExecutor$Worker.run() line: not available 
TaskThread(Thread).run() line: not available [local variables unavailable]  
1

There are 1 answers

3
BalusC On

First of all, getter methods are not supposed to perform business logic: How and when should I load the model from database for h:dataTable. Once you fix your getter method to solely return the property, the concern of it being called multiple times becomes completely unnecessary.

Coming back to the observed behavior, the getter method of an unrendered <h:dataTable> may be called at "unexpected" times when code performs a UIComponent#visitTree() without VisitHint#SKIP_UNRENDERED and/or VisitHint#SKIP_ITERATION. The CombinedResourceHandler itself isn't doing that. At least, not directly. It only manipulates the component resources in the component tree. It however has a RichFaces specific hack in place in order to extract its homegrown resource library approach. Still, its source code doesn't seem to trigger a visitTree() anywhere. To nail down the real cause, just put a debug breakpoint on the getter method and explore the call stack who is responsible for the call.

As to the apparent JSTL <c:if> aversion (the word "obviously" is a bit too strong here), this is food for read: JSTL in JSF2 Facelets... makes sense?

That said, latest OmniFaces 1.x release is the 1.8.3, not 1.8. See also download section. It would also be worth the effort to upgrade Mojarra to latest which is as of now 2.2.12.