Jakarta EE Servers: GlassFish, WildFly, Payara, Open Liberty and TomEE

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Jakarta Faces 4.1 (API jakarta.faces-api 4.1.2, part of Jakarta EE 11), Java 17 or later, and the server releases current on 2026-10-08: Eclipse GlassFish 8.0.5, WildFly 41.0.1.Final, Payara 7.2026.9, Open Liberty 26.0.0.10 and Apache TomEE 10.2.0. Every server was downloaded from Maven Central, started with the same small Bookshelf WAR and queried for the Faces implementation it actually used (the RuntimeInfoBean below). All five were run on JDK 21. Claims that were not run are marked as such. Apache Tomcat is not a Jakarta EE server and has no bundled Faces implementation; running Faces on it is the subject of a separate page (Tomcat and Weld).

An application server already contains a Faces implementation, a CDI container, Servlet, Expression Language and (on most) Bean Validation. A Faces application written for a server therefore ships no Faces JARs: the WAR holds the views, the classes and the descriptors, and compiles against the APIs with provided scope. That makes the WAR small, but it also means the Faces implementation, and its version, is chosen by the server and not by your pom.xml. This page shows which one you get, how to deploy, and what is different between the five products.

One WAR for Every Server

The WAR used on all five servers is the Bookshelf catalogue in its smallest useful form: a Book record, an application-scoped BookService, a request-scoped CatalogueBean and two Facelets views that use the shared layout template. The packages follow the conventions of this section (org.example.bookshelf.model, .service and .web, resource bundle variable msg, template /WEB-INF/templates/layout.xhtml).

pom.xml: APIs only, nothing from Faces is packaged
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>bookshelf</artifactId>
  <version>1.0.0</version>
  <packaging>war</packaging>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

  <dependencies>
    <!-- APIs only: the server supplies Faces, CDI, Servlet and EL at run time -->
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-web-api</artifactId>
      <version>11.0.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>bookshelf</finalName>
  </build>
</project>

The Web Profile API JAR contains the Faces, CDI, Servlet and Expression Language APIs, so one provided dependency is enough (see the Maven WAR section of Project Setup for the alternative of declaring jakarta.faces-api alone). Packaging the API JAR in WEB-INF/lib instead would put a second copy of jakarta.faces.* next to the server’s own and is the classic cause of class-loading errors on every server below.

web.xml, beans.xml and faces-config.xml: the descriptors need no server-specific content
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
  <servlet>
    <servlet-name>facesServlet</servlet-name>
    <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  <context-param>
    <param-name>jakarta.faces.PROJECT_STAGE</param-name>
    <param-value>Production</param-value>
  </context-param>
  <welcome-file-list>
    <welcome-file>catalogue.xhtml</welcome-file>
  </welcome-file-list>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
       version="4.0" bean-discovery-mode="annotated"/>

The descriptors are written at Servlet 6.0, beans.xml 4.0 and faces-config.xml 4.0 on purpose: the same WAR has to deploy on TomEE 10.2.0, which is a Jakarta EE 10 server (see Apache TomEE 10.2), as well as on the four Jakarta EE 11 servers, and nothing in it needs Faces 4.1. If you only target EE 11 servers you can move the descriptors to the 6.1 and 4.1 schemas. The PROJECT_STAGE context parameter is set to Production here; see Project Stages for what each stage changes. The Facelets views are the catalogue and a diagnostic page:

catalogue.xhtml: the Bookshelf catalogue using the layout template
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="jakarta.faces.html"
      xmlns:f="jakarta.faces.core"
      xmlns:ui="jakarta.faces.facelets">
<ui:composition template="/WEB-INF/templates/layout.xhtml">
  <ui:define name="title">#{msg['catalogue.title']}</ui:define>
  <ui:define name="content">
    <h:dataTable value="#{catalogueBean.books}" var="book">
      <h:column><f:facet name="header">#{msg['catalogue.isbn']}</f:facet>#{book.isbn}</h:column>
      <h:column><f:facet name="header">#{msg['catalogue.titleColumn']}</f:facet>#{book.title}</h:column>
      <h:column><f:facet name="header">#{msg['catalogue.author']}</f:facet>#{book.author}</h:column>
      <h:column><f:facet name="header">#{msg['catalogue.year']}</f:facet>#{book.year}</h:column>
    </h:dataTable>
    <p><h:link outcome="runtime" value="Runtime info"/></p>
  </ui:define>
</ui:composition>
</html>

The diagnostic page prints which implementation answered. It uses only public Java and Faces API calls, so it works unchanged on every server and is the quickest way to confirm what a deployment really runs.

RuntimeInfoBean: reports the Faces implementation, API and CDI container in use
package org.example.bookshelf.web;

import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Named;

/** Reports which Faces implementation and API the server supplied. */
@Named
@RequestScoped
public class RuntimeInfoBean {

    public String getImplementation() {
        Class<?> impl = FacesContext.getCurrentInstance().getExternalContext().getClass();
        Package p = impl.getPackage();
        var src = impl.getProtectionDomain().getCodeSource();
        return impl.getName() + " / " + p.getImplementationTitle() + " " + p.getImplementationVersion()
                + " / " + (src == null ? "unknown" : src.getLocation());
    }

    public String getApi() {
        Package p = FacesContext.class.getPackage();
        return p.getSpecificationTitle() + " " + p.getSpecificationVersion()
                + " (API jar " + p.getImplementationVersion() + ")";
    }

    public String getCdi() {
        return jakarta.enterprise.inject.spi.CDI.current().getBeanManager().getClass().getName();
    }
}

runtime.xhtml shows the three properties with h:outputText. Never expose such a page in production: it names the implementation and its file system location, which helps an attacker and helps nobody else. Remove it, or restrict it with a security constraint (see the security page), before a real deployment.

Which Implementation Each Server Bundles

This table is what the diagnostic page printed, plus the module versions read from each distribution, on 2026-10-08. The Faces implementation is not always the newest release: servers freeze it per server release.

Server (release) Jakarta EE Faces implementation (observed) CDI container Notes

GlassFish 8.0.5

11

Eclipse Mojarra 4.1.17 (modules/jakarta.faces.jar, API and implementation in one JAR)

Weld 6.0.4.Final

Hibernate Validator in modules/

WildFly 41.0.1.Final

11

Eclipse Mojarra 4.1.13 (jakarta/faces/impl/main/jakarta.faces-4.1.13.jar)

Weld 6.0.4.Final (weld-core-impl-6.0.4.Final.jar)

jsf subsystem; Hibernate Validator 9.1.1.Final

Payara 7.2026.9

11

Mojarra 4.1.14.payara-p1 (Payara’s build of Mojarra)

Weld 6.0.4.Final

Same GlassFish-derived module layout

Open Liberty 26.0.0.10

11 (faces-4.1 feature; enabled by webProfile-11.0)

Apache MyFaces 4.1.1 repackaged by Liberty (lib/io.openliberty.org.apache.myfaces.4.1_*.jar)

Weld (org.jboss.weld.manager.BeanManagerImpl)

Features are opt-in, see Open Liberty 26 (faces-4.1)

Apache TomEE 10.2.0 (Web Profile)

10

Apache MyFaces 4.0.3 (lib/myfaces-api-4.0.3.jar, myfaces-impl-4.0.3.jar)

Apache OpenWebBeans 4.0.3

Faces 4.0 only; Bean Validation bval-jsr-3.0.1.jar. A Tomcat 10.1-based server

Two practical consequences follow. First, the same page may behave differently on Mojarra and on MyFaces; the differences are collected in Mojarra versus MyFaces and in the "Mojarra vs MyFaces" part of each page of this section, so test on the server you ship on. Second, the Faces version is also fixed: on TomEE 10.2.0 you get Faces 4.0, so Faces 4.1 additions (see what Faces 4.1 added) are not available there even though the WAR compiles against the 4.1 API. The Jakarta EE site lists which products are certified compatible with which platform and profile; the certification list is what makes "runs on Jakarta EE 11" a claim you can check, and it is where a server that has just released (like the WildFly 42 beta that exists today) is listed once it is certified.

Eclipse GlassFish 8

GlassFish 8.0.5 is the Jakarta EE 11 compatible implementation and the home of Mojarra, so it is the reference for "what the specification does". The distribution is a ZIP that unpacks to a glassfish8 directory; everything is driven by the asadmin command.

Create a domain, start it and deploy the WAR (GlassFish)
export JAVA_HOME=/path/to/jdk-21
GF=glassfish8

# a throw-away development domain without an admin password
$GF/bin/asadmin create-domain --nopassword=true --instanceport 8080 --adminport 4848 bookdomain
$GF/bin/asadmin start-domain bookdomain

$GF/bin/asadmin deploy --contextroot bookshelf target/bookshelf.war
curl -s http://localhost:8080/bookshelf/catalogue.xhtml | grep 'Clean Code'

$GF/bin/asadmin undeploy bookshelf
$GF/bin/asadmin stop-domain bookdomain

Observed on 8.0.5: the deployment printed "Application deployed with name bookshelf", the catalogue rendered, and the diagnostic page reported com.sun.faces.context.ExternalContextImpl from Mojarra 4.1.17, API jar 4.1.17 (the same JAR, modules/jakarta.faces.jar) and org.jboss.weld.bean.builtin.BeanManagerProxy. Configuration notes:

  • Domains. A GlassFish installation holds any number of domains, each with its own config/domain.xml, ports and applications. --nopassword=true creates an admin user without a password, which is acceptable for a throw-away local domain and nothing else; for a shared or production domain create it with a password and keep the admin listener off public networks.

  • Ports. The domain creation flags move only the HTTP and admin ports. The other listeners (HTTPS, IIOP, JMS, JMX) have defaults in domain.xml that clash when two domains run on one machine; edit them or create the domain with --portbase.

  • Context root. --contextroot on deploy or a glassfish-web.xml with a context-root element sets it; without either, the application name (the WAR name) is used.

  • Class loading. The Faces JAR is a module of the server (an OSGi bundle), visible to every application. Applications are isolated from each other but see the server’s Mojarra.

  • Java. The run used JDK 21.

WildFly 41

WildFly assembles the server from modules and configures subsystems in standalone.xml. Faces is handled by the jsf subsystem; Mojarra 4.1.13 lives in the module jakarta.faces.impl, with the API in jakarta.faces.api and the CDI integration in org.jboss.as.jsf-injection. Faces is added to a deployment’s class path only when the deployment uses Faces (for example, when it declares the FacesServlet).

Start WildFly and deploy by copying the WAR into the deployment scanner directory
export JAVA_HOME=/path/to/jdk-21
WF=wildfly-41.0.1.Final

$WF/bin/standalone.sh &

cp target/bookshelf.war $WF/standalone/deployments/
# the scanner writes bookshelf.war.deployed when it succeeds (bookshelf.war.failed otherwise)
curl -s http://localhost:8080/bookshelf/runtime.xhtml

$WF/bin/jboss-cli.sh --connect --commands=':shutdown'

Observed: deployment took a few seconds and created bookshelf.war.deployed; the diagnostic page reported Mojarra 4.1.13 from modules/system/layers/base/jakarta/faces/impl/main/jakarta.faces-4.1.13.jar and Weld’s BeanManagerProxy. The context root is the WAR file name (/bookshelf). The management interface listened on 127.0.0.1 only, which is the safe default; keep it that way and do not add a remote binding without credentials. Useful details:

  • Ports. Standalone mode opens HTTP (8080), HTTPS (8443) and the management ports. They are system-property controlled (-Djboss.http.port=..., -Djboss.management.http.port=...), which is how parallel servers are started on one machine.

  • Which implementations are installed. The CLI command /subsystem=jsf:list-active-jsf-impls returned ["main"] (the bundled Mojarra), and /subsystem=jsf:read-resource showed default-jsf-impl-slot => "main".

  • Maven. The WildFly Maven plugin can deploy from the build; it was not run for this page.

Payara 7

Payara Server is derived from GlassFish and keeps its layout (glassfish/domains, asadmin), so GlassFish know-how transfers. Payara 7.2026.9 reported Mojarra 4.1.14.payara-p1 and Weld 6.0.4.Final, so it is one Mojarra patch release behind GlassFish 8.0.5 and carries Payara’s own build identifier.

Create a domain and deploy (Payara)
export JAVA_HOME=/path/to/jdk-21
PAYARA=payara7

$PAYARA/bin/asadmin create-domain --nopassword=true --instanceport 8080 --adminport 4848 bookdomain
$PAYARA/bin/asadmin start-domain bookdomain
$PAYARA/bin/asadmin deploy --contextroot bookshelf target/bookshelf.war
curl -s http://localhost:8080/bookshelf/runtime.xhtml
$PAYARA/bin/asadmin stop-domain bookdomain

Observed: identical commands to GlassFish worked. Two differences matter for operations. Payara ships a Hazelcast-based clustering runtime configured in domain.xml (hazelcast-runtime-configuration, with its own ports) that GlassFish does not have; the cluster aspects of Faces are covered in the session replication part of the deployment and clustering page and in the cluster part of the state-saving page. And the Payara version number is a release train (7.2026.9 is a 2026 release), so "Payara 7" says little about the Faces patch level; always check the actual JAR as shown above.

Open Liberty 26 (faces-4.1)

Open Liberty is the odd one out in three ways: it is configured by an XML file listing features, it bundles MyFaces and not Mojarra, and nothing is available until a feature is enabled. A server is a directory under usr/servers with a server.xml.

server.xml: the faces-4.1 feature, an HTTP endpoint and the application
<?xml version="1.0" encoding="UTF-8"?>
<server description="Bookshelf on Open Liberty">
  <featureManager>
    <feature>faces-4.1</feature>
    <feature>cdi-4.1</feature>
  </featureManager>

  <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" httpsPort="9443"/>

  <webApplication id="bookshelf" location="bookshelf.war" contextRoot="/bookshelf"/>
</server>
Create the server, copy the WAR into apps/ and start (Open Liberty)
export JAVA_HOME=/path/to/jdk-21
WLP=wlp

$WLP/bin/server create bookshelf
# write usr/servers/bookshelf/server.xml as above, then:
mkdir -p $WLP/usr/servers/bookshelf/apps
cp target/bookshelf.war $WLP/usr/servers/bookshelf/apps/
$WLP/bin/server start bookshelf
curl -s http://localhost:9080/bookshelf/runtime.xhtml
$WLP/bin/server stop bookshelf

Observed with 26.0.0.10: the server reported the installed features cdi-4.1, expressionLanguage-6.0, faces-4.1 and servlet-6.1; the diagnostic page reported org.apache.myfaces.context.servlet.ServletExternalContextImpl from Liberty’s io.openliberty.org.apache.myfaces.4.1 JAR with implementation version 4.1.1, the API from dev/api/spec/io.openliberty.jakarta.faces.4.1_*.jar, and Weld’s BeanManagerImpl. Notes:

  • Version lag. The bundled MyFaces is 4.1.1, behind MyFaces Core 4.1.4. Fixes released after 4.1.1 reach Liberty only with a Liberty release.

  • Features. The Jakarta Faces 4.1 feature enables CDI, Expression Language and Servlet; the webProfile-11.0 feature enables faces-4.1 for you. The documentation lists Java 17, 21, 25 and 27 as supported for it. A feature you do not list is absent: if a view uses Bean Validation or WebSocket push, add the matching feature (this page’s run did not use them).

  • Mojarra in the WAR. With the faces-4.1 feature the server’s MyFaces is on the class path, so a WAR that carries Mojarra conflicts. In the run, a WAR holding jakarta.faces-4.1.17.jar failed at startup with a Weld DefinitionException caused by NoClassDefFoundError: jakarta/websocket/CloseReason$CloseCode while loading a Mojarra push class (the websocket-2.2 feature was not enabled). The supported way to bring your own implementation is the next feature.

  • facesContainer-4.1. This feature contains only the container integration for Faces; it provides no API and no implementation, so the implementation must be packaged in the WAR. It enables cdi-4.1, expressionLanguage-6.0, jndi-1.0, servlet-6.1 and websocket-2.2. Replacing faces-4.1 with facesContainer-4.1 in the server.xml above, and deploying a WAR that packages Mojarra 4.1.17, started cleanly and the diagnostic page reported Mojarra 4.1.17 loaded from the WAR’s WEB-INF/lib (the JAR was downloaded with featureUtility installFeature facesContainer-4.1; the zip used here did not contain it).

Apache TomEE 10.2

TomEE is Apache Tomcat plus the Jakarta EE Web Profile implementations from Apache: MyFaces for Faces, OpenWebBeans for CDI, Bean Validation from Apache BVal. TomEE 10.2.0 is a Jakarta EE 10 server on Tomcat 10.1 (the 404 page printed "Apache Tomcat (TomEE)/10.1.57 (10.2.0)"). Its distribution contained myfaces-api-4.0.3.jar, myfaces-impl-4.0.3.jar, openwebbeans-impl-4.0.3.jar and openwebbeans-jsf-4.0.3.jar in lib/. It is therefore a Faces 4.0 server; the Jakarta EE 11 release, TomEE 11, was a milestone (11.0.0-M1) on Maven Central at the time of writing.

Deploy by copying the WAR into webapps (TomEE)
export JAVA_HOME=/path/to/jdk-21
TOMEE=apache-tomee-webprofile-10.2.0

chmod +x $TOMEE/bin/*.sh            # the ZIP did not preserve the executable bit
cp target/bookshelf.war $TOMEE/webapps/
$TOMEE/bin/catalina.sh run &        # or startup.sh to detach
curl -s http://localhost:8080/bookshelf/runtime.xhtml
$TOMEE/bin/catalina.sh stop

Observed: the WAR deployed unchanged and the diagnostic page reported org.apache.myfaces.context.servlet.ServletExternalContextImpl, Apache MyFaces Core 4.0 Impl 4.0.3, and OpenWebBeans' InjectableBeanManager. Configuration is Tomcat’s: ports are in conf/server.xml (HTTP connector 8080, shutdown 8005), the context root is the WAR name, and the webapps directory is the deployment directory. Because the CDI container is OpenWebBeans and not Weld, Weld-specific API (for example org.jboss.weld.*) must not be used in code meant for TomEE,.

Replacing MyFaces with Mojarra on TomEE by packaging jakarta.faces-4.1.17.jar in the WAR failed at deployment in the run with an OpenWebBeans AmbiguousResolutionException for the bean name application: the server’s MyFaces CDI producer and the bean from the WAR’s copy of Faces collided. No configuration that makes a bundled Mojarra work on this server was found, so for Faces 4.1 wait for TomEE 11.

Replacing the Bundled Implementation

Sometimes a project needs a different implementation or a newer patch release than the one the server carries (a fix in a later Mojarra, or MyFaces for consistency with another environment). The outcome varies by server and was tested for each:

Server Result in the run

GlassFish 8.0.5

Packaging MyFaces 4.1.4 (API and implementation) in WEB-INF/lib, with and without <class-loader delegate="false"/> in glassfish-web.xml, failed at deployment: Weld WELD-001414: Bean name is ambiguous (requestScope, then initParam), because both the server’s Mojarra and the WAR’s MyFaces register CDI producers with the same EL names. Not workable; use the bundled Mojarra.

Payara 7.2026.9

The same MyFaces WAR failed the same way (WELD-001414, name requestScope). Use the bundled Mojarra.

WildFly 41.0.1

Worked. A WAR with MyFaces 4.1.4 in WEB-INF/lib and the context parameter org.jboss.jbossfaces.WAR_BUNDLES_JSF_IMPL set to true ran on MyFaces: the diagnostic page reported org.apache.myfaces...ServletExternalContextImpl, implementation 4.1.4, loaded from the WAR. That parameter is not described in the WildFly 41 Administration Guide; it was observed to work, so treat it as an implementation detail that may change. The documented route is to install the implementation as a server module (for example with the WildFly MyFaces feature pack, then org.jboss.jbossfaces.JSF_CONFIG_NAME in web.xml, or the default-jsf-impl-slot attribute to change the default for the whole server).

Open Liberty 26.0.0.10

Works with the facesContainer-4.1 feature (see Open Liberty 26 (faces-4.1)): Mojarra 4.1.17 packaged in the WAR ran. With faces-4.1 it does not.

TomEE 10.2.0

Mojarra 4.1.17 packaged in the WAR failed at deployment (AmbiguousResolutionException, name application).

The lesson is that the "bring your own implementation" capability is a feature of the server, not of Faces. Where a server has no supported switch, the answer is to use the bundled implementation and to follow its release schedule, or to move to a server (or to Tomcat plus Weld, see Tomcat and Weld) where you control the implementation.

WildFly: select an installed implementation for one application
<context-param>
  <param-name>org.jboss.jbossfaces.JSF_CONFIG_NAME</param-name>
  <param-value>myfaces-4.1.4</param-value>
</context-param>

The slot name above follows the documentation’s <name>-<version> convention (its example uses myfaces-4.0.0); the actual slot depends on how the implementation was installed, and this snippet was not run.

Configuration Differences at a Glance

GlassFish 8 WildFly 41 Payara 7 Open Liberty 26 TomEE 10.2

Deploy

asadmin deploy

copy to standalone/deployments

asadmin deploy

copy to apps/ or webApplication in server.xml

copy to webapps/

Context root

--contextroot, glassfish-web.xml

WAR name

--contextroot, glassfish-web.xml

contextRoot attribute

WAR name

Server config

domain.xml by asadmin

standalone.xml by CLI

domain.xml by asadmin

server.xml by hand

Tomcat server.xml, tomee.xml

Faces / CDI

Mojarra 4.1.17 / Weld

Mojarra 4.1.13 / Weld

Mojarra 4.1.14.payara-p1 / Weld

MyFaces 4.1.1 / Weld

MyFaces 4.0.3 / OpenWebBeans

Faces version

4.1

4.1

4.1

4.1

4.0

What does not differ is the part you control: the WAR, the Facelets, the CDI beans and the descriptors above ran unchanged on all five servers (the catalogue rendered the three books on each). A few behaviours do depend on the server and must be tested: the exact text of the default error pages (see error pages in the exception-handling page), whether CDI scopes beyond the standard ones are available, how client-side state is encrypted in a cluster (MyFaces needs an explicit secret on every node; see client-state keys), and how long a server keeps view state (Mojarra and MyFaces use different defaults, see state saving and stateless views).

Component Libraries and Servers

PrimeFaces and OmniFaces are packaged in the WAR and work with both implementations; the JAR versions required are in PrimeFaces getting started and OmniFaces. Two server-specific points apply: OmniFaces 5 needs Faces 4.1, so it does not run on TomEE 10.2.0 (Faces 4.0, use an OmniFaces 4.x line there), and a library that ships its own CDI extension can trigger the duplicate bean problems shown in Replacing the Bundled Implementation if it also carries a Faces implementation. Never package a Faces implementation as a transitive dependency of a library; mark it provided or exclude it.

Mojarra vs MyFaces on Servers

  • GlassFish, WildFly and Payara run Mojarra; Open Liberty and TomEE run MyFaces. The implementation is a property of the server choice, so a team that develops on one family and ships on the other meets the behavioural differences in Mojarra versus MyFaces for the first time in production.

  • The diagnostic page is a more reliable way to see which implementation runs than reading logs.

  • The "replace the implementation" experiments above show that the two implementations register CDI producers under the same EL names (requestScope, initParam, application), so they cannot coexist in one CDI container.

What Changed

  • Faces 4.0 / Jakarta EE 10. The namespace is jakarta.* and javax.faces is gone; the managed-bean machinery was removed in favour of CDI (see Backing Beans and CDI). Servers in this release line include GlassFish 7, Payara 6 and TomEE 10.

  • Faces 4.1 / Jakarta EE 11. Java 17 minimum; the servers in this page’s first four rows implement it (see the version matrix). TomEE 11 is a milestone only.

  • Faces 5.0 / Jakarta EE 12 (upcoming). Milestones exist (jakarta.faces-api:5.0.0-M9); no server in this page implements it, so nothing here describes 5.0 behaviour.

  • Server packaging. Open Liberty offers a container-only facesContainer-x.y feature for each Faces version from 2.2 to 4.1, and WildFly’s jsf subsystem allows several implementations to be installed side by side (any implementation at spec level 4.0 or higher).

Where the Books Are Outdated

  • The JSF 1.x and 2.x books describe deployment on the Sun Java System Application Server, JBoss AS 4 to 7, WebLogic and Tomcat. Those products and their javax.faces JARs, sun-web.xml and jboss-web.xml conventions belong to a different generation. Today the names are GlassFish, WildFly, Payara, Open Liberty and TomEE, and the descriptors are glassfish-web.xml, jboss-web.xml and server.xml features.

  • Several books assume you copy a Faces implementation JAR into the WAR for every server and declare it in web.xml. On a Jakarta EE server this is the cause of the failures shown above; the implementation is provided, and only a few servers allow replacing it.

  • The older books show the application server as a place for managed beans declared in faces-config.xml; on the servers on this page, beans are CDI beans discovered by the container.

  • Ant, hand-assembled directory deployments and "hot deploy by copying files" appear in the older books. The same idea is still possible (copying a WAR to deployments, apps or webapps), but the build is Maven.

  • The books predate containers and cloud deployments. Running the same WAR in Docker images of WildFly, Payara or Open Liberty is the current practice; see Dockerfile fundamentals, and the Docker part of the deployment and clustering page for the Faces side.

Pitfalls

  • Packaging the Faces API or implementation in the WAR on a server that bundles one. Result: class-loading errors or the CDI ambiguity shown in Replacing the Bundled Implementation. Keep the API provided, and add an implementation only where the server documents it.

  • Assuming the same Faces version everywhere. TomEE 10.2.0 is Faces 4.0, WildFly 41 carries Mojarra 4.1.13 while GlassFish 8.0.5 carries 4.1.17, and Open Liberty carries MyFaces 4.1.1. Read the version from the running server.

  • Creating domains with an empty admin password and forgetting it. --nopassword=true is for local, throw-away domains. A GlassFish or Payara admin console without a password on a reachable port is a remote administration hole.

  • Leaving the diagnostic or development pages deployed. Use Production as the project stage and remove pages that print implementation details.

  • Port conflicts. A second GlassFish, Payara or WildFly on the same host needs changed ports for every listener, not only HTTP.

  • Features absent in Open Liberty. The application fails at startup with a missing class when a feature (WebSocket, validation, JNDI) is not listed in server.xml.

  • Weld-only code on TomEE. TomEE uses OpenWebBeans.

The Tomcat and Weld page covers the "no application server" alternative where you choose the implementation yourself; the Spring Boot with JoinFaces and Quarkus pages cover running Faces inside other runtimes; the Deployment and Clustering page covers packaging, the Production stage, Docker images and session replication on these servers; the Persistence and Services page covers data sources and transactions that a full server provides, and the Testing page covers Arquillian against them. The Best Practices checklist lists the server-related rules, and Migrating to Jakarta Faces covers moving an application from an older server. See also Project Setup and Versions and Implementations.