没有长时间运行的对话 - IllegalArgumentException:Stack不能为null

时间:2010-04-13 13:13:23

标签: java redirect seam seam-conversation redirect-after-post

我有一个非常简单的应用程序,在WebLogic 10.3.2(11g),Seam 2.2.0.GA上只有2页。我在每个中都有一个命令按钮,它会在另一个后面进行重定向。这很有效,因为我在地址栏中看到了当前页面的URL。

但是,即使我已经定义了没有长时间运行的对话,经过随机的点击次数,并且 - 我认为 - 在一个随机的秒数之后(〜 10s - 60s)我在这篇文章的最后得到了一个可爱的例外。

现在,如果我已经理解了重定向时临时对话的工作原理:

  1. 当我第一次看到我的应用程序时,网址为http://localhost:7001/myapp
  2. 当我点击pageA.xhtml中的按钮时,我最终进入“pageB.xhtml?cid = 26”。这是正常的,因为Seam会将第一个请求的临时会话延长到重定向的renderResponse阶段。因此,它使用扩展临时对话的cid(对话ID)来查找任何传播的参数。

  3. 当我点击pageB.xhtml中的按钮时,我最终进入pageA.xhtml?cid = 26

  4. 同样的cid被赋予新的扩展临时对话。这是正常的,因为会话在之前的重定向后结束时结束,而不是数字26可以作为cid使用。

    这一切都正确吗?如果是,为什么会发生这种情况:如果我重新键入应用程序主页地址(显示pageA)并重新点击,我最终会在pageB.xhtml?cid = 29,这是一个不同于26的数字。但是26已经结束在之前的RenderResponse阶段之后,我会重新输入网址。为什么不使用而不是29?

    所以,要补充,2个问题:

    1. 为什么我得到例外,即使我还没有开始任何长时间的对话?
    2. cid究竟发生了什么?它会以什么为基础改变?
    3. 干杯,

      更新

      附加信息:我在第A页中使用h:commandButtons:

      <h:commandButton action="showPageB" value="Show page B" />
      

      和第B页

      <h:commandButton action="showPageA" value="Show page A" />
      

      导航pageA.page.xml:

      <page view-id="/pageA.xhtml">
      <navigation>
          <rule if-outcome="showPageB">
              <redirect view-id="/pageB.xhtml" />
          </rule>
      </navigation>
      </page>
      

      和pageB非常相似。

      对于会话超时,我已将其设置为1h。请注意,这是无关紧要的,因为在我阅读here时,它仅用于后台对话。堆栈跟踪如下:

      Error 500--Internal Server Error
      
          java.lang.IllegalArgumentException: Stack must not be null
          at org.jboss.seam.core.ConversationEntry.(ConversationEntry.java:45)
          at org.jboss.seam.core.ConversationEntries.createConversationEntry(ConversationEntries.java:53)
          at org.jboss.seam.core.Manager.createConversationEntry(Manager.java:664)
          at org.jboss.seam.core.Manager.beforeRedirect(Manager.java:836)
          at org.jboss.seam.faces.FacesManager.beforeRedirect(FacesManager.java:66)
          at org.jboss.seam.faces.FacesManager.redirect(FacesManager.java:182)
          at org.jboss.seam.faces.Navigator.redirect(Navigator.java:55)
          at org.jboss.seam.navigation.RedirectNavigationHandler.navigate(RedirectNavigationHandler.java:61)
          at org.jboss.seam.navigation.Rule.execute(Rule.java:101)
          at org.jboss.seam.navigation.Navigation.navigate(Navigation.java:58)
          at org.jboss.seam.navigation.Pages.navigate(Pages.java:203)
          at org.jboss.seam.jsf.SeamNavigationHandler.handleNavigation(SeamNavigationHandler.java:42)
          at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:130)
          at javax.faces.component.UICommand.broadcast(UICommand.java:387)
          at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:324)
          at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:299)
          at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:256)
          at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:469)
          at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82)
          at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
          at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
          at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
          at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
          at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
          at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
          at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
          at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
          at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:530)
          at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
          at org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:178)
          at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
          at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:388)
          at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:515)
          at org.jboss.seam.web.Ajax4jsfFilter.doFilter(Ajax4jsfFilter.java:56)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53)
          at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
          at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
          at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
          at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
          at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
          at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3592)
          at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
          at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)
          at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2202)
          at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2108)
          at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1432)
          at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
          at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
      

3 个答案:

答案 0 :(得分:1)

首先,在尝试调试问题时查看相关代码和堆栈跟踪总是有帮助的。

因此,我不能回答你的第一个问题。但是,我将尝试解释对话模型的工作原理。

这是来自Seam in Action的书:

@ScopeType.EVENT = Goes from Restore View to Render Response, but not redirect @ScopeType.CONVERSATION = Goes from Restore VIew to Render Response, and redirect. If long-running conversation, then it spans multiple JSF life cycles.

因此,假设您在a.xhtml按下了一个按钮,该按钮将ComponentA并填充其中的一些数据。要在b.xhtml中注入和使用的此组件 即:

Push commandbutton in a.xhtml performing post, putting some data in ComponentA 现在,您重定向到使用b.xhtml

的下一页(ComponentA
@Name("componentB")
@Scope(ScopeType.CONVERSATION)
public class ComponentB {
    @In(create=true)
    ComponentA componentA; //OK
}

因此,如果您现在从b.xhtml推送另一个按钮,希望能够再次注入ComponentA,那么将会失败。 即:

@Name("componentC")
@Scope(ScopeType.CONVERSATION)
public class ComponentC {
    @In(create=true)
    ComponentA componentA; //Injection of the component you really want fails (you will get default component)
}

所以现在在后台,seam为你创建了一个新的cid,结束了之前的cid,因为会话范围的组件只能生成一个请求。

答案 1 :(得分:1)

在看到您的StackTrace和您的用例(随机点击次数后)

让我们看看FacesManages.beforeRedirect(如StackTrace所示)文档

  

在浏览器重定向期间暂时将对话提升为长时间对话

现在,让我们看一下beforeRedirect方法的一些代码

if (isDifferentConversationId(currentPage, targetPage))
    updateCurrentConversationId(targetPage.getConversationId());

...

updateCurrentConversationId负责创建堆栈,它必须不为空再次查看StackTrace

public void updateCurrentConversationId(String id) {
    if (id != null && id.equals(currentConversationId)) {
     // The conversation id has not changed, do nothing       
     return;
  }

在上面显示的代码之后,您的堆栈将被创建。所以我认为会话ID没有改变因为浏览器重定向的持续时间(由随机点击次数引起)或甚至是处理导航时的Seam错误从一个页面重定向到另一个页面,反之亦然

针对每个页面规则尝试以下(请参阅timeout =“0”)

<page view-id="/pageA.xhtml" timeout="0">
    <navigation>
        <rule if-outcome="showPageB">
            <redirect view-id="/pageB.xhtml" />
        </rule>
    </navigation>
</page>

我希望现在它运作正常!但是,如果没有,现在,你知道为什么你会得到你的例外

<强>更新

尝试&lt;结束对话/&gt;作为解决方法(针对每个页面)

<page view-id="/pageA.xhtml">
    <navigation>
        <rule if-outcome="showPageB">
            <end-conversation/>
            <redirect view-id="/pageB.xhtml" />
        </rule>
    </navigation>
</page>

或(参见之前的重定向)

<page view-id="/pageA.xhtml">
    <navigation>
        <rule if-outcome="showPageB">
            <end-conversation before-redirect="true"/>
            <redirect view-id="/pageB.xhtml" />
        </rule>
    </navigation>
</page>

现在我希望它运作正常!

修改

正如beforeRedirect方法所说的

  

暂时在浏览器重定向期间提升与长时间对话的临时对话。重定向后,会话将被降级为临时会话。

它解释了为什么#{conversation.longRunning}在转到pageB时输出为true。在渲染响应阶段之后,应该销毁由重定向引起的“长时间运行的对话”。

使用重定向时,Seam会将会话ID paratemer附加到URL。

Seam in Action书中说

  

在Seam生命周期开始时,Seam在网址参数

中查找会话ID

但是因为当你回到pageA时,你再次看到相同的会话ID参数,我想 Seam只是在网址不包含任何人时创建一个 。而且因为每个长时间运行的对话都有自己的超时时间,所以长时间运行的对话会保持活跃。

要验证我说的是否属实,请执行以下操作

  • 将全局超时时间缩短为五秒(5000毫秒)

...

<core:manager conversation-timeout="5000"/>

对于每个页面,请查看#{conversation.timeout}输出内容。我希望看到像5秒或5000毫秒的东西。等待超过5秒(约10秒),然后按按钮再次重定向。并查看对话ID参数是否已更改。

答案 2 :(得分:0)

很久以前你应该提供这些信息。现在问题是什么更清楚了。

首先,您不应该使用具有类似空操作的commandButton。当您在pages.xml中写下以下内容时:

<page view-id="/pageA.xhtml">
<navigation>
    <rule if-outcome="showPageB">
        <redirect view-id="/pageB.xhtml" />
    </rule>
</navigation>
</page>

通常意味着您有一些返回showPageB的操作:

public String someAction() {
    //Do something complex
    return "showPageB";
}

无论如何,回到你的问题。 帮自己一个忙,创建一个Seam组件。

@Name("myComponent")
public Class MyComponent {

public String showPageB() {
    return "showPageB";
}

public String showPageA() {
    return "showPageA";
}

}

将pages.xml更改为:

<page view-id="/pageA.xhtml">
<navigation from action="#{myComponent.showPageB}">
    <redirect view-id="/pageB.xhtml" />
</navigation>

<navigation from action="#{myComponent.showPageA}">
    <redirect view-id="/pageA.xhtml" />
</navigation>

<!-- OR you can do like this -->
<navigation from action="#{myComponent.showPageB}">
    <rule if-outcome="showPageA">
        <redirect view-id="/pageA.xhtml" />
    </rule>
    <rule if-outcome="showPageB">
        <redirect view-id="/pageA.xhtml" />
    </rule>
</navigation>
</page>

然后将xhtml h:commandButton更改为

<h:commandButton action="#{myComponent.showPageA}" value="showA"/>
<h:commandButton action="#{myComponent.showPageB}" value="showB"/>