Configuration and Project Stages

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, with Java 17 or later. Faces 4.0 (Jakarta EE 10, Java 11 or later) has the same configuration model; the differences are collected in What Changed and nothing here was run on 4.0. The examples were compiled and deployed in a scratch Bookshelf WAR on Apache Tomcat 11.0.26 with Weld 6.0.4.Final, once with Mojarra and once with MyFaces, and driven with curl: every status code, HTML fragment, log line and "what happened" statement below comes from those runs (checked on 2026-10-08). A parameter described without such a statement is described from the specification, the API documentation or the Mojarra and MyFaces parameter references only, and the page says so. Faces 5.0 is still a milestone release (5.0.0-M9 on 2026-09-18) and nothing here describes it as current behaviour.

The page is the reference for how a Faces application is configured and how that configuration behaves. The Project Setup page shows how to put a first web.xml and faces-config.xml into a project; this page goes through every standard context parameter, the ProjectStage and its real effects, the faces-config.xml document, the relation between annotations and XML, the loading and ordering of several configuration files, and the parameters that only Mojarra or only MyFaces understand. Faces is configured from a handful of places, and most configuration mistakes come from not knowing which of them wins.

Where Faces Reads Its Configuration

Five mechanisms feed the configuration of a Faces application. They are independent and they overlap on purpose, so that a library can configure itself, an application can override the library, and an operations team can change a setting without rebuilding the WAR.

Table 1. The sources of Faces configuration
Source What it configures Who writes it

Context parameters (<context-param> in web.xml or web-fragment.xml)

Behaviour switches: project stage, Facelets, state saving, validation, mapping. Names start with jakarta.faces. for the standard ones, com.sun.faces. for Mojarra and org.apache.myfaces. for MyFaces.

The application, or a library through its web-fragment.xml

JNDI environment entry java:comp/env/faces/ProjectStage

The project stage only.

The operations team, in the server or in the descriptor

faces-config.xml documents

Objects and registrations: locales, resource bundles, navigation rules, converters, validators, components, renderers, phase listeners, factories, flows, protected views, ordering.

The application and every library that ships a META-INF/faces-config.xml

Annotations (@FacesConverter, @FacesValidator, @FacesComponent, @FacesRenderer, @FacesBehavior, …​)

The same registrations as the XML, written next to the class.

The application and libraries

Code: ApplicationConfigurationPopulator, Application methods

Registrations computed at startup.

Frameworks and integrations

The sequence at startup is the same on both implementations. The container merges the descriptors, Faces decides whether it has anything to do, collects the configuration resources, sorts them, combines the annotation metadata with them, and publishes PostConstructApplicationEvent when it is finished. All of this happens before the first request is processed, which is why configuration errors are deployment errors, not request errors.

flowchart TD A["Servlet container starts the web application"] --> B["Merge web.xml and web-fragment.xml, web.xml wins on conflicts"] B --> C{"Does Faces start?"} C -- "a FacesServlet mapping, WEB-INF/faces-config.xml or a Faces annotation is found" --> D["Collect configuration resources"] C -- "none of these" --> Z["Faces may stay inactive"] D --> D1["META-INF/faces-config.xml and META-INF/*.faces-config.xml of JARs and WEB-INF/classes"] D --> D2["Files listed in jakarta.faces.CONFIG_FILES"] D --> D3["ApplicationConfigurationPopulator services"] D1 --> E["Sort: relative ordering of the named files"] D2 --> E D3 --> E E --> F["WEB-INF/faces-config.xml goes last, its absolute-ordering is applied"] F --> G["Combine with annotation metadata, an XML entry beats an annotation for the same id"] G --> H["Discover resource library contracts and flows"] H --> I["Publish PostConstructApplicationEvent"] I --> J["First request"]
Figure 1. From container start to the first request

Two details of the diagram deserve a sentence. The FacesServlet is mapped for you when you do not map it yourself and one of these is true: WEB-INF/faces-config.xml exists, a META-INF/faces-config.xml or *.faces-config.xml file exists in a JAR, jakarta.faces.CONFIG_FILES is declared, or the container initializer found a class with a Faces annotation (the API documentation of FacesServlet lists the annotations); the section on Extensionless Mapping and the FacesServlet Mappings shows what that automatic mapping covers. And the Mojarra reference adds a case for applications with no web.xml and no faces-config.xml at all (a Spring Boot application that registers everything programmatically): com.sun.faces.forceLoadConfiguration starts the configuration even though none of the usual triggers is present.

Startup and Shutdown

Startup ends with an event and shutdown starts with one. At startup the specification (11.3.2) has the runtime publish PostConstructApplicationEvent immediately after all configuration resources have been processed, flows and resource library contracts included; the events page lists it as an application system event. A parsing error in a configuration resource is fatal to startup and the container does not make the application available. At shutdown (11.3.4) the runtime must make sure that FacesContext.getCurrentInstance() returns successfully while the application is going down (the API documentation of FacesContext says that getApplication() is valid during startup and shutdown), then publish PreDestroyApplicationEvent, and finally call FactoryFinder.releaseFactories(). So PreDestroyApplicationEvent is the hook for releasing what an application-wide extension holds (a thread pool, a cache, a connection), and by the time the factories are released nothing of Faces may be used any more.

A listener that releases resources at shutdown, registered in faces-config.xml
package org.example.bookshelf.config;

import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import java.util.logging.Logger;

/** Runs when the web application stops: close what the application opened at startup. */
public class ShutdownHook implements SystemEventListener {

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

    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        FacesContext ctx = FacesContext.getCurrentInstance();
        String line = "shutdown: FacesContext present = " + (ctx != null)
                + ", project stage = " + (ctx == null ? "?" : ctx.getApplication().getProjectStage());
        LOG.info(line);
        System.err.println(line);
        // close the catalogue cache, stop the import scheduler ...
    }

    @Override
    public boolean isListenerForSource(Object source) {
        return true;
    }
}
<application>
    <system-event-listener>
        <system-event-listener-class>org.example.bookshelf.config.ShutdownHook</system-event-listener-class>
        <system-event-class>jakarta.faces.event.PreDestroyApplicationEvent</system-event-class>
        <source-class>jakarta.faces.application.Application</source-class>
    </system-event-listener>
</application>

Run on Mojarra 4.1.17 and MyFaces 4.1.4 on Tomcat 11.0.26 (the embedded Tomcat was stopped with SIGTERM, with a hook that stops the context): both implementations published the event, and in the listener FacesContext.getCurrentInstance() was not null and getProjectStage() answered Production. In the first run the java.util.logging line of the listener did not appear in the log at all (the cause was not investigated), so the listener also writes to System.err, which did appear; if a shutdown message is missing in your logs, check the logging configuration before suspecting the event. Treat the FacesContext of such a listener as a way to reach the Application, not as a request: the specification only promises the context and the application object.

Context Parameters

A context parameter is a name and a string value that the servlet container exposes through the ServletContext. Faces reads them once, at startup, so changing one needs a redeploy or restart. (The Facelets refresh period is no exception: it sets how often the view files are checked, and is itself read at startup.)

Setting a Parameter

There are three places, and they combine under the rules of the Servlet specification, not of Faces.

The Bookshelf web.xml with the three development parameters
<?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>
  <context-param>
    <param-name>jakarta.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param>

  <servlet>
    <servlet-name>facesServlet</servlet-name>
    <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>

A JAR can contribute parameters of its own through a META-INF/web-fragment.xml. A library that needs a Faces parameter to work (a tag library registered through jakarta.faces.FACELETS_LIBRARIES, for example) ships it that way, so the application does not have to copy it:

A web-fragment.xml in a library JAR
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment 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-fragment_6_1.xsd"
              version="6.1">
  <name>bookshelfDefaults</name>
  <context-param>
    <param-name>jakarta.faces.FACELETS_SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
  </context-param>
</web-fragment>

Source: Jakarta Faces 4.1 API: FacesServlet (the API documentation of FacesServlet names web.xml and web-fragment.xml as the places where Faces looks)

In the scratch run a fragment that set jakarta.faces.PROJECT_STAGE to Development was honoured by both implementations when web.xml set nothing, and lost to web.xml when web.xml set Production; a second parameter of the same fragment that web.xml did not mention still applied. That is the Servlet rule (the main descriptor overrides a fragment), so a library’s defaults can be overridden by the application and never the other way round.

The third place is the container itself. Tomcat’s context.xml can define an environment entry, and the project stage (and only the project stage) is read from JNDI before the context parameter is consulted; the section on How the Stage Is Determined shows it. Servers other than Tomcat have their own way to define the same JNDI name.

How Values Are Read

The values are strings and each parameter has its own rule. The specification states the rules of the boolean ones the same way for most of them: the runtime calls toLowerCase().equals("true") on the string, so true, TRUE and True enable the parameter, and anything else (yes, 1, on) leaves it off. jakarta.faces.STATE_SAVING_METHOD is compared ignoring case, jakarta.faces.PROJECT_STAGE is not: it has to be the exact name of a constant. The lists are separated by different characters depending on the parameter (semicolon for the Facelets libraries, decorators and view mappings, comma for CONFIG_FILES and FULL_STATE_SAVING_VIEW_IDS, a space for RESOURCE_EXCLUDES and FACELETS_SUFFIX); the tables below say which. A mistyped parameter name is not an error: it is simply never read, and the application runs with the default. In the scratch run jakarta.faces.PROJECT_STAG, a made-up com.sun.faces.numberOfLogicalViewz and the old javax.faces.PROJECT_STAGE were all ignored on both implementations, without a log line, while the correct jakarta.faces.PROJECT_STAGE next to them set Development. Both implementations can print what they read, which is the quickest way to find a typo (the sections on Mojarra Parameters and MyFaces Parameters show how).

The Standard Parameters

The specification requires every implementation to support the parameters of its section 11.1.3, and a few more are defined only in the API documentation. The next tables list all of them, grouped by purpose, with the default, the release that introduced them and what they do. "Tested" in the last column means that the behaviour was run in the scratch application and is described under the table or in the section named there.

Table 2. Startup, mapping and lifecycle
Parameter Default Since Effect Tested

jakarta.faces.PROJECT_STAGE

Production

2.0

Where the application is in its lifecycle: Development, UnitTest, SystemTest or Production. See Project Stages.

yes

jakarta.faces.CONFIG_FILES

none

1.0

Comma-separated, context-relative paths of additional configuration files. /WEB-INF/faces-config.xml is always loaded and is ignored if listed. See The CONFIG_FILES Parameter.

yes

jakarta.faces.LIFECYCLE_ID

DEFAULT

1.0

Identifier of the Lifecycle the FacesServlet uses, as registered with the LifecycleFactory. Only useful with a custom lifecycle.

no

jakarta.faces.AUTOMATIC_EXTENSIONLESS_MAPPING

false

4.0

Maps the FacesServlet to the extensionless variant of every view. See Extensionless Mapping and the FacesServlet Mappings.

yes

jakarta.faces.DISABLE_FACESSERVLET_TO_XHTML

false

2.3

Switches off the automatic *.xhtml mapping. See Extensionless Mapping and the FacesServlet Mappings for the risk.

yes

jakarta.faces.VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS

false

2.3

Exceptions thrown by phase listeners installed on the UIViewRoot are queued to the ExceptionHandler instead of being logged and swallowed.

no

Table 3. Facelets
Parameter Default Since Effect Tested

jakarta.faces.FACELETS_REFRESH_PERIOD

0; -1 in Production

2.0

Seconds between two checks whether a view file changed. -1 never checks again.

yes

jakarta.faces.FACELETS_SKIP_COMMENTS

false

2.0

XML comments of the Facelets source are not delivered to the client.

yes

jakarta.faces.FACELETS_BUFFER_SIZE

1024

2.0

Response buffer size, in bytes, while the view renders. -1 leaves the container’s size. A larger buffer lets an error that occurs late in rendering still replace the whole response.

no

jakarta.faces.FACELETS_SUFFIX

.xhtml

2.0

Space-separated list of the extensions under which view files may be stored; the first physical file found wins.

yes

jakarta.faces.FACELETS_VIEW_MAPPINGS

none

2.0

Semicolon-separated extensions (*.xhtml) or prefixes (/user/*) that are Facelets views whatever their extension.

yes

jakarta.faces.FACELETS_LIBRARIES

none

2.0

Semicolon-separated, web-root-relative paths (each starting with /) of tag library files to load. Covered on the Tag Files and Tag Libraries page.

yes (earlier page)

jakarta.faces.FACELETS_DECORATORS

none

2.0

Semicolon-separated class names of TagDecorator implementations applied when a view is compiled.

yes

jakarta.faces.DEFAULT_SUFFIX

.xhtml

1.1

Deprecated for removal since 4.0 because Jakarta Pages views are gone; use FACELETS_SUFFIX.

no

Table 4. State saving
Parameter Default Since Effect Tested

jakarta.faces.STATE_SAVING_METHOD

server

1.0

server keeps the view state in the session, client sends it to the browser in the hidden field jakarta.faces.ViewState. Compared ignoring case.

yes

jakarta.faces.SERIALIZE_SERVER_STATE

false

2.2

With server state saving, guarantees that the stored state is serializable, so that a NotSerializableException appears in the request that caused it.

no

jakarta.faces.PARTIAL_STATE_SAVING

true

2.0

Saves only the difference from the initial state. Deprecated since 4.1 (full state saving will go away).

no

jakarta.faces.FULL_STATE_SAVING_VIEW_IDS

none

2.0

Comma-separated view ids that use full state saving. Deprecated since 4.1.

no

Table 5. Conversion and validation
Parameter Default Since Effect Tested

jakarta.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE

false

2.0

The default time zone of f:convertDateTime is the JVM’s, not GMT. See Conversion and Validation Parameters.

yes

jakarta.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE

false

2.2

A required input is validated even when its request parameter is absent. See Conversion and Validation Parameters.

yes

jakarta.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL

false

2.0

An empty submitted string becomes null before conversion and validation. Covered on the Validators page.

yes (earlier page)

jakarta.faces.VALIDATE_EMPTY_FIELDS

auto

2.0

Validators also run on empty values; auto means yes when Jakarta Validation is on the class path. Covered on the Validators page.

yes (earlier page)

jakarta.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR

false (Mojarra)

2.0

Do not add the Jakarta Validation validator to every input. Covered on the Bean Validation page.

no

jakarta.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN

false

2.3

Without it f:validateWholeBean is a no-op. Covered on the Bean Validation page.

yes (earlier page)

Table 6. Rendering, resources, windows and WebSocket
Parameter Default Since Effect Tested

jakarta.faces.SEPARATOR_CHAR

:

2.0

The character between the segments of a client id; only its first character is used.

yes

jakarta.faces.RESOURCE_EXCLUDES

.class .jsp .jspx .properties .xhtml .groovy

2.0

Extensions the resource handler never serves; a configured value replaces the default entirely.

no

jakarta.faces.WEBAPP_RESOURCES_DIRECTORY

resources

2.2

Web-root-relative directory of the resources; must not start with /. Covered on the resource handling page.

no

jakarta.faces.WEBAPP_CONTRACTS_DIRECTORY

contracts

2.2

Web-root-relative directory of the resource library contracts. Covered on the resource library contracts page.

no

jakarta.faces.CLIENT_WINDOW_MODE

none

2.2

Enables the ClientWindow feature behind @ClientWindowScoped. Covered on the Scopes page.

yes (earlier page)

jakarta.faces.NUMBER_OF_CLIENT_WINDOWS

10

4.0

How many client windows the session remembers. Covered on the multi-window page and the Scopes page.

yes (earlier page)

jakarta.faces.ENABLE_WEBSOCKET_ENDPOINT

false

2.3

Registers the WebSocket endpoint behind f:websocket. Covered on the WebSocket Push page.

yes (earlier page)

jakarta.faces.WEBSOCKET_ENDPOINT_PORT

0 (the port of the request)

2.3

Port of the WebSocket endpoint when it differs from the HTTP port.

no

Three rows need a comment on the source of the default. jakarta.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR is false in the Mojarra reference; the MyFaces reference table lists true in its default column, which contradicts the specification wording (the validator is added unless the parameter is true) and was not checked at run time, so the table above gives the Mojarra value. The defaults of RESOURCE_EXCLUDES and SEPARATOR_CHAR come from the specification text and the Mojarra reference. And NUMBER_OF_CLIENT_WINDOWS is absent from the specification’s list in chapter 11, yet both implementations document it and the Scopes page observed it working on both.

Facelets Parameters in Practice

The refresh period and the comment switch are demonstrated on the Project Setup page, where the combination with the project stage is tabulated. The other Facelets parameters change how a view id is mapped to a file or what the compiler does, and they have traps.

FACELETS_SUFFIX

A request for /books.xhtml becomes the view id /books.xhtml, and the Facelets compiler reads the physical file books.xhtml. jakarta.faces.FACELETS_SUFFIX changes the file suffix that is looked for: it is a space-separated list of extensions, each starting with a dot; the first physical file that matches one of them becomes the file behind the view id. A project that keeps its views in .facelet files while the URLs stay .xhtml configures it like this:

Views stored as .facelet files
<context-param>
  <param-name>jakarta.faces.FACELETS_SUFFIX</param-name>
  <param-value>.facelet .xhtml</param-value>
</context-param>

The Bookshelf scratch application had suffix.facelet and index.xhtml in its web root, the FacesServlet mapped to *.xhtml only, and the two implementations answered like this:

Table 7. FACELETS_SUFFIX observed on both implementations
Parameter value Request Mojarra 4.1.17 MyFaces 4.1.4

.facelet

/suffix.xhtml (file suffix.facelet)

200

200

.facelet

/index.xhtml (file index.xhtml)

404

500

.facelet .xhtml

/suffix.xhtml

200

404

.facelet .xhtml

/index.xhtml

200

200

Two lessons follow. First, a list of suffixes is not portable: with both .facelet and .xhtml in the list, Mojarra found suffix.facelet for the request /suffix.xhtml and MyFaces answered 404 (it did not substitute the suffix because the request already ended with one of the listed ones), while with the single value .facelet both implementations served the page. The single value is the safe choice, and it means every view file must use it.

Second, and worse: the file suffix.facelet was answered with status 200 and its raw Facelets source on a direct request, in every variant, because the container serves a file nobody maps as static content. The source of a page, with its EL expressions, bean names and comments, was readable by anyone. A suffix other than .xhtml therefore needs a constraint that blocks direct access:

Blocking direct access to alternative view files
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Facelets sources</web-resource-name>
    <url-pattern>*.facelet</url-pattern>
  </web-resource-collection>
  <auth-constraint/>
</security-constraint>

With this constraint the direct request answered 403 on both implementations, and the view requested as /index.xhtml still rendered. (The constraint has an empty auth-constraint, which in the Servlet specification means no role may access the resource.) Views under WEB-INF need no constraint, because the container never serves that directory; that is the reason templates are kept in WEB-INF/templates in the Bookshelf layout.

FACELETS_VIEW_MAPPINGS

jakarta.faces.FACELETS_VIEW_MAPPINGS declares that some resources are Facelets views whatever their extension. Each entry of the semicolon-separated list is a file extension written as *.xhtml or a prefix starting with a slash and ending in *, as in /user/*. A legacy application with views called *.html that maps the FacesServlet to *.html too would write:

Treating .html files as Facelets views
<context-param>
  <param-name>jakarta.faces.FACELETS_VIEW_MAPPINGS</param-name>
  <param-value>*.html</param-value>
</context-param>
<servlet-mapping>
  <servlet-name>facesServlet</servlet-name>
  <url-pattern>*.xhtml</url-pattern>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>

In the scratch run a file legacy.html with Facelets content was served by MyFaces with this parameter alone (status 200, the EL expression evaluated) and answered 404 on Mojarra. Adding jakarta.faces.FACELETS_SUFFIX with the value .html made both implementations serve it, with or without the view mappings parameter. So FACELETS_SUFFIX is the parameter that decides which file is read; FACELETS_VIEW_MAPPINGS decides which view ids count as Facelets, and Mojarra needs the first even when the second is set.

FACELETS_DECORATORS

A TagDecorator sees each tag while a view is compiled and may replace it. The classes are named in the semicolon-separated parameter, need a public no-argument constructor, and are created when the first request for a Facelets view reaches the ViewHandler. A decorator that only observes returns null:

A TagDecorator that logs every command button as the view is compiled
package org.example.bookshelf.config;

import jakarta.faces.view.facelets.Tag;
import jakarta.faces.view.facelets.TagDecorator;
import java.util.logging.Logger;

public class CompileLogDecorator implements TagDecorator {

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

    public CompileLogDecorator() {
        LOG.info("decorator created");
    }

    @Override
    public Tag decorate(Tag tag) {
        if ("commandButton".equals(tag.getLocalName())) {
            LOG.info("compiling " + tag.getQName() + " at " + tag.getLocation());
        }
        return null;
    }
}
<context-param>
  <param-name>jakarta.faces.FACELETS_DECORATORS</param-name>
  <param-value>org.example.bookshelf.config.CompileLogDecorator</param-value>
</context-param>

On the first request for index.xhtml both implementations logged decorator created once and then compiling h:commandButton at /index.xhtml @6,90 (Mojarra) or ... at /index.xhtml at line 6 and column 90 (MyFaces). Because the compiled view is cached, the decorator runs again only when the refresh period causes a recompilation. A decorator that returns a different Tag rewrites the view before it is built; the tag libraries and templates documented elsewhere in this section are the usual way to change markup, and a decorator is the tool for a rewrite that has to touch every view.

Conversion and Validation Parameters

Six parameters change what happens in the Process Validations phase (see the lifecycle page). Two are covered by earlier pages (INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL and VALIDATE_EMPTY_FIELDS on the Validators page, the Bean Validation parameters on the Bean Validation page); the other two have run-time consequences worth demonstrating.

The time zone of f:convertDateTime

f:convertDateTime formats with GMT unless a timeZone attribute is given, a surprise for every application not running in London. jakarta.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE switches the default to the JVM’s zone:

Making the JVM time zone the default of every date converter
<context-param>
  <param-name>jakarta.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name>
  <param-value>true</param-value>
</context-param>
<h:outputText value="#{dateBean.published}">
  <f:convertDateTime pattern="yyyy-MM-dd HH:mm z"/>
</h:outputText>
package org.example.bookshelf.web;

import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;
import java.time.Instant;
import java.util.Date;

@Named
@RequestScoped
public class DateBean {
    public Date getPublished() { return Date.from(Instant.parse("2026-07-01T12:00:00Z")); }
}

For a date of 2026-07-01 at 12:00 UTC and a JVM started with -Duser.timezone=Europe/Madrid, the page printed 2026-07-01 12:00 GMT without the parameter and 2026-07-01 14:00 CEST with it, on both implementations. The converter is applied on output in the Render Response phase and on input in Process Validations, so a value typed in a form is parsed in the same zone it was displayed in. Prefer an explicit timeZone attribute when the zone is part of the business rule rather than of the server.

Required fields that are missing from the request

required="true" is checked when the input is decoded and validated, and only for the parameters that are present in the request. A request that leaves the parameter out entirely is not an empty submission: the component has nothing submitted and, by default, is not validated. A browser always sends the field of a text input, even empty, so the problem is invisible in the browser and shows up in a scripted or malicious request.

A form with a required field
<h:form id="f">
  <h:inputText id="title" value="#{reqBean.title}" required="true"/>
  <h:message for="title"/>
  <h:inputText id="note" value="#{reqBean.note}"/>
  <h:commandButton id="b" value="save" action="#{reqBean.save}"/>
</h:form>
The bean behind the form
package org.example.bookshelf.web;

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

@Named
@RequestScoped
public class ReqBean {
    private String title;
    private String note;
    private String last = "none";
    public String getTitle() { return title; }
    public void setTitle(String t) { this.title = t; }
    public String getNote() { return note; }
    public void setNote(String n) { this.note = n; }
    public String save() { last = "saved title=[" + title + "] note=[" + note + "]"; return null; }
    public String getLast() { return last; }
}

A POST that carried f:b and f:note but not f:title called the action with title=null on both implementations, and no message was added; a POST with f:title= (present but empty) produced "Validation Error: Value is required." With the parameter jakarta.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE set to true the first request was rejected as well, with the same message, and the action did not run.

Validating required inputs even when their parameter is missing
<context-param>
  <param-name>jakarta.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE</param-name>
  <param-value>true</param-value>
</context-param>

Treat the parameter as a hardening setting for any application where an action depends on a required value: the server must not rely on the browser to send the fields. It costs one validation call per required input and changes no markup.

The Client Id Separator

jakarta.faces.SEPARATOR_CHAR changes the character between the segments of a client id (f:title becomes f_title). Only the first character of the value is used. It exists for style sheets and scripts that cannot cope with the colon, and it has a price: every id with that character in it, every search expression and every piece of JavaScript written with the old character stops working. In the scratch run a form f with the input in and the button b rendered the ids f_in and f_b on both implementations, and the view state field j_id1_jakarta.faces.ViewState_0 (Mojarra) and j_id__v_0_jakarta.faces.ViewState_1 (MyFaces). The Search Expressions page lists the places that must stay in step: the update and render attributes, @form:id style absolute expressions, and CSS selectors that escape the colon.

Underscore instead of colon in client ids
<context-param>
  <param-name>jakarta.faces.SEPARATOR_CHAR</param-name>
  <param-value>_</param-value>
</context-param>

State Saving Parameters

jakarta.faces.STATE_SAVING_METHOD decides where the view state lives, and it is the most visible parameter in the page source. The state saving page covers the mechanism, the limits of the server variant, the protection of the client variant and the Mojarra and MyFaces parameters around it; here only the configuration is shown.

Switching to client state saving
<context-param>
  <param-name>jakarta.faces.STATE_SAVING_METHOD</param-name>
  <param-value>client</param-value>
</context-param>

The hidden field jakarta.faces.ViewState of a trivial page held 40 characters on Mojarra and 32 on MyFaces with the default server method (an identifier of a state kept in the session), and 704 and 128 characters respectively with client (the state itself, protected by the implementation). The size grows with the component tree on every response and every postback, and the state leaves the server, so it must be protected: both implementations encrypt it and attach a message authentication code by default, and the parameters that turn that off (com.sun.faces.disableClientStateEncryption, org.apache.myfaces.USE_ENCRYPTION) must never be set in production.

Parameters in the Specification Text That Do Nothing

Two parameters are listed in chapter 11 of the specification and in the MyFaces reference table, yet no trace of them was found in the code that is delivered. jakarta.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES and jakarta.faces.FACELETS_RESOURCE_RESOLVER do not appear as strings in the jakarta.faces-api 4.1.2 JAR, in jakarta.faces 4.1.17 (Mojarra) or in the myfaces-api and myfaces-impl 4.1.4 JARs. The second also names a ResourceResolver class that is not part of the 4.1 API any more (it was removed in Faces 4.0). Do not configure them; if a book or an old answer suggests it, the resource handling and extending Faces pages describe the current extension points.

Project Stages

A project stage tells the runtime where the application is in its life: being written, being tested by machines, being tested by people, or running for real. It is the Faces equivalent of the RAILS_ENV of Ruby on Rails, as the API documentation puts it, and it is the one setting that both the specification and the implementations use to trade diagnostics against speed and safety. The constants of jakarta.faces.application.ProjectStage are:

Table 8. The four stages
Constant Meant for

Development

Programmers working on the application. Diagnostics are on, caches check for changes and error pages explain what went wrong.

UnitTest

Automated tests of single classes, run by the build.

SystemTest

Automated or manual tests of the deployed application as a whole.

Production

The default. Fast and quiet: nothing that explains the internals to the client.

The specification gives the stages no behaviour beyond a handful of points listed in What the Stage Changes. What each implementation does with them is its own business, and the scratch measurements below show that UnitTest and SystemTest mean almost nothing in practice: the practical choice is between Development and everything else.

How the Stage Is Determined

Application.getProjectStage() computes the stage the first time it is called and keeps it. The algorithm in the API documentation looks for a JNDI environment entry first and for the context parameter second, and falls back to Production when the value cannot be used.

flowchart TD A["First call of Application.getProjectStage()"] --> B{"JNDI entry java:comp/env/faces/ProjectStage?"} B -- yes --> V["Use that value"] B -- no --> C{"Context parameter jakarta.faces.PROJECT_STAGE?"} C -- yes --> V C -- no --> P["Production"] V --> D{"Exactly the name of a ProjectStage constant?"} D -- yes --> S["That stage"] D -- no --> L["Log a message and use Production"]
Figure 2. How the project stage is chosen

The JNDI name is the constant ProjectStage.PROJECT_STAGE_JNDI_NAME, which holds java:comp/env/faces/ProjectStage (not jakarta.faces.PROJECT_STAGE, which is the name of the context parameter). Two ways to define it were run on Tomcat with the JNDI naming enabled, each time with the context parameter set to Production, and in each case both implementations reported Development: the JNDI entry won. In web.xml:

The stage as a JNDI environment entry in web.xml
<env-entry>
  <env-entry-name>faces/ProjectStage</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>Development</env-entry-value>
</env-entry>

and in the Tomcat META-INF/context.xml of the application, which is the variant an operations team controls without touching the WAR:

The stage as a Tomcat environment entry
<Context>
  <Environment name="faces/ProjectStage" value="Development" type="java.lang.String" override="false"/>
</Context>

A value that is not exactly one of the four names is not an error. Mojarra logged INFO: Unable to discern ProjectStage for value Staging. and MyFaces WARNING: Wrong value in context init parameter 'jakarta.faces.PROJECT_STAGE' (='Staging'), using default value 'Production'; both then ran as Production. The lower-case development was refused in the same way, because the value goes through ProjectStage.valueOf(). The Jakarta EE Tutorial says custom stages can be added; that cannot work with a Java enum, and in both implementations a custom name means Production plus a log line. The mistake to avoid is a typo in the one value that should enable diagnostics (or, worse, a typo that is meant to disable them): the application starts without complaint and runs in Production.

Setting the Stage per Environment

The stage must follow the environment, not the source tree. Three arrangements work; the first is the simplest, the second is the safest against a mistake in the build, the third is the one for containers that offer a JNDI configuration.

A Maven property filtered into web.xml. The default is Production, so that a build without a profile is a safe one, and a dev profile selects Development and a one-second refresh period:

web.xml with filtered values
<context-param>
  <param-name>jakarta.faces.PROJECT_STAGE</param-name>
  <param-value>${faces.project.stage}</param-value>
</context-param>
<context-param>
  <param-name>jakarta.faces.FACELETS_REFRESH_PERIOD</param-name>
  <param-value>${faces.refresh.period}</param-value>
</context-param>
pom.xml: Production by default, Development with -Pdev
<properties>
  <faces.project.stage>Production</faces.project.stage>
  <faces.refresh.period>-1</faces.refresh.period>
</properties>

<build>
  <plugins>
    <plugin>
      <artifactId>maven-war-plugin</artifactId>
      <configuration>
        <webResources>
          <webResource>
            <directory>src/main/webapp</directory>
            <includes>
              <include>WEB-INF/web.xml</include>
            </includes>
            <filtering>true</filtering>
          </webResource>
        </webResources>
      </configuration>
    </plugin>
  </plugins>
</build>

<profiles>
  <profile>
    <id>dev</id>
    <properties>
      <faces.project.stage>Development</faces.project.stage>
      <faces.refresh.period>1</faces.refresh.period>
    </properties>
  </profile>
</profiles>

With these files mvn package wrote Production and -1 into the packaged WEB-INF/web.xml, and mvn package -Pdev wrote Development and 1. The war plugin filters only the resources listed, which keeps stray ${...} expressions in other files untouched.

The JNDI entry in the server. The context.xml and env-entry above move the decision out of the WAR: one artifact is deployed to every environment and the stage comes from the server. Because the JNDI value wins over the context parameter, it can override a Development left in web.xml, which makes it the safer of the two for a production server.

A web-fragment.xml in a library. A fragment can set the stage too (it applied in the scratch run), but a library that picks Development for the application it is bundled in is a trap; keep the stage out of libraries.

Reading the Stage

The stage can be read from three places, and a page may adapt to it, within the limits of ui:debug and the Stage.

A bean that exposes the stage to views
package org.example.bookshelf.web;

import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.application.ProjectStage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;

@Named
@RequestScoped
public class StageInfo {

    @Inject
    private FacesContext facesContext;

    public ProjectStage getStage() {
        return facesContext.getApplication().getProjectStage();
    }

    public boolean isDevelopment() {
        return getStage() == ProjectStage.Development;
    }

    public boolean isProduction() {
        return getStage() == ProjectStage.Production;
    }
}
Using the stage in a view
<ui:debug hotkey="d" rendered="#{stageInfo.development}"/>
<p>stage=#{stageInfo.stage}</p>

The page printed stage=Development dev=true prod=false, stage=UnitTest dev=false prod=false and stage=Production dev=false prod=true under the three settings, on both implementations, and the debug script was rendered only in the first. The same test can be written without a bean as #{facesContext.application.projectStage eq 'Development'}: EL coerces the string to the enum, and the page behaved identically. In JavaScript faces.getProjectStage() returns the stage as a string without a request (see the Ajax page), which the Faces scripts themselves use to decide whether to show an alert.

What the Stage Changes

The specification names a short list of stage-dependent behaviour, and the implementations add their own. The first table is the specification’s; the second is what the two implementations did in the scratch run, with the same views and the same requests under each stage.

Table 9. Stage-dependent behaviour required or allowed by the specification
Where Rule

FACELETS_REFRESH_PERIOD not set

-1 in Production, 0 in every other stage (run; see the Project Setup page).

Navigation case not found for a call node of a Faces Flow

A message explaining that there was no match is rendered when the stage is not Production.

Ajax errors

In Development the client script signals errors with an alert. Mojarra did, MyFaces 4.1.4 did not (the Ajax page).

Composite component interface metadata

displayName, shortDescription, expert, hidden and preferred are exposed only in Development.

Invalid composite library name

An informative message is placed in the page in Development, and it is only logged otherwise.

Error information for Facelets error pages

In Development the debug information (exception, tree, variables) is available to an error page through <ui:include src="jakarta.faces.error.xhtml"/>.

Resource metadata

An implementation may cache the resolved resources when the stage is Production.

Beyond that list each implementation decides. Every row of the next table was produced by one request to the same view under Development, UnitTest, SystemTest, Production, no setting, Staging and development (the last three behaved as Production).

Table 10. Stage-dependent behaviour observed
Situation Mojarra 4.1.17 MyFaces 4.1.4

h:link whose outcome matches no navigation case

Text plus the sentence "This link is disabled because a navigation case could not be matched." in Development, UnitTest and SystemTest; only the text in Production.

Only the text, in every stage.

An exception thrown while the view renders (a bean getter)

Development: status 500 and a Faces error page of about 14 KB with the message, the stack trace, the component tree and the variables. Other stages: status 500 and the container’s own error report (about 3.5 KB with Tomcat’s default).

Development: status 500 and a Faces error page of about 11 KB. Other stages: status 500 with only the part of the page rendered before the failure (73 bytes in the test).

h:outputStylesheet / h:outputScript naming a resource that does not exist

href="RES_NOT_FOUND" and src="RES_NOT_FOUND" in every stage; in Development also a FacesMessage "Unable to find resource bookshelf, nope.css" that an h:messages shows.

Nothing rendered, no message and no log line, in every stage.

A FacesMessage that no h:message or h:messages shows

Development only: an element with the id jakarta_faces_developmentstage_messages (a ul titled "Project Stage[Development]: Unhandled Messages") at the end of the body lists it.

Development only: the same element id, with the message.

Two components with the same id

The view fails with status 500 in every stage; the log says JSF1007: Duplicate component ID j_idt5:a found in view in every stage and the Development error page is titled "Component ID j_idt5:a has already been found in the view."

The view fails in every stage with a DuplicateIdException ("Component with duplicate id "j_id_6:a" found"); the Development error page shows it with the location in the source (line 4, column 38).

ui:debug

The script and the debug output are produced in every stage (see ui:debug and the Stage).

The same.

Implementation switches tied to the stage

Not applicable.

LOG_WEB_CONTEXT_PARAMS defaults to dev-only (logged in Development only) and ERROR_HANDLING to true in Development and false otherwise.

Three things stand out. Mojarra gives UnitTest and SystemTest the "not production" behaviour in the link row only, MyFaces never distinguishes the three non-production stages, and a build that uses the two test stages to get development diagnostics will not get them. Second, the error information of the Development page is exactly what an attacker wants (class names, source lines, bean variables), so the stage is a security setting as much as a convenience; the same is true of MyFaces' ERROR_HANDLING, which switched the error page on in Production when set to true in the scratch run (the page title was the exception message, status 500, 11 KB). Third, a Production rendering failure is not pretty by default: Mojarra hands the exception to the container and MyFaces sends what it has already rendered. The exception handling page shows how to configure an <error-page> and the ExceptionHandler so that a failure ends in a deliberate page.

ui:debug and the Stage

The documentation of ui:debug says the tag works only when the stage is Development. In the scratch run that was not the case. A page with <ui:debug hotkey="d"/> rendered the keyboard script in Development and in Production on both implementations, and the URL that the script opens (/bookshelf/debug.xhtml?facelets.ui.DebugOutput=..., with the generated number copied from the page) answered 200 with a page that listed the component tree, the request parameters and the scoped variables: 10 KB on Mojarra and 8 KB on MyFaces in Production. The request needs the session of the user who rendered the page, so it is not an anonymous leak, but anyone who can use the page can read bean state with it. Guard the tag explicitly:

Rendering ui:debug only in Development
<ui:debug hotkey="d" rendered="#{stageInfo.development}"/>

With this guard the script was absent from the Production and UnitTest pages and present in Development, on both implementations. Put the tag in the one template that all views use, as the tag documentation suggests, so that the guard exists once.

faces-config.xml

faces-config.xml is the application configuration resource: an XML document in the Jakarta EE namespace that registers objects with the Application and the factories, and that carries the settings that have no annotation. The same format is used by the WEB-INF/faces-config.xml of the application, by the META-INF/faces-config.xml of every library and by the extra files named in jakarta.faces.CONFIG_FILES.

The Document

A Faces 4.1 document starts with this declaration, which the specification requires:

The faces-config.xml root element for Faces 4.1
<?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">
  <!-- name, ordering, application, factory, lifecycle, ... in any order -->
</faces-config>

The schema of 4.1 defines the root as a choice of its children, repeated, so the elements can appear in any order and the ordering of the schema never causes a validation error. The root has three attributes: version (4.1), id, and metadata-complete, which stops annotation scanning when it is true in WEB-INF/faces-config.xml (see Annotations or XML). The Project Setup page shows the namespaces and version numbers of all the earlier releases; a document that names an older schema is still read by 4.1 runtimes, but only a 4.1 document can use the elements added since. A <managed-bean> element in a 4.1 document does not validate against the schema, and both runtimes ignored it silently in the scratch run: the managed bean facility is gone, and CDI replaces it (the Backing Beans and CDI page).

The Elements

The next table is the complete list of the children of the root in the 4.1 schema, and of the children of application and factory, with the annotation or other mechanism that replaces them where there is one.

Table 11. The top-level elements of faces-config.xml 4.1
Element Purpose Annotation or alternative

name

The name other files use in ordering; see Ordering of Configuration Files.

none

ordering / absolute-ordering

Relative ordering of a library’s file, or the absolute order chosen by the application.

none

application

Application-wide settings and replacements; the children are in the next table.

none

factory

Replaces or decorates a factory; the children are in the table after the next.

none

lifecycle

Holds phase-listener elements, the listeners of the lifecycle phases.

f:phaseListener in a view, or f:view attributes, for a single view

navigation-rule

Rule-based navigation: from-view-id, navigation-case, from-outcome, to-view-id, redirect, if. See the Navigation page.

implicit navigation needs no configuration

component

Registers a UIComponent under a component type.

@FacesComponent

converter

Registers a converter by converter-id or by converter-for-class.

@FacesConverter

validator

Registers a validator by validator-id.

@FacesValidator (isDefault for a default validator)

behavior

Registers a client behavior by behavior-id.

@FacesBehavior

render-kit

Registers a render kit and its renderers, by component family and renderer type.

@FacesRenderer, and @FacesBehaviorRenderer for behaviors

flow-definition

A flow described in XML.

a CDI producer annotated with @FlowDefinition; see the Faces Flows page

protected-views

url-pattern entries of the views that may only be reached from another view of the same application.

none

referenced-bean

A design-time promise that an object will exist; the schema says it has no effect on the runtime.

none

faces-config-extension

Vendor extension point.

none

Table 12. The children of application
Element Purpose

locale-config

default-locale and supported-locale entries. The messages page explains how Faces picks the locale.

resource-bundle

base-name and var: the bundle becomes available in every view as #{var}.

message-bundle

A bundle whose keys replace the standard Faces messages.

system-event-listener

system-event-listener-class, system-event-class and source-class: a listener subscribed to a system event.

action-listener, navigation-handler, view-handler, state-manager, resource-handler, el-resolver

Replace or decorate the default implementation: the class must have a public constructor taking the previous implementation if it wants to wrap it (the decorator pattern of 11.3.7).

search-expression-handler, search-keyword-resolver

The search expression extension points of 2.3.

default-render-kit-id

The render kit used when a view does not choose one.

default-validators

validator-id entries that are added to every input component.

resource-library-contracts

Maps view ids to contracts; the resource library contracts page.

application-extension

Vendor extension point.

Table 13. The children of factory
Element Factory of

application-factory

the Application

exception-handler-factory

the ExceptionHandler

external-context-factory

the ExternalContext

faces-context-factory

the FacesContext

facelet-cache-factory

the cache of compiled views

partial-view-context-factory

the PartialViewContext of Ajax requests

lifecycle-factory

the Lifecycle instances

view-declaration-language-factory

the ViewDeclarationLanguage (Facelets)

tag-handler-delegate-factory

the delegates behind Facelets tag handlers

render-kit-factory

the RenderKit instances

visit-context-factory

the VisitContext used when the tree is visited

flash-factory

the Flash

flow-handler-factory

the FlowHandler

client-window-factory

the ClientWindow

search-expression-context-factory

the SearchExpressionContext

A factory is replaced by naming a class that extends the factory type and has a constructor taking the factory it wraps; Faces passes the previous one, and the new class delegates to it. The extending Faces page works through the factories that an application needs.

A Complete Configuration for Bookshelf

The next file configures the Bookshelf with the elements that have no annotation: the locales and the message bundle, a system event listener, a phase listener, a navigation rule, a converter, protected views and a name. The two classes it names follow it.

WEB-INF/faces-config.xml of the Bookshelf
<?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">

  <name>bookshelf</name>

  <application>
    <locale-config>
      <default-locale>en</default-locale>
      <supported-locale>es</supported-locale>
    </locale-config>
    <resource-bundle>
      <base-name>org.example.bookshelf.messages</base-name>
      <var>msg</var>
    </resource-bundle>
    <system-event-listener>
      <system-event-listener-class>org.example.bookshelf.config.StageBanner</system-event-listener-class>
      <system-event-class>jakarta.faces.event.PostConstructApplicationEvent</system-event-class>
      <source-class>jakarta.faces.application.Application</source-class>
    </system-event-listener>
  </application>

  <lifecycle>
    <phase-listener>org.example.bookshelf.config.RenderTimer</phase-listener>
  </lifecycle>

  <navigation-rule>
    <from-view-id>/index.xhtml</from-view-id>
    <navigation-case>
      <from-outcome>details</from-outcome>
      <to-view-id>/other.xhtml</to-view-id>
      <redirect/>
    </navigation-case>
  </navigation-rule>

  <converter>
    <converter-id>bookshelf.demo</converter-id>
    <converter-class>org.example.bookshelf.convert.XmlConverter</converter-class>
  </converter>

  <protected-views>
    <url-pattern>/admin/*</url-pattern>
  </protected-views>

</faces-config>
A phase listener registered from faces-config.xml
package org.example.bookshelf.config;

import jakarta.faces.event.PhaseEvent;
import jakarta.faces.event.PhaseId;
import jakarta.faces.event.PhaseListener;
import java.util.logging.Logger;

/** Registered from faces-config.xml: logs how long the Render Response phase took. */
public class RenderTimer implements PhaseListener {

    private static final Logger LOG = Logger.getLogger(RenderTimer.class.getName());
    private static final String START = RenderTimer.class.getName() + ".start";

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

    @Override
    public void beforePhase(PhaseEvent event) {
        event.getFacesContext().getAttributes().put(START, System.nanoTime());
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        Long start = (Long) event.getFacesContext().getAttributes().get(START);
        if (start != null) {
            LOG.info("render " + event.getFacesContext().getViewRoot().getViewId() + " took "
                    + (System.nanoTime() - start) / 1_000_000 + " ms");
        }
    }
}
A system event listener registered from faces-config.xml
package org.example.bookshelf.config;

import jakarta.faces.application.Application;
import jakarta.faces.event.AbortProcessingException;
import jakarta.faces.event.PostConstructApplicationEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.faces.event.SystemEventListener;
import java.util.logging.Logger;

/** Registered from faces-config.xml: logs the project stage once, when configuration processing is done. */
public class StageBanner implements SystemEventListener {

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

    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        Application app = ((PostConstructApplicationEvent) event).getApplication();
        LOG.info("Bookshelf started in project stage " + app.getProjectStage());
    }

    @Override
    public boolean isListenerForSource(Object source) {
        return source instanceof Application;
    }
}

The bundle behind resource-bundle is two files, org/example/bookshelf/messages.properties (catalogue.title=Catalogue, book.saved=Book saved) and messages_es.properties (catalogue.title=Catalogo, book.saved=Libro guardado). A view uses it as #{msg['catalogue.title']}.

The file started on both implementations with the validation parameters switched on (com.sun.faces.validateXml, com.sun.faces.verifyObjects for Mojarra; org.apache.myfaces.VALIDATE_XML and org.apache.myfaces.VALIDATE for MyFaces). The log showed Bookshelf started in project stage Production once, produced by the system event listener when the configuration was finished, and render /index.xhtml took 43 ms (Mojarra) for the first request and ... took 2 ms for the second, produced by the phase listener. A request with Accept-Language: es printed Catalogo / Libro guardado, one with en printed Catalogue / Book saved, and one with fr, which is not a supported locale, fell back to the default Catalogue / Book saved, on both runtimes.

Checking the File

Both implementations can validate configuration files against the schema, and by their references neither does by default (the specification requires the check only of implementations that are part of a Jakarta EE server). With a bogus element added to a 4.1 document, MyFaces with org.apache.myfaces.VALIDATE_XML set to true logged SEVERE: cvc-complex-type.2.4.a: Invalid content was found starting with element '{"https://jakarta.ee/xml/ns/jakartaee":bogus-element}' and kept running; Mojarra with com.sun.faces.validateXml set to true logged nothing and started. So the switch reported the error on MyFaces and not on Mojarra in this run, which makes it a poor safety net. An editor that knows the xsi:schemaLocation (every IDE does) and a build step that runs xmllint --schema are better tools:

Validating faces-config.xml in the build
curl -sO https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_1.xsd
xmllint --noout --schema web-facesconfig_4_1.xsd src/main/webapp/WEB-INF/faces-config.xml

Annotations or XML

Every registration that has an annotation can be written either way, and the specification fixes the winner when the two disagree: an entry in an application configuration resource takes precedence over an annotation. The annotated classes are found by scanning, and the scan can be restricted or switched off.

What Is Scanned

The specification (11.4.1) says that all classes in WEB-INF/classes are scanned, that the classes of a JAR in WEB-INF/lib are scanned only if the JAR contains a META-INF/faces-config.xml (or a file ending in .faces-config.xml, even empty), and that a metadata-complete="true" in WEB-INF/faces-config.xml turns the scan off for everything except the implementation’s own classes. The containers' initializer is told which annotations to look for (the list is in the API documentation of FacesServlet); that is also what triggers the automatic mapping of the servlet. A library that wants its annotated classes found therefore ships a META-INF/faces-config.xml, even an empty one. Mojarra scanned a library JAR without it in the scratch runs of earlier pages and MyFaces did not, so the file is what makes the library portable.

Two parameters restrict the scan and shorten the startup of an application with a large class path:

Restricting the annotation scan
<!-- Mojarra: package names for WEB-INF/classes, or jar:name:packages -->
<context-param>
  <param-name>com.sun.faces.annotationScanPackages</param-name>
  <param-value>org.example.bookshelf.convert org.example.bookshelf.web</param-value>
</context-param>

<!-- MyFaces: comma separated packages -->
<context-param>
  <param-name>org.apache.myfaces.annotation.SCAN_PACKAGES</param-name>
  <param-value>org.example.bookshelf.convert,org.example.bookshelf.web</param-value>
</context-param>

In the scratch run, restricting the scan to org.example.bookshelf.config (a package without the @FacesConverter class) made the page that uses the converter fail with status 500 on both implementations, and restricting it to the package that contains the converter made the page work. The mistake it invites is silent: a class in a package that is not listed is never registered, and the first error is at the first use of the id.

Annotation and XML for the Same Id

The Bookshelf scratch application registers the converter id bookshelf.demo with an annotation:

A converter registered by annotation
package org.example.bookshelf.convert;

import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.FacesConverter;

@FacesConverter("bookshelf.demo")
public class AnnotatedConverter implements Converter<String> {
    @Override public String getAsObject(FacesContext c, UIComponent u, String v) { return "annotation:" + v; }
    @Override public String getAsString(FacesContext c, UIComponent u, String v) { return "annotation:" + v; }
}

and, in some runs, with a class that only an XML entry names:

The same id registered by an XML entry
package org.example.bookshelf.convert;

import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;

public class XmlConverter implements Converter<String> {
    @Override public String getAsObject(FacesContext c, UIComponent u, String v) { return "xml:" + v; }
    @Override public String getAsString(FacesContext c, UIComponent u, String v) { return "xml:" + v; }
}
<converter>
  <converter-id>bookshelf.demo</converter-id>
  <converter-class>org.example.bookshelf.convert.XmlConverter</converter-class>
</converter>

A view printed <h:outputText value="x" converter="bookshelf.demo"/> under each arrangement, with the same result on Mojarra and on MyFaces:

Table 14. Which registration wins
Arrangement Output

the annotation only, no faces-config.xml

annotation:x

the annotation and an XML entry for the same id in WEB-INF/faces-config.xml

xml:x (XML wins)

the annotation and an XML entry in a library’s META-INF/faces-config.xml

the library’s class (XML wins)

a library’s XML entry and an XML entry in WEB-INF/faces-config.xml

the application’s class (WEB-INF/faces-config.xml is read last)

two libraries with the entry, one ordered after the other

the class of the library ordered last

metadata-complete="true" in WEB-INF/faces-config.xml, annotation only

status 500: Mojarra Expression Error: Named Object: bookshelf.demo not found., MyFaces Could not find any registered converter-class by converterId : bookshelf.demo

The rule is the one the specification gives: a registration that comes later replaces an earlier one with the same id, XML entries are applied over the annotations, and the application’s own file is the last of the XML. An application can therefore change a library’s behaviour without editing the library, by registering the same id in its own faces-config.xml.

Replacing a Standard Converter

The same replacement works on the identifiers that Faces itself registers, as long as the new class is a subclass of the standard one. The standard integer converter has the id jakarta.faces.Integer; a subclass that accepts an underscore as a digit separator and an XML entry that replaces it:

A replacement for the standard integer converter
package org.example.bookshelf.convert;

import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.IntegerConverter;

/** Replacement for the standard converter id jakarta.faces.Integer: accepts a thousands separator. */
public class UnderscoreIntegerConverter extends IntegerConverter {
    @Override
    public Object getAsObject(FacesContext c, UIComponent u, String v) {
        return super.getAsObject(c, u, v == null ? null : v.replace("_", ""));
    }
}
<converter>
  <converter-id>jakarta.faces.Integer</converter-id>
  <converter-class>org.example.bookshelf.convert.UnderscoreIntegerConverter</converter-class>
</converter>
package org.example.bookshelf.web;

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

@Named
@RequestScoped
public class NumBean {
    private Integer qty;
    public Integer getQty() { return qty; }
    public void setQty(Integer q) { this.qty = q; }
}

A form with <h:inputText value="#{numBean.qty}" converter="jakarta.faces.Integer"/> bound to this bean received 1_000. Without the entry both runtimes reported "must be a number consisting of one or more digits." and kept the form; with it the bean got 1000 and the page printed qty=1000. The id has to be used explicitly: the replacement applies to the converter that is looked up by id, which is what converter="jakarta.faces.Integer" does. Use this technique sparingly: it changes every use of the id in the application, in every library, and a reader of the view sees nothing unusual.

Which to Use

Prefer the annotation where one exists: the registration sits next to the class and cannot drift from it. Use XML when the registration must be changeable without recompiling (a library default the application overrides), when the same class is registered under two ids, when the item has no annotation (locales, bundles, navigation rules, phase listeners, factories, protected-views, ordering), or when the annotation scan is too expensive and metadata-complete="true" is set so that everything is listed explicitly. A project can mix the two freely; the rule above settles every conflict.

Several Configuration Files

An application is rarely a single file. Every JAR on the class path may bring its own faces-config.xml, the application has its own, and a large application may split its configuration into parts. This section covers how the files are found, in which order they are read, and what happens when they disagree.

Where the Files Are Found

The discovery rules are fixed by the specification (11.3.2) and were checked, one location at a time, with a phase listener registered by each file and a page that printed the list of the registered phase listeners (Lifecycle.getPhaseListeners()).

Table 15. Where configuration files are found
Location Found automatically? Observed

META-INF/faces-config.xml in a JAR in WEB-INF/lib

yes

read on Mojarra and MyFaces

META-INF/<name>.faces-config.xml in a JAR (any file name ending in .faces-config.xml directly in META-INF)

yes

read on both

WEB-INF/classes/META-INF/faces-config.xml

yes

read on both

WEB-INF/faces-config.xml

yes, and read last

read on both, its listener last in the list

WEB-INF/other.faces-config.xml, or any file outside META-INF

no

ignored by both until it is listed in jakarta.faces.CONFIG_FILES

a service file META-INF/services/jakarta.faces.application.ApplicationConfigurationPopulator

yes

its class contributes a document on both; see Configuration from Code: ApplicationConfigurationPopulator

The tutorial adds a sentence on the class path: files are found in the JARs of WEB-INF/lib and in parent class loaders. Beware that the tutorial names the parameter jakarta.faces.application.CONFIG_FILES in that section: the name defined by the specification and the API (FacesServlet.CONFIG_FILES_ATTR) is jakarta.faces.CONFIG_FILES.

The CONFIG_FILES Parameter

jakarta.faces.CONFIG_FILES lists additional files, as comma-separated paths relative to the web application root. It is meant for an application whose configuration is split by team or by module and kept in WEB-INF:

Splitting the configuration of a large application
<context-param>
  <param-name>jakarta.faces.CONFIG_FILES</param-name>
  <param-value>/WEB-INF/navigation.faces-config.xml,/WEB-INF/converters.faces-config.xml</param-value>
</context-param>

The specification says that when this parameter exists the search for META-INF files is skipped, and the API documentation of FacesServlet says the listed files are "in addition to" /WEB-INF/faces-config.xml. The scratch application had two library JARs with a META-INF/faces-config.xml (lib-a, lib-b with a b.faces-config.xml), a file in WEB-INF/classes/META-INF, the WEB-INF/faces-config.xml and WEB-INF/other.faces-config.xml, each registering one phase listener. The lists of registered listeners were:

Table 16. Phase listeners registered, in order
Parameter Mojarra 4.1.17 MyFaces 4.1.4

not set

A, B, K, my

A, K, B, my

/WEB-INF/other.faces-config.xml

A, B, K, O, my

O, A, K, B, my

/WEB-INF/faces-config.xml,/WEB-INF/other.faces-config.xml

A, B, K, O, my

O, A, K, B, my

(A and B are the JARs, K the file in WEB-INF/classes/META-INF, O the listed file and my the WEB-INF/faces-config.xml.) Three facts follow. Neither implementation skipped the META-INF search when the parameter was set, so the sentence in the specification does not describe what the two implementations do. Listing /WEB-INF/faces-config.xml is harmless: the file was loaded once, at the end, as the specification requires. And the position of the listed file differs: Mojarra read it after the META-INF files and MyFaces before them, so a configuration that depends on the order of the extra files must name them in ordering elements (see Ordering of Configuration Files) rather than rely on the list.

Configuration from Code: ApplicationConfigurationPopulator

Before the documents are parsed, Faces asks every ApplicationConfigurationPopulator found with the ServiceLoader to fill an empty faces-config document. The result is processed like a file, so a framework can compute its configuration (from a classpath scan, from Spring properties, from annotations of its own) instead of shipping an XML file. The class below adds the render timer of the previous section with the same elements the XML file would have contained:

A populator that adds a phase listener
package org.example.bookshelf.config;

import jakarta.faces.application.ApplicationConfigurationPopulator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/** Adds configuration in code: the same elements a faces-config.xml would contain. */
public class AuditConfigPopulator extends ApplicationConfigurationPopulator {

    private static final String NS = "https://jakarta.ee/xml/ns/jakartaee";

    @Override
    public void populateApplicationConfiguration(Document toPopulate) {
        Element root = toPopulate.getDocumentElement();
        Element lifecycle = toPopulate.createElementNS(NS, "lifecycle");
        Element listener = toPopulate.createElementNS(NS, "phase-listener");
        listener.setTextContent("org.example.bookshelf.config.RenderTimer");
        lifecycle.appendChild(listener);
        root.appendChild(lifecycle);
    }
}
META-INF/services/jakarta.faces.application.ApplicationConfigurationPopulator
org.example.bookshelf.config.AuditConfigPopulator

With the service file in WEB-INF/classes/META-INF/services and no faces-config.xml listing the listener, both implementations registered RenderTimer and logged the render times. The specification text of 11.3.2 calls the service jakarta.faces.ApplicationConfigurationResourceDocumentPopulator; no such class exists in the 4.1 API, whose class is jakarta.faces.application.ApplicationConfigurationPopulator, and the service file must carry that name. Mojarra’s own runtime configuration is contributed by a subclass of it (com.sun.faces.config.configpopulator.MojarraRuntimePopulator). Application code rarely needs this, because a faces-config.xml does the same in a form the next programmer can read; the extending Faces page lists the other extension points for integrations.

Ordering of Configuration Files

When two files register something that can only have one value, or something that wraps what was registered before, the order in which they are read decides the result. The classpath order is not a contract: it depends on the container and the build. The specification therefore defines how files declare their order. The rules (11.3.8) are borrowed from the Servlet specification’s ordering of web fragments.

The Rules

  • A file may have a top-level <name>. Only named files take part in ordering.

  • A library file may have an <ordering> with at most one <before> and one <after>; each contains the names of other files and may contain <others/>, the set of all other files.

  • <before><others/></before> moves the file to the beginning of the list, <after><others/></after> to the end; several files that say so are in unspecified order among themselves.

  • The application’s own WEB-INF/faces-config.xml is always read last, and it alone may contain an <absolute-ordering>: a list of <name> elements, optionally with one <others/>, that fixes the order of all the library files. A library file not named and without <others/> in an absolute ordering is ignored (its configuration is not read at all).

  • An <ordering> in WEB-INF/faces-config.xml or an <absolute-ordering> in a library is ignored, with a log message.

  • A cycle is an error that must fail the deployment, and so must two files with the same name.

The specification’s example
<!-- library A -->
<faces-config ...>
  <name>A</name>
  <ordering><after><name>B</name></after></ordering>
  <lifecycle><phase-listener>lib.PL_A</phase-listener></lifecycle>
</faces-config>

<!-- library B: no ordering -->
<faces-config ...>
  <name>B</name>
  <lifecycle><phase-listener>lib.PL_B</phase-listener></lifecycle>
</faces-config>

<!-- library C -->
<faces-config ...>
  <name>C</name>
  <ordering><before><others/></before></ordering>
  <lifecycle><phase-listener>lib.PL_C</phase-listener></lifecycle>
</faces-config>

<!-- WEB-INF/faces-config.xml: name "my", no ordering -->

The specification derives the order C, B, A, my. The scratch application built these four files plus an unnamed library D, and printed the list of phase listeners:

flowchart LR C["C: before others"] --> B["B"] B --> A["A: after B"] A --> M["my: WEB-INF/faces-config.xml, always last"] D["D: unnamed, no ordering, anywhere between C and my"] -.-> M
Figure 3. Order produced for the specification's example

What the Two Implementations Do

Each case below was a separate deployment with the libraries named, the WEB-INF/faces-config.xml shown and one phase listener per file; the cell is the order of the registered phase listeners.

Table 17. Ordering, observed
Case Mojarra 4.1.17 MyFaces 4.1.4

The specification’s example (A after B, C before others, D unnamed)

C, B, D, A, my

C, D, B, A, my

<before><name>B</name></before> in A, <after><others/></after> in C, D unordered

A, D, B, C, my

D, A, B, C, my

Absolute ordering C, <others/>, A in WEB-INF/faces-config.xml

C, D, B, A, my

C, D, B, A, my

Absolute ordering C, A (no <others/>; B and D exist)

startup fails: CONFIGURATION FAILED! Index 2 out of bounds for length 2

C, A, my (B and D ignored)

Absolute ordering C, A, B (all libraries named, no <others/>)

startup fails: ... Index 3 out of bounds for length 3

C, A, B, my

No ordering in any file (libraries z-first, a-second, m-third)

A, M, Z, my (the order of the file names)

A, M, Z, my

<ordering> inside WEB-INF/faces-config.xml

ignored: WARNING: JSF1081: /WEB-INF/faces-config.xml contains <ordering> elements. These will be ignored.

ignored, no message in the log

Two libraries both named A

deployed; A, B, my

deployed; B, A, my

A after B and B after A

startup fails: Circular dependencies detected!

SEVERE: Circular references detected when sorting application config resources. Use absolute ordering instead. at startup, and status 500 for the request

Four results deserve attention.

  • Where the specification leaves a choice, the implementations choose differently. The unnamed library D landed between B and A on Mojarra and before B on MyFaces, and with no ordering at all the unordered libraries were read in the alphabetical order of their file names in both runs; that order came from the container’s listing of WEB-INF/lib and must not be relied upon.

  • Mojarra 4.1.17 cannot start with an absolute ordering that leaves out <others/> when there are library files, whether it names all of them or not. The stack trace is an ArrayIndexOutOfBoundsException that ends in com.sun.faces.config.manager.Documents.sortDocuments(Documents.java:217). Until that is fixed, write <others/> in every <absolute-ordering> (the case with <others/> worked), and use <before>/<after> rather than an absolute ordering when you can.

  • Duplicate names did not fail the deployment on either implementation, although the specification requires it, so a clash is not detected for you: two libraries must not use the same <name>.

  • A cycle fails differently: Mojarra aborted the deployment (the application answered 404 afterwards), MyFaces logged a SEVERE message and answered with status 500.

What Ordering Changes

Ordering decides two things. The first is the order of decoratable artifacts: a ViewHandler, NavigationHandler, ActionListener, factory or other class named in <application> or <factory> wraps the one registered before it, so the file read last is the outermost wrapper. The second is every registration that has an id: a later file replaces an earlier one, as the converter table of Annotations or XML shows. For phase listeners, the order of the list is also the order of the calls: in both runs beforePhase was called for the listeners in list order (C, B, D, A, my on Mojarra) and afterPhase in reverse order (my, A, D, B, C), so the first listener registered is the outermost one. The Events and Listeners page describes how listeners registered in faces-config.xml relate to those of a view.

Mojarra Parameters

Mojarra reads about fifty parameters of its own, all in the com.sun.faces. namespace, and documents each in the file CONTEXT-PARAMS.md of its 4.1 branch, which a test keeps in step with the code. The table lists all of them, grouped as in that file, with the default and what they do. A parameter that Mojarra does not know is ignored without a message, and MyFaces ignores the com.sun.faces. names in the same way (it reads the names of the next section), so a shared web.xml can carry both sets. Run Mojarra with com.sun.faces.displayConfiguration set to true and every parameter it reads is logged at startup; the scratch run printed lines such as INFO: JSF1021: [/bookshelf] Configuration option 'com.sun.faces.validateXml' - DISABLED and INFO: JSF1018: [/bookshelf] Configuration option 'jakarta.faces.STATE_SAVING_METHOD' set to 'client'.

Table 18. Mojarra 4.1 parameters: startup and configuration
Parameter Default Effect

com.sun.faces.annotationScanPackages

none

Restricts the annotation scan to the listed packages (and jar:<name>:<packages> entries); the largest startup saving on a big class path. Run: see Annotations or XML.

com.sun.faces.displayConfiguration

false

Logs every parameter value at INFO instead of FINE. Run.

com.sun.faces.forceLoadConfiguration

false

Starts the configuration even when no FacesServlet, faces-config.xml or Faces annotation is found (Spring Boot style, programmatic registration).

com.sun.faces.enableThreading

false

Parses the configuration files and scans annotations on a thread pool; also gates a watcher of the configuration files that runs in Development.

com.sun.faces.duplicateJARPattern

none

Regular expression that recognises the same library packaged twice, so its faces-config.xml is read once.

com.sun.faces.validateXml

false

Validates configuration files against the schema. In the scratch run it logged nothing for an invalid file.

com.sun.faces.verifyObjects

false

Checks at startup that every class named in the configuration exists and has a public no-argument constructor. Logs JSF1059 as a warning to say that it slows startup. Run.

com.sun.faces.injectionProvider

none

Class name of the InjectionProvider SPI that injects into Faces artifacts.

com.sun.faces.sendPoweredByHeader

false

Sends an X-Powered-By header. Run: X-Powered-By: Faces/4.1.

com.sun.faces.exceptionTypesToIgnoreInLogging

none

Comma-separated exception classes the default ExceptionHandler does not log (since 4.0.14).

com.sun.faces.disableOptionalELResolver

false

Removes OptionalELResolver from the EL chain, so Optional values are not unwrapped (since 4.1.4).

Table 19. Mojarra 4.1 parameters: view build, state saving and rendering
Parameter Default Effect

com.sun.faces.disableIdUniquenessCheck

false

true drops the duplicate-id walk on every state save, auto drops it only in Production. The check fired in every stage in the default configuration (see What the Stage Changes).

com.sun.faces.refreshTransientBuildOnPSS

false

Re-applies the Facelets page to the restored tree before rendering: a second build of the view on every postback (since 4.0.19). A migration crutch for views that change outside Faces.

com.sun.faces.restoreBuildTimeDecisions

false

Remembers what c:if, c:forEach and variable ui:include evaluated to when the page was rendered and replays it when the postback restores the view (since 4.0.23).

com.sun.faces.useFaceletsID

false

Generated component ids follow the position of the tag in the page, which keeps them stable across views that include the same page.

com.sun.faces.numberOfLogicalViews

15

Server state saving: how many initially requested views the session remembers. Together with the next parameter it sizes the session.

com.sun.faces.numberOfViewsInSession

15

Server state saving: how many postback states are kept per logical view.

com.sun.faces.compressViewState

true

GZIP-compresses the serialized view state (client state, or server state when SERIALIZE_SERVER_STATE is on).

com.sun.faces.clientStateTimeout

none

Minutes a client-side state stays valid; a later restore throws ViewExpiredException.

com.sun.faces.clientStateWriteBufferSize

8192

Buffer size, in bytes, used while writing client state.

com.sun.faces.serializationProvider

none

SerializationProvider class used to serialize the state on every save and restore.

com.sun.faces.enableClientStateDebugging

false

Deserializes the client state right after writing it, to surface serialization problems. Doubles the cost.

com.sun.faces.enableViewStateIdRendering

true

Renders the id attribute of the hidden view state field.

com.sun.faces.autoCompleteOffOnViewState

false

Renders autocomplete="off" on the hidden view state field instead of one-time-code.

com.sun.faces.writeStateAtFormEnd

true

Writes the hidden state field before </form> instead of after <form>.

com.sun.faces.preferXHTML

false

Answers with application/xhtml+xml instead of text/html if the client accepts both.

com.sun.faces.disableUnicodeEscaping

auto

Whether non-ASCII characters are escaped into entities; auto escapes only what the response encoding cannot represent.

com.sun.faces.enableJSStyleHiding

false

Wraps inline script in a comment for browsers that do not know <script>; obsolete.

com.sun.faces.allowTextChildren

false

Renders children of h:outputText and h:inputText again, as Mojarra 1.x did; a migration aid only.

Table 20. Mojarra 4.1 parameters: resources, scopes, WebSocket and security
Parameter Default Effect

com.sun.faces.cacheResourceModificationTimestamp

false

Caches the last-modified time of a resource instead of reading the file on every request.

com.sun.faces.compressableMimeTypes

none

Comma-separated MIME types (text/* allowed) of resources served GZIP-compressed.

com.sun.faces.defaultResourceMaxAge

604800000

Milliseconds a client may cache a resource (seven days).

com.sun.faces.enableMissingResourceLibraryDetection

false

Fails the view when a composite component names a resource library that does not exist.

com.sun.faces.resourceBufferSize

2048

Buffer size, in bytes, used while streaming a resource.

com.sun.faces.resourceUpdateCheckPeriod

5

Minutes between two checks whether a cached resource changed; -1 never checks.

com.sun.faces.registerConverterPropertyEditors

false

Registers a PropertyEditor for every converter, so EL coercion outside Faces uses them.

com.sun.faces.enableDistributable

false

Re-sets session attributes after every change so a clustered container replicates them.

com.sun.faces.enableTransitionTimeNoOpFlash

false

Returns a no-op Flash during startup and shutdown.

com.sun.faces.forceAlwaysWriteFlashCookie

false

Writes the flash cookie on every response.

com.sun.faces.numberOfActiveViewMaps

25

View scope maps kept in the session; the least recently used is destroyed beyond that. The Scopes page observed it.

com.sun.faces.numberOfConcurrentFlashUsers

5000

Size of the application-wide flash store (since 4.0.7). The misspelled numberOfConcerrentFlashUsers is the deprecated name.

com.sun.faces.numberOfFlashesBetweenFlashReapings

5000

Flash creations between two sweeps of expired entries.

com.sun.faces.websocketEndpointIdleTimeout

0

Milliseconds after which an idle WebSocket session is closed; 0 never closes one (since 4.0.21).

com.sun.faces.websocketMaxSessionsPerChannel

none

Maximum concurrent WebSocket sessions per channel; unbounded by default (since 4.0.21).

com.sun.faces.allowedHttpMethods

OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT

Space-separated HTTP methods the FacesServlet accepts, * for all. Run: with GET POST HEAD a PUT and an OPTIONS answered 400 and a GET 200.

com.sun.faces.ENABLE_HTTP_METHOD_RESTRICTION_PHASE_LISTENER

false

Completes the response in Restore View for OPTIONS.

com.sun.faces.generateUniqueServerStateIds

true

Random identifiers for server-side state instead of a counter.

com.sun.faces.disableClientStateEncryption

false

Turns off encryption and the tamper evidence of client state; a debugging aid, never for production.

com.sun.faces.enableCspNonce and com.sun.faces.cspPolicy

false; script-src 'self' 'nonce-#{nonce}' 'strict-dynamic'

Per-response nonce on rendered scripts and the Content-Security-Policy header that uses it (since 4.0.16). The security page uses it.

com.sun.faces.enableScriptsInAttributeValues

true

Allows attribute values that start with javascript:; false closes a class of XSS.

com.sun.faces.disallowDoctypeDecl

false

Rejects a Facelets source with a <!DOCTYPE>; relevant only when views come from an untrusted store.

Two practical notes. The page of CONTEXT-PARAMS.md also says that a custom value is allowed for PROJECT_STAGE; as shown in How the Stage Is Determined, in practice a name that is not a constant becomes Production. And parameters with a com.sun.faces. name that you find in older articles (for example com.sun.faces.expressionFactory) are not in the 4.1 reference and should be treated as inert.

MyFaces Parameters

MyFaces documents its parameters in the configuration section of its Core 4.1 page, in groups (common, validation, EL, render, view handler, resources, state). They live in the org.apache.myfaces. namespace, apart from the standard ones the page repeats. MyFaces prints its parameters with org.apache.myfaces.LOG_WEB_CONTEXT_PARAMS: the default is dev-only, which logs in Development and nowhere else; true logs in every stage. The scratch run printed INFO: Init context parameter found 'jakarta.faces.PROJECT_STAGE' set to 'Production' for a parameter that was set and INFO: No context init parameter 'org.apache.myfaces.MAC_SECRET' found. for one that was not. A placeholder value set for org.apache.myfaces.SECRET was not printed in the log of that run, which is how it should be: do not put a real secret in a parameter you also print.

In the table the prefix org.apache.myfaces. is left out (the reference writes it o.a.m.). The table lists the parameters that matter in practice; the page of the reference has the rest.

Table 21. MyFaces 4.1 parameters: startup, errors and logging
Parameter Default Effect

LOG_WEB_CONTEXT_PARAMS

dev-only

Logs the parameters at startup: true, dev-only (not in Production) or false. Run.

ERROR_HANDLING

false; true in Development

MyFaces writes its own error page for an unhandled exception. Run: switched on in Production it answered 500 with an 11 KB page whose title was the exception message.

ERROR_TEMPLATE_RESOURCE, DEBUG_TEMPLATE_RESOURCE

META-INF/rsc/myfaces-dev-error.xml, META-INF/rsc/myfaces-dev-debug.xml

The templates of the error page and of the ui:debug page.

DEBUG_PHASE_LISTENER

false

Adds a listener that records submitted, local and converted values.

VALIDATE_XML and VALIDATE

off

Schema validation of configuration and tag library files; and a consistency check of the configuration (the reference names managed beans and navigation rules). Run: the first logged a SEVERE for an invalid file.

EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING

none

Comma-separated exception classes the ExceptionHandler does not log (since 4.1.1).

WRAP_TAG_EXCEPTIONS_AS_CONTEXT_AWARE

true

Wraps EL exceptions so the handler can report the location, expression and tag.

CONFIG_REFRESH_PERIOD

2

Seconds between checks for changes of the web configuration.

INITIALIZE_ALWAYS_STANDALONE

false

Skips the check for a FacesServlet mapping when starting.

INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN

false

Skips scanning JARs for faces-config.xml when deciding whether the servlet must be added; also readable as a system property.

FACES_INITIALIZER, FACES_INIT_PLUGINS, SERVICE_PROVIDER_FINDER, spi.InjectionProvider

none

Class names of integration points for containers and frameworks.

annotation.SCAN_PACKAGES

none

Comma-separated packages scanned for annotations. Run: see Annotations or XML.

annotation.USE_CDI_FOR_ANNOTATION_SCANNING

false

Uses CDI’s discovery instead of MyFaces’s own scan.

LAZY_LOAD_CONFIG_OBJECTS

true

Loads the classes of components, converters, validators and behaviors when first used.

USE_LAMBDA_METAFACTORY

false

Uses LambdaMetafactory for property access instead of reflection.

VIEW_POOL_MAX_POOL_SIZE, VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT, VIEW_POOL_ENTRY_MODE, VIEW_POOL_DEFERRED_NAVIGATION

5, 2, soft, false

Tuning of the view pool. The pool needs CACHE_EL_EXPRESSIONS set to alwaysRecompile: the scratch log said "View Pooling disabled" with the default.

ALWAYS_FORCE_SESSION_CREATION

false

Creates the session before rendering if there is none.

FLASH_SCOPE_DISABLED

false

Disables the flash scope and its cookie.

EL_RESOLVER_TRACING

false

Logs the resolvers consulted per request (Development only).

WEBSOCKET_MAX_CONNECTIONS, WEBSOCKET_MAX_IDLE_TIMEOUT

none, 300000

Limits of the f:websocket endpoint.

Table 22. MyFaces 4.1 parameters: validation, EL and rendering
Parameter Default Effect

CLEAR_INPUT_WHEN_SUBMITTED_VALUE_IS_NULL_OR_EMPTY

true

Input fields are cleared when null or empty values are submitted.

ENUM_CONVERTER_ALLOW_STRING_PASSTROUGH

false

A String value goes through the enum converter unchanged.

validator.BEAN_BEFORE_JSF_VALIDATION

false

f:validateBean runs before any other validator.

CACHE_EL_EXPRESSIONS

noCache

noCache, strict, allowCset, always or alwaysRecompile: how generated expressions are cached.

enableOptionalELResolver

false

Adds OptionalELResolver to the chain (since 4.1.2; Mojarra enables it and has a switch to remove it).

SUPPORT_EL_3_IMPORT_HANDLER

false

Enables the EL ImportHandler.

EXPRESSION_FACTORY, EL_RESOLVER_COMPARATOR, EL_RESOLVER_PREDICATE

none

Replace the expression factory, order the EL resolvers, or leave one out. The reference warns that this can break the resolvers the specification requires.

STRICT_JSF_2_CC_EL_RESOLVER

false

Composite component getType() uses the declared attribute type.

STRICT_XHTML_LINKS

true

Uses &amp; in generated links.

EARLY_FLUSH_ENABLED

false

Sends the <head> while the body renders.

RENDER_FORM_VIEW_STATE_AT_BEGIN

false

Renders the view state and hidden fields at the beginning of the form.

JSF_JS_MODE, USE_MULTIPLE_JS_FILES_FOR_JSF_UNCOMPRESSED_JS

normal, false

Which build of faces.js is served, and whether its sources are separate files in Development.

DEFAULT_RESPONSE_WRITER_CONTENT_TYPE_MODE

text/html

Content type when the Accept header has no match.

RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING

false

Client behavior scripts are passed as strings.

STRICT_JSF_2_VIEW_NOT_FOUND

false

Restore View answers 404 when the view id does not exist.

STRICT_JSF_2_FACELETS_COMPATIBILITY

false

c:set, ui:param, ui:decorate, ui:composition and ui:include behave as in Facelets 1.1.x.

VIEWID_CACHE_SIZE, VIEWID_EXISTS_CACHE_ENABLED, VIEWID_PROTECTED_CACHE_ENABLED, VIEWID_DERIVE_CACHE_ENABLED

500, true, true, true

Caches that avoid repeated look-ups of view ids.

VIEW_UNIQUE_IDS_CACHE_ENABLED, COMPONENT_UNIQUE_IDS_CACHE_SIZE

true, 300

Caches for generated ids.

RESOURCE_HANDLER_CACHE_ENABLED, RESOURCE_HANDLER_CACHE_SIZE

true, 500

Cache of "does this resource exist".

RESOURCE_MAX_TIME_EXPIRES

604800000

Milliseconds in the Expires header of resources.

RESOURCE_BUFFER_SIZE, RESOURCE_CACHE_LAST_MODIFIED

2048, true

Buffer while streaming a resource; cache of the last-modified time in Production.

TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED, STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME

false, false

Cache resources of JARs in a temporary directory; allow a slash in a library name.

Table 23. MyFaces 4.1 parameters: state saving
Parameter Default Effect

NUMBER_OF_VIEWS_IN_SESSION

20

Latest views kept in the session with server state saving. Evicted state leads to ViewExpiredException; the Scopes page observed that MyFaces destroys the view beans with it.

NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION

4

Views kept for a sequence of POST or POST-redirect-GET navigations.

USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION

false

Lets old views be purged after Post/Redirect/Get.

NUMBER_OF_FLASH_TOKENS_IN_SESSION

none

Maximum flash tokens in the session.

USE_ENCRYPTION

true

Encrypts the view state (client saving). Never set to false in production.

ALGORITHM, ALGORITHM.PARAMETERS, ALGORITHM.IV, SECRET, SECRET.CACHE

AES, ECB/PKCS5Padding, none, none, none

The cipher, its mode and padding, the initialization vector and the key (Base64). The reference gives no default for the key: a cluster needs the same one on every node, supplied from the environment (see below). The documented default mode is ECB/PKCS5Padding; the state saving page says what to use.

MAC_ALGORITHM, MAC_SECRET, MAC_SECRET.CACHE

HmacSHA256, none, none

The message authentication code of the state and its key.

COMPRESS_STATE_IN_CLIENT, COMPRESS_STATE_IN_SESSION

false, true

Compression before encryption in client state, and in the session.

SERIAL_FACTORY

none

Factory class for serializing the state.

CLIENT_VIEW_STATE_TIMEOUT

0

Minutes a client state stays valid.

RANDOM_KEY_IN_VIEW_STATE_SESSION_TOKEN and the ..._LENGTH, ..._SECURE_RANDOM_CLASS, ..._PROVIDER, ..._ALGORITHM variants

random, 8

The random key added to the server view state token (and RANDOM_KEY_IN_CSRF_SESSION_TOKEN for the CSRF token, default none, length 16).

RENDER_VIEWSTATE_ID

true

Renders the id of the hidden state field.

AUTOCOMPLETE_OFF_VIEW_STATE

one-time-code

autocomplete value of the hidden field; true renders off (the default changed in 4.1.4).

REFRESH_TRANSIENT_BUILD_ON_PSS, REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE

auto, false

Whether the page is re-applied to a refreshed view and whether state is preserved for c:if-style blocks.

CHECK_ID_PRODUCTION_MODE

auto

How duplicate ids are checked in Production; auto checks the components that do not wrap markup.

The key parameters are the one place where a configuration file tempts a programmer into a security mistake. A key must be the same on every node of a cluster and must not sit in the version control system of the project. Supply it at build or deploy time from the environment, and build or deploy fails when it is missing:

A secret filled in from the environment, never written in the file
<context-param>
  <param-name>org.apache.myfaces.SECRET</param-name>
  <param-value>${env.BOOKSHELF_VIEWSTATE_SECRET}</param-value>
</context-param>

With the war plugin filtering of the earlier section, a build with BOOKSHELF_VIEWSTATE_SECRET=placeholder-from-ci in its environment wrote that value into the packaged descriptor, and a build without the variable left the text ${env.BOOKSHELF_VIEWSTATE_SECRET} in it unreplaced, which is why the value must be checked after packaging (a deploy script that greps the descriptor for ${ is enough). The value shown is a placeholder; a real key is generated with a secure random source and kept in the CI secret store or the server’s configuration. The state saving page explains the format of the key and the cluster implications.

Extensionless Mapping and the FacesServlet Mappings

Which URLs reach the FacesServlet is configuration too. The Project Setup page covers the three mapping styles of web.xml (extension, prefix and exact); this section covers the parts that depend on parameters: the mapping Faces adds when you declare none, the Faces 4.0 extensionless mapping, and what each setting does to the URLs the application generates.

The Automatic Mapping

When the servlet is not mapped in web.xml or web-fragment.xml and Faces detects that the application uses it (the triggers of the diagram at the start of this page), the implementation maps it to four patterns: /faces/*, *.jsf, *.faces and *.xhtml. jakarta.faces.DISABLE_FACESSERVLET_TO_XHTML removes the last of them. A mapping that you declare yourself replaces all four.

AUTOMATIC_EXTENSIONLESS_MAPPING

With jakarta.faces.AUTOMATIC_EXTENSIONLESS_MAPPING set to true, the implementation scans the web application at startup and adds a mapping for the extensionless variant of every view it finds (/index for /index.xhtml). Mojarra’s reference warns that the scan walks the whole web root and registers a mapping per view, so the startup cost grows with the number of views.

Automatic extensionless mapping
<context-param>
  <param-name>jakarta.faces.AUTOMATIC_EXTENSIONLESS_MAPPING</param-name>
  <param-value>true</param-value>
</context-param>

The exact alternative, which the Jakarta EE Tutorial describes, lists the views yourself and costs nothing at startup:

Exact mappings for two views
<servlet-mapping>
  <servlet-name>facesServlet</servlet-name>
  <url-pattern>*.xhtml</url-pattern>
  <url-pattern>/index</url-pattern>
  <url-pattern>/other</url-pattern>
</servlet-mapping>

Five configurations of the scratch application were requested at six URLs. Mojarra and MyFaces gave the same status code in every cell; "raw" means that the response was the unprocessed source of the view.

Table 24. Which URLs reach the FacesServlet (identical on Mojarra and MyFaces)
URL No servlet declared, no parameter Parameter true, no servlet declared Parameter true, *.xhtml declared Exact /index and /other added to *.xhtml DISABLE_FACESSERVLET_TO_XHTML, no servlet declared

/index

404

200

200

200

404

/index.xhtml

200

200

200

200

200, raw source

/faces/index.xhtml

200

200

404

404

200

/index.jsf

200

200

404

404

200

/index.faces

200

200

404

404

200

/other

404

200

200

200

404

The table has three consequences.

  • A declared mapping replaces the automatic ones. With *.xhtml declared, /faces/index.xhtml and /index.jsf stop working, even though the extensionless parameter is on. That is usually what you want, and it explains a bookmarked old URL that suddenly answers 404 after someone adds a <servlet-mapping>.

  • The extensionless parameter adds, it does not replace. The .xhtml URLs keep working, which is why a site migrating to clean URLs must redirect or canonicalise the old ones itself.

  • DISABLE_FACESSERVLET_TO_XHTML without a mapping of your own serves the views as text. /index.xhtml answered 200 with the literal stage=#{configInfoBean.stage} of the source on both implementations, and /faces/index.xhtml still rendered: an attacker reads every view’s source, including the EL expressions and the structure of the application, by dropping the prefix. Use the parameter only together with an explicit mapping for each URL pattern that must work, and a security-constraint for the rest, exactly as for prefix mapping.

URLs Generated by Faces

Faces builds the URLs of links, form actions and redirects from the mapping of the current request. With the extensionless parameter on, a page requested with its extension linked to the other pages with their extension, and a page requested extensionless linked to extensionless URLs:

Table 25. URLs generated for h:link, h:form and a faces-redirect=true navigation
Request h:link outcome="other" Form action Redirect from action="other?faces-redirect=true"

/index.xhtml

/bookshelf/other.xhtml

/bookshelf/index.xhtml

/bookshelf/other.xhtml

/index

/bookshelf/other

/bookshelf/index

/bookshelf/other

/faces/index.xhtml

/bookshelf/faces/other.xhtml

/bookshelf/faces/index.xhtml

not run

/index.jsf

/bookshelf/other.jsf

not run

not run

The results are identical for Mojarra and MyFaces, and also hold for the configuration with exact mappings for /index and /other. The rule has one consequence for links: the first URL of a visit decides the form of every later one, so a canonical form has to be imposed on the first request (a redirect from the extension form to the clean form, or a link from outside that uses the clean form). Another consequence is that the view id never changes: navigation outcomes, include-view-params, f:viewParam and the security patterns are written with view ids, and the mapping is only a prefix or suffix applied when the URL is built. The Navigation page and the View Parameters page show that in use.

Mojarra and MyFaces

The two implementations pass the same specification and agreed on most of what this page measured: the parameter semantics of the standard names, the requirement that XML beats annotations, the discovery locations, the mapping matrix, the generated URLs, the JNDI precedence of the stage and the silent handling of unknown names. They differ where the specification leaves a choice, and in a few places where it does not.

Table 26. Configuration differences observed
Topic Mojarra 4.1.17 MyFaces 4.1.4

Parameter namespace and printing

com.sun.faces.*; displayConfiguration logs every parameter at INFO.

org.apache.myfaces.*; LOG_WEB_CONTEXT_PARAMS logs the parameters, dev-only by default.

UnitTest and SystemTest

Behave as non-production for unresolved h:link outcomes only.

Behave as Production in every measured respect.

Failure while rendering

Development: a Faces error page with tree and variables. Otherwise the container’s error report.

Development, or ERROR_HANDLING=true: a Faces error page. Otherwise status 500 with the part rendered so far.

A resource that does not exist

RES_NOT_FOUND in the markup; a message in Development.

Nothing rendered, nothing logged.

CONFIG_FILES and the META-INF search

The scan is not skipped; the listed file is read after the META-INF files.

The scan is not skipped; the listed file is read before them.

Libraries with no ordering

Alphabetical by file name. In the specification’s example the unnamed library D landed between B and A.

Alphabetical by file name. In the specification’s example the unnamed library D landed before B.

absolute-ordering without <others/>

Startup fails with ArrayIndexOutOfBoundsException (Documents.sortDocuments).

Works; unlisted files are ignored, as specified.

<ordering> in WEB-INF/faces-config.xml

Ignored with the warning JSF1081.

Ignored without a message.

Two files with the same name

Both deployed (the specification asks for a failed deployment).

Both deployed.

Cycle in the ordering

Deployment aborted.

SEVERE at startup; status 500.

XML validation switch

validateXml logged nothing for an invalid file.

VALIDATE_XML logged SEVERE: cvc-complex-type... and went on.

FACELETS_SUFFIX with a list and FACELETS_VIEW_MAPPINGS alone

A list works; the view mappings need the matching suffix.

A list of .facelet .xhtml answered 404 for /suffix.xhtml; the view mappings alone serve the .html file.

Ajax error in Development

alert.

No alert.

The practical rule stays the one of the Project Setup page: keep the implementation parameters in a profile or a container-specific descriptor, and test configuration-dependent behaviour on the implementation that production uses.

What Changed

Table 27. Configuration changes by release
Version Change

Faces 2.0

ProjectStage and the FACELETS_* family of parameters; the annotations @FacesComponent, @FacesConverter, @FacesValidator, @FacesRenderer and @FacesBehavior so that most of faces-config.xml became optional.

Faces 2.2

CLIENT_WINDOW_MODE, WEBAPP_RESOURCES_DIRECTORY, WEBAPP_CONTRACTS_DIRECTORY, SERIALIZE_SERVER_STATE, ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE; ApplicationConfigurationPopulator; protected views, flows and resource library contracts in faces-config.xml.

Faces 2.3

DISABLE_FACESSERVLET_TO_XHTML, ENABLE_WEBSOCKET_ENDPOINT, WEBSOCKET_ENDPOINT_PORT, VIEWROOT_PHASE_LISTENER_QUEUES_EXCEPTIONS, validator.ENABLE_VALIDATE_WHOLE_BEAN; the search expression elements in application; @FacesConfig.

Faces 3.0

The jakarta.faces.* names replace javax.faces.* for every parameter; the https://jakarta.ee/xml/ns/jakartaee namespace and the 3.0 schema.

Faces 4.0

AUTOMATIC_EXTENSIONLESS_MAPPING and NUMBER_OF_CLIENT_WINDOWS; JSP views, the managed bean facility with its XML elements and the ResourceResolver are removed, and DEFAULT_SUFFIX is deprecated for removal.

Faces 4.1

The 4.1 schema and version="4.1"; PARTIAL_STATE_SAVING and FULL_STATE_SAVING_VIEW_IDS are deprecated. Implementation news: Mojarra disableOptionalELResolver (4.1.4), MyFaces enableOptionalELResolver (4.1.2), EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING (4.1.1) and the one-time-code default of AUTOCOMPLETE_OFF_VIEW_STATE (4.1.4).

Faces 5.0 (upcoming)

Still at 5.0.0-M9. Support for the old java.sun.com and xmlns.jcp.org Facelets namespaces is planned to be removed and Java 21 becomes the minimum; treat all of it as subject to change until a final release.

Where the Books Are Outdated

The books consulted for this section describe Faces 1.0 to 2.2. The faces-config.xml reference of Burns and Schalk and the configuration chapter of Dudney and colleagues are the ones that touch this page, and the configuration they teach has changed in these ways:

  • javax.faces. parameter names.* Every parameter of the books has a jakarta.faces. name since 3.0, and the old names are ignored without a message (verified: javax.faces.PROJECT_STAGE had no effect on either implementation).

  • <managed-bean>, <managed-property> and <referenced-bean> in faces-config.xml. The first two are gone from the schema and from the runtime, and a <managed-bean> element is ignored; <referenced-bean> remains in the schema only as a note for tools. Beans are CDI beans.

  • The Pages-era configuration. JSP views, DEFAULT_SUFFIX and a FacesServlet mapped to *.jsp or *.jsf for JSP pages belong to the past; the default mapping and suffix are .xhtml and the Pages suffix is deprecated for removal.

  • DTD and old schemas. The Faces 1.0 and 1.1 DOCTYPE with web-facesconfig_1_x.dtd and the schemas of 1.2 to 2.3 name namespaces that the 4.1 documents do not use; the Project Setup page tabulates them.

  • The Facelets 1.x parameters. The standalone Facelets book configures the library through its own facelets.* context parameters and a com.sun.facelets view handler. Facelets is part of Faces since 2.0, and the jakarta.faces.FACELETS_* parameters of this page replace them.

  • Implementation parameters. The Reference Implementation parameters of older books and articles come from Mojarra 1.x and 2.x. Check every name against CONTEXT-PARAMS.md: a name that is not there (for example com.sun.faces.expressionFactory) is not a 4.1 parameter, and an unknown name is ignored without a message.

  • Project stages and development mode. Standalone Facelets had a development setting of its own. The ProjectStage of Faces 2.0 replaces it, and its effects are the ones of Project Stages, fewer than a reader may assume.

  • Secrets in configuration. Nothing in Faces configuration needs a literal secret: take keys and passwords from the environment or the server, as shown for the MyFaces key, and keep placeholders in the files under version control.

Source: Jakarta EE Tutorial: Configuring Jakarta Faces Applications (the tutorial documents Jakarta EE 10; its managed-bean pages and the schema 3.0 shown in it are outdated for Faces 4.1)

Common Mistakes

  • Leaving Development in web.xml of a release. The error page, the unhandled-message list and the refresh checks reach production. Make Production the default of the build and select Development with a profile, or take the stage from JNDI.

  • Believing UnitTest and SystemTest do something. On MyFaces they equal Production; on Mojarra they differ in one rendering detail.

  • Believing ui:debug is switched off in production. It is not; guard it with the stage, once, in the master template.

  • Enabling ERROR_HANDLING (MyFaces) or the Development stage on a public server to find a problem. Stack traces, bean state and the component tree go to the client.

  • Mistyping a parameter name. Nothing complains; print the parameters at startup and look.

  • Trusting required for server-side rules. A request that omits the field skips validation unless ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE is set; validate on the server whatever the browser sends.

  • A view suffix other than .xhtml without a constraint. The raw Facelets source is served by the container.

  • DISABLE_FACESSERVLET_TO_XHTML without a mapping of your own. The .xhtml views are then served as plain text.

  • Absolute ordering without <others/> on Mojarra 4.1.17. The application does not start.

  • Relying on classpath order. The order of unordered files is whatever the container lists; use <ordering>.

  • Relying on CONFIG_FILES to replace the META-INF search. Neither implementation skipped it.

  • Duplicate <name> values. The deployment is not refused, and one of the files may be shadowed.

  • Restricting the annotation scan too far. A converter in a package that is not listed fails at first use, not at startup.

  • Real secrets in web.xml or faces-config.xml. Keys, passwords and database URLs belong in the environment or the server; the files in version control carry placeholders.

  • Changing a descriptor and expecting a reload. Only the view files are re-read; every parameter is read at startup.

The Project Setup page introduces the descriptors and the hot-reload loop that this page extends. The Scopes page measures NUMBER_OF_CLIENT_WINDOWS, CLIENT_WINDOW_MODE and the eviction parameters, the lifecycle page explains where the stage-dependent behaviour of validation sits, and the Navigation page covers the rules that go in faces-config.xml. The state saving, resource handling, resource library contracts, exception handling, extending Faces, performance and security pages pick up the parameters that belong to them, and the Tomcat and Weld, servers, Spring Boot with JoinFaces and Quarkus pages show how the same configuration is expressed where there is no web.xml.