问题
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:
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).
来源:https://stackoverflow.com/questions/25330662/stackoverflowerror-in-seam-spring-webflow-application