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 заменены в код).