Нет длительных разговоров - IllegalArgumentException: стек не должен быть нулевым
У меня очень простое приложение с двумя страницами на WebLogic 10.3.2 (11g), Seam 2.2.0.GA. У меня есть командная кнопка в каждом, которая делает перенаправление после поста для другого. Это хорошо работает, так как я вижу URL текущей страницы, которую вижу в адресной строке.
НО, даже если у меня не определены длительные разговоры после случайного количества нажатий и, я думаю, через случайное количество секунд (~10 с - 60 с), я получаю прекрасное исключение в конце этого поста.
Теперь, если я понял, как временные разговоры работают при перенаправлении, это происходит:
- Когда я впервые вижу свое приложение, URL-адрес является http://localhost:7001/myapp
Когда я нажимаю кнопку в pageA.xhtml, я оказываюсь в "pageB.xhtml? Cid=26". Это нормально, потому что Seam продлевает временную беседу первого запроса до фазы renderResponse перенаправления. Таким образом, он использует cid (Conversation Id) расширенного временного диалога, чтобы найти любые распространяемые параметры.
Когда я нажимаю кнопку в pageB.xhtml, я оказываюсь в pageA.xhtml? Cid = 26
Тот же самый диск был передан новому расширенному временному разговору. Это нормально, потому что разговор закончился в конце предыдущего редиректа после поста, а номер 26 не может свободно использоваться в качестве cid.
Это все правильно? Если да, то почему это происходит: если я повторно наберу домашний адрес приложения (показываю страницу A) и повторно нажму, я получу страницу pageB.xhtml? Cid=29, которая отличается от числа 26. Но 26 закончилось после предыдущей фазы RenderResponse перед повторным вводом URL-адреса. Почему он не используется вместо 29?
Итак, в заключение, 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"/>