Getting Started

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), Eclipse Mojarra 4.1.17 and Apache MyFaces Core 4.1.4, Java 17 or later (Java 21 for GlassFish 8), WildFly 41.0.1.Final, GlassFish 8.0.5, Apache Tomcat 11.0.26 and Weld weld-servlet-shaded 6.0.4.Final. The versions were checked on 2026-10-08. Faces 4.0 (Jakarta EE 10, Java 11 or later) behaves the same for everything on this page except where the text says otherwise; Faces 5.0 (Jakarta EE 12) is still a milestone (jakarta.faces-api 5.0.0-M9) and is mentioned only as upcoming.

The Tomcat and Weld part of the page was run for real: the exact WAR below was built with Maven and served by Apache Tomcat 11.0.26 (embedded) with Weld 6.0.4.Final, once with Mojarra 4.1.17 and once with MyFaces 4.1.4, and the initial request, an invalid postback and a valid postback were driven with curl. The lifecycle trace in the walk below is the output of that run. The WildFly and GlassFish commands are taken from their official guides and were not run on this page.

The goal is the smallest application that shows every moving part of Faces: a view, a backing bean, a form that posts back, a validation message and a navigation-free round trip. The rest of this section grows the same application, the Bookshelf (a catalogue of books, authors and reviews), one concept at a time; here it only has a text box that adds a book title to a shelf.

What You Build

A single page, index.xhtml, shows a text field and an Add button. The field is required. When it is empty the page is shown again with a message; when it has a title the page is shown again with a confirmation. Behind the page there is one Java class, ShelfBean.

flowchart TB browser["Browser
GET and POST /bookshelf/index.xhtml"] servlet["FacesServlet
runs the lifecycle"] view["index.xhtml
Facelets view with h:form,
h:inputText, h:commandButton"] bean["ShelfBean
@Named @RequestScoped"] browser --> servlet servlet --> view view -->|"EL: title property,
addBook action"| bean servlet -->|"HTML plus view state"| browser

The Project

The application is an ordinary Maven WAR. The tree below is everything it needs; the full production layout of the Bookshelf (templates, resources, contracts) is shown on the project setup page of this section.

Layout of the first application
bookshelf/
  pom.xml
  src/main/java/org/example/bookshelf/web/ShelfBean.java
  src/main/java/org/example/bookshelf/web/PhaseTracer.java      (optional, used in the lifecycle walk)
  src/main/webapp/index.xhtml
  src/main/webapp/WEB-INF/beans.xml
  src/main/webapp/WEB-INF/faces-config.xml
  src/main/webapp/WEB-INF/web.xml

The pom.xml below builds one WAR that targets either kind of server through Maven profiles. By default the Jakarta EE API is provided, because WildFly and GlassFish already contain Faces, CDI and everything else; the WAR is then tiny. The tomcat and myfaces profiles add what a plain servlet container lacks: a Faces implementation and a CDI container, as the Mojarra and MyFaces documentation require.

pom.xml for a WAR that runs on a Jakarta EE server or on Tomcat
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>bookshelf</artifactId>
  <version>1.0.0-SNAPSHOT</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>
    <!-- Faces, CDI, Servlet, EL ... all provided by a Jakarta EE 11 server -->
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>11.0.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

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

  <profiles>
    <!-- mvn clean package -Ptomcat : Tomcat 11 with Mojarra and Weld -->
    <profile>
      <id>tomcat</id>
      <dependencies>
        <dependency>
          <groupId>org.glassfish</groupId>
          <artifactId>jakarta.faces</artifactId>
          <version>4.1.17</version>
        </dependency>
        <dependency>
          <groupId>org.jboss.weld.servlet</groupId>
          <artifactId>weld-servlet-shaded</artifactId>
          <version>6.0.4.Final</version>
        </dependency>
      </dependencies>
    </profile>

    <!-- mvn clean package -Pmyfaces : Tomcat 11 with MyFaces and Weld -->
    <profile>
      <id>myfaces</id>
      <dependencies>
        <dependency>
          <groupId>org.apache.myfaces.core</groupId>
          <artifactId>myfaces-api</artifactId>
          <version>4.1.4</version>
        </dependency>
        <dependency>
          <groupId>org.apache.myfaces.core</groupId>
          <artifactId>myfaces-impl</artifactId>
          <version>4.1.4</version>
        </dependency>
        <dependency>
          <groupId>org.jboss.weld.servlet</groupId>
          <artifactId>weld-servlet-shaded</artifactId>
          <version>6.0.4.Final</version>
        </dependency>
      </dependencies>
    </profile>
  </profiles>
</project>

Three small descriptors complete the WAR. beans.xml switches CDI on for the archive, faces-config.xml declares the Faces 4.1 schema and registers the optional lifecycle listener used below, and web.xml sets two context parameters and a welcome file.

src/main/webapp/WEB-INF/beans.xml
<?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_1.xsd"
       version="4.1" bean-discovery-mode="annotated"/>
src/main/webapp/WEB-INF/faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config 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-facesconfig_4_1.xsd"
              version="4.1">
    <lifecycle>
        <phase-listener>org.example.bookshelf.web.PhaseTracer</phase-listener>
    </lifecycle>
</faces-config>
src/main/webapp/WEB-INF/web.xml
<?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_1.xsd"
         version="6.1">

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

    <context-param>
        <param-name>jakarta.faces.FACELETS_REFRESH_PERIOD</param-name>
        <param-value>1</param-value>
    </context-param>

    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

A few points about these files deserve a sentence each.

  • There is no FacesServlet declaration in web.xml. Both implementations register the servlet themselves when they find a Faces application (observed in the test runs), and Mojarra’s README states that without an explicit mapping the servlet is reachable on *.xhtml, *.jsf, *.faces and /faces/*. The running application above relied on exactly that. Declaring the mapping yourself (the project setup page does) is optional but removes the extra mappings.

  • jakarta.faces.PROJECT_STAGE defaults to Production when it is not set. In that stage the Facelets compiler does not look for changed pages. Development turns on extra diagnostics, and the explicit jakarta.faces.FACELETS_REFRESH_PERIOD (seconds between checks, -1 for never) makes edited XHTML visible after a page reload. Set it explicitly rather than relying on the stage: with only the stage set, an edited page was not picked up within a few seconds in the Mojarra 4.1.17 test run, while 1 picked it up on the next reload. Remove both parameters, or set the stage to Production, before deploying for real.

  • The namespace and schema versions (web-app_6_1, beans_4_1, web-facesconfig_4_1) are the Jakarta EE 11 ones. A Faces 4.0 application uses web-app_6_0, beans_4_0 and web-facesconfig_4_0 with version="4.0".

  • bean-discovery-mode="annotated" means that only classes with a bean-defining annotation (@RequestScoped, @ApplicationScoped, …​) become beans. That is what the bean below carries.

The Backing Bean

A backing bean is a plain Java class that the view reads and writes through Expression Language. In current Faces it is a CDI bean: @Named gives it an EL name and a scope annotation from jakarta.enterprise.context gives it a lifetime. @RequestScoped means one instance per HTTP request, which is the right default for a form that does not need to remember anything between requests.

ShelfBean.java
package org.example.bookshelf.web;

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

@Named
@RequestScoped
public class ShelfBean {

    private String title;
    private String message;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getMessage() {
        return message;
    }

    /** Action method: runs only when the submitted data is valid. */
    public void addBook() {
        message = "Added \"" + title + "\" to the shelf.";
    }
}

The default EL name of a @Named bean is the simple class name with the first letter in lower case, so ShelfBean is shelfBean. The getters and setters are what EL uses: #{shelfBean.title} calls getTitle() when the page is rendered and setTitle(...) when a value is submitted. Note that the getters only return fields. Anything slow or with side effects belongs in an action method such as addBook, because a getter can be called several times in one request.

Where the books are outdated: the books written for JSF 1.x and 2.x declare this class with @ManagedBean (or a <managed-bean> element in faces-config.xml) and a javax.faces.bean scope. Those annotations were deprecated in Faces 2.3 and removed in Faces 4.0, together with the javax namespace itself, so the same class must be written with CDI as above. The Jakarta EE Tutorial’s configuration chapter still contains passages about configuring managed beans that describe what Faces 4.0 removed; do not follow them.

The View

A Faces view is written in Facelets, the view declaration language, as an XHTML page. The tags come from the standard HTML component library, bound to the URN jakarta.faces.html.

index.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="jakarta.faces.html">
<h:head>
    <title>Bookshelf</title>
</h:head>
<h:body>
    <h1>Bookshelf</h1>
    <h:form id="shelfForm">
        <h:messages id="msgs"/>
        <h:outputLabel for="title" value="Title: "/>
        <h:inputText id="title" value="#{shelfBean.title}" required="true"
                     requiredMessage="Please type a title."/>
        <h:commandButton id="add" value="Add" action="#{shelfBean.addBook}"/>
        <p><h:outputText value="#{shelfBean.message}" rendered="#{not empty shelfBean.message}"/></p>
    </h:form>
</h:body>
</html>

Each tag has a job.

Table 1. The tags used in the first page
Tag What it does

h:head, h:body

Render head and body, and give Faces a place to add resources (scripts and style sheets) that components request. Use them instead of plain head and body.

h:form

Renders an HTML form that posts back to the same URL, plus the hidden view state field. Every input and command of a page must be inside a form. Its id becomes the prefix of its children’s client ids (shelfForm:title).

h:inputText

Renders a text input. value is a value expression bound to a bean property; required="true" makes an empty submission invalid; requiredMessage replaces the default text of that error.

h:commandButton

Renders a submit button. action is a method expression: the bean method to call, and later the outcome used for navigation.

h:messages

Renders every message queued during the request (validation, conversion or application messages), as a list. It is how the user sees why a submission was rejected. h:message for="..." shows the messages of one component.

h:outputLabel, h:outputText

A label tied to an input by its for attribute, and escaped text output. rendered removes a component from the page when its expression is false.

The #{...} syntax is deferred Expression Language: Faces evaluates it later, as part of the lifecycle, and it can both read and write. That is the key difference from a one-shot ${...} expression of a JSP-style page.

Source: h:form, h:messages, h:commandButton and h:outputText (VDL 4.1)

What changed across versions for this page: the jakarta.faces.html URN replaced http://xmlns.jcp.org/jsf/html (which itself replaced http://java.sun.com/jsf/html) in Faces 4.0. The old namespaces still worked in the Mojarra 4.1.17 test run, but their removal is planned for Faces 5.0, so new pages should use the URNs. JSP views, which the first books use, were removed in Faces 4.0; a view is always Facelets XHTML now.

Run It on WildFly or GlassFish

A Jakarta EE server already contains a Faces implementation and a CDI container, so the default build is enough: no profile, and the Jakarta EE API stays provided.

Build the server flavour of the WAR
mvn clean package
# result: target/bookshelf.war

WildFly 41. The Getting Started guide asks for Java SE 17 or later. Download the distribution from https://wildfly.org/downloads/ and unzip it; $JBOSS_HOME below is the unzipped directory. Start the standalone server, then deploy by copying the archive: the deployment scanner notices it. The application is served at http://localhost:8080/bookshelf/, because by default the context root is the WAR file name without its extension.

Start WildFly and deploy the WAR
$JBOSS_HOME/bin/standalone.sh                      # terminal 1: leave it running
cp target/bookshelf.war $JBOSS_HOME/standalone/deployments/    # terminal 2

The command-line interface does the same and is better for repeated redeployments. After ./jboss-cli.sh --connect (which connects to localhost:9990), deploy takes the path of the archive and --force replaces an existing deployment of the same name; undeploy bookshelf.war removes it.

Deploy or redeploy with the WildFly CLI
$JBOSS_HOME/bin/jboss-cli.sh --connect
deploy target/bookshelf.war --force

GlassFish 8. The Installation Guide states that Eclipse GlassFish 8 requires Java 21 or higher, and the Quick Start Guide describes it as the Jakarta EE 11 platform. Download and unzip it from https://glassfish.org/download.html (it unpacks to a glassfish8 directory, as-install below), start the default domain and deploy with asadmin. The HTTP port is 8080 and the Administration Console is on port 4848. --force=true redeploys an application that already exists, and undeploy takes the application name, not the file name.

Start GlassFish and deploy the WAR
as-install/bin/asadmin start-domain
as-install/bin/asadmin deploy target/bookshelf.war
# redeploy after a rebuild
as-install/bin/asadmin deploy --force=true target/bookshelf.war
# remove it
as-install/bin/asadmin undeploy bookshelf

Open http://localhost:8080/bookshelf/ in the browser. Both servers log deployment problems to their server log, which is the first place to look if the page does not appear.

Run It on Tomcat 11 with Weld

Tomcat is a servlet container, not a Jakarta EE server. It has the Servlet 6.1 and Expression Language 6.0 APIs but no Faces and no CDI, so the WAR carries both. Mojarra’s README lists the Maven coordinates for servlet containers (org.glassfish:jakarta.faces 4.1.x and org.jboss.weld.servlet:weld-servlet-shaded 6.0.x) and MyFaces lists Java 17, Servlet 6.1, EL 6.0 and CDI 4.1 as its requirements; the tomcat and myfaces profiles above are exactly that. Tomcat 11.0.x supports Java 17 and later.

Build the Tomcat flavour of the WAR
mvn clean package -Ptomcat        # Mojarra 4.1.17 + Weld 6.0.4.Final
# or
mvn clean package -Pmyfaces       # MyFaces 4.1.4 + Weld 6.0.4.Final

The two profiles produce the same page. Only the jars in WEB-INF/lib differ: for the first one jakarta.faces-4.1.17.jar, weld-servlet-shaded-6.0.4.Final.jar and jakarta.annotation-api-3.0.0.jar; for the second myfaces-api-4.1.4.jar, myfaces-impl-4.1.4.jar, the Weld jar and the annotation jar. Use one implementation per WAR, never both.

To run it, download Tomcat 11 from https://tomcat.apache.org/download-11.cgi, unzip it, copy the WAR into webapps and start the server. With the default configuration a running Tomcat deploys a WAR dropped into webapps and redeploys it when a newer file replaces it. The application is then at http://localhost:8080/bookshelf/.

Deploy to Tomcat 11
cp target/bookshelf.war "$CATALINA_HOME/webapps/"
"$CATALINA_HOME/bin/catalina.sh" run        # foreground, logs in the terminal

The wiring that people expect to write by hand turned out to be unnecessary, and it helps to know why.

  • No listener in web.xml. The Weld reference guide says the listener org.jboss.weld.environment.servlet.Listener needs registering in web.xml only for a container without ServletContainerInitializer support, or to control listener ordering. In the test run Weld started by itself ("Initialize Weld using ServletContainerInitializer" in the log), and Faces, found in WEB-INF/lib, installed itself the same way (Mojarra’s ConfigureListener, or MyFaces' MyFacesContainerInitializer).

  • No META-INF/context.xml for this page. Weld’s guide describes a context.xml resource named BeanManager (factory org.jboss.weld.resources.ManagerObjectFactory) plus a matching resource-env-ref in web.xml. That binds the CDI BeanManager to JNDI so that other code can look it up at java:comp/env/BeanManager. Faces beans did not need it in the test run; add it only when something in the WAR looks the manager up through JNDI.

  • beans.xml is required. Without WEB-INF/beans.xml the test run found no shelfBean, even though the class has @RequestScoped: Weld in a servlet container did not discover the bean without it. This is the likeliest cause of the first-run error in the last section.

  • Harmless start-up lines. Weld logs that it falls back to Java reflection for annotated bean discovery (add io.smallrye:jandex to speed start-up up) and that transactional services are not available. Neither is a problem for a Faces application.

The Weld reference guide states that Tomcat 10.1 is the supported version, because that is the one it is tested with. The test run on Tomcat 11.0.26 with Weld 6.0.4.Final worked, and Mojarra’s README names weld-servlet-shaded 6.0.x for servlet containers. Weld 7.0.0.Final also exists, but it is the CDI 5 line for Jakarta EE 12; stay on 6.0.x with Faces 4.1. The Tomcat and Weld page of this section covers the full Tomcat setup (JNDI, JSTL, validation, production hardening).

Mojarra or MyFaces

Both implementations implement the same specification, so the page and the bean did not change between the two runs. The differences visible at this level are small, and worth knowing because they surface in error messages and in browser developer tools.

Table 2. What differed between the two runs of the same WAR
Observation Mojarra 4.1.17 MyFaces 4.1.4

Extra hidden field in the form

none besides the view state

shelfForm_SUBMIT, which MyFaces expects in the postback

HTML output

indented as in the Facelets source

compact, whitespace between tags removed

Development stage banner

none seen

a console warning that MyFaces is in development mode and an empty message container at the end of the body

Server log in Development

an INFO line announcing Mojarra 4.1.17 for the context

an INFO line "MyFaces Core has started" with the elapsed time

Those are rendering details, not contracts: never write code or tests that depend on them. The configuration parameters differ too: MyFaces documents its own org.apache.myfaces.* parameters beside the standard jakarta.faces.* ones in its configuration table, and Mojarra uses com.sun.faces.* ones. A parameter with one of those prefixes does nothing on the other implementation. Stay with the jakarta.faces.* parameters unless you have chosen an implementation.

The Edit, Deploy and Reload Loop

Development goes fastest when you know which edits need a redeployment and which only need a browser refresh.

flowchart TD edit(["Edit"]) --> kind{"What changed?"} kind -->|"XHTML page,
template or resource"| save["Save the file
in src/main/webapp"] kind -->|"Java class,
web.xml, faces-config.xml,
beans.xml, pom.xml"| build["mvn clean package"] save --> sync["Copy to the deployed WAR
or let the IDE or server
sync the exploded WAR"] build --> deploy["Redeploy the WAR
asadmin deploy --force=true,
jboss-cli deploy --force,
or replace the file in webapps"] sync --> reload["Reload the page in the browser"] deploy --> reload reload --> look{"Looks right?"} look -->|"No: read the
server log"| edit look -->|"Yes"| done(["Next change"]) done --> edit
  • XHTML only. The Facelets compiler re-reads a page when its refresh period has elapsed, so a saved edit shows after the next reload. This works against an exploded (unzipped) deployment, which every server and IDE can produce from the project; for a zipped WAR you must redeploy, because the file the server reads is inside the archive.

  • Anything compiled or configured. Java classes, faces-config.xml and beans.xml are read when the application starts, so those edits need a redeploy. With the default server-side state saving, Faces keeps the saved view of each page in the user’s session, so the page that was open before the redeployment is stale and its next postback fails with a ViewExpiredException; reload the page first. Both are normal and not a sign that anything is broken.

  • Hot-swapping Java. A debugger session can replace method bodies without a redeploy, and servers offer hot-reload features, but a changed field, annotation or class set still needs a redeploy. The project setup page and the testing page of this section go deeper.

A Walk Through the Lifecycle

Faces processes every request through a lifecycle of six phases, run by the FacesServlet: Restore View, Apply Request Values, Process Validations, Update Model Values, Invoke Application and Render Response. The request processing lifecycle page of this section explains them all. This is what each does for the page above.

To see it, the optional PhaseTracer class prints a line before and after every phase. It implements jakarta.faces.event.PhaseListener, listens to PhaseId.ANY_PHASE, and is registered by the <lifecycle> element of faces-config.xml shown earlier.

PhaseTracer.java
package org.example.bookshelf.web;

import java.util.logging.Logger;

import jakarta.faces.event.PhaseEvent;
import jakarta.faces.event.PhaseId;
import jakarta.faces.event.PhaseListener;

public class PhaseTracer implements PhaseListener {

    private static final Logger LOG = Logger.getLogger(PhaseTracer.class.getName());

    @Override
    public void beforePhase(PhaseEvent event) {
        LOG.info("before " + event.getPhaseId());
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        LOG.info("after  " + event.getPhaseId());
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }
}

The initial request

The user types the address, so the browser sends a GET. The log of the test run shows two phases:

Log of the initial GET
before RESTORE_VIEW 1
after  RESTORE_VIEW 1
before RENDER_RESPONSE 6
after  RENDER_RESPONSE 6
  • Restore View. There is no view state in the request, so Faces creates an empty view for /index.xhtml and, because nothing was submitted, goes straight to Render Response.

  • Render Response. Facelets reads index.xhtml and builds the component tree: a form, a messages component, an input, a button and an output. EL expressions are evaluated to produce the output (#{shelfBean.title} calls getTitle(), which returns null, shown as an empty field). Each component then writes its HTML. The form also writes a hidden jakarta.faces.ViewState field, and the server saves the tree so that it can be restored when the form comes back.

Source: Jakarta Faces 4.1 specification: Standard Request Processing Lifecycle Phases (the log is the output of the Tomcat 11 test run described above)

A postback that fails validation

The user clicks Add with an empty field. The browser sends a POST to the same URL carrying the form fields and the view state: this is a postback.

Log of a postback with an empty title
before RESTORE_VIEW 1
after  RESTORE_VIEW 1
before APPLY_REQUEST_VALUES 2
after  APPLY_REQUEST_VALUES 2
before PROCESS_VALIDATIONS 3
after  PROCESS_VALIDATIONS 3
before RENDER_RESPONSE 6
after  RENDER_RESPONSE 6
  • Restore View. The view state in the request identifies the tree saved earlier, and Faces restores it.

  • Apply Request Values. Each component reads its own value from the request parameters; the input stores the empty string as its submitted value. Nothing has touched the bean yet.

  • Process Validations. The input is required, and an empty submitted value is invalid, so Faces queues a FacesMessage with the text of requiredMessage and skips ahead to Render Response after this phase.

  • Skipped phases. Update Model Values and Invoke Application do not run once validation failed: setTitle and addBook were never called, and the bean is not changed. Faces jumps directly to Render Response.

  • Render Response. The same tree is written again. h:messages now has a message and shows it; the input keeps what the user typed. The response is 200 OK with the page, not an error.

Source: Jakarta Faces 4.1 specification: Standard Request Processing Lifecycle Phases (the log is the output of the Tomcat 11 test run described above)

A successful postback

With a title, for example Dune, the lifecycle runs all six phases:

Log of a postback with a title
before RESTORE_VIEW 1
after  RESTORE_VIEW 1
before APPLY_REQUEST_VALUES 2
after  APPLY_REQUEST_VALUES 2
before PROCESS_VALIDATIONS 3
after  PROCESS_VALIDATIONS 3
before UPDATE_MODEL_VALUES 4
after  UPDATE_MODEL_VALUES 4
before INVOKE_APPLICATION 5
after  INVOKE_APPLICATION 5
before RENDER_RESPONSE 6
after  RENDER_RESPONSE 6
  • Process Validations. The value Dune passes the required check.

  • Update Model Values. Only now is the bean written: Faces calls setTitle("Dune") on a new request-scoped ShelfBean instance.

  • Invoke Application. The command button was the one clicked, so its action runs: addBook() builds the message.

  • Render Response. Because addBook returns nothing (void), there is no navigation outcome and the same view is shown again. The bean instance of this very request still holds the message, so h:outputText renders Added "Dune" to the shelf.. The next request starts with a fresh bean and an empty message, which is why a page that must show a confirmation after a redirect needs the flash scope; the flash and post-redirect-get page of this section covers it.

Table 3. The phases at a glance for the Bookshelf page
Phase Initial GET POST, title empty POST, title typed

1 Restore View

creates an empty view, goes to phase 6

restores the saved tree

restores the saved tree

2 Apply Request Values

skipped

input keeps the submitted text

input keeps the submitted text

3 Process Validations

skipped

required fails, message queued

passes

4 Update Model Values

skipped

skipped

setTitle called

5 Invoke Application

skipped

skipped

addBook called

6 Render Response

builds the tree, writes HTML and view state

same tree with the message

same tree with the confirmation

If you know ASP.NET Web Forms, this is the same idea as its postback and page lifecycle, with the difference that Faces separates conversion and validation from the model update and never calls the action when the data is invalid; the Web Forms page lifecycle and postback page compares them.

Common First-Run Errors

The errors below are the ones met most often with a first Faces application. The first three were reproduced with the application of this page.

Table 4. Errors, causes and fixes
Symptom Cause Fix

Target Unreachable, identifier [shelfBean] resolved to null (a PropertyNotFoundException) on the first postback, and a blank field on a plain GET

EL found no bean of that name. On Tomcat the test run showed it when WEB-INF/beans.xml was missing; it also happens when the name is mistyped (shelfbean), when @Named or the scope annotation is missing, when the bean imports an old javax.* annotation, or when bean-discovery-mode is none.

Check the EL name (class name with a lower-case first letter), keep @Named and @RequestScoped from jakarta.*, and add beans.xml.

The WAR fails to start on Tomcat with ClassNotFoundException: jakarta.enterprise.inject.spi.el.ELAwareBeanManager

Mojarra 4.1 needs CDI 4.1 and the WAR has no CDI container.

Add org.jboss.weld.servlet:weld-servlet-shaded 6.0.x to the WAR (the tomcat profile). Do not add it on WildFly or GlassFish, which already have CDI.

jakarta.faces.application.ViewExpiredException: ... could not restore the view /index.xhtml (HTTP 500) after a redeploy or after a long pause

The posted form carries the view state of a tree the server no longer has: the application restarted, the session expired, or the server dropped it to save memory.

Reload the page before submitting again. Tune the session timeout for real applications, and use the exception handling page of this section to show a friendly page.

Clicking Add shows the same page and seems to do nothing

A validation or conversion error queued a message but the page has no h:messages or h:message. The lifecycle jumped to Render Response and addBook never ran.

Add h:messages (or h:message for="...") near the form and read it; it names the failing component.

The literal text #{shelfBean.title} appears in the browser, or the browser offers the .xhtml file for download

The request did not go through the FacesServlet: the URL pattern is not mapped, or the file was opened from disk.

Request the page through the server with a mapped suffix (*.xhtml is the default), and check the FacesServlet mapping if you declared one.

404 for /bookshelf/

The context root is not what you assumed, the application failed to deploy, or the welcome file is missing.

Check the server log for a deployment error. WildFly, GlassFish and Tomcat use the WAR file name as the context root by default; keep index.xhtml as the welcome file.

UnsupportedClassVersionError at start-up

The WAR was compiled for a newer Java than the server runs.

Compile with maven.compiler.release 17 (the lowest Java for Faces 4.1) and run on Java 17 or later; GlassFish 8 needs Java 21.

Edits to the page do not appear

The project stage is Production (the default) and the refresh period is -1, or the server serves a zipped WAR.

Set jakarta.faces.PROJECT_STAGE to Development and jakarta.faces.FACELETS_REFRESH_PERIOD to a small number, and deploy exploded, as shown in the loop above.

Address already in use on port 8080

Another server, or a previous copy of the same one, holds the port.

Stop it, or start the new server on another port.

Where the Books Are Outdated

The first application in the Jakarta Faces 2.0 reference and in the older titles of the bibliography is written for JSP views (or early Facelets) and javax.faces. Compared with this page:

  • A view was a .jsp file with <%@ taglib %> directives, <f:view> and <h:form>, mapped through /faces/* or *.jsf. JSP support was removed in Faces 4.0; the view is Facelets XHTML, and mapping the FacesServlet is optional.

  • Beans were declared in faces-config.xml with <managed-bean> or with @ManagedBean. Both are gone in Faces 4.0 in favour of CDI @Named and jakarta.enterprise.context scopes.

  • The API was javax.faces.* and the namespaces were http://java.sun.com/jsf/*; they are jakarta.faces.* and the jakarta.faces.html URN family, with the old URIs planned for removal in Faces 5.0.

  • Faces on a plain servlet container needed only a Faces implementation; today CDI is mandatory for the framework, so Tomcat 11 needs Weld as well, while WildFly and GlassFish need nothing.