Servlet生成的会话触发ViewExpiredException



我花了一些时间阅读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作为同一会话的一部分。

尽管该程序现在有效,但有两个问题尚未解决:

  1. 在所有无关的会话中,每个会话都得到一个具有不同值的JSESSIONID cookie。但是对于Firebug,我找不到任何来自服务器的相应设置cookie。那么这些价值观是从哪里来的呢?

  2. 这种性质的JSF应用程序(使用Servlet为页面中的对象提供服务(似乎需要Servlet在与主应用程序相同的会话上下文中执行。无法解释的(至少对我来说(问题是,为什么Faclets页面崩溃只是因为src标记中的URL格式错误,并且生成了无关的会话?如果URL指向不同的服务器(例如图像存储服务(,我不会想到会这样。我还希望Glassfish能够毫无问题地处理数千个会话。

希望这篇文章有一天能把一些人从这个陷阱中拯救出来。

相关内容

  • 没有找到相关文章

最新更新