JSF recursive ui:include jumbles output tree on multiple postback - jsf

I have a report that displays college course enrollment information in an expandable tree format. It displays the course prefix with the summary enrollment numbers and the specific course enrollment numbers when the prefix is clicked on. The report works fine when it is first run. Should you decide to run the report again for another session, however, things start to get jumbled up.
When Report One is run, it is correct. When Report Two is run (for a different session, same session runs work fine), notice the second has duplicated/missing elements. Both of these reports will work correctly if they're run "first", ie, no other report has been run in that users session.
I'm using a recursively included page to build the tree. Here's the pertinent code from enrollment.xhtml, the report page itself:
<div id="resultSet" class="treeContainer">
<c:if test="${EnrollmentBean.reportRan}">
<ui:include src="/WEB-INF/includes/reportTree.xhtml">
<ui:param name="nodes" value="#{EnrollmentBean.reportTreeData.getChildren()}" />
<ui:param name="isHidden" value="false" />
</ui:include>
</c:if>
</div>
The included reportTree.xhtml code (tree expansion code done with CSS/jQuery):
<ul class="${isHidden ? 'hidden' : ''}">
<c:forEach items="#{nodes}" var="node">
<li><a href="#" class="reportTreeLabel">#{node.getData().getKey()}
<span class="reportTreeData">#{node.getData().getValue()}</span>
</a>
<c:if test="#{node.hasChildren()}">
<ui:include src="/WEB-INF/includes/reportTree.xhtml">
<ui:param name="nodes" value="#{node.getChildren()}" />
<ui:param name="isHidden" value="true" />
</ui:include>
</c:if>
</li>
</c:forEach>
</ul>
The pertinent parts of the backing bean, EnrollmentBean.java:
#Named("EnrollmentBean")
#SessionScoped
public class EnrollmentBean implements Serializable {
/** Holds Report Data */
private List< List<String> > reportData;
/** Hold Report Data in tree format */
private TreeNode<SimpleEntry<String, Integer>> reportTreeData;
private void buildReportTree() {
this.reportTreeData = new TreeNode<SimpleEntry<String,Integer>>();
String prefix = "";
Integer prefixEnrollSum = 0;
// Stores the tree data for the prefix being processed. Once all the data has been processed, this is added as a child to the reportTreeData field.
TreeNode<SimpleEntry<String,Integer>> PrefixTree = null;
SimpleEntry<String,Integer> data = null;
for (List<String> line : this.reportData) { // for each raw data line
String course = line.get(0);
Integer enrollments = Integer.parseInt(line.get(1));
if (!prefix.equals(this.getPrefix(course)) ) { // if the prefix changed since the last line that was processed
prefix = this.getPrefix(course); // set the new prefix
if (PrefixTree != null) { // if we're not dealing with the very first line of data
// set the sum of enrollments gathered for this prefix and reset the counter for the next prefix.
PrefixTree.getData().setValue(prefixEnrollSum);
prefixEnrollSum = 0;
}
// prepare a data element for the prefix summary node, then create the node, passing in the data and the parent for this node.
data = new SimpleEntry<String,Integer>(prefix, 0);
PrefixTree = new TreeNode<SimpleEntry<String,Integer>>(data);
this.reportTreeData.addChild(PrefixTree);
} // end prefix changed
data = new SimpleEntry<String,Integer>(course, enrollments);
PrefixTree.addChild(data);
prefixEnrollSum += enrollments;
} // end for each data line
// If there was data to process, upon processing the final row, assign the summed enrollments to the final prefix tree.
if (PrefixTree != null) { PrefixTree.getData().setValue(prefixEnrollSum); }
}
}
The TreeNode class is one that I've created and it provides simple tracking of data, parent, and children. I can post that code if needed, but I believe it is superfluous at this time.
During my troubleshooting of this problem, I've verified the reportData the report tree is built on in the backing bean is always correct. Through using the Logger class, I've verified that the tree is being generated correctly (by writing to the server log each line that was being processed into the tree). I've even verified that the reportTreeData is correct after every run by writing out the tree to the server log after the tree was constructed.
I can only conclude that something is going wrong in the Render Response phase of the JSF lifecycle, as I did notice that if I change the backing bean from #SessionScoped to #RequestScoped, the report is generated correctly every time. I'd rather not us this as my fix, as I have a "Download CSV" link that uses the already generated report data in the backing bean so the report logic doesn't need to re-run to generate the CSV.
Does anyone know why this is happening and what I can do to correct this behavior? I'm using JSF 2.2 with Java EE 7 on GlassFish Open Source Edition 4.1 (build 13)
UPDATE 12/24/15
I've been stepping through the render response phase JSF code and it seems that the EL expression is just being evaluated with the wrong data on a second report run. I can see where it is making the function call to evaluate the EL expression and it is coming back with the wrong data. I will try to get the weld-osgi-bundle.jar source so I can step into that function call at a later date.

Based on a lot of debugging and research, but specifically this answer, I assume my issue has something to do with the view state trying to be restored and the recursion I'm using is making it hard on the JSF framework to properly update the component tree model.

Related

Access iteration variable in passthrough attribute?

I'm wondering why I get a NullPointerException on the following:
<h:dataTable value="#{testBean.entities}" var="d">
<h:column p:data-order="#{d.modifiedOn}">
<f:facet name="header">Date</f:facet>
<h:outputText value="#{d.modifiedOn}">
<f:convertDateTime pattern="#{msg.datePattern}" />
</h:outputText>
</h:column>
</h:dataTable>
Where the p namespace is xmlns:p="http://xmlns.jcp.org/jsf/passthrough", modifiedOn is a (non-null) Date field.
The exception looks like this:
Caused by: java.lang.NullPointerException
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.getAttributeValue(HtmlResponseWriter.java:1211)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.flushAttributes(HtmlResponseWriter.java:1171)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.closeStartIfNecessary(HtmlResponseWriter.java:1113)
at com.sun.faces.renderkit.html_basic.HtmlResponseWriter.writeText(HtmlResponseWriter.java:936)
at com.sun.faces.facelets.el.ELText$ELTextVariable.writeText(ELText.java:240)
at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:85)
at com.sun.faces.facelets.compiler.UIInstructions.encodeBegin(UIInstructions.java:82)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:302)
at com.sun.faces.renderkit.html_basic.TableRenderer.renderHeader(TableRenderer.java:339)
However, use of #{d.modifiedOn} is fine in any non-passthrough attributes like headerClass. As is any value not referencing the iteration variable in a passthrough attribute.
My question is: is this intended?
This is using Wildfly-8.0.0.Final's JSF implementation, version 2.2.5-jbossorg-3.
In case anyone's wondering: I'm trying to support datatables orthogonal data attributes to enhance my tables.
Edited to add: I've also tried, but got the same Exception, with adding the following to the <h:column>:
<f:passThroughAttribute name="data-order" value="#{d.modifiedOn}" />
This is apparently caused by JSF trying to render this attribute on the header where rowIndex is -1 and iteration data is not yet present. So there's an hack solution to my specific problem:
<h:column p:data-order="#{empty d or empty d.modifiedOn ? '' : d.modifiedOn.time}">
...
</h:column>
Which still renders an empty/useless data-order attribute into my thead.
Further testing:
More testing has confirmed my suspicion that this only occurs with a <f:facet name="header"> on any column (doesn't have to be the one with the attribute). Leaving those out defeats my goal of using the table as a base for datatables since that requires a well-formed table with a <thead>.
Is this intended? Definitelly it's not. Furthermore, here is the definition of a passthrough attribute according to the JSF 2.2 specification:
Pass Through Attributes
For any given JSF component tag in a view, the set of available attributes that component supports is determined by a
combination of the UIComponentand Renderer for that tag. In some cases the value of the attribute is interpreted by
the UIComponentor Renderer(for example, the columns attribute of h:panelGrid) and in others the value is
passed straight through to the user agent (for example, the lang attribute of h:inputText).
In both cases, the
UIComponent/Renderer has a priori knowledge of the set of allowable attributes. Pass Through Attributes allows
the view author to list arbitrary name value pairs that are passed straight through to the user agent without interpretation
by the UIComponent/Renderer. This behavior is normatively specified in the “Rendering Pass Through Attributes”
section of the overview of the standard HTML_BASIC render kit.
So the only functionality of that kind of attributes is to notify the renderer that it must process the value for the attribute, but that it's not tied to any of the UIComponent features, so must end up at the final output.
I have performed a test with Tomcat 7 + Mojarra 2.2.6 and it perfectly works for me. HTML elements get rendered in the DOM tree with their data-order attribute, with the given date. That's the test case I've used:
<html xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:head />
<h:body>
<h:dataTable value="#{bean.entities}" var="entity">
<h:column p:data-order="#{entity.modifiedOn}">
#{entity.name}
</h:column>
</h:dataTable>
<h:messages />
</h:body>
</html>
#ManagedBean
#ViewScoped
public class Bean {
public class Entity {
private String name;
private Date modifiedOn;
public Entity(String name, Date modifiedOn) {
this.name = name;
this.modifiedOn = modifiedOn;
}
public Date getModifiedOn() {
return modifiedOn;
}
public String getName() {
return name;
}
}
/**
* Create a List of entities with dates differing from now to now + 2 days
*/
public List<Entity> entities = Arrays.asList(
new Entity("name1", new Date()), new Entity("name2", new Date(
new Date().getTime() + (1000 * 60 * 60 * 24))), new Entity(
"name0", new Date(new Date().getTime()
+ (1000 * 60 * 60 * 48))));
public List<Entity> getEntities() {
return entities;
}
}
This means it's a bug in your JSF implementation (implementation which I didn't know about BTW). Your best is opening a bug track unless the authors have already fixed it in a later version.
Edit
The rendering fails with the given error when there's an f:facet for header or footer involved for the datatable. Apparently, Mojarra tries to evaluate the passthrough value for the facet before building the iterator, that causes it to fail as the variable is not yet available. Opened an issue.
See also:
JSF 2.2 specification

Render JSF h:message with p element instead of span

I would like to create a custom message renderer to renders h:message as a 'p' html element instead of as a 'span' element. It concerns the following message tag:
<h:message id="firstNameErrorMsg" for="firstname" class="error-msg" />
I've written to code underneath, but that's only rendering an empty 'p' element. I suppose I have to copy all attributes and text from the original component and write it to the writer. However, I don't know where to find everything and it seems to be a lot of work for just a replacement of a tag.
Is there a better way to get an h:message tag rendered as a 'p' element?
Code:
#FacesRenderer(componentFamily = "javax.faces.Message", rendererType = "javax.faces.Message")
public class FoutmeldingRenderer extends Renderer {
#Override
public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("p", component);
writer.endElement("p");
}
}
It isn't exactly "a lot of work". It's basically a matter of extending from the standard JSF messages renderer, copypasting its encodeEnd() method consisting about 200 lines then editing only 2 lines to replace "span" by "p". It's doable in less than a minute.
But yes, I agree that this is a plain ugly approach.
You can consider the following alternatives which are not necessarily more easy, but at least more clean:
First of all, what's the semantic value of using a <p> instead of a <span> in this specific case? To be honest, I'm not seeing any semantic value for this. So, I suggest to just keep it a <span>. If the sole functional requirement is to let it appear like a <p>, then just throw in some CSS. E.g.
.error-msg {
display: block;
margin: 1em 0;
}
You can obtain all messages for a particular client ID directly in EL as follows, assuming that the parent form has the ID formId:
#{facesContext.getMessageList('formId:firstName')}
So, to print the summary and detail of the first message, just do:
<c:set var="message" value="#{facesContext.getMessageList('formId:firstName')[0]}" />
<p title="#{message.detail}">#{message.summary}</p>
You can always hide it away into a custom tag file like so:
<my:message id="firstNameErrorMsg" for="firstname" class="error-msg" />
Use OmniFaces <o:messages>. When the var attribute is specified, then you can use it like an <ui:repeat>.
<o:messages for="firstNameErrorMsg" var="message">
<p title="#{message.detail}">#{message.summary}</p>
</o:messages>

How do I implement a NamingContainer? All children get the same client ID

I try to write my own tree component. A tree node renders as a div containing child components of the tree component, for example:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</my:tree>
So far, so good - everything works as expected, but the h:outputText gets the same id repeatedly.
So I had my component implement javax.faces.NamingController, overwriting getContainerClientId():
#Override
public String getContainerClientId(FacesContext context) {
String clientId = super.getClientId(context);
String containerClientId = clientId + ":" + index;
return containerClientId;
}
index is set and updated during iteration over the nodes. But getContainerClientId() is called only once for every children (not for every iteration and every children, as I would expect). That causes every child id to be prefixed with the same container id:
form:treeid:0:xxx
Same thing for overwriting getClientId().
What did I miss?
The answer is hidden in the bottom of chapter 3.1.6 of JSF 1.2 specification:
3.1.6 Client Identifiers
...
The value returned from this method must be the same throughout
the lifetime of the component instance unless setId() is called, in which case it will be
recalculated by the next call to getClientId().
In other words, the outcome of getClientId() is by default cached by the JSF component as implemented in UIComponentBase#getClientId() (see also the nullcheck at line 275 as it is in Mojarra 1.2_15) and this cache is resetted when the UIComponentBase#setId() is called (see also line 358 as it is in Mojarra 1.2_15). As long as you don't reset the cached client ID, it will return the same value on every getClientId() call.
So, while rendering the children in encodeChildren() implementation of your component or the renderer which shall most probably look like this,
for (UIComponent child : getChildren()) {
child.encodeAll(context);
}
you should for every child be calling UIComponent#setId() with the outcome of UIComponent#getId() to reset the internally cached client ID before encoding the child:
for (UIComponent child : getChildren()) {
child.setId(child.getId());
child.encodeAll(context);
}
The UIData class behind the <h:dataTable> implementation does that by the way also (see line 1382 as it is in Mojarra 1.2_15). Note that this is not JSF 1.x specific, the same applies on JSF 2.x as well (and also on UIRepeat class behind Facelets <ui:repeat>).
It's worth mentioning that if your component's children have children, then it may also be necessary to refresh their cached ids, too. With this mark-up, slightly adapted from the original:
<my:tree id="extendedTree"
value="#{controller.rootNode}"
var="node">
<h:panelGroup layout="block" id="nodeBlock">
<h:outputText id="xxx" value="#{node.name}" />
<h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</h:panelGroup>
</my:tree>
The id for the <panelGroup> comes out OK after applying BalusC's fix above but all the sub-components come out with 0 in the iterator.
To fix that, iterate through all the levels of children too and refresh their cached ids. So:
child.setId(child.getId()); becomes uncacheId(child); where the uncacheId function is defined:
private void uncacheId(UIComponent el) {
el.setId(el.getId());
el.getChildren().forEach(this::uncacheId);
}
That may be obvious but it took me a while to figure out, so ...
h:outputText id gives you same as you didn't make it dynamic. You can create it like:
<h:outputText id="xxx_#{node.id}" value="#{node.name}" />
Assume node has an 'id' attribute which is an unique.

JSF: why is empty test in rendered invoked during apply request values phase during form submission under request scoped POST REDIRECT GET

This question is spawned from the partial answer to JSF2: why does empty test in rendered of panelGroup in composite prevent action from being called?
In the following an Element is an #Entity with a name and id. A view.xhtml JSF page takes the id as a viewParam and uses setID(Long id) of the #ManagedBean #RequestScoped ElementController to trigger loading of the corresponding Element by id from database (that plays no further role in the question) and this found Element is set as the 'current' Element available (for historical reasons by a slightly different name) as Element getSelected().
The view.xhtml page performs a rendered attribute test #{not empty elementController.selected}, and has a h:commandButton with action that performs a faces-redirect, along with the id as query parameter, back to the view.xhtml page.
For some reason I do not fully understand, on form submission the test (and thus getSelected) is invoked in both the apply request phase and the process validations phase, before the viewParam id can be set (and thus before the current/selected Element can be found and set) in the update model values phase.
The greatly abbreviated view.xhtml page is:
<f:view>
<f:metadata>
<f:viewParam name="id" value="#{elementController.id}"/>
</f:metadata>
</f:view>
<h:body>
<h:form>
<h:panelGroup rendered="#{not empty elementController.selected}">
<h:outputText value="#{elementController.selected.name}"/>
</h:panelGroup>
<h:commandButton value="Apply" action="#{elementController.action}" />
</h:form>
</h:body>
(The sense of the form submission is lost above, but it does not matter for the this question.)
ElementController extends RequestController:
public void setId(Long id) {
log_debug("setId","id",id);
if (id != null) {
this.id = id;
T found = (T) getAbstractFacade().find(id);
if (found == null) {
String $error = "No object with id(" + id + ") found for class " + getManagedClass().getSimpleName();
log_error($error);
}
setCurrent(found);
}
}
public T getSelected() {
log_debug("getSelected","current",current);
if (current == null) {
log_warn("getSelected","null current Element");
}
return current;
}
public Object action() {
String $i = "action";
log_debug($i);
if (current==null) {
log_warn($i, "can't generate action outcome for null current element");
return null;
}
return "/view?faces-redirect=true&id="+current.getId();
}
Now on form submission, getSelected() happens to get called twice, and when current==null, once during the apply request values phases and once during the process validations phase, due to the test #{not empty elementController.selected} before the setting of the id (and thus loading of the Element entity) can occur thanks to the viewParam in the view.xhtml.
The question is, why is the rendered=#{not empty elementController.selected} invoked at all during the apply request phase and process validations phase ?
It is not invoked during those phases when I perform an initial GET load of the view.xhtml with id parameter, only during a form submission POST and subsequent redirect and GET.
The reason that the rendered attribute is consulted twice or more after a post back is because JSF traverses the component tree in each phase.
The name 'rendered' is perhaps not the best possible name, as it doesn't just make rendering of the component to which it applies conditional, but actually processing it in general.
It's consulted in the first place for 'apply request values' to see if that component and its children should be processed to have those request values applied to them. It's consulted again in 'process validations', since its value might have changed between phases.
It's not invoked 'during those phases when I perform an initial GET load', because when you perform a GET the component tree isn't traversed in those phases (only the metadata is processed, which is the reason view params are put in a special metadata section).
In order to make the id that you received from the GET request available in the action method after the post back, you'd best use the view scope (#ViewScoped) for your backing bean.

Synchronizing session scoped managed bean

I have 2 screens in my JSF 2.0 application. Screen 1 searches for students and lists students in a table. The student name in the results table is a link to the student details page (screen 2).
My managed bean:
#SessionScoped
public class TestController {
private Student studentOnUI; // Student being viewed on the UI
// synchronized public getters and setters
public String viewStudentAction(String studentId) {
this.studentOnUI = getStudentFromDB( studentId );
return "studentDetailedPage";
}
public synchronized String clearSearchAction() {
this.studentOnUI = null;
return "studentSearchPage";
}
screen 1 xhtml snippet
<!-- search fields -->
<h:commandButton
value="Clear Search"
action="#{testController.clearSearchAction()}"/>
<!-- Search button -->
<!-- search results table -->
Screen 2 xhtml snippet
<h:outputText value="#{testController.studentOnUI.name}" />
<h:dataTable
value="#{testController.studentOnUI.subjects}"
var="subject">
<h:outputText value="#{subject.score}"/>
</h:dataTable>
I face the following issue:
After running search, user clicks on
a student name (to move into screen
2)
When the render response phase of
screen 2 is in progress (which has
EL references to
testController.studentOnUI), He
clicks on clear button (without waiting for the first request to complete). Now the
thread handling the clear request
sets testController.studentOnUI to null and
the first thread that is in render
response phase throws
NullPointerException when it
evaluates
{testController.studentOnUI.*}.
Although the managed bean handles synchronization (correctly?) it does not solve the concurrency issue here because, the following could happen
Thread 1 (handling request to navigate to screen 2) - evaluates #{testController.studentOnUI.name} and happily renders the value. And then exits the testController.getStudentOnUI() synchronized method. So thread 1 no longer has the lock on the controller instance (in session scope). context switch happens.
Thread 2 (handling request to clear
search results) - does
testController.studentOnUI = null in
clearSearchAction(). context switch
happens.
Thread 1 - evaluates the next EL in the page (#{testController.studentOnUI.subjects}) and throws a NullPointerException (as testController.studentOnUI is now null).
Appreciate any pointers on what I am doing wrong with this approach or if a different approach needs to be adopted here.
this is really strange behaviour but then i would rather disable the link on the first page with JavaScript to make it "unclickable".
try with jQuery:
1 button: $('#button').attr("disabled", true);
2 link: $('a.something').click(function(e) {
e.preventDefault();
});
I'm not sure about the synchronization in the backing bean since the JSF Controller has to put the lock on your Student object, not the clear method. Am I wrong?

Resources