大学のコース登録情報を展開可能なツリー形式で表示するレポートがあります。プレフィックスをクリックすると、概要登録番号と特定のコース登録番号を含むコースプレフィックスが表示されます。レポートは、最初の実行時には正常に機能します。ただし、別のセッションでレポートを再度実行することにした場合、状況はごちゃごちゃになります。
Report Oneを実行すると、正しいです。レポート 2が実行されると (別のセッションの場合、同じセッションの実行は正常に機能します)、2 番目の要素が重複または欠落していることに注意してください。これらのレポートはどちらも、「最初に」実行された場合、つまり、そのユーザー セッションで他のレポートが実行されていない場合に正しく機能します。
再帰的に含まれるページを使用してツリーを構築しています。これは、レポート ページ自体である registration.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>
バッキング 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); }
}
}
TreeNode クラスは私が作成したクラスで、データ、親、および子の単純な追跡を提供します。必要に応じてそのコードを投稿できますが、現時点では不要だと思います。
この問題のトラブルシューティング中に、バッキング Bean でレポート ツリーが構築されている reportData が常に正しいことを確認しました。Logger クラスを使用して、ツリーが正しく生成されていることを確認しました (ツリーに処理された各行をサーバー ログに書き込むことによって)。ツリーが構築された後にサーバー ログにツリーを書き出すことで、毎回の実行後に reportTreeData が正しいことも確認しました。
@SessionScoped
バッキング Bean を からに変更する@RequestScoped
と、レポートが毎回正しく生成されることに気付いたので、JSF ライフサイクルの Render Response フェーズで何か問題が発生しているとしか言えません。バッキング Bean で既に生成されたレポート データを使用する「CSV のダウンロード」リンクがあるため、CSV を生成するためにレポート ロジックを再実行する必要がないため、これを修正として使用したくありません。
なぜこれが起こっているのか、この動作を修正するために私にできることを誰かが知っていますか? GlassFish Open Source Edition 4.1 (ビルド 13) で JSF 2.2 と Java EE 7 を使用しています。
2015 年 12 月 24 日更新
レンダー レスポンス フェーズの JSF コードを調べてみましたが、2 回目のレポート実行で間違ったデータを使用して EL 式が評価されているようです。EL式を評価するために関数呼び出しを行っている場所がわかり、間違ったデータが返されています。後でその関数呼び出しにステップインできるように、weld-osgi-bundle.jar ソースを取得しようとします。