back to all blogsSee all blog posts

Jakarta EE 9.1 support and configurable response headers in Open Liberty

image of author
Ryan Storey on Nov 26, 2021
Post available in languages:

Jakarta EE 9.1 support is now available as part of Open Liberty, alongside configurable response headers, which offer more granular control over response headers! Several significant bug fixes are also part of this release.

In Open Liberty

Along with the new features and functions added to the runtime, we’ve also made updates to our guides.

Run your apps using

If you’re using Maven, here are the coordinates:


Or for Gradle:

dependencies {
    libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '[,)'

Or if you’re using Docker:

FROM open-liberty

Or take a look at our Downloads page.

Ask a question on Stack Overflow

Jakarta EE 9.1 support

jakarta ee

Jakarta EE 9.1 support is now available in Open Liberty!! This support allows you to run Jakarta EE 9.1 applications using Java 8, 11, or 17 with other Open Liberty value-add features that are updated to support Jakarta EE 9.1. Many of you have followed our progress for delivering Jakarta EE 9.1 via our beta releases and corresponding blogs, and we’d like to thank those that provided feedback along the way. If you’re targeting a new application for Jakarta EE 9.1, make sure to use the jakarta namespace. For existing applications that you’d like to move from Java EE (and its javax namespace) to Jakarta EE (and its jakarta namespace) we recommend trying the Eclipse Transformer, an open source project originally developed by members of the Open Liberty team and then contributed to the Eclipse Foundation.

With the RESTful Web Services 3.0 (formerly called JAXRS) support in Open Liberty, there is a significant performance improvement for applications which use the RESTful Web Services function. This performance improvement was achieved by moving our RESTful Web Services implementation from Apache CXF to using RestEasy. With this new version, CDI is enabled by default, and JSON Binding is not enabled by default with the feature being specified in your server.xml.

Any Liberty features with API and/or SPI functions that use Jakarta EE APIs as part of their method signatures have been updated to have their package versions be reset to 10.0 when using those API / SPIs with Jakarta EE 9.1. Any bundles used for user features that depend on those packages will need to change the import package version range when updating the user feature to use Jakarta EE 9.1.

With the introduction of Jakarta EE 9.1, the Jakarta Enterprise Beans 4.0 specification includes a few minor changes over the prior version of the specification, Enterprise JavaBeans (EJB) 3.2, as follows:

  • Note the new names of the features; all of the same features exist, but the feature name prefix has changed from ejb to enterpriseBeans. For example, enterpriseBeansLite-4.0 is the new version of ejbLite-3.2.

  • The API package has changed from javax.ejb to jakarta.ejb

  • The @Schedule annotation is now repeatable

  • The following API methods have been removed:

    • javax.ejb.EJBContext.getCallerIdentity() → use getCallerPrincipal()

    • javax.ejb.EJBContext.getEnvironment() → use JNDI lookup in java:comp/env

    • javax.ejb.EJBContext.isCallerInRole( → use isCallerInRole(String)

    • javax.ejb.SessionContext.getMessageContext() (removed with JAX-RPC)

All other capabilities of Enterprise Beans remain the same as the prior specification version (3.2).

Although many of the Jakarta EE 9.1 features have only received a version update, the majority have also had their name changed. The following table lists the features for which both the short name and the version number are changed. To update one of these features for Jakarta EE 9.1, you must change both the feature short name and version number in your server.xml file.

Table 1. Jakarta EE 9.1 feature updates, short name and version
Jakarta EE 9.1 feature name Java EE/Jakarta EE 8 short name and version Jakarta EE 9.1 short name and version

Jakarta Enterprise Beans



Jakarta Enterprise Beans Home Interfaces



Jakarta Enterprise Beans Lite



Jakarta Enterprise Beans Persistent Timers



Jakarta Enterprise Beans Remote



Jakarta Expression Language



Jakarta Authorization



Jakarta Authentication



Jakarta EE Platform



Jakarta EE Application Client



Jakarta Mail



Jakarta XML Binding



Jakarta RESTful Web Services



Jakarta RESTful Web Services Client



Jakarta XML Web Services



Jakarta Connectors



Jakarta Connectors Inbound Security



Jakarta Messaging



Jakarta Persistence



Jakarta Persistence Container



Jakarta Server Faces



Jakarta Server Faces Container



Jakarta Server Pages



Messaging Server Client



Messaging Server Security



Messaging Server



For a full overview of what has changed, visit the Jakarta EE 9.1 feature updates page.

To enable the Jakarta EE 9.1 features, add the corresponding feature to your server.xml. You can enable either the individual features you want or you can add the Jakarta EE 9.1 convenience features. For example, to enable all of the Jakarta EE 9.1 features at once add:


Or you can add the Web Profile convenience feature to enable all of the Jakarta EE 9.1 Web Profile features at once:


For details regarding the APIs and SPIs, check out the Jakarta EE 9.1 javadoc.

Configurable Response Headers

You can now configure Open Liberty to modify response headers. The available configuration options allow for headers to be appended, for existing headers to be overwritten, for missing headers to be added, and for undesired headers to be removed from all responses being serviced by an HTTP endpoint. This configuration offers more granular control over response headers, which offers a solution to modifying headers without the need to change existing applications, filters, or otherwise.

To use configurable response headers, begin by defining a new element called <headers> in the server.xml. You can configure this for individual HTTP endpoints or for all endpoints at once.

Configuring for individual HTTP endpoints:

<httpEndpoint id="defaultHttpEndpoint"


Configuring for all HTTP endpoints:

<httpEndpoint id="defaultHttpEndpoint"

<httpEndpoint id="otherHttpEndpoint"

<headers id="myHeadersID">

The add attribute allows multiple headers with the same name to be added to a response, similar to the HttpServletResponse’s addHeader API. Similarly, the set attribute is analogous to the setHeader API, which sets a response header to the given name and value. This overwrites existing headers that share the same name. The setIfMissing attribute will only set the configured headers if they are not already present on the response. Lastly, the remove attribute will remove any response headers whose name matches a name defined by the configuration.

Each header entry for the add, set, and setIfMissing attributes can be provided as a stand-alone header name. Optionally, a header value can be added by appending the colon : character after every header name. Note, however, that the remove attribute only expects header names and not a header name:value pair.

As seen in the example above, one way to configure the <headers> element is to declare each individual header within it own add, set, setIfMissing, or remove attribute. In addition to this configuration, headers can be provided as a comma delimited list.

The following server.xml configuration declares individual headers within the desired configuration attributes:


This configuration can also be declared as comma delimited lists, such as:

<headers add="foo:bar, foo:bar2" set="customHeader:customValue" setIfMissing="X-Forwarded-Proto:https" remove="Via"/>

There are three warning messages relating to misconfigurations for this feature. Note that if a configuration value is considered to be misconfigured, it will not be utilized. Furthermore, if the misconfigured value had a non-empty header name, any further configurations with this same name will also be ignored.

The first warning message, CWWKT0042W, will be logged whenever a header name is left empty. While header values are completely optional, the configuration does expect a non-empty header name.

The add configuration allows for multiple headers with the same name to be configured. However, it would be ambiguous to repeat a header name in any other configuration attribute. For instance, consider the set attribute option, which is meant to overwrite an existing header that shared the declared header name. If the set configuration contained two headers with the same name, it would be unclear which of the two values should be chosen. Similarly, if the same header name is present in two or more configurations, the same ambiguity is true. As such, and excluding repetitions in the add configuration, whenever a header name is found to be used more than once, the warning message CWWKT0043W will be logged.

The third warning message, CWWKT0044W, is logged if a header that has already been flagged as a duplicate by the CWWKT0043W warning message, continues to be utilized by further configurations.

Warning Message Descriptions:

CWWKT0042W : An empty header name was found when the 'set` configuration was parsed. This value is ignored.

CWWKT0043W : A duplicate header name was found in the [foo] header using the set configuration. All configurations for the [foo] header are ignored. Any header that is defined by the remove, add, set, or setIfMissing configurations must be unique across all configurations.

CWWKT0044W : The [foo] header, which is marked as a duplicate header name, was found in the set configuration. The [foo] header is ignored. Any header that is defined by the set configuration must contain unique header names.


Open Liberty now provides a way to control response headers for a given HTTP endpoint. These can be appended, configured to overwrite, to only be added if not already present, or completely removed from all responses. Try it out for yourself!

Notable bugs fixed in this release

We’ve spent some time fixing bugs. The following sections describe just some of the issues resolved in this release. If you’re interested, here’s the full list of bugs fixed in

  • Throughput performance degradation in eclipselink due to Thread.getStackTrace calls

    We discovered an issue where a change to the org.eclipse.persistence.internal.helper.ConcurrencyManager class caused a ~75% throughput performance degradation in eclipselink. This lost throughput was caused by calls to Thread.getStackTrace(). This regression showed up for jpa-2.2 in and persistence-3.0 in This issue has now been fixed by removing the getStackTrace() calls.

  • MicroProfile OpenAPI 2.0 includes non-public fields in the generated documentation

    Previously, when a schema was created for a class which includes a private field, the private field would be listed in the generated OpenAPI document, for example:

    public class Example {
        private String field1;
        public String field2;

    results in

          type: object
              type: string
              type: string

    The field field1 should not have appeared in the generated OpenAPI document as it is private. This issue has been fixed by setting the mp.openapi.extensions.smallrye.private-properties.enable property to disable non-public properties by default.

  • Port bind skipped at server startup

    Previously, in an extremely rare scenario, configured ports could silently fail to bind - preventing Liberty from using them. This issue was caused by a subtle race condition in the code responsible for delaying the port bind until the server is ready to handle traffic.

    In the failing scenario, the port started message would not be emitted - for example the following message would be missing:

    CWWKO0219I: TCP Channel defaultHttpEndpoint has been started and is now listening for requests on host * (IPv4) port 9080.

    and the following FFDC will be seen:

    Exception = java.lang.RuntimeException
    Source =
    probeid = 254
    Stack Dump = java.lang.RuntimeException: java.nio.channels.NotYetBoundException
            at java.base/
    Caused by: java.nio.channels.NotYetBoundException
            at java.base/
            at java.base/
            ... 2 more

    This issue has now been fixed so that all configured ports should start, or if there is a problem some meaningful error message should be logged.

  • Application fails to restart in server.xml update scenario

    We discovered an issue where an application would fail to restart, due to a race condition during server reconfiguration when multiple apps are installed. The problem occurs when one app starts before another app is finished uninstalling. In theory this shouldn’t be a problem - however for this scenario these apps are sharing a VirtualHost configuration object, and in this case one app updates the parent VirtualHost as part of its uninstall process in such a way that the other gets into an invalid state. The server log will show a warning such as CWWKZ0020I: Application <app_name> not updated. This issue was fixed by fixing the race condition that caused the failure.

  • HTTP upgrade to WebSocket can cause quiesce errors

    When a WebSocket connection is started, it starts as an HTTP connection. Previously, If an error occurred during the transition between an HTTP and a WebSocket connection, which was known to be a WebSocket upgrade, the error processing would neglect to decrement a connection counter, which then caused the server to believe there is an open connection during server stop. There were two scenarios where these quiesce errors would occur:

    • When a read error occurred during the transition between an HTTP and a WebSocket connection, the error processing neglected to decrement a connection counter, which then causes the server to believe there is an open connection during server stop.

    • If a client immediately closed the WebSocket connection after it was opened, the original upgrade request handling may not have had enough time to close properly on the server. Once again, the connection counter failed to decrement leading the server to believe there is an open connection during the server stop.

      This issue has been fixed by adding a new flag called decrementNeeded which helps to ensure that the decrement is not neglected.

  • Ensure ServletRequestListener#requestDestroyed is always called

    We discovered a bug where the ServletRequestListener#requestDestroyed call does not occur, if an exception occurs during async servlet while an appSecurity-x.0 is enabled. For this bug to occur, two conditions must be met: the webContainer property deferServletRequestListenerDestroyOnError is true and an appSecurity-x.0 feature is enabled. This issue has now been resolved.

  • ClassCastException in JSP relating to JDT internal classes

    Open Liberty introduced a bug where the following error occurred for certain class lookups in JSP:

    Error 500: java.lang.ClassCastException: class org.eclipse.jdt.internal.compiler.lookup.PlainPackageBinding cannot be cast to class org.eclipse.jdt.internal.compiler.lookup.TypeBinding (org.eclipse.jdt.internal.compiler.lookup.PlainPackageBinding and org.eclipse.jdt.internal.compiler.lookup.TypeBinding are in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @3522bc53)

    This issue has now been fixed.

New and updated guides since the previous release

As Open Liberty features and functionality continue to grow, we continue to add new guides to on those topics to make their adoption as easy as possible. Existing guides also receive updates in order to address any reported bugs/issues, keep their content current, and expand what their topic covers.

  • Creating a multi-module application

    • Previously the guide demonstrated how to build an application with multiple modules using Maven and Open Liberty. With this update, it now also introduces how to use the Liberty Maven plug-in to develop a multi-module application in development mode without having to prebuild the JAR and WAR files.

Get Open Liberty now