Нет длительных разговоров - IllegalArgumentException: стек не должен быть нулевым

У меня очень простое приложение с двумя страницами на WebLogic 10.3.2 (11g), Seam 2.2.0.GA. У меня есть командная кнопка в каждом, которая делает перенаправление после поста для другого. Это хорошо работает, так как я вижу URL текущей страницы, которую вижу в адресной строке.

НО, даже если у меня не определены длительные разговоры после случайного количества нажатий и, я думаю, через случайное количество секунд (~10 с - 60 с), я получаю прекрасное исключение в конце этого поста.

Теперь, если я понял, как временные разговоры работают при перенаправлении, это происходит:

  1. Когда я впервые вижу свое приложение, URL-адрес является http://localhost:7001/myapp
  2. Когда я нажимаю кнопку в pageA.xhtml, я оказываюсь в "pageB.xhtml? Cid=26". Это нормально, потому что Seam продлевает временную беседу первого запроса до фазы renderResponse перенаправления. Таким образом, он использует cid (Conversation Id) расширенного временного диалога, чтобы найти любые распространяемые параметры.

  3. Когда я нажимаю кнопку в pageB.xhtml, я оказываюсь в pageA.xhtml? Cid = 26

Тот же самый диск был передан новому расширенному временному разговору. Это нормально, потому что разговор закончился в конце предыдущего редиректа после поста, а номер 26 не может свободно использоваться в качестве cid.

Это все правильно? Если да, то почему это происходит: если я повторно наберу домашний адрес приложения (показываю страницу A) и повторно нажму, я получу страницу pageB.xhtml? Cid=29, которая отличается от числа 26. Но 26 закончилось после предыдущей фазы RenderResponse перед повторным вводом URL-адреса. Почему он не используется вместо 29?

Итак, в заключение, 2 вопроса:

  1. Почему я получаю исключение, несмотря на то, что я не начал никаких длительных разговоров?
  2. Что именно происходит с CID? На каком основании это меняется?

Ура,

ОБНОВИТЬ:

Дополнительная информация: я использую h:commandButtons как это на странице A:

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

и на странице B

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

страница навигации A.page.xml:

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

и очень похоже на pageB.

Что касается времени ожидания разговора, я установил его на 1 час. Обратите внимание, что это не имеет значения, потому что, как я читаю здесь, оно предназначено только для фоновых разговоров. Трассировка стека следующая:

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 ответа

После просмотра вашего 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;
  }

После кода, показанного выше, ваш стек будет создан. Поэтому я полагаю, что идентификатор разговора не изменился из-за продолжительности перенаправления браузера (вызванного случайным количеством нажатий) или даже ошибки шва при работе с навигацией с перенаправлением с одной страницы на другую и наоборот

Попробуйте следующее для каждого правила страницы (см. Timeout="0")

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

Я ожидаю, что теперь это работает отлично! Но если нет, то теперь вы знаете, почему вы получаете свое исключение

ОБНОВИТЬ

Попробуйте как обходной путь (для каждой страницы)

<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

Временно переведите временную беседу в длительную беседу на время переадресации браузера. После перенаправления разговор будет переведен обратно во временный разговор.

1 ° Это объясняет, почему #{разговор.longRunning} выводит true, когда вы переходите на страницу B. Ваш "длительный разговор", вызванный вашим перенаправлением, должен быть уничтожен после фазы ответа ответа.

При использовании перенаправления Seam добавляет к URL URL-адрес paratemer.

Книга "Шов в действии" гласит

В начале жизненного цикла Seam Seam ищет идентификатор разговора в параметре URL.

Но поскольку, вернувшись на страницу A, вы снова видите тот же параметр идентификатора разговора, я полагаю, что Seam просто создает новый, когда в URL-адресе никого нет. И поскольку каждый длительный разговор имеет свой собственный период ожидания, ваш продолжительный разговор сохраняется.

Чтобы проверить, верно ли то, что я сказал, сделайте следующее

  • уменьшить глобальный период ожидания до пяти секунд (5000 миллисекунд)

...

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

Для каждой страницы посмотрите, что выводит #{разговор.timeout}. Я ожидаю увидеть что-то вроде 5 секунд или 5000 миллисекунд. Подождите более 5 секунд (около 10 секунд) и нажмите кнопку для повторного перенаправления. И посмотрите, был ли изменен параметр идентификатора разговора.

Прежде всего, всегда полезно видеть соответствующий код и трассировку стека при попытке отладки проблемы.

Поэтому я не могу дать ответ на ваш первый вопрос. Однако я попытаюсь объяснить, как работает модель разговора.

Это из книги "Шов в действии":

@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, потому что компонент с областью диалога может выполнять только один запрос.

Вы должны были предоставить эту информацию давно. Теперь стало намного понятнее, в чем проблема.

Прежде всего, вы не должны использовать 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";
}

Во всяком случае, вернемся к вашей проблеме. Сделайте себе одолжение и создайте шовный компонент.

@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"/>
Другие вопросы по тегам