我花了一些时间阅读BalusC关于这个问题的答案,但我似乎无法将这些知识应用于我的情况。我还得到ViewExpiredException,但仅在特定情况下。
我的页面由@ViewScopedbean支持,并使用两个p:layoutUnits在中心窗格中显示一个包含文件夹内容的p:tree(west(。在树中选择文件夹会触发AJAX事件,用所选文件夹的内容更新中心窗格。JSF的框架如下:
<p:layout id="workLayout">
<p:layoutUnit id="paneTree"
position="west"
size="200">
<h:form id="treeForm">
<p:tree value="#{workspace.listTree}"
var="node"
dynamic="true"
selectionMode="single"
>
<p:ajax event="select"
listener="#{workspace.onNodeSelect}"
update="panelData"
/>
<p:treeNode>
<h:outputText value="#{node.name}" />
</p:treeNode>
</p:tree>
</h:form>
</p:layoutUnit>
<p:layoutUnit id="paneData"
position="center"
resizable="true">
<h:panelGroup id="panelData" >
<h:form id="formData" rendered="#{workspace.selected ne null}">
<h2>#{workspace.selected.name}: #{workspace.selected.title}</h2>
<p>
<div id="divImages"
style="width: 49%; height: 99%; float: right">
<p:panel rendered="#{empty workspace.listImages}">
<p>No Images In This List.</p>
</p:panel>
<ui:repeat value="#{workspace.listImages}"
var="img"
>
<p>
#{img.name}: #{img.title}
</p>
<img src="#{workspace.getUrl(img)}"
style="max-width: 250px"
/>
<hr/>
</ui:repeat>
</div>
<div id="divDocs"
style="width: 49%; height: 99%; float: left">
<p:dataTable id="tableFiles"
value="#{workspace.listDocuments}"
>
<p:column ...
<p:column ...
<p:column ...
</p:dataTable>
</div>
</p>
</h:form>
</h:panelGroup>
</p:layoutUnit>
当你选择的文件夹中有几个图像时,问题就开始了。只要10个或更少的图像就足以造成问题。这导致ui:repeat元素呈现img标记的数量,每个标记都有一个加载图像的URL。
URL触发应用程序中相同CDI域的Servlet,这样它就可以像托管bean一样注入相同的资源。Servlet只使用一个包含MIME类型和数据的响应标头进行响应。然而,每次img访问都会创建一个新的@SessionScoped bean——与这个JSF页面使用的相同。它没有获得与JSF页面相同的实例,并且各种img访问也没有在它们之间共享会话bean。每次访问都会生成一个新的访问。
Servlet看起来像这样:
@WebServlet(name = "Obj", urlPatterns = {"/obj/*"})
public class Obj extends HttpServlet {
@Inject ProtoObjectControl poc;
@Inject UserSession us;
UserSession是@SessionScoped对象,该对象包含登录信息(由于它总是生成新信息,因此未登录(和各种实用程序(如日志函数(。ProtoObjectControl是一个DAO对象,用于获取要显示的图像。
最终的结果是,进行显示的页面最终以ViewExpiredException崩溃。我认为这是因为会话的爆炸式增长消耗了越来越多的资源。如果我去掉img标记并使用该标记运行页面,则ViewExpiredException永远不会出现,并且该页面与Mac Finder或Windows文件资源管理器一样可靠。
那么,有什么方法可以解释为什么会发生这种情况,并围绕它进行设计吗?我不想对显示器上出现的图像数量有限制。理想情况下,我希望Servlet访问与JSF页面相同的UserSessionbean——毕竟是从同一个客户端访问的——但这可能是不可能的。还有其他方法吗?
根据BalusC上面的输入,我认为这个问题已经解决了。错误出现在我的代码中,如下所示:
public String getBaseUrl() {
if (baseURL != null) return baseURL;
FacesContext fc = FacesContext.getCurrentInstance();
if (fc == null) return "";
ExternalContext ec = fc.getExternalContext();
baseURL = String.format("http://%s:%d/%s/",
ec.getRequestServerName(),
ec.getRequestServerPort(),
ec.getRequestContextPath());
return baseURL;
}
使用此方法生成以下形式的URL:
http://localhost:8080//MyApp/obj?id=nnn
当然,问题是应用程序上下文路径之前的双斜杠。更正此错误将不会创建新的会话,因为容器将GET请求正确地关联到servlet作为同一会话的一部分。
尽管该程序现在有效,但有两个问题尚未解决:
在所有无关的会话中,每个会话都得到一个具有不同值的JSESSIONID cookie。但是对于Firebug,我找不到任何来自服务器的相应设置cookie。那么这些价值观是从哪里来的呢?
这种性质的JSF应用程序(使用Servlet为页面中的对象提供服务(似乎需要Servlet在与主应用程序相同的会话上下文中执行。无法解释的(至少对我来说(问题是,为什么Faclets页面崩溃只是因为src标记中的URL格式错误,并且生成了无关的会话?如果URL指向不同的服务器(例如图像存储服务(,我不会想到会这样。我还希望Glassfish能够毫无问题地处理数千个会话。
希望这篇文章有一天能把一些人从这个陷阱中拯救出来。