StackOverflowError в Seam/Spring Приложение WebFlow
Мы постепенно заменяем компоненты шва Spring -MVC и Spring -Webflow.
Запуск JMeter-тестов: журналы регистрируются через StackOverFlowErrors через пару часов:
javax.servlet.ServletException: Servlet execution threw an exception
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:341)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:530)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
...
Caused by: java.lang.StackOverflowError
at org.jboss.seam.jsf.SeamApplication.getMessageBundle(SeamApplication.java:264)
at org.springframework.faces.webflow.FlowApplication.getMessageBundle(FlowApplication.java:214)
at org.jboss.seam.jsf.SeamApplication.getMessageBundle(SeamApplication.java:264)
at org.springframework.faces.webflow.FlowApplication.getMessageBundle(FlowApplication.java:214)
at org.jboss.seam.jsf.SeamApplication.getMessageBundle(SeamApplication.java:264)
at org.springframework.faces.webflow.FlowApplication.getMessageBundle(FlowApplication.java:214)
at org.jboss.seam.jsf.SeamApplication.getMessageBundle(SeamApplication.java:264)
Таким образом метод getMessageBundle вызывается двумя экземплярами: SeamApplication и FlowApplication.
Глядя на класс javax.faces.application.Application, он говорит:
"Поскольку этот экземпляр является общим, он должен быть реализован поточно-безопасным образом".
Возможно, два экземпляра приложения пытаются получить доступ к тому же пакету, который вызывает условия гонки?
EDIT:
После того, как приложение больше не отвечало, мы перезапустили сервер, и теперь ошибка появляется в другом месте:
Caused by: java.lang.StackOverflowError
at org.jboss.seam.contexts.BasicContext.get(BasicContext.java:49)
at org.jboss.seam.contexts.BasicContext.get(BasicContext.java:44)
at org.jboss.seam.core.Init.instance(Init.java:117)
at org.jboss.seam.jsf.SeamApplication$ConverterLocator.<init>(SeamApplication.java:140)
at org.jboss.seam.jsf.SeamApplication.createConverter(SeamApplication.java:122)
at org.springframework.faces.webflow.FlowApplication.createConverter(FlowApplication.java:161)
at org.jboss.seam.jsf.SeamApplication.createConverter(SeamApplication.java:126)
at org.springframework.faces.webflow.FlowApplication.createConverter(FlowApplication.java:161)
at org.jboss.seam.jsf.SeamApplication.createConverter(SeamApplication.java:126)
Последние 2 строки повторяются тысячи раз в файле журнала.
Мы работаем со следующими составными версиями:
JSF-1.2
Шов-2.2.0
Spring WebFlow 2.3.4
Spring MVC 3.0.5
Обновление любого из компонентов не является вариантом.
Ответы
Ответ 1
Оба SeamApplication
и FlowApplication
имеют ошибки в деле делегирования завернутого приложения. Один из способов исправить это через FlowApplicationFactory
.
Сначала возьмите его исходный исходный код и отбросьте его в исходную папку Java вашего проекта webapp, сохранив исходный пакет. Вам не обязательно управлять JAR. Классы в /WEB-INF/classes
имеют более высокий приоритет при загрузке по сравнению с классами JAR.
Затем манипулируйте классом следующим образом (исходя из OmniFaces OmniApplicationFactory
):
public class FlowApplicationFactory extends ApplicationFactory {
private final ApplicationFactory wrapped;
private volatile Application application;
public FlowApplicationFactory(ApplicationFactory wrapped) {
this.wrapped = wrapped;
}
@Override
public Application getApplication() {
return (application == null) ? createFlowApplication(wrapped.getApplication()) : application;
}
@Override
public synchronized void setApplication(Application application) {
wrapped.setApplication(createFlowApplication(application));
}
private Application createFlowApplication(final Application application) {
Application newApplication = application;
while (!(newApplication instanceof FlowApplication) && newApplication instanceof SeamApplication) {
newApplication = ((SeamApplication) application).getDelegate();
}
if (!(newApplication instanceof FlowApplication)) {
newApplication = new FlowApplication(application);
}
return (this.application = newApplication);
}
}
Таким образом, при создании FlowApplication
он сначала проверяет завернутые приложения, если он еще не создан раньше, и если да, то используйте его повторно.
Обратите внимание, что зависимость SeamApplication
неудобна, но это просто для ее исправления. JSF2 упростил новый класс ApplicationWrapper
, который вы могли бы использовать вместо SeamApplication
в блоке createFlowApplication()
.
Если это все еще не работает, возможно, SeamApplicationFactory
инициализируется после FlowApplicationFactory
. Вы можете заставить упорядочить, явно переопределяя записи <application-factory>
в собственном веб-приложении faces-config.xml
в нужном порядке (последний с ошибкой):
<factory>
<application-factory>org.jboss.seam.jsf.SeamApplicationFactory</application-factory>
<application-factory>org.springframework.faces.webflow.FlowApplicationFactory</application-factory>
</factory>
В противном случае вы можете сделать то же самое, что и выше, для SeamApplicationFactory
(очевидно, с FlowApplication
и SeamApplication
заменены в код).