【问题标题】:StackOverflowError in Seam / Spring WebFlow ApplicationSeam / Spring WebFlow 应用程序中的 StackOverflowError
【发布时间】:2014-10-09 10:33:04
【问题描述】:

我们正在逐步用 Spring-MVC 和 Spring-Webflow 替换 Seam 组件。

运行 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 类它说:

“因为这个实例是共享的,所以必须以线程安全的方式实现。”

也许这两个应用实例试图访问同一个包,这会导致竞争条件?

编辑: 在应用不再响应后,我们重新启动了服务器,现在错误出现在另一个地方:

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)

最后两行在日志文件中重复了数千次。

我们正在使用以下组件版本:

JSF-1.2

Seam-2.2.0

Spring WebFlow 2.3.4

Spring MVC 3.0.5

不能更新任何组件。

【问题讨论】:

  • 我查看了SeamApplicationFlowApplication的源代码。两者都有错误,无法正确委派给包装应用程序,Spring WebFlow 中的应用程序已在 2.4.0 版中修复。在发布答案之前,您能否尝试升级到 WebFlow 2.4.0 并让我知道这是否确实解决了问题? SeamApplication 仍然暴露了当前 2.3.1 版本中的错误,但可以通过更改 faces-config.xml 中的加载顺序来解决。
  • 感谢您的更新!不幸的是,我们无法升级到 webflow 2.4,因为它会导致一些其他问题。我们将尝试更改加载顺序,但然后查看我们的 faces-config 没有。您能否详细说明您的意思是什么顺序?
  • 抱歉,至少其中一个Application 实现确实需要修复。否则,您需要修改源代码或至少向负责人报告错误并等待更新。至于排序,在 JSF 1.x 中,您可以通过在 webapp 自己的 faces-config.xml 中以正确的顺序重新声明这些库的 &lt;application-factory&gt; 来强制重新排序。在 JSF 2.x 中,您可以通过新的 &lt;absolute-ordering&gt; 元素强制重新排序。在这两种情况下,修复错误的那个必须作为最后一个出现,以便它包裹损坏的那个而不是其他方式。
  • 或者,只需详细说明“一些其他问题”,以便您也可以修复它们,以便最终能够使用已修复错误的版本。顺便说一句,我希望您非常清楚 Spring MVC 和 JSF 是完全的竞争对手,并且不打算在 1 页/请求中混在一起。您可以将 Spring DI 或 Spring WF 与 JSF 一起使用(作为 Java EE 自己的 @Named/@EJB@FlowScoped 的“替代品”),但绝对不能使用 Spring MVC。
  • 如果我们更新到 webflow 2.4 会抛出 AbstractMethodError:java.lang.AbstractMethodError at org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder.createViewFactory(FlowModelFlowBuilder.java:639)

标签: jsf spring-mvc seam spring-webflow


【解决方案1】:

SeamApplicationFlowApplication 都存在错误,无法正确委派给包装的应用程序。修复它的一种方法是通过FlowApplicationFactory

首先获取它的raw source code 并将其放入您的webapp 项目的Java 源文件夹中,保留其原始包。您不一定需要操作 JAR。 /WEB-INF/classes 中的类比 JAR 中的类具有更高的类加载优先级。

然后按如下方式操作类(基于OmniFacesOmniApplicationFactory):

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 类使其变得更容易,您可以使用它来代替 createFlowApplication() 块中的 SeamApplication

如果这一切仍然不起作用,那么SeamApplicationFactory 可能在FlowApplicationFactory 之后被初始化。您可以通过在 webapp 自己的 faces-config.xml 中以所需的顺序显式重新声明 &lt;application-factory&gt; 条目来强制排序(已修复错误的条目为最后一个):

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

否则,您可能希望对SeamApplicationFactory 执行与上述相同的操作(显然在代码中交换了FlowApplicationSeamApplication)。

【讨论】:

  • 感谢您的修复。 getWrapped() 方法不会覆盖 ApplicationFactory 的任何方法。它被声明为抽象的并且只公开 getApplication() 和 setApplication()。所以我们需要另一个版本,我猜。
  • 对,这确实是 JSF2 特有的。只需将其删除。
猜你喜欢
  • 2011-08-28
  • 2017-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
  • 2013-05-28
  • 1970-01-01
  • 2015-01-25
相关资源
最近更新 更多