迁移到 JSF 2.3,MyFaces 初始化问题

Migrating to JSF 2.3, Problems with MyFaces Initialization

我正在升级一个由两个项目组成的应用程序,"common" 和 "myapp"。我现在正在将它升级到 JSF 2.3 (MyFaces)Primefaces 7,但我不知道如何克服失败初始化。

我应该注意到还有 很多 的其他变化。这两个项目都已成功升级到 OpenJDK 11Tomcat 9,并使用 Maven.随着对 JSF 2.3 的更改,托管 bean 不再受支持以支持 JAVA 的 CDI API,这在 JAVA 11 中默认不再受不便地支持。所以我已经引入了 OpenWebBeans 2.0DeltaSpike 1.9.1。尽管有所有这些移动部件,该应用程序仍在运行以更新 JSF,因此我相信问题就在那里。

此时我有两种可能的配置,都失败了。使用一种配置我得到这个错误:

[main] ERROR [Catalina].[localhost].[/myapp] - StandardWrapper.Throwable
No Factories configured for this Application. This happens if the faces-initialization 
does not work at all - make sure that you properly include all configuration settings
necessary for a basic faces application and that all the necessary libs are included. Also 
check the logging output of your web application and your container for any exceptions!
If you did that and find nothing, the mistake might be due to the fact that you use some
special web-containers which do not support registering context-listeners via TLD files 
and a context listener is not setup in your web.xml.
A typical config looks like this;
<listener>
  <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>

所以,显而易见的解决方案是添加侦听器,但后来我得到了这个:

[main] ERROR myfaces.config.FacesConfigurator - No ManagedBeanDestroyerListener instance
found, thus @PreDestroy methods won't get called in every case. This instance needs to be
published before configuration is started.

这似乎是一个顺序问题,其中 StartupServletContextListener 没有在 Faces 配置开始之前发布 ManagedBeanDestroyerListener。但是,在我的研究中,我读到 StartupServletContextListener 不应该是必需的,因为它是从 JSF 罐子中的 TLD 自动加载的。

有人知道这是怎么回事吗?我怎样才能克服这个问题?

这是我的 pom 和 web.xml 的删节版:

common/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.company</groupId>
    <artifactId>common</artifactId>
    <version>2.1.8</version>

    <properties>
       <dependency.locations.enabled>false</dependency.locations.enabled>   
       <owb.version>2.0.12</owb.version>
       <deltaspike.version>1.9.1</deltaspike.version>      
    </properties>

    <repositories>
        <repository>
            <id>local_repository</id>
            <url>https://server.company.com/repository</url>
        </repository>
    </repositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.deltaspike.distribution</groupId>
                <artifactId>distributions-bom</artifactId>
                <version>${deltaspike.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.myfaces.core</groupId>
            <artifactId>myfaces-api</artifactId>
            <version>2.3.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.myfaces.core</groupId>
            <artifactId>myfaces-impl</artifactId>
            <version>2.3.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.myfaces.core.internal</groupId>
            <artifactId>myfaces-impl-shared-public</artifactId>
            <version>2.3.5</version>
        </dependency>

        <!-- Stored in local maven repository -->
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>7.0</version>
        </dependency>

        <!-- Stored in local maven repository -->
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>all-themes</artifactId>
            <version>1.0.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>4.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.0.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>catalina</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-util</artifactId>
            <version>9.0.12</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-impl</artifactId>
            <version>1.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.taglibs</groupId>
            <artifactId>taglibs-standard-spec</artifactId>
            <version>1.2.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.myfaces.core</groupId>
            <artifactId>myfaces-bundle</artifactId>
            <version>2.3.5</version>
        </dependency>


        <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>2.0.SP1</version>
        </dependency>

        <!-- OpenWebBeans - implements CDI Container -->
        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-spi</artifactId>
            <version>${owb.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-impl</artifactId>
            <version>${owb.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-web</artifactId>
            <version>${owb.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.openwebbeans</groupId>
            <artifactId>openwebbeans-jsf</artifactId>
            <version>${owb.version}</version>
        </dependency>

        <!-- DeltaSpike - manages CDI container -->
        <dependency>
            <groupId>org.apache.deltaspike.core</groupId>
            <artifactId>deltaspike-core-api</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.deltaspike.modules</groupId>
            <artifactId>deltaspike-jsf-module-api</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.deltaspike.core</groupId>
            <artifactId>deltaspike-core-impl</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.deltaspike.modules</groupId>
            <artifactId>deltaspike-jsf-module-impl</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--  CDI Control API -->
        <dependency>
            <groupId>org.apache.deltaspike.cdictrl</groupId>
            <artifactId>deltaspike-cdictrl-api</artifactId>
            <scope>compile</scope>
        </dependency>       

        <!--  CDI Control for OpenWebBeans -->
        <dependency>
            <groupId>org.apache.deltaspike.cdictrl</groupId>
            <artifactId>deltaspike-cdictrl-owb</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.deltaspike.modules</groupId>
            <artifactId>deltaspike-servlet-module-api</artifactId>
            <version>${deltaspike.version}</version>
            <scope>compile</scope>
        </dependency>       
        <dependency>
            <groupId>org.apache.deltaspike.modules</groupId>
            <artifactId>deltaspike-servlet-module-impl</artifactId>
            <version>${deltaspike.version}</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <testSourceDirectory>test</testSourceDirectory>
        <resources>
            ...
        </resources>

        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <debug>${debugBuild}</debug>
                    <debuglevel>lines,vars,source</debuglevel> 
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
                ...
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                ...
            </plugin>

        </plugins>
    </build>    
</project>

myapp/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.company</groupId>
  <artifactId>myapp</artifactId>
  <version>0.1.0</version>
  <packaging>war</packaging>

  <properties>
       <commonVersion>2.1.8</commonVersion>
       <dependency.locations.enabled>false</dependency.locations.enabled>
  </properties>

  <dependencies>
    <dependency>
        <groupId>com.company</groupId>
        <artifactId>common</artifactId>
        <version>${commonVersion}</version>
    </dependency>
  </dependencies>

  <build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>test</testSourceDirectory>
    <resources>
      <resource>
        <directory>resources</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.1</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <source>11</source>
          <target>11</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

common/META-INF/web-fragment.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment metadata-complete="true" version="3.0"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">

    <name>common</name>

    <filter>
        <filter-name>ResourceFilter</filter-name>
        <filter-class>com.company.common.web.ResourceFilter</filter-class>
        <async-supported>true</async-supported>
    </filter>
    <filter-mapping>
        <filter-name>ResourceFilter</filter-name>
        <url-pattern>/javax.faces.resource/*</url-pattern>
        <url-pattern>/resources/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
        <init-param>
            <param-name>thresholdSize</param-name>
            <param-value>100000000</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>PrimeFaces FileUpload Filter</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>


    <listener>
        <display-name>httpSessionListener</display-name>
        <listener-class>com.company.common.usersession.UserSessionListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
      <param-name>org.apache.myfaces.annotation.USE_CDI_FOR_ANNOTATION_SCANNING</param-name>
      <param-value>true</param-value>
    </context-param>   
    <context-param>
        <param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>
        <param-value>65535</param-value>
    </context-param>
    <context-param>
        <param-name>primefaces.THEME</param-name>
        <param-value>mytheme</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</param-name>
        <param-value>20</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION</param-name>
        <param-value>2</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
        <param-value>false</param-value>
    </context-param>
    <context-param>
        <param-name>org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES</param-name>
        <param-value>31536000000</param-value> <!-- 1 year -->
    </context-param>
    <context-param>
        <param-name>primefaces.UPLOADER</param-name>
        <param-value>commons</param-value>
    </context-param>
</web-fragment>

myapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   version="3.0">

  <absolute-ordering>
    <name>common</name> <!-- Get the common web fragment -->
  </absolute-ordering>

    <!-- 
        AppApplicationContextListenerhas to happen before WebBeansConfigurationListener
        so that the application context (e.g. app name, db connections) is set before
        web beans are scanned  
    -->
    <listener>
        <listener-class>com.company.application.app.AppApplicationContextListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
    </listener>

<!-- INCLUDE ??? -->
    <listener>
      <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
    </listener>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>



  <!-- Faces Servlet can't be moved into common's web-fragment.xml due to a bug in Apache's MyFaces -->
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
<!-- or possibly:                         -->
<!--     <url-pattern>*.jsf</url-pattern> -->
  </servlet-mapping>

</web-app>

[2020 年 1 月 3 日更新:我正在更新此条目以删除一些不好的建议。具体来说,将 faces-config.xml 重命名为 standard-faces-config.xml 会阻止 MyFaces 正确初始化。]

我已经解决了大部分问题。有几个组件干扰了我的项目。


行家:

作为使用 Eclipse 的 Maven 的新手,这是一个痛苦的教训我真的必须确保我正在重新运行构建 Maven 并清理我的部署文件夹以便我真正执行我的改变。之后,这是我发现和修复的要点。

  • 显然,在处理第二个引用项目时,m2e 无法正确处理测试 jar。因此使用 <classifier>tests</classifier><type>test-jar</type><dependency> 块将不起作用,但以下将把测试源复制到依赖项目进行编译。 (一定要根据需要编辑 <source> 值,并在 Eclipse 中的 Maven 运行 中启用 m2e 配置文件)
    <profiles>
      <profile>
        <id>m2e</id>
        <activation>
          <property>
            <name>m2e.version</name>
          </property>
        </activation>
        <build>
          <plugins>
            <plugin>
              <groupId>org.codehaus.mojo</groupId>
              <artifactId>build-helper-maven-plugin</artifactId>
              <version>3.0.0</version>
              <executions>
                <execution>
                  <id>include-test-source-eclipse</id>
                  <phase>generate-test-sources</phase>
                  <goals>
                    <goal>add-test-source</goal>
                  </goals>
                  <configuration>
                    <sources>
                      <source>../common/test/java</source>
                    </sources>
                  </configuration>
                </execution>
              </executions>
            </plugin>
          </plugins>
        </build>
      </profile>
    </profiles>
  • org.apache.myfaces.core:myfaces-bundle 只是 org.apache.myfaces.core:myfaces-apiorg.apache.myfaces.core:myfaces-impl 组合成一个包。包括 either/or 但不能同时包括两者。 (我收到的建议是使用单独的包,而不是捆绑包。)

  • 尽管我在 Apache 上读到了,DeltaSpike 并没有使启动 CDI (OpenWebBeans) 变得更容易,所以我删除了它。似乎注册 OpenWebBeans 启动侦听器就足够了(见下文)。

  • JDK 11 不受 cobertura (org.codehaus.mojo:cobertura-maven-plugin) 支持。人们推荐 JaCoCo #67 代替它。


web.xml

在从托管 bean 切换到 CDI 时,web.xml 中需要以下几行。托管 bean 虽然已弃用,但仍默认启用。

<context-param>
    <param-name>org.apache.myfaces.SUPPORT_MANAGED_BEANS</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>org.apache.myfaces.annotation.USE_CDI_FOR_ANNOTATION_SCANNING</param-name>
    <param-value>true</param-value>
</context-param> 

以下是我目前使用的监听器(及其顺序):

<listener>
    <listener-class>com.company.application.app.MyApplicationContextListener</listener-class>
</listener>

<listener>
    <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
</listener>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
  <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>


面孔-config.xml:

不要做我做的事。 不要 创建 standard-faces-config.xml 文件。这会阻止 Faces 正确初始化。 [未找到公共项目的 faces-config.xml 文件,重命名它似乎可以解决问题。糟糕的主意。] 这个错误导致 MyFaces 无法创建 <action-listener>org.primefaces.application.DialogActionListener</action-listener>


所以,总而言之...

  • 看来 MyFaces StartupServletContextListener 确实有必要包含在 web.xml 中。
  • 奇怪的 "Bean Destroyer not published" 错误似乎是由于切换到 CDI 而不是禁用 MyFaces 的托管 Bean 支持。
  • 由于我的 standard-faces-config.xml 文件阻止正确初始化,导致多个启动问题。


[@tandraschko:非常感谢您在我努力解决这个问题时的帮助和关注!]