JSF рекурсивный интерфейс: включить выходное дерево беспорядка при множественной обратной передаче
У меня есть отчет, который отображает информацию о зачислении в колледж в расширяемом древовидном формате. При нажатии на префикс отображается префикс курса с итоговыми номерами зачисления и номерами зачисления на конкретный курс. Отчет работает нормально при первом запуске. Однако, если вы решите снова запустить отчет для другого сеанса, все начнет сбиваться с толку.
Когда запускается первый отчет, это правильно. Когда запускается второй отчет (для другого сеанса тот же сеанс работает нормально), обратите внимание, что второй содержит дублированные / отсутствующие элементы. Оба этих отчета будут работать правильно, если они выполняются "первыми", т. Е. В этом сеансе пользователей не было выполнено ни одного другого отчета.
Я использую рекурсивно включенную страницу для построения дерева. Вот соответствующий код из enrollment.xhtml, самой страницы отчета:
<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>
Включенный код reportTree.xhtml (древовидный код расширения, выполненный с помощью 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>
Соответствующие части поддерживающего компонента, 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); }
}
}
Созданный класс TreeNode обеспечивает простое отслеживание данных, родительских и дочерних классов. Я могу опубликовать этот код, если это необходимо, но я считаю, что в настоящее время он излишен.
Во время устранения этой проблемы я проверил, что данные отчета, из которых построено дерево отчетов в компоненте, всегда верны. Используя класс Logger, я убедился, что дерево генерируется правильно (записывая в журнал сервера каждую строку, которая была обработана в дереве). Я даже проверил правильность reportTreeData после каждого запуска, записав дерево в журнал сервера после того, как дерево было построено.
Я могу только заключить, что что-то идет не так на этапе ответа на визуализацию жизненного цикла JSF, так как я заметил, что если я изменю компонент поддержки с @SessionScoped
в @RequestScoped
, отчет генерируется правильно каждый раз. Я бы предпочел не использовать это в качестве исправления, так как у меня есть ссылка "Загрузить CSV", которая использует уже сгенерированные данные отчета в компоненте поддержки, поэтому логику отчета не нужно повторно запускать для генерации CSV.
Кто-нибудь знает, почему это происходит и что я могу сделать, чтобы исправить это поведение? Я использую JSF 2.2 с Java EE 7 в GlassFish Open Source Edition 4.1 (сборка 13)
ОБНОВЛЕНИЕ 24/24/15
Я проходил через код JSF фазы ответа рендеринга, и кажется, что выражение EL просто оценивается с неверными данными во втором прогоне отчета. Я вижу, где выполняется вызов функции для оценки выражения EL, и он возвращается с неверными данными. Я постараюсь получить исходный код weld-osgi-bundle.jar, чтобы позже перейти к этому вызову функции.
1 ответ
Основываясь на большом количестве отладок и исследований, но, в частности, на этом ответе, я предполагаю, что моя проблема как-то связана с состоянием представления, пытающимся быть восстановленным, и рекурсия, которую я использую, затрудняет в структуре JSF правильное обновление компонента. модель дерева.