mavenjsftomcatconfigurationinstallation

How to properly install and configure JSF libraries via Maven?


I'm trying to deploy a JSF based application to Tomcat 6. The way my build system is setup, the WAR itself doesn't have any libraries in it, because this server is serving a total of 43 apps. Instead, the libraries are copied into a shared library folder and shared among the apps. When I deploy, I get this error

SEVERE: Error deploying configuration descriptor SSOAdmin.xml
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/faces/webapp/FacesServlet
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1667)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
    at org.apache.catalina.startup.WebAnnotationSet.loadApplicationServletAnnotations(WebAnnotationSet.java:108)
    at org.apache.catalina.startup.WebAnnotationSet.loadApplicationAnnotations(WebAnnotationSet.java:58)
    at org.apache.catalina.startup.ContextConfig.applicationAnnotationsConfig(ContextConfig.java:297)
    at org.apache.catalina.startup.ContextConfig.start(ContextConfig.java:1078)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:261)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4611)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:675)
    at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:601)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:502)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1315)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:324)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1061)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
    at org.apache.catalina.core.StandardService.start(StandardService.java:525)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)

Now in my research, I see that this is supposed to be solved by downloading the JSF source code and compiling it myself. That is a horrible solution in my case. That will cause huge problems on my team with the various configurations we have to contend with. Is there another fix for this?

Here is my pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.nms.sso</groupId>
  <artifactId>SSOAdmin</artifactId>
  <version>09142011-BETA</version>
  <packaging>war</packaging>
  <dependencies>
    <dependency>
      <groupId>asm</groupId>
      <artifactId>asm</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <!-- <dependency> -->
    <!-- <groupId>com.sun.faces</groupId> -->
    <!-- <artifactId>jsf-api</artifactId> -->
    <!-- <scope>${myExeScope}</scope> -->
    <!-- </dependency> -->
    <!-- <dependency> -->
    <!-- <groupId>com.sun.faces</groupId> -->
    <!-- <artifactId>jsf-impl</artifactId> -->
    <!-- <scope>${myExeScope}</scope> -->
    <!-- </dependency> -->
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.faces</groupId>
      <artifactId>javax.faces-api</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>net.sf.jt400</groupId>
      <artifactId>jt400</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>nmsc</groupId>
      <artifactId>nmsc_api</artifactId>
      <version>09142011-BETA</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.icefaces</groupId>
      <artifactId>icefaces</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.icefaces</groupId>
      <artifactId>icefaces-ace</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.icefaces</groupId>
      <artifactId>icefaces-compat</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.jibx</groupId>
      <artifactId>jibx-extras</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.jibx</groupId>
      <artifactId>jibx-run</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>${myExeScope}</scope>
    </dependency>
  </dependencies>
  <parent>
    <groupId>nmsc</groupId>
    <artifactId>nmsc_lib</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <relativePath>../libs</relativePath>
  </parent>
  <build>
    <finalName>SSOAdmin</finalName>
  </build>
  <name>SSOAdmin Maven Webapp</name>
</project>

There has got to be a solution here. I can't for a second believe that the Maven distributable for JSF is only good for compiling and not good for deployment.


Solution

  • When you're facing a "weird" exception suggesting that classes/methods/files/components/tags are absent or different while they are seemingly explicitly included in the web application such as the ones below,

    java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/faces/webapp/FacesServlet

    java.util.MissingResourceException: Can't find javax.faces.LogStrings bundle

    com.sun.faces.vendor.WebContainerInjectionProvider cannot be cast to com.sun.faces.spi.InjectionProvider

    com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED

    The tag named inputFile from namespace http://xmlns.jcp.org/jsf/html has a null handler-class defined.

    java.lang.NullPointerException at javax.faces.CurrentThreadToServletContext.getFallbackFactory

    java.lang.AbstractMethodError at javax.faces.application.ViewHandlerWrapper.getWebsocketURL

    java.lang.NullPointerException at com.sun.faces.config.InitFacesContext.cleanupInitMaps

    or when you're facing "weird" runtime behavior such as broken HTTP sessions (jsessionid appears in link URLs over all place), and/or broken Faces view scope (it behaves as request scoped), and/or broken CSS/JS/image resources, then the chance is big that the webapp's runtime classpath is polluted with duplicate different versioned JAR files.

    In your specific case with the ClassFormatError on the FacesServlet, it means that the JAR file containing the mentioned class has been found for the first time is actually a "blueprint" API JAR file, intented for implementation vendors (such as developers working for Mojarra and MyFaces). It contains class files with only class and method signatures, without any code bodies and resource files. That's exactly what "absent code attribute" means. It's purely intented for javadocs and compilation.

    Always mark server-provided libraries as provided

    All dependencies marked "Java Specifications" in Maven and having -api suffix in the artifact ID are those blueprint APIs. You should absolutely not have them in the runtime classpath. You should always mark them <scope>provided</scope> if you really need to have it in your pom. A well known example is the Jakarta EE (Web) API (formerly known as Java EE):

    <dependency>
        <groupId>jakarta.platform</groupId>
        <artifactId>jakarta.jakartaee-api</artifactId>
        <version><!-- 8.0.0 or 9.0.0 or 9.1.0 or 10.0.0 or newer --></version>
        <scope>provided</scope>
    </dependency>
    

    If the provided scope is absent, then this JAR will end up in webapp's /WEB-INF/lib, causing all trouble you're facing now. This JAR also contains the blueprint class of FacesServlet.

    In your specific case, you have an unnecessary Faces API dependency:

    <dependency>
        <groupId>jakarta.faces</groupId>
        <artifactId>jakarta.faces-api</artifactId>
    </dependency>
    

    This is causing trouble because this contains the blueprint class of FacesServlet. Removing it and relying on a provided Jakarta EE (Web) API as shown above should solve it.

    Tomcat as being a barebones JSP/Servlet container already provides JSP, Servlet and EL (and since 8 also WebSocket) out the box. So you should mark at least jsp-api, servlet-api, and el-api as provided. Tomcat only doesn't provide Faces (nor JSTL) out the box. So you'd need to install it via the webapp.

    Full fledged Jakarta EE servers such as WildFly, TomEE, GlassFish, Payara, WebSphere, etc already provide the entire Jakarta EE API out the box, including Faces. So you do absolutely not need to install Faces via the webapp. It would only result in conflicts if the server already provides a different implementation and/or version out the box. The only dependency you need is the jakartaee-api exactly as shown here above.

    See also How to properly configure Jakarta EE libraries in Maven pom.xml for Tomcat? for more elaborate explanation and examples of pom.xml for Tomcat 10 and 9.

    Installing Faces on Tomcat 10 or newer

    Tomcat 10.0.x is the first version using jakarta.* package instead of javax.* package. You'll for Tomcat 10.0.x thus need a minimum of Faces 3.0 instead of 2.3 because the javax.* package has been renamed to jakarta.* since Faces 3.0 only. In case you have Tomcat 10.1.x, then you need a minimum Faces version of 4.0.

    There are two Faces implementations: Mojarra and MyFaces. You should choose to install one of them and thus not both.

    Installing Mojarra 4.0 on Tomcat 10 or newer:

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.faces</artifactId>
        <version><!-- Check https://github.com/eclipse-ee4j/mojarra --></version>
    </dependency>
    

    You can also check org.glassfish:jakarta.faces repository for current latest 4.0.x release version (which is currently 4.0.8). See also Mojarra installation instructions for other necessary dependencies (CDI, BV, JSONP).

    Installing MyFaces 4.0 on Tomcat 10 or newer:

    <dependency>
        <groupId>org.apache.myfaces.core</groupId>
        <artifactId>myfaces-impl</artifactId>
        <version><!-- Check http://myfaces.apache.org --></version>
    </dependency>
    

    You can also check org.apache.myfaces.core:myfaces-impl repository for current latest 4.0.x release version (which is currently 4.0.2).

    Don't forget to install at least the JSTL API along, by the way. This is also absent in Tomcat.

    <dependency>
        <groupId>jakarta.servlet.jsp.jstl</groupId>
        <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
        <version>3.0.0</version>
    </dependency>
    

    No, you don't necessarily need the JSTL impl along as instructed in How to install JSTL? It fails with "The absolute uri cannot be resolved" or "Unable to find taglib" or NoClassDefFoundError or ClassCastException. That's only needed when you also want to use JSTL in JSP files. Facelets has its own JSTL impl built-in. In case you want to use both Facelets and JSP, then remove the JSTL API dependency and install the JSTL impl as instructed in aforementioned link.

    Also note that since Faces 2.3, CDI has become a required dependency. This is available out the box in normal Jakarta EE servers but not on servletcontainers such as Tomcat. In this case head to How to install and use CDI on Tomcat?

    To be clear, after having installed the Faces implementation Mojarra or MyFaces, you do not need to separately declare the jakarta.faces-api dependency. This is already transitively included via the implementation.

    Installing Faces on Tomcat 9 or older

    You can only use at maximum Faces 2.3 on Tomcat 9 or older because it is the latest version still using javax.* package.

    Installing Mojarra 2.3 on Tomcat 9 or older:

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.faces</artifactId>
        <version><!-- Check https://github.com/eclipse-ee4j/mojarra --></version>
    </dependency>
    

    You can also check org.glassfish:jakarta.faces repository for current latest 2.3.x release version (which is currently 2.3.21). See also Mojarra installation instructions for other necessary dependencies (CDI, BV, JSONP).

    Installing MyFaces 2.3 on Tomcat 9 or older:

    <dependency>
        <groupId>org.apache.myfaces.core</groupId>
        <artifactId>myfaces-impl</artifactId>
        <version><!-- Check http://myfaces.apache.org --></version>
    </dependency>
    

    You can also check org.apache.myfaces.core:myfaces-impl repository for current latest 2.3.x release version (which is currently 2.3.10).

    Note that Tomcat 6 as being Servlet 2.5 container supports at maximum Faces 2.1.

    Don't forget to install JSTL API along, by the way. This is also absent in Tomcat.

    <dependency>
        <groupId>jakarta.servlet.jsp.jstl</groupId>
        <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
        <version>1.2.7</version>
    </dependency>
    

    Also note that since Faces 2.3, CDI has become a required dependency. This is available out the box in normal Jakarta EE servers but not on servletcontainers such as Tomcat. In this case head to How to install and use CDI on Tomcat?

    See also: