3

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.

recalcitrant
  • 939
  • 11
  • 23
  • 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 Aug 18 '14 at 10:20
  • 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 Aug 18 '14 at 11:10
  • 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 `` of those libraries in right order in webapp's own `faces-config.xml`. In JSF 2.x, you can force reordering by new `` 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 Aug 18 '14 at 11:26
  • 1
    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 Aug 18 '14 at 11:33
  • 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 Aug 18 '14 at 13:24
  • That's strange as it doesn't implement an abstract method according to the source code. It's however in turn calling some abstract methods. What's the bottommost `Caused by` on this exception? – BalusC Aug 18 '14 at 13:50
  • Caused by: java.lang.AbstractMethodError: org.springframework.faces.webflow.JsfViewFactoryCreator.createViewFactory – recalcitrant Aug 18 '14 at 14:00
  • That method has indeed changed between SWF 2.3.4 and 2.4.0. This exception suggests that you've still SWF 2.3.4 (or even older!) libraries wandering elsewhere in the runtime classpath. As first step, explore the `/WEB-INF/lib` folder of the built WAR file to verify one and other. – BalusC Aug 18 '14 at 14:26
  • Thanks, we found spring-faces-2.3.3 and replaced it with the 2.4.0 version but now the server won't start as it throws: Exception sending context initialized event to listener instance of class com.sun.faces.config.ConfigureListener com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED! cvc-enumeration-valid: Value '2.0' is not facet-valid with respect to enumeration '[1.2]'. It must be a value from the enumeration. – recalcitrant Aug 18 '14 at 14:59
  • Apparently Spring WF 2.4.0 is designed for JSF 2.0. That's unfortunate. But as you've found a 2.3.3 version which apparently polluted the runtime classpath all the time, could you retry with 2.3.4? It's theoreticaly possible that this classpath pollution has also caused the whole problem. – BalusC Aug 18 '14 at 15:03
  • Unfortunately the problem still exists. Could you point us to the source of the webflow-class which needs to be fixed and elaborate a little what needs to be done so we can patch it ourselves? – recalcitrant Aug 19 '14 at 08:14

1 Answers1

1

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).

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks for the fix. The getWrapped() method does not override any method of ApplicationFactory though. It is declared abstract and only exposes getApplication() and setApplication(). So we need another version, I guess. – recalcitrant Aug 19 '14 at 08:54
  • Ah right, that's indeed JSF2 specific. Just remove it. – BalusC Aug 19 '14 at 08:56