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]
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 aUIComponent#visitTree()
withoutVisitHint#SKIP_UNRENDERED
and/orVisitHint#SKIP_ITERATION
. TheCombinedResourceHandler
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 avisitTree()
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.