3
votes

We are gradually replacing Seam components with Spring-MVC and Spring-Webflow.

Running JMeter-tests the logs get cluttered with StackOverFlowErrors after a couple of hours:

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)

So the getMessageBundle method is called by two instances: SeamApplication and FlowApplication.

Looking at the javax.faces.application.Application class it says:

"Because this instance is shared, it must be implemented in a thread-safe manner."

Maybe the two app instances are trying to access the same bundle which causes race conditions?

EDIT: After the app did not respond anymore we restarted the server and now the error shows up in another place:

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)

The last 2 lines are repeated thousands of times in the log file.

We are working with the following component-versions:

JSF-1.2

Seam-2.2.0

Spring WebFlow 2.3.4

Spring MVC 3.0.5

Updating any of the components is not an option.

1
I inspected the source code of SeamApplication and FlowApplication. Both have bugs as to properly delegating to the wrapped application of which those in Spring WebFlow is been fixed in version 2.4.0. Before posting an answer, can you please try upgrading to WebFlow 2.4.0 and let me know if that indeed solved the problem? SeamApplication still exposes the bug in the current 2.3.1 version, but that could be solved by changing the loading order in faces-config.xml.BalusC
Thanks for the update! Unfortunately we cannot upgrade to webflow 2.4 as it causes some other problems. We will try to change the loading order, but then looking at our faces-config there is none. Could you elaborate on what order you mean?recalcitrant
Sorry, at least one of those Application implementations has really to be fixed. Otherwise, you need to hack in the source code or at least report as a bug to the responsibles and wait for an update. As to ordering, in JSF 1.x, you can force reordering by redeclaring the <application-factory> of those libraries in right order in webapp's own faces-config.xml. In JSF 2.x, you can force reordering by new <absolute-ordering> element. In both cases, the bugfixed one must appear as the last one, so that it wraps the broken one instead of other way round.BalusC
Alternatively, just elaborate "some other problems" in detail so that you can just fix them as well so that you'd ultimately be able to use the bugfixed version. By the way, I hope that you're very well aware that Spring MVC and JSF are full competitors and not intented to be mixed in 1 page/request. You can use Spring DI or Spring WF together with JSF (as "alternative" to Java EE's own @Named/@EJB resp. @FlowScoped), but definitely not Spring MVC.BalusC
If we update to webflow 2.4 an AbstractMethodError is thrown: java.lang.AbstractMethodError at org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder.createViewFactory(FlowModelFlowBuilder.java:639)recalcitrant

1 Answers

1
votes

Both the SeamApplication and FlowApplication have bugs as to properly delegating to the wrapped application. One way to fix it is via the FlowApplicationFactory.

First grab its raw source code and drop it in the Java source folder of your webapp project, maintaining its original package. You don't necessarily need to manipulate JARs. Classes in /WEB-INF/classes have higher classloading precedence over those in JARs.

Then manipulate the class as follows (based off from 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);
    }

}

Thus, when creating FlowApplication, it will first inspect the wrapped applications if it isn't already created before and if so, then reuse it instead.

Note that the SeamApplication dependency is awkward, but that's just to bugfix it. JSF2 has made it easier by new ApplicationWrapper class which you could use instead of SeamApplication in the createFlowApplication() block.

If this all still doesn't work, then perhaps the SeamApplicationFactory is initialized after the FlowApplicationFactory. You could force the ordering by explicitly redeclaring the <application-factory> entries in webapp's own faces-config.xml in the desired order (the bugfixed one as last one):

<factory>
    <application-factory>org.jboss.seam.jsf.SeamApplicationFactory</application-factory>
    <application-factory>org.springframework.faces.webflow.FlowApplicationFactory</application-factory>
</factory>

Otherwise, you might want to do the same as above for SeamApplicationFactory (obviously with FlowApplication and SeamApplication swapped in the code).