The symptom was an exception thrown while trying to construct the ViewScoped bean which holds the datatable's "value", occurring at the end of the InvokeApplication phase. The commandLink's action method is also coded in the same ViewScoped bean, and was executed appropriately (with existing ViewScoped bean instance, no constructor call). AFTER the action method was executed the constructor was called, resulting in the excecption because everything was set up for the next view by the action method. Current view would not reconstruct at that point, nor was it intended to!
Running in debugger, I found that the datatable was making a call to the "value" getter. See Strack Trace here:
Uploaded with ImageShack.us
I was able to create a small sample that exhibits the inappropriate constructor call.
ViewScoped Bean:
Code: Select all
package com.test.view;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import com.---.util.Log; // log4j console logger
import com.test.vo.TestTableLinkData;
@ManagedBean
@ViewScoped
public class TestTableLinkView implements Serializable {
private static final long serialVersionUID = 1L;
private List<TestTableLinkData> tableData;
public TestTableLinkView() {
Log.logDebug("TestTableLinkView", "ctor");
tableData = new ArrayList<TestTableLinkData>();
loadTableData();
}
public List<TestTableLinkData> getTableData() {
Log.logDebug("TestTableLinkView", "getTableData");
return tableData;
}
public void setTableData(List<TestTableLinkData> tableData) {
this.tableData = tableData;
}
public String actionGoToOutcome() {
String returnValue = null;
returnValue = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("gotoOutcome");
Log.logDebug("TestTableLinkView", "actionGoToOutcome returnValue=" + returnValue);
return (returnValue == null ? "" : returnValue);
}
private void loadTableData() {
//set up some strings for test
tableData.add(new TestTableLinkData("homePage","index"));
tableData.add(new TestTableLinkData("errorPage","errorPage"));
tableData.add(new TestTableLinkData("viewExpired","viewExpired"));
}
}
Code: Select all
package com.test.vo;
import java.io.Serializable;
public class TestTableLinkData implements Serializable {
private static final long serialVersionUID = 1L;
private String linkText;
private String linkOutcome;
public TestTableLinkData() {
super();
}
public TestTableLinkData(String linkText, String linkOutcome) {
super();
this.linkText = linkText;
this.linkOutcome = linkOutcome;
}
public String getLinkText() {
return linkText;
}
public void setLinkText(String linkText) {
this.linkText = linkText;
}
public String getLinkOutcome() {
return linkOutcome;
}
public void setLinkOutcome(String linkOutcome) {
this.linkOutcome = linkOutcome;
}
}
Code: Select all
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>testTableLink</title>
<meta http-equiv="Content-Type"
content="application/xhtml+xml; charset=UTF-8" />
</h:head>
<h:body>
<h:form id="testForm">
<p:dataTable id="linkTestTable" var="test"
value="#{testTableLinkView.tableData}"
style="width:20%">
<p:column id="linkTextCol">
<f:facet name="header">
<h:outputText id="textHeader" value="Clickable" />
</f:facet>
<h:commandLink id="cmdLink"
action="#{testTableLinkView.actionGoToOutcome}">
<h:outputText value="#{test.linkText}" />
<f:param name="gotoOutcome" value="#{test.linkOutcome}" />
</h:commandLink>
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
Code: Select all
[WebContainer : 1] DEBUG PhaseLogger - BEFORE RESTORE_VIEW 1 viewId=null
[WebContainer : 1] DEBUG PhaseLogger - AFTER RESTORE_VIEW 1 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE RENDER_RESPONSE 6 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG TestTableLinkView - ctor
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG PhaseLogger - AFTER RENDER_RESPONSE 6 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE RESTORE_VIEW 1 viewId=null
[WebContainer : 1] DEBUG PhaseLogger - AFTER RESTORE_VIEW 1 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE APPLY_REQUEST_VALUES 2 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG PhaseLogger - AFTER APPLY_REQUEST_VALUES 2 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE PROCESS_VALIDATIONS 3 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG PhaseLogger - AFTER PROCESS_VALIDATIONS 3 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE UPDATE_MODEL_VALUES 4 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG PhaseLogger - AFTER UPDATE_MODEL_VALUES 4 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE INVOKE_APPLICATION 5 viewId=/testTableLink.xhtml
[WebContainer : 1] DEBUG TestTableLinkView - actionGoToOutcome returnValue=errorPage
[WebContainer : 1] DEBUG TestTableLinkView - ctor
[WebContainer : 1] DEBUG TestTableLinkView - getTableData
[WebContainer : 1] DEBUG PhaseLogger - AFTER INVOKE_APPLICATION 5 viewId=/errorPage.xhtml
[WebContainer : 1] DEBUG PhaseLogger - BEFORE RENDER_RESPONSE 6 viewId=/errorPage.xhtml
[WebContainer : 1] DEBUG PhaseLogger - AFTER RENDER_RESPONSE 6 viewId=/errorPage.xhtml
Do I need to redesign so that the constructors are benign, and do not do any data loading? Currently I load data into a session object during actions, and pull them into the ViewScope beans from the session object at construction/post construction?
Is this a valid constructor call, at the end of the Invoke Application phase? I'm wondering if it is a bug in the DataTable, since that is the only change I made to "break" this code.