Datatable and CommandLink - issue converting to PF 3.4

UI Components for JSF
seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

07 Sep 2012, 23:29

While trying to convert from 3.3.1 to 3.4, I encountered an issue with Datatable containing h:commandLink, upon click of the commandLink. Works fine in 3.3.1.

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: Image

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"));
	}

}
Here is the tableData object:

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;
	}
}
Here is the xhtml page:

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>
The Console log shows this. I should mention that I have a PhaseListener called PhaseLogger to show me the different phase executions, so I keep my head straight!

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

Note the call to "ctor" at the end of the Invoke Application phase. That is the trouble-maker!

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.
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

08 Sep 2012, 02:21

More info. The stack trace was taken at a break-point in constructor, at the second call to the constructor. The UIData broadcast method is on the last line of the method, beyond the "finally", in the stack trace.

The console log shows initial page load, then click of errorPage link.

Is this the way it is supposed to work? Is the constructor of the view you are leaving supposed to get called at the end of the Invoke Application phase?
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

13 Sep 2012, 23:54

More info. Issue happens in last statement of javax.faces.component.UIData broadcast method, a call to setRowIndex(oldRowIndex);

Using PF version 3.3.1, this setRowIndex method is implemented in the javax UIData class. In PF version 3.4, the setRowIndex method is implemented in org.primefaces.component.api.UIData.

javax.faces.component.UIData setRowIndex method

Code: Select all

    public void setRowIndex(int rowIndex)
    {
        if (isRowStatePreserved())
        {
            setRowIndexRowStatePreserved(rowIndex);
        }
        else
        {
            setRowIndexWithoutRowStatePreserved(rowIndex);
        }
    }
PF 3.4 org.primefaces.component.api.UIData setRowIndex method over-ride

Code: Select all

    public void setRowIndex(int rowIndex) {
        saveDescendantState();

        setRowModel(rowIndex);

        restoreDescendantState();
    }
In the PF 3.3.1 scenario, the only time that the javax setRowIndex calls getValue, is when isRowStatePreserved returns true. My test case is not row-state-preserved, so no call to getValue is made, hence no constructor call on my viewScoped bean.

In the PF 3.4 scenario, setRowModel(rowIndex) calls getValue.

Still digging, gotta find out why my viewScoped bean is gone before the end of the InvokeApplication phase in the first place.
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

14 Sep 2012, 22:43

OK, obviously I am missing something really basic. Somebody please take pity and point out what I am missing!

Issue is that an HtmlCommandLink in each row of a dataTable is intended to navigate to another page when clicked. Upon clicking a command link, the viewScoped bean behind the view is "re-constructed" in the Invoke Application phase, AFTER the action method executes.

Tracked this thing through the debugger. Here's the highlights after the command link is clicked.
1) DataTable wraps the ActionEvent in WrapperEvent class, in method named queueEvent, before adding it to the stack. The WrappedEvent holds on to a rowIndex, which is set to the rowIndex where the HtmlCommandLink was rendered - the link which was clicked to initiate the action. Lets say rowIndex = 2 in the WrappedEvent.
2) Moving on through the phases, getting to InvokeApplication. WrappedEvent is processed, with DataTable as it's source. This calls the broadcast method, with WrappedEvent as parameter.
3) Looking at the DataTable method named "broadcast", which is implemented in javax.faces.component.UIData. The initial "getRowIndex()" call returns -1, the default value, and saves it in a variable named oldRowIndex. THIS IS IMPORTANT.
4) Continuing in broadcast, DataTable gets its rowIndex set to 2, the value from the WrapperEvent.
5) DataTable broadcast method goes on to get the source of the WrapperEvent, which is the HtmlCommandLInk, and then call broadcast on the HtmlCommandLink.

6) Calling broadcast on the HtmlCommandLink goes through and calls the action method on my ViewScope bean from inside ActionListenerImpl class (com.sun.faces.application package). Method is processAction. The method on my viewScoped bean is called and the result is returned to the processAction method. It represents the next view.

7) still inside processAction, the new view name is given to the NavigationHandler. The Navigation handler sets the new view in FacesContext AND CLEARS THE VIEWSCOPE!!! It also sets "renderResponse".

8) We then return back up through the stack to DataTable's broadcast method. HtmlCommandLink is popped from EL and then the problem happens. At the end of the broadcast method, setRowIndex is called to reset the DataTable to the oldRowIndex (remember I said that was important?).

9) setRowIndex calls setRowModel, which is implemented in the PF version of UIData. Method setRowModel is called with parameter rowIndex set to -1. The first two lines work fine. THEN THIS HAPPENS:

Code: Select all

        //clear datamodel
        if(rowIndex == -1) {
            setDataModel(null);
        }
Once the model instance var is set to null, the code continues on to get the var = "test" and calls isRowAvailable().

10) isRowAvailable (implemented in javax.faces.component.UIData) executes getDataModel on the PF implementation of UIData. Since model was set to null, the getValue() code executes

Code: Select all

    @Override
    protected DataModel getDataModel() {
        if(this.model != null) {
            return (model);
        }

        Object current = getValue();
...

This has to reconstruct my ViewScoped bean in order to get the dataTable value. Remember, the NavigationHandler has already set the next view and CLEARED viewScope map, so constructor is called again.

Mystery solved as to why constructor was called in Invoke Application phase! NOW WHAT DO I DO ABOUT IT?

ViewScope bean should not be reconstructed just before it goes "out of scope"! OR do I misunderstand WHEN exactly it goes out of scope?

Can somebody help me out here?? Is this expected behavior??
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

mkienenb
Posts: 123
Joined: 03 Aug 2012, 04:42
Location: Elmira, NY
Contact:

15 Sep 2012, 00:19

Since you've created a small example, try replacing p:dataTable with h:dataTable and see if that works.

If it does, then there's a bug in p:dataTable. Seems to make sense since you said it worked under 3.3 and found the code that changed how setRowIndex worked.

If it does not, then there's something more going on here.
I have used commandButtons in h:dataTable without problems. I haven't yet migrated one of those tables to p:dataTable.
Mike Kienenberger
Myfaces 2.1.14, Primefaces-5.3

seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

17 Sep 2012, 15:35

Thanks for the suggestion! I changed the p:dataTable to h:dataTable and re-ran. Below is the console log, for two JSF lifecycles. First cycle is upon opening the page which contains the dataTable, second one is initiated by clicking a commandLink inside the rendered dataTable. The log shows the behavior I would expect - constructor of the ViewScoped bean is called at initiation of the view, and is NOT called again.

Image

Uploaded with ImageShack.us

Looks like there is a bug in the PF dataTable logic.

How should I proceed?
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

mkienenb
Posts: 123
Joined: 03 Aug 2012, 04:42
Location: Elmira, NY
Contact:

17 Sep 2012, 18:11

Open an bug report in the issue tracker. Realize that it may not get fixed any time soon unless you provide a patch to fix it or buy Primefaces PRO support.
Mike Kienenberger
Myfaces 2.1.14, Primefaces-5.3

mpup371
Posts: 3
Joined: 04 May 2011, 17:15

24 Sep 2012, 17:06

HI,
have the same problem: commandlink in a datatable with "action" parameter dynamically set to another page.Worked fine in 3.1.
It's a critical problem for my application, and I will not be able to update primefaces until the problem will be solved.
I'm not skilled enough to submit a bug report, but I suppose many users will have the same problem.
If there is a workaround, please tell me.
jf

seycsx
Posts: 18
Joined: 04 Dec 2010, 04:31
Location: Jacksonville, FL, USA

24 Sep 2012, 17:34

My workaround was to code the constructor of myViewScoped bean so that an extra "construction" at the end of the invoke application phase would not cause an exception. This issue originally surfaced when the extra "construction" failed with an exception, because the Invoke Application action had set up outside objects for the new View (the destination of the CommandLink).

I would recommend setting the value reference of the dataTable to a property of a session-scoped bean, as a possible workaround. If that's not possible, make sure your ViewScoped constructor doesn't cause issues if called at the end of the Invoke Application phase.

I do not have a patch for the dataTable logic. I will eventually put out the issue, but other things are pressing me for time right now.
IBM Websphere App Server v7.0.0.19
Mojarra 2.1.9 (SNAPSHOT 20120531-1326)
Primefaces 3.3.1 - experimenting 3.4

smithh032772
Posts: 6144
Joined: 10 Sep 2011, 21:10

06 Feb 2013, 14:32

My recommendation is replace action="#{bean.doSomething()}" with actionListener="#{bean.doSomething()}" and use navigationhandler in your bean.

Why is that my recommendation? because how many times do you see action="#{bean.doSomething()}" in PrimeFaces showcase examples or user guide? I'm sure you will see actionListener="#{bean.doSomething()}" in-and-throughout the PrimeFaces world/realm, and you will see recommendations/chatter in the forums about navigationhandler.

I just searched google for the following:

jsf balusc navigation handler actionlistener

If I were you, I would definitely read any/all what showed up in that list of search results. I have learned a lot about JSF from BalusC. :)

FYI, someone was kind enough to create an issue in issue tracker; that is why I am responding here; i was strolling through issue tracker and decided to reply here.

Issue 5225
Howard

PrimeFaces 6.0, Extensions 6.0.0, Push (Atmosphere 2.4.0)
TomEE+ 1.7.4 (Tomcat 7.0.68), MyFaces Core 2.2.9, JDK8
JUEL 2.2.7 | OmniFaces | EclipseLink-JPA/Derby | Chrome

Java EE 6 Tutorial|NetBeans|Google|Stackoverflow|PrimeFaces|Apache

Post Reply

Return to “PrimeFaces”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 36 guests