<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.8.6">Jekyll</generator><link href="https://openliberty.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://openliberty.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-06-03T08:56:05+00:00</updated><id>https://openliberty.io/feed.xml</id><title type="html">Open Liberty</title><subtitle>Open Liberty is the most flexible server runtime available to Earth’s Java developers.
</subtitle><entry><title type="html">Netty‑based HTTP transport in 26.0.0.6-beta</title><link href="https://openliberty.io/blog/2026/06/02/26.0.0.6-beta.html" rel="alternate" type="text/html" title="Netty‑based HTTP transport in 26.0.0.6-beta" /><published>2026-06-02T00:00:00+00:00</published><updated>2026-06-02T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/06/02/26.0.0.6-beta</id><content type="html" xml:base="https://openliberty.io/blog/2026/06/02/26.0.0.6-beta.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This beta release introduces a preview of a Netty-based HTTP transport in Open Liberty, providing a modern, scalable foundation for HTTP and related communication protocols.</p>
</div>
<div class="paragraph">
<p>The <a href="/">Open Liberty</a> 26.0.0.6-beta includes the following beta features (along with <a href="/docs/latest/reference/feature/feature-overview.html">all GA features</a>):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#netty">Netty‑based HTTP transport</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>See also <a href="/blog/?search=beta&amp;key=tag">previous Open Liberty beta blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="netty">Netty‑based HTTP transport</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Open Liberty now provides a <strong>Netty‑based HTTP transport</strong> as a beta preview. This is an internal implementation swap for the underlying transport used for HTTP/1.1, HTTP/2, WebSocket, JMS, and SIP communications. Compared to previous releases, the key update in this release is the fix for the user-reported <a href="https://github.com/OpenLiberty/open-liberty/issues/34672">100-continue issue</a>. It is designed for <strong>zero migration</strong>: your applications and <code>server.xml</code> file should continue to behave the same. We are looking forward and counting on your feedback before GA!</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Target persona:</strong> Application developers, performance engineers, and operations/site engineers who need to validate application behavior, measure throughput, and monitor observability.</p>
</li>
<li>
<p><strong>Why it matters:</strong> Netty&#8217;s event-driven I/O model provides a modern foundation for long-term scalability, easier maintenance, and future performance enhancements— all without requiring changes to your APIs or configuration!</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="how-to-use-it">How to use it:</h3>
<div class="paragraph">
<p>No changes are required to benefit from the current <strong>All Beta Features</strong> runtime for this release.</p>
</div>
<div class="paragraph">
<p>To help validate parity and performance for your real-world scenarios, try testing these areas:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>HTTP/1.1 and HTTP/2</strong>: Large uploads or downloads, chunked transfers, compression-enabled content, keep-alive behavior.</p>
</li>
<li>
<p><strong>WebSocket</strong>: Long-lived communications, backpressure scenarios</p>
</li>
<li>
<p><strong>Timeouts</strong>: Read/write/keep-alive timeouts under load</p>
</li>
<li>
<p><strong>Access logging</strong>: Verify formatting and log results compared to previous builds</p>
</li>
<li>
<p><strong>JMS communications</strong>: Messages send/receive throughput, durable subscriptions</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="limitations-of-the-beta-release">Limitations of the Beta release:</h3>
<div class="ulist">
<ul>
<li>
<p><strong>HTTP</strong></p>
<div class="ulist">
<ul>
<li>
<p>HTTP requests with content length greater than the maximum integer value fail due to internal limitations on request size with Netty.</p>
</li>
<li>
<p>When the HTTP option <code>maxKeepAliveRequests</code> is unlimited, HTTP 1.1 supports a maximum of 50 pipelined requests.</p>
</li>
<li>
<p>The HTTP option <code>resetFramesWindow</code> is reduced from millisecond to second precision due to limitations in the Netty library.</p>
</li>
<li>
<p>Due to internal limitations of the Netty library, the HTTP option <code>MessageSizeLimit</code> is now adjusted to be capped at the maximum integer value for HTTP/2.0 connections.</p>
</li>
<li>
<p>Due to internal differences with Netty, the HTTP option <code>ThrowIOEForInboundConnections</code> can behave differently from the Channel Framework implementation.</p>
</li>
<li>
<p>Due to internal limitations of Netty, the TCP options <code>acceptThread</code> and <code>waitToAccept</code> are not implemented in this Beta and are ignored if set.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>WebSocket</strong></p>
<div class="ulist">
<ul>
<li>
<p>WebSocket inbound requests can generate First Failure Data Capture RuntimeExceptions on connection cleanup when a connection is closed from the client side.</p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>Application-Layer Protocol Negotiation (ALPN)</strong></p>
<div class="ulist">
<ul>
<li>
<p>Currently, our Netty implementation supports only the native JDK ALPN implementation. Additional information for the ALPN implementations that are currently supported by the Channel Framework but not our Netty beta can be found in the <a href="https://www.ibm.com/docs/en/was-liberty/base?topic=40-alpn-support">ALPN documentation</a>.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="run">Try it now</h3>
<div class="paragraph">
<p>To try out these features, update your build tools to pull the Open Liberty All Beta Features package instead of the main release. The beta works with Java SE 21, Java SE 17, Java SE 11, and Java SE 8.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, you can install the All Beta Features package using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;configuration&gt;</span>
        <span class="tag">&lt;runtimeArtifact&gt;</span>
          <span class="tag">&lt;groupId&gt;</span>io.openliberty.beta<span class="tag">&lt;/groupId&gt;</span>
          <span class="tag">&lt;artifactId&gt;</span>openliberty-runtime<span class="tag">&lt;/artifactId&gt;</span>
          <span class="tag">&lt;version&gt;</span>26.0.0.6-beta<span class="tag">&lt;/version&gt;</span>
          <span class="tag">&lt;type&gt;</span>zip<span class="tag">&lt;/type&gt;</span>
        <span class="tag">&lt;/runtimeArtifact&gt;</span>
    <span class="tag">&lt;/configuration&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You must also add dependencies to your pom.xml file for the beta version of the APIs that are associated with the beta features that you want to try. For example, the following block adds dependencies for two example beta APIs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>org.example.spec<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>exampleApi<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>7.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;type&gt;</span>pom<span class="tag">&lt;/type&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span>
<span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>example.platform<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>example.example-api<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>11.0.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[26.0.0.6-beta,)'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty:beta</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/downloads/#runtime_betas">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="https://openliberty.io/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="paragraph">
<p>For more information on using a beta release, refer to the <a href="docs/latest/installing-open-liberty-betas.html">Installing Open Liberty beta releases</a> documentation.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="feedback">We welcome your feedback</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let us know what you think on <a href="https://groups.io/g/openliberty">our mailing list</a>. If you hit a problem, <a href="https://stackoverflow.com/questions/tagged/open-liberty">post a question on StackOverflow</a>. If you hit a bug, <a href="https://github.com/OpenLiberty/open-liberty/issues">please raise an issue</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><summary type="html"><![CDATA[This beta release introduces a preview of a Netty-based HTTP transport in Open Liberty, providing a modern, scalable foundation for HTTP and related communication protocols.]]></summary></entry><entry><title type="html">Jakarta EE 11 from Newbie to Pro with Open Liberty</title><link href="https://openliberty.io/blog/2026/05/27/Jakarta-EE-11-in-Open-Liberty.html" rel="alternate" type="text/html" title="Jakarta EE 11 from Newbie to Pro with Open Liberty" /><published>2026-05-27T00:00:00+00:00</published><updated>2026-05-27T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/05/27/Jakarta%20EE%2011%20in%20Open%20Liberty</id><content type="html" xml:base="https://openliberty.io/blog/2026/05/27/Jakarta-EE-11-in-Open-Liberty.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://jakarta.ee/release/11/">Jakarta EE 11</a> marks a significant milestone in the evolution of enterprise Java development. This release delivers enhanced developer productivity, improved performance, and modernized APIs that align with the Java LTS releases: Java SE 17, Java SE 21. For more information, see the <a href="https://jakarta.ee/specifications/platform/11/">Jakarta EE Platform 11 specification</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="whats-new-in-jakarta-ee-11">What&#8217;s New in Jakarta EE 11?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Jakarta EE 11 represents a major step forward for cloud-native enterprise Java applications. This release includes modernizing and restructuring the Test Compatibility Kits (TCKs), the new Jakarta Data specification, major updates to existing specifications, and support for the latest Java LTS releases, which enables developers to leverage enhancements in Java 21, including Virtual Threads and Records. The new and updated specifications are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#data">Data 1.0</a></p>
</li>
<li>
<p><a href="#authentication">Authentication 3.1</a></p>
</li>
<li>
<p><a href="#authorization">Authorization 3.0</a></p>
</li>
<li>
<p><a href="#concurrency">Concurrency 3.1</a></p>
</li>
<li>
<p><a href="#annotations">Annotations 3.0</a></p>
</li>
<li>
<p><a href="#interceptors">Interceptors 2.2</a></p>
</li>
<li>
<p><a href="#cdi">CDI 4.1</a></p>
</li>
<li>
<p><a href="#faces">Faces 4.1</a></p>
</li>
<li>
<p><a href="#el">Expression Language 6.0</a></p>
</li>
<li>
<p><a href="#servlet">Servlet 6.1</a></p>
</li>
<li>
<p><a href="#websocket">WebSocket 2.2</a></p>
</li>
<li>
<p><a href="#restful">RESTful Web Services 4.0</a></p>
</li>
<li>
<p><a href="#persistence">Persistence 3.2</a></p>
</li>
<li>
<p><a href="#pages">Pages 4.0</a></p>
</li>
<li>
<p><a href="#security">Security 4.0</a></p>
</li>
<li>
<p><a href="#validation">Validation 3.1</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="data">Jakarta Data 1.0: A Game Changer for Data Access</h2>
<div class="sectionbody">
<div class="paragraph">
<p>One of the most exciting additions to Jakarta EE 11 is <strong>Jakarta Data 1.0</strong>, a new specification that revolutionizes how developers interact with databases in enterprise applications.</p>
</div>
<div class="sect2">
<h3 id="what-is-jakarta-data">What is Jakarta Data?</h3>
<div class="paragraph">
<p>Jakarta Data provides an API for easier data access. A Java developer can split the details of persistence from the data model with several features, such as the ability to compose custom query methods on a Repository interface. Jakarta Data&#8217;s goal is to provide a familiar and consistent, Jakarta-based programming model for data access while still retaining the particular traits of the underlying data store. It is designed to be flexible and extensible, allowing developers to use it with various types of databases, including relational databases, NoSQL databases, and even in-memory data stores.</p>
</div>
</div>
<div class="sect2">
<h3 id="key-features-of-jakarta-data-1-0">Key Features of Jakarta Data 1.0</h3>
<div class="paragraph">
<p>Jakarta Data 1.0 includes:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Repository Pattern</strong>: Define data access through simple interfaces</p>
</li>
<li>
<p><strong>Query by Method Name</strong>: Automatic query generation from method names (e.g., <code>findByType</code>, <code>findByName</code>)</p>
</li>
<li>
<p><strong>Type Safety with StaticMetamodel</strong>: Enhanced type safety for queries</p>
</li>
<li>
<p><strong>Pagination Support</strong>: Built-in pagination on Repository</p>
</li>
<li>
<p><strong>Platform Integrations</strong>: Works with CDI, Persistence, NoSQL, Transactions, and Validation</p>
</li>
<li>
<p><strong>Entity Support</strong>: Works with persistence and nosql entities</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="jakarta-data-code-examples">Jakarta Data Code Examples</h3>
<div class="sect3">
<h4 id="defining-an-entity">Defining an Entity</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.persistence.Entity</span>;
<span class="keyword">import</span> <span class="include">jakarta.persistence.Id</span>;

<span class="annotation">@Entity</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">Product</span> {
    <span class="annotation">@Id</span>
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;
    <span class="directive">private</span> <span class="predefined-type">String</span> name;
    <span class="directive">private</span> <span class="predefined-type">String</span> description;
    <span class="directive">private</span> <span class="type">double</span> price;
    <span class="directive">private</span> <span class="type">int</span> stockQuantity;

    <span class="comment">// Constructors, getters, and setters</span>
    <span class="directive">public</span> Product() {}

    <span class="directive">public</span> Product(<span class="predefined-type">String</span> name, <span class="type">double</span> price) {
        <span class="local-variable">this</span>.name = name;
        <span class="local-variable">this</span>.price = price;
    }

    <span class="comment">// Getters and setters omitted for brevity</span>
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="creating-a-repository-interface">Creating a Repository Interface</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.data.repository.Repository</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.repository.Find</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.repository.Query</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.repository.Save</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.repository.Delete</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.repository.OrderBy</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.page.Page</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.page.PageRequest</span>;
<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.Optional</span>;

<span class="annotation">@Repository</span>
<span class="directive">public</span> <span class="type">interface</span> <span class="class">ProductRepository</span> {

    <span class="comment">// Basic CRUD operations</span>
    <span class="annotation">@Save</span>
    Product save(Product product);

    <span class="annotation">@Find</span>
    Optional&lt;Product&gt; findById(<span class="predefined-type">Long</span> id);

    <span class="annotation">@Delete</span>
    <span class="type">void</span> delete(Product product);

    <span class="comment">// Query by method name - automatically generates query</span>
    <span class="predefined-type">List</span>&lt;Product&gt; findByName(<span class="predefined-type">String</span> name);

    <span class="predefined-type">List</span>&lt;Product&gt; findByPriceLessThan(<span class="type">double</span> price);

    <span class="predefined-type">List</span>&lt;Product&gt; findByPriceBetween(<span class="type">double</span> minPrice, <span class="type">double</span> maxPrice);

    <span class="predefined-type">List</span>&lt;Product&gt; findByNameContains(<span class="predefined-type">String</span> keyword);

    <span class="comment">// Sorting and pagination - requires @OrderBy for pagination</span>
    <span class="annotation">@OrderBy</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">price</span><span class="delimiter">&quot;</span></span>)
    Page&lt;Product&gt; findByPriceGreaterThan(<span class="type">double</span> price, PageRequest pageRequest);

    <span class="comment">// Custom queries using @Query annotation</span>
    <span class="annotation">@Query</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT p FROM Product p WHERE p.stockQuantity &lt; 10</span><span class="delimiter">&quot;</span></span>)
    <span class="predefined-type">List</span>&lt;Product&gt; findLowStockProducts();

    <span class="annotation">@Query</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT p FROM Product p WHERE p.price &gt; ?1 ORDER BY p.price DESC</span><span class="delimiter">&quot;</span></span>)
    <span class="predefined-type">List</span>&lt;Product&gt; findExpensiveProducts(<span class="type">double</span> minPrice);
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="rest-endpoint-using-jakarta-data">REST Endpoint Using Jakarta Data</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core.MediaType</span>;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core.Response</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Inject</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.page.Page</span>;
<span class="keyword">import</span> <span class="include">jakarta.data.page.PageRequest</span>;
<span class="keyword">import</span> <span class="include">java.util.List</span>;

<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/products</span><span class="delimiter">&quot;</span></span>)
<span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
<span class="annotation">@Consumes</span>(MediaType.APPLICATION_JSON)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProductResource</span> {

    <span class="annotation">@Inject</span>
    <span class="directive">private</span> ProductRepository productRepository;

    <span class="annotation">@POST</span>
    <span class="directive">public</span> Response createProduct(Product product) {
        Product created = productRepository.save(product);
        <span class="keyword">return</span> Response.status(Response.Status.CREATED).entity(created).build();
    }

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/search</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Product&gt; searchProducts(<span class="annotation">@QueryParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">keyword</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> keyword) {
        <span class="keyword">return</span> productRepository.findByNameContains(keyword);
    }

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/expensive</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> Page&lt;Product&gt; getExpensiveProducts(
            <span class="annotation">@QueryParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">minPrice</span><span class="delimiter">&quot;</span></span>) <span class="annotation">@DefaultValue</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">100.0</span><span class="delimiter">&quot;</span></span>) <span class="type">double</span> minPrice,
            <span class="annotation">@QueryParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">page</span><span class="delimiter">&quot;</span></span>) <span class="annotation">@DefaultValue</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">1</span><span class="delimiter">&quot;</span></span>) <span class="type">int</span> page,
            <span class="annotation">@QueryParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">size</span><span class="delimiter">&quot;</span></span>) <span class="annotation">@DefaultValue</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">20</span><span class="delimiter">&quot;</span></span>) <span class="type">int</span> size) {
        PageRequest pageRequest = PageRequest.ofPage(page).size(size);
        <span class="keyword">return</span> productRepository.findByPriceGreaterThan(minPrice, pageRequest);
    }

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/low-stock</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Product&gt; getLowStockProducts() {
        <span class="keyword">return</span> productRepository.findLowStockProducts();
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="benefits-of-jakarta-data">Benefits of Jakarta Data</h3>
<div class="ulist">
<ul>
<li>
<p><strong>Reduced Boilerplate</strong>: No need to write repetitive DAO/repository implementations</p>
</li>
<li>
<p><strong>Type Safety</strong>: Compile-time checking of queries and method signatures</p>
</li>
<li>
<p><strong>Database Flexibility</strong>: Switch between databases without changing application code</p>
</li>
<li>
<p><strong>Improved Productivity</strong>: Focus on business logic instead of data access code</p>
</li>
<li>
<p><strong>Standardization</strong>: Vendor-neutral API backed by the Jakarta EE community</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="updated-specifications-with-code-examples">Updated Specifications with Code Examples</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="authentication">Authentication 3.1</h3>
<div class="paragraph">
<p>Jakarta Authentication defines a general low-level SPI for authentication mechanisms, which are controllers that interact with a caller and a container&#8217;s environment to obtain the caller&#8217;s credentials, validate these, and pass an authenticated identity (such as name and groups) to the container.</p>
</div>
<div class="paragraph">
<p><strong>Key changes in Authentication 3.1:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Removes references to the SecurityManager (aligned with Java&#8217;s deprecation and removal of SecurityManager)</p>
</li>
<li>
<p>Evolves the API in a smaller way to support the overall goals of Jakarta Security</p>
</li>
<li>
<p>Consists of several profiles, with each profile telling how a specific container (such as Jakarta Servlet) can integrate with and adapt to this SPI</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The 3.1 version removes the deprecated Permission-related fields in the <code>jakarta.security.auth.message.config.AuthConfigFactory</code> class. The methods in the class no longer do permission checking. These changes are part of Jakarta EE 11&#8217;s removal of SecurityManager support.</p>
</div>
<div class="sect3">
<h4 id="authentication-3-1-code-example">Authentication 3.1 Code Example</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.security.auth.message.AuthException</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.AuthStatus</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.MessageInfo</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.MessagePolicy</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.module.ServerAuthModule</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.callback.CallerPrincipalCallback</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.callback.GroupPrincipalCallback</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.callback.Callback</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.callback.CallbackHandler</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.auth.message.callback.UnsupportedCallbackException</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.security.Principal</span>;
<span class="keyword">import</span> <span class="include">java.util.Map</span>;
<span class="keyword">import</span> <span class="include">java.util.Set</span>;

<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomServerAuthModule</span> <span class="directive">implements</span> ServerAuthModule {

    <span class="directive">private</span> <span class="predefined-type">CallbackHandler</span> handler;

    <span class="annotation">@Override</span>
    <span class="annotation">@SuppressWarnings</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">rawtypes</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> <span class="type">void</span> initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
                          <span class="predefined-type">CallbackHandler</span> handler, <span class="predefined-type">Map</span> options) <span class="directive">throws</span> AuthException {
        <span class="local-variable">this</span>.handler = handler;
    }

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthStatus validateRequest(MessageInfo messageInfo,
                                      jakarta.security.auth.message.callback.Subject clientSubject,
                                      jakarta.security.auth.message.callback.Subject serviceSubject)
                                      <span class="directive">throws</span> AuthException {
        <span class="comment">// Extract credentials from request</span>
        <span class="predefined-type">String</span> username = extractUsername(messageInfo);
        <span class="predefined-type">String</span> password = extractPassword(messageInfo);

        <span class="keyword">if</span> (validateCredentials(username, password)) {
            <span class="comment">// Set the caller principal</span>
            CallerPrincipalCallback principalCallback =
                <span class="keyword">new</span> CallerPrincipalCallback(clientSubject, username);

            <span class="comment">// Set the groups</span>
            GroupPrincipalCallback groupCallback =
                <span class="keyword">new</span> GroupPrincipalCallback(clientSubject, <span class="keyword">new</span> <span class="predefined-type">String</span><span class="type">[]</span>{<span class="string"><span class="delimiter">&quot;</span><span class="content">users</span><span class="delimiter">&quot;</span></span>});

            <span class="keyword">try</span> {
                handler.handle(<span class="keyword">new</span> <span class="predefined-type">Callback</span><span class="type">[]</span>{principalCallback, groupCallback});
            } <span class="keyword">catch</span> (<span class="exception">IOException</span> | <span class="exception">UnsupportedCallbackException</span> e) {
                <span class="keyword">throw</span> <span class="keyword">new</span> AuthException(e.getMessage());
            }

            <span class="keyword">return</span> AuthStatus.SUCCESS;
        }

        <span class="keyword">return</span> AuthStatus.FAILURE;
    }

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthStatus secureResponse(MessageInfo messageInfo,
                                     jakarta.security.auth.message.callback.Subject serviceSubject)
            <span class="directive">throws</span> AuthException {
        <span class="keyword">return</span> AuthStatus.SEND_SUCCESS;
    }

    <span class="annotation">@Override</span>
    <span class="directive">public</span> <span class="type">void</span> cleanSubject(MessageInfo messageInfo,
                            jakarta.security.auth.message.callback.Subject subject)
                            <span class="directive">throws</span> AuthException {
        <span class="keyword">if</span> (subject != <span class="predefined-constant">null</span>) {
            <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Principal</span>&gt; principals = subject.getPrincipals();
            <span class="keyword">if</span> (principals != <span class="predefined-constant">null</span>) {
                principals.clear();
            }
        }
    }

    <span class="annotation">@Override</span>
    <span class="directive">public</span> <span class="predefined-type">Class</span>&lt;?&gt;<span class="type">[]</span> getSupportedMessageTypes() {
        <span class="keyword">return</span> <span class="keyword">new</span> <span class="predefined-type">Class</span>&lt;?&gt;<span class="type">[]</span>{HttpServletRequest.class, HttpServletResponse.class};
    }

    <span class="directive">private</span> <span class="predefined-type">String</span> extractUsername(MessageInfo messageInfo) {
        <span class="comment">// Extract username from request</span>
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span>;
    }

    <span class="directive">private</span> <span class="predefined-type">String</span> extractPassword(MessageInfo messageInfo) {
        <span class="comment">// Extract password from request</span>
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">password</span><span class="delimiter">&quot;</span></span>;
    }

    <span class="directive">private</span> <span class="type">boolean</span> validateCredentials(<span class="predefined-type">String</span> username, <span class="predefined-type">String</span> password) {
        <span class="comment">// Validate credentials</span>
        <span class="keyword">return</span> username != <span class="predefined-constant">null</span> &amp;&amp; password != <span class="predefined-constant">null</span>;
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="authorization">Authorization 3.0</h3>
<div class="paragraph">
<p>Jakarta Authorization defines an SPI for authorization modules, which are repositories of permissions that facilitate subject-based security by determining whether a subject has a specific permission.</p>
</div>
<div class="paragraph">
<p><strong>What&#8217;s New in Authorization 3.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>New <code>PolicyFactory</code> and <code>Policy</code> classes</strong>: Introduces <code>jakarta.security.jacc.PolicyFactory</code> and <code>jakarta.security.jacc.Policy</code> as replacements for the deprecated <code>java.security.Policy</code> class. With the new <code>PolicyFactory</code> API, you can now have a <code>Policy</code> per policy context instead of a global policy, allowing separate policies to be maintained for each application.</p>
</li>
<li>
<p><strong>Programmatic policy provider registration</strong>: Adds the ability to register policy providers programmatically on a per-application basis, making Jakarta Authorization more suitable for cloud deployments. This mirrors the API available in Jakarta Authentication.</p>
</li>
<li>
<p><strong>Convenience methods</strong>: Adds several convenience methods to make the API easier to use.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals and Changes in Authorization 3.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Removal of <code>java.security.Policy</code> dependency</strong>: Eliminates dependency on the deprecated <code>java.security.Policy</code> and <code>java.security.SecurityManager</code> classes, aligning with Java SE&#8217;s deprecation and removal of SecurityManager.</p>
</li>
<li>
<p><strong>Breaking API changes</strong>: This is a major breaking update. Applications using Jakarta Authorization 2.x will need to migrate to the new <code>PolicyFactory</code> and <code>Policy</code> classes.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>To configure your authorization modules in your application&#8217;s <code>web.xml</code> file, add specification defined context parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="preprocessor">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
<span class="tag">&lt;web-app</span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://jakarta.ee/xml/ns/jakartaee</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">xmlns:xsi</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://www.w3.org/2001/XMLSchema-instance</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">xsi:schemaLocation</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">version</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">6.1</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>

  <span class="tag">&lt;context-param&gt;</span>
    <span class="tag">&lt;param-name&gt;</span>jakarta.security.jacc.PolicyConfigurationFactory.provider<span class="tag">&lt;/param-name&gt;</span>
    <span class="tag">&lt;param-value&gt;</span>com.example.MyPolicyConfigurationFactory<span class="tag">&lt;/param-value&gt;</span>
  <span class="tag">&lt;/context-param&gt;</span>

  <span class="tag">&lt;context-param&gt;</span>
    <span class="tag">&lt;param-name&gt;</span>jakarta.security.jacc.PolicyFactory.provider<span class="tag">&lt;/param-name&gt;</span>
    <span class="tag">&lt;param-value&gt;</span>com.example.MyPolicyFactory<span class="tag">&lt;/param-value&gt;</span>
  <span class="tag">&lt;/context-param&gt;</span>

<span class="tag">&lt;/web-app&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Due to Jakarta Authorization 3.0 no longer using the <code>java.security.Policy</code> class and introducing a new configuration mechanism for authorization modules, the <code>com.ibm.wsspi.security.authorization.jacc.ProviderService</code> Liberty API is no longer available with the appAuthorization-3.0 feature. If a Liberty user feature configures authorization modules, the OSGi service that provided a <code>ProviderService</code> implementation must be updated to use the <code>PolicyConfigurationFactory</code> and <code>PolicyFactory</code> set methods. These methods configure the modules in the OSGi service. Alternatively, you can use a Web Application Bundle (WAB) in your user feature to specify your security modules in a <code>web.xml</code> file.</p>
</div>
<div class="paragraph">
<p>Finally, the 3.0 API adds a new <code>jakarta.security.jacc.PrincipalMapper</code> class that you can obtain from the <code>PolicyContext</code> class when authorization processing is done in your <code>Policy</code> implementation. From this class, you can obtain the roles that are associated with a specific Subject to be able to determine whether the Subject is in the required role.</p>
</div>
<div class="paragraph">
<p>You can use the <code>PrincipalMapper</code> class in your <code>Policy</code> implementation&#8217;s <code>impliesByRole</code> (or <code>implies</code>) method, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">boolean</span> impliesByRole(<span class="predefined-type">Permission</span> p, <span class="predefined-type">Subject</span> subject) {
    <span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">PermissionCollection</span>&gt; perRolePermissions =
        PolicyConfigurationFactory.get().getPolicyConfiguration(contextID).getPerRolePermissions();
    PrincipalMapper principalMapper = PolicyContext.get(PolicyContext.PRINCIPAL_MAPPER);

    <span class="comment">// Check to see if the Permission is in the all authenticated users role</span>
    <span class="keyword">if</span> (!principalMapper.isAnyAuthenticatedUserRoleMapped() &amp;&amp; !subject.getPrincipals().isEmpty()) {
        <span class="predefined-type">PermissionCollection</span> rolePermissions = perRolePermissions.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">**</span><span class="delimiter">&quot;</span></span>);
        <span class="keyword">if</span> (rolePermissions != <span class="predefined-constant">null</span> &amp;&amp; rolePermissions.implies(p)) {
            <span class="keyword">return</span> <span class="predefined-constant">true</span>;
        }
    }

    <span class="comment">// Check to see if the roles for the Subject provided imply the permission</span>
    <span class="predefined-type">Set</span>&lt;<span class="predefined-type">String</span>&gt; mappedRoles = principalMapper.getMappedRoles(subject);
    <span class="keyword">for</span> (<span class="predefined-type">String</span> mappedRole : mappedRoles) {
        <span class="predefined-type">PermissionCollection</span> rolePermissions = perRolePermissions.get(mappedRole);
        <span class="keyword">if</span> (rolePermissions != <span class="predefined-constant">null</span> &amp;&amp; rolePermissions.implies(p)) {
            <span class="keyword">return</span> <span class="predefined-constant">true</span>;
        }
    }

    <span class="keyword">return</span> <span class="predefined-constant">false</span>;
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="concurrency">Concurrency 3.1</h3>
<div class="paragraph">
<p>Jakarta Concurrency provides asynchronous capabilities to Jakarta EE application components. Jakarta Concurrency 3.1 provides enhanced concurrency utilities with several new features and improvements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Integration with Java 21 Virtual Threads</strong> - Native support for virtual threads to improve scalability</p>
</li>
<li>
<p><strong>Java Flow/ReactiveStreams and context propagation</strong> - Better integration with reactive programming models</p>
</li>
<li>
<p><strong>Replace more features from EJB</strong> - Migrated scheduling and asynchronous capabilities (such as <code>@Schedule</code> and <code>@Asynchronous</code> annotations)</p>
</li>
<li>
<p><strong>Become more CDI-centric</strong> - Improved integration with Jakarta CDI</p>
</li>
<li>
<p><strong>Specification bug fixes and clarifications</strong> - Enhanced clarity and resolved ambiguities</p>
</li>
<li>
<p><strong>TCK fixes and enhancements</strong> - Improved test coverage and reliability</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.enterprise.concurrent.ManagedExecutorService</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.concurrent.ManagedScheduledExecutorService</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.concurrent.ManagedExecutorDefinition</span>;
<span class="keyword">import</span> <span class="include">jakarta.annotation.Resource</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.context.ApplicationScoped</span>;
<span class="keyword">import</span> <span class="include">java.util.concurrent.CompletableFuture</span>;
<span class="keyword">import</span> <span class="include">java.util.concurrent.TimeUnit</span>;

<span class="annotation">@ApplicationScoped</span>
<span class="annotation">@ManagedExecutorDefinition</span>(
    name = <span class="string"><span class="delimiter">&quot;</span><span class="content">java:module/concurrent/VirtualThreadExecutor</span><span class="delimiter">&quot;</span></span>,
    virtual = <span class="predefined-constant">true</span>
)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ConcurrencyExample</span> {

    <span class="annotation">@Resource</span>(lookup = <span class="string"><span class="delimiter">&quot;</span><span class="content">java:module/concurrent/VirtualThreadExecutor</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> ManagedExecutorService executorService;

    <span class="directive">public</span> CompletableFuture&lt;<span class="predefined-type">String</span>&gt; asyncOperation() {
        <span class="comment">// Leverages Virtual Threads on Java 21+</span>
        <span class="keyword">return</span> executorService.supplyAsync(<span class="local-variable">this</span>::processData);
    }

    <span class="directive">private</span> <span class="predefined-type">String</span> processData() {
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">Processed data</span><span class="delimiter">&quot;</span></span>;
    }
    <span class="annotation">@Asynchronous</span>(
            executor = <span class="string"><span class="delimiter">&quot;</span><span class="content">java:module/concurrent/VirtualThreadExecutor</span><span class="delimiter">&quot;</span></span>,
            runAt = <span class="annotation">@Schedule</span>(cron = <span class="string"><span class="delimiter">&quot;</span><span class="content">0 3 * * *</span><span class="delimiter">&quot;</span></span>)) <span class="comment">// daily at 3 AM</span>
    <span class="directive">private</span> <span class="type">void</span> performMaintenanceTask(<span class="annotation">@Observes</span> Startup event) {
        <span class="comment">// Maintenance logic</span>
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="annotations">Annotations 3.0</h3>
<div class="paragraph">
<p>Jakarta Annotations defines a collection of annotations representing common semantic concepts that enable a declarative style of programming in the Jakarta EE platform.</p>
</div>
<div class="paragraph">
<p><strong>Changes in Annotations 3.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Removal of @ManagedBean</strong>: The deprecated <code>@ManagedBean</code> annotation has been fully removed from the specification. Developers must migrate to CDI managed beans using <code>@Named</code> and appropriate scope annotations.</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="migrating-from-managedbean">Migrating from @ManagedBean</h4>
<div class="paragraph">
<p>If your application uses <code>@ManagedBean</code>, you must migrate to CDI managed beans. The <code>@ManagedBean</code> annotation was deprecated in an earlier release and has been removed in Jakarta EE 11.</p>
</div>
<div class="paragraph">
<p><strong>Migration Options:</strong></p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><strong>Use CDI <code>@Named</code> for Faces or Expression Language access</strong> - If the bean needs to be accessible from Faces pages or Expression Language</p>
</li>
<li>
<p><strong>Use CDI scope annotations without <code>@Named</code></strong> - If the bean is only used for dependency injection</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect2">
<h3 id="interceptors">Interceptors 2.2</h3>
<div class="paragraph">
<p>Jakarta Interceptors defines a means of interposing on business method invocations and specific events in the lifecycle of beans.</p>
</div>
<div class="paragraph">
<p><strong>New features, enhancements or additions:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Updated dependencies for Jakarta EE 11</strong></p>
</li>
<li>
<p>Jakarta Annotations to 3.0.0</p>
</li>
<li>
<p><strong>Provide access to interceptor bindings from InvocationContext</strong> - New method <code>getInterceptorBindings()</code> added to <code>InvocationContext</code> interface</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals, deprecations or backwards incompatible changes:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>None</strong></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>See the following CDI section for its usage.</p>
</div>
</div>
<div class="sect2">
<h3 id="cdi">CDI 4.1</h3>
<div class="paragraph">
<p>Jakarta Contexts and Dependency Injection (CDI) specifies a means for obtaining objects in such a way as to maximize reusability, testability and maintainability compared to traditional approaches such as constructors, factories, and service locators (e.g., JNDI). CDI 4.1 brings important architectural improvements and new APIs to help framework developers build on CDI. CDI allows objects to be bound to lifecycle contexts, injected into application code, be subject to interceptors and decorators, and interact in a loosely coupled fashion via events.</p>
</div>
<div class="paragraph">
<p><strong>Key changes in CDI 4.1:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Specification restructuring</strong> - Integration requirements with other Jakarta EE specs moved from CDI specification to Jakarta EE Platform, Web Profile, and Core Profile specifications</p>
</li>
<li>
<p><strong>Expression Language separation</strong> - EL-related methods moved to new API jar (<code>jakarta.enterprise.cdi-el-api</code>) to remove CDI&#8217;s dependency on EL API</p>
</li>
<li>
<p><strong>Interceptor binding access</strong> - New methods on <code>InvocationContext</code> to retrieve interceptor binding annotations</p>
</li>
<li>
<p><strong>Method Invokers</strong> - New API allowing frameworks to call methods with CDI-managed arguments and instances</p>
</li>
<li>
<p><strong>@Priority on producers</strong> - Producer methods and fields can now be annotated with <code>@Priority</code> for fine-grained alternative selection</p>
</li>
<li>
<p><strong>Programmatic assignability rules</strong> - New <code>BeanContainer</code> methods to check if beans match injection points</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="specification-restructuring">Specification Restructuring</h4>
<div class="paragraph">
<p>One of the major changes in CDI 4.1 is the restructuring of the specification to remove circular dependencies and improve modularity:</p>
</div>
<div class="paragraph">
<p><strong>Integration requirements moved to platform specs:</strong>
Previously, CDI defined requirements for integration with other Jakarta EE specifications including Servlet, Expression Language, Enterprise Beans, Transactions, Security, Validation, and Persistence. These integration requirements have now been moved to the Jakarta EE Platform, Web Profile, and Core Profile specifications as appropriate. This makes it easier to pass the CDI TCK independently and clarifies which integrations are required at each platform level.</p>
</div>
<div class="paragraph">
<p><strong>Expression Language separation:</strong>
The CDI API previously had a direct dependency on the Expression Language (EL) API because <code>BeanManager</code> included methods that referenced EL classes. In CDI 4.1:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>EL-related methods on <code>BeanManager</code> (<code>getELResolver()</code> and <code>wrapExpressionFactory()</code>) are deprecated for removal in CDI 5.0</p>
</li>
<li>
<p>A new supplemental API jar <code>jakarta.enterprise.cdi-el-api</code> provides <code>ELAwareBeanManager</code>, a sub-interface of <code>BeanManager</code> with the same methods</p>
</li>
<li>
<p>This allows the core CDI API to remove its dependency on EL in the next major version</p>
</li>
<li>
<p>Existing users will see deprecation warnings and should migrate to <code>ELAwareBeanManager</code> before CDI 5.0</p>
</li>
</ul>
</div>
</div>
<div class="sect3">
<h4 id="retrieve-interceptor-binding-information">Retrieve Interceptor Binding Information</h4>
<div class="paragraph">
<p>CDI 4.1 adds support to allow interceptors to retrieve and inspect the annotations that are used to bind them. Interceptors can now call <code>InvocationContext.getInterceptorBindings()</code> or one of the related methods to retrieve the annotations so that they can read values from them. This capability is particularly useful when you need to configure interceptor behavior based on annotation parameters.</p>
</div>
<div class="paragraph">
<p>For example, you might define a custom <code>@Logged</code> annotation with a parameter:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.enterprise.util.Nonbinding</span>;
<span class="keyword">import</span> <span class="include">jakarta.interceptor.InterceptorBinding</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.ElementType</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.Retention</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.RetentionPolicy</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.Target</span>;

<span class="annotation">@InterceptorBinding</span>
<span class="annotation">@Target</span>({<span class="predefined-type">ElementType</span>.TYPE, <span class="predefined-type">ElementType</span>.METHOD})
<span class="annotation">@Retention</span>(<span class="predefined-type">RetentionPolicy</span>.RUNTIME)
<span class="directive">public</span> <span class="annotation">@interface</span> Logged {
    <span class="annotation">@Nonbinding</span>
    <span class="predefined-type">String</span> value();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Then apply it to a method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Logged</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">myName</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">void</span> myMethod() {
    <span class="comment">// ....</span>
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>An interceptor like this can read the <code>myName</code> value from the annotation and include it in the log message:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Interceptor</span>
<span class="annotation">@Logged</span>(<span class="string"><span class="delimiter">&quot;</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">LoggedInterceptor</span> {

    <span class="annotation">@AroundInvoke</span>
    <span class="directive">public</span> <span class="predefined-type">Object</span> logInvocation(InvocationContext context) <span class="directive">throws</span> <span class="exception">Exception</span> {
        <span class="comment">// NEW in CDI 4.1: Find the @Logged annotation and extract its value</span>
        <span class="predefined-type">String</span> logName = context.getInterceptorBinding(Logged.class).value();

        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Invoking method with log name: </span><span class="delimiter">&quot;</span></span> + logName);

        <span class="keyword">try</span> {
            <span class="predefined-type">Object</span> result = context.proceed();
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Method completed successfully</span><span class="delimiter">&quot;</span></span>);
            <span class="keyword">return</span> result;
        } <span class="keyword">catch</span> (<span class="exception">Exception</span> e) {
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Method failed with exception: </span><span class="delimiter">&quot;</span></span> + e.getMessage());
            <span class="keyword">throw</span> e;
        }
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="method-invokers">Method Invokers</h4>
<div class="paragraph">
<p>Method invokers provide a way to invoke methods programmatically while allowing CDI to look up and inject some of the method parameters. This is particularly useful for frameworks that want to allow users to annotate methods and have those methods called with a mix of framework-provided and CDI-injected arguments.</p>
</div>
<div class="paragraph">
<p><strong>Example use case:</strong> An alert system where users can apply <code>@Alert</code> to methods on managed bean classes. When an alert happens, the annotated methods are called with the alert ID as the first argument, and other arguments looked up from CDI.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">MyBean</span> {

    <span class="annotation">@Alert</span>
    <span class="directive">public</span> <span class="type">void</span> myAlert1(<span class="type">int</span> id) {
        <span class="comment">// Do something</span>
    }

    <span class="annotation">@Alert</span>
    <span class="directive">public</span> <span class="type">void</span> myAlert2(<span class="type">int</span> id, MyOtherBean otherBean) {
        <span class="comment">// Do something with otherBean</span>
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Creating invokers</strong> (done within a CDI extension):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">InvokerExtension</span> <span class="directive">implements</span> Extension {

    <span class="directive">public</span> <span class="directive">static</span> record AlertMethod(Invoker&lt;?,?&gt; invoker, <span class="type">int</span> parameterCount) { }
    <span class="predefined-type">List</span>&lt;AlertMethod&gt; alertMethods = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span>&lt;&gt;();

    <span class="directive">public</span> &lt;T&gt; <span class="type">void</span> createInvokers(<span class="annotation">@Observes</span> <span class="annotation">@WithAnnotations</span>(Alert.class) ProcessManagedBean&lt;T&gt; pmb) {
        <span class="keyword">for</span> (AnnotatedMethod&lt;? <span class="local-variable">super</span> T&gt; m : pmb.getAnnotatedBeanClass().getMethods()) {
            <span class="keyword">if</span> (m.isAnnotationPresent(Alert.class)) {
                validate(m);

                InvokerBuilder&lt;Invoker&lt;T, ?&gt;&gt; builder = pmb.createInvoker(m);

                <span class="comment">// Look up the bean instance when invoking</span>
                builder.withInstanceLookup();

                <span class="comment">// Look up all arguments except the first</span>
                <span class="type">int</span> parameterCount = m.getParameters().size();
                <span class="keyword">for</span> (<span class="type">int</span> i = <span class="integer">1</span>; i &lt; parameterCount; i++) {
                    builder.withArgumentLookup(i);
                }

                alertMethods.add(<span class="keyword">new</span> AlertMethod(builder.build(), parameterCount));
            }
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Calling invokers:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">void</span> invokeAlerts(<span class="type">int</span> i) <span class="directive">throws</span> <span class="exception">Exception</span> {
    <span class="keyword">for</span> (AlertMethod method : alertMethods) {
        <span class="comment">// Construct an array of arguments</span>
        <span class="predefined-type">Object</span><span class="type">[]</span> args = <span class="keyword">new</span> <span class="predefined-type">Object</span>[method.parameterCount];
        <span class="comment">// First argument is the alert id</span>
        args[<span class="integer">0</span>] = i;
        <span class="comment">// All other arguments are looked up from CDI, so we pass in null</span>

        <span class="comment">// Call the method</span>
        method.invoker.invoke(<span class="predefined-constant">null</span>, args);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Note:</strong> <code>null</code> must be passed for any instance or argument that is to be looked up from CDI. In this example, the bean instance and all arguments except the first are looked up from CDI, so we pass <code>null</code> for the instance and <code>null</code> for arguments at positions 1 and beyond.</p>
</div>
</div>
<div class="sect3">
<h4 id="priority-on-producer-methods-and-fields">@Priority on Producer Methods and Fields</h4>
<div class="paragraph">
<p>Previously, a producer method or field declared as an alternative could only be enabled and have a priority assigned by putting the <code>@Priority</code> annotation on the containing bean class. If a class contained several alternative producer methods, there was no way to assign a different priority to each.</p>
</div>
<div class="paragraph">
<p>CDI 4.1 allows the <code>@Priority</code> annotation to be placed directly on the producer field or method, enabling fine-grained control over alternative selection:</p>
</div>
<div class="paragraph">
<p><strong>Before CDI 4.1</strong> (priority on class):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="annotation">@Priority</span>(<span class="integer">10</span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProducerClass</span> {

    <span class="annotation">@Produces</span>
    <span class="annotation">@Alternative</span>
    <span class="directive">public</span> <span class="predefined-type">String</span> produceString() {
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">OK</span><span class="delimiter">&quot;</span></span>;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>CDI 4.1</strong> (priority on producer method):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProducerClass</span> {

    <span class="annotation">@Produces</span>
    <span class="annotation">@Alternative</span>
    <span class="annotation">@Priority</span>(<span class="integer">10</span>)
    <span class="directive">public</span> <span class="predefined-type">String</span> produceString() {
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">OK</span><span class="delimiter">&quot;</span></span>;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This allows different producer methods in the same class to have different priorities, giving you more flexibility when working with alternatives.</p>
</div>
</div>
<div class="sect3">
<h4 id="programmatic-access-to-assignability-rules">Programmatic Access to Assignability Rules</h4>
<div class="paragraph">
<p>CDI defines resolution rules that determine which beans can be injected into each injection point. Previously, there was no way to apply these rules programmatically without re-implementing the logic. CDI 4.1 adds two new methods to <code>BeanContainer</code> that implement the matching rules:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="comment">// Check if a bean matches an injection point</span>
<span class="type">boolean</span> isMatchingBean(<span class="predefined-type">Set</span>&lt;<span class="predefined-type">Type</span>&gt; beanTypes,
                      <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Annotation</span>&gt; beanQualifiers,
                      <span class="predefined-type">Type</span> requiredType,
                      <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Annotation</span>&gt; requiredQualifiers);

<span class="comment">// Check if an event matches an observer</span>
<span class="type">boolean</span> isMatchingEvent(<span class="predefined-type">Type</span> specifiedType,
                       <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Annotation</span>&gt; specifiedQualifiers,
                       <span class="predefined-type">Type</span> observedEventType,
                       <span class="predefined-type">Set</span>&lt;<span class="predefined-type">Annotation</span>&gt; observedEventQualifiers);</code></pre>
</div>
</div>
<div class="paragraph">
<p>These methods allow frameworks and extensions to programmatically check whether beans or events match specific requirements using the same rules that CDI uses internally.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="el">Expression Language 6.0</h3>
<div class="paragraph">
<p>Jakarta Expression Language defines an expression language for Java applications. This release makes the dependency on the java.desktop module optional, removes references to the SecurityManager, and provides a small number of usability improvements.</p>
</div>
<div class="paragraph">
<p><strong>New features in Expression Language 6.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>java.desktop module no longer required</strong>: The java.desktop module is no longer required at runtime, improving modularity</p>
</li>
<li>
<p><strong>New <code>length</code> property for arrays</strong>: A new property, <code>length</code>, is now supported for arrays, making it easier to get array sizes in EL expressions</p>
</li>
<li>
<p><strong>Java Records support</strong>: Added support, enabled by default, for <code>java.lang.Record</code> instances via the new <code>RecordELResolver</code></p>
</li>
<li>
<p><strong>Java Optional support</strong>: Added support, disabled by default, for <code>java.lang.Optional</code> instances via the new <code>OptionalELResolver</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>All code deprecated as of Expression Language 5.0 has been removed, specifically the <code>getFeatureDescriptors()</code> method from the <code>ELResolver</code> interface</p>
</li>
<li>
<p>All references to the Java SecurityManager and associated APIs have been removed</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="using-the-new-length-property-for-arrays">Using the new <code>length</code> property for arrays</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.el</span>.*;
<span class="keyword">import</span> <span class="include">java.util.Arrays</span>;

<span class="directive">public</span> <span class="type">class</span> <span class="class">ArrayLengthExample</span> {

    <span class="directive">public</span> <span class="directive">static</span> <span class="type">void</span> main(<span class="predefined-type">String</span><span class="type">[]</span> args) {
        <span class="comment">// Create EL context</span>
        ExpressionFactory factory = ExpressionFactory.newInstance();
        StandardELContext context = <span class="keyword">new</span> StandardELContext(factory);

        <span class="comment">// Create an array and add it to the context</span>
        <span class="predefined-type">String</span><span class="type">[]</span> fruits = {<span class="string"><span class="delimiter">&quot;</span><span class="content">Apple</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Banana</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Cherry</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Date</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Elderberry</span><span class="delimiter">&quot;</span></span>};
        context.getELResolver().setValue(context, <span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">fruits</span><span class="delimiter">&quot;</span></span>, fruits);

        <span class="comment">// NEW in EL 6.0: Use the 'length' property to get array size</span>
        ValueExpression lengthExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${fruits.length}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">Integer</span>.class);
        <span class="predefined-type">Integer</span> length = (<span class="predefined-type">Integer</span>) lengthExpr.getValue(context);

        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Array length: </span><span class="delimiter">&quot;</span></span> + length); <span class="comment">// Output: 5</span>

        <span class="comment">// You can also use it in conditional expressions</span>
        ValueExpression hasItemsExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${fruits.length &gt; 0}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">Boolean</span>.class);
        <span class="predefined-type">Boolean</span> hasItems = (<span class="predefined-type">Boolean</span>) hasItemsExpr.getValue(context);

        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Has items: </span><span class="delimiter">&quot;</span></span> + hasItems); <span class="comment">// Output: true</span>
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-recordelresolver-for-java-records">Using RecordELResolver for Java Records</h4>
<div class="paragraph">
<p>Java Records are now supported by default in EL 6.0:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.el</span>.*;

<span class="comment">// Define a Java Record</span>
<span class="directive">public</span> record Product(<span class="predefined-type">String</span> name, <span class="type">double</span> price, <span class="type">int</span> quantity) {
    <span class="directive">public</span> <span class="type">double</span> totalValue() {
        <span class="keyword">return</span> price * quantity;
    }
}

<span class="directive">public</span> <span class="type">class</span> <span class="class">RecordELResolverExample</span> {

    <span class="directive">public</span> <span class="directive">static</span> <span class="type">void</span> main(<span class="predefined-type">String</span><span class="type">[]</span> args) {
        <span class="comment">// Create EL context</span>
        ExpressionFactory factory = ExpressionFactory.newInstance();
        StandardELContext context = <span class="keyword">new</span> StandardELContext(factory);

        <span class="comment">// Create a Record instance</span>
        Product product = <span class="keyword">new</span> Product(<span class="string"><span class="delimiter">&quot;</span><span class="content">Laptop</span><span class="delimiter">&quot;</span></span>, <span class="float">999.99</span>, <span class="integer">5</span>);
        context.getELResolver().setValue(context, <span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>, product);

        <span class="comment">// NEW in EL 6.0: Access Record components directly</span>
        ValueExpression nameExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${product.name}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">String</span>.class);
        <span class="predefined-type">String</span> name = (<span class="predefined-type">String</span>) nameExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Product name: </span><span class="delimiter">&quot;</span></span> + name); <span class="comment">// Output: Laptop</span>

        ValueExpression priceExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${product.price}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">Double</span>.class);
        <span class="predefined-type">Double</span> price = (<span class="predefined-type">Double</span>) priceExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Product price: </span><span class="delimiter">&quot;</span></span> + price); <span class="comment">// Output: 999.99</span>

        <span class="comment">// Access Record methods</span>
        ValueExpression totalExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${product.totalValue()}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">Double</span>.class);
        <span class="predefined-type">Double</span> total = (<span class="predefined-type">Double</span>) totalExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Total value: </span><span class="delimiter">&quot;</span></span> + total); <span class="comment">// Output: 4999.95</span>
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-optionalelresolver-for-java-optional">Using OptionalELResolver for Java Optional</h4>
<div class="paragraph">
<p>Support for <code>java.lang.Optional</code> is available but disabled by default. To enable it, you need to add the <code>OptionalELResolver</code> to your EL context:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.el</span>.*;
<span class="keyword">import</span> <span class="include">java.util.Optional</span>;

<span class="directive">public</span> <span class="type">class</span> <span class="class">OptionalELResolverExample</span> {

    <span class="directive">public</span> <span class="directive">static</span> <span class="type">void</span> main(<span class="predefined-type">String</span><span class="type">[]</span> args) {
        <span class="comment">// Create EL context</span>
        ExpressionFactory factory = ExpressionFactory.newInstance();
        StandardELContext context = <span class="keyword">new</span> StandardELContext(factory);

        context.addELResolver(<span class="keyword">new</span> OptionalELResolver());

        <span class="comment">// Create Optional values</span>
        Optional&lt;<span class="predefined-type">String</span>&gt; presentValue = Optional.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, EL 6.0!</span><span class="delimiter">&quot;</span></span>);
        Optional&lt;<span class="predefined-type">String</span>&gt; emptyValue = Optional.empty();

        context.getELResolver().setValue(context, <span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">message</span><span class="delimiter">&quot;</span></span>, presentValue);
        context.getELResolver().setValue(context, <span class="predefined-constant">null</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">emptyValue</span><span class="delimiter">&quot;</span></span>, emptyValue);

        <span class="comment">// Access Optional values - automatically unwrapped if present</span>
        ValueExpression messageExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${message}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">String</span>.class);
        <span class="predefined-type">String</span> message = (<span class="predefined-type">String</span>) messageExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Message: </span><span class="delimiter">&quot;</span></span> + message); <span class="comment">// Output: Hello, EL 6.0!</span>

        <span class="comment">// Empty Optional returns null</span>
        ValueExpression emptyExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${emptyValue}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">String</span>.class);
        <span class="predefined-type">String</span> emptyResult = (<span class="predefined-type">String</span>) emptyExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">emptyValue: </span><span class="delimiter">&quot;</span></span> + emptyResult); <span class="comment">// Output: null</span>

        <span class="comment">// Use with conditional expressions</span>
        ValueExpression hasMsgExpr = factory.createValueExpression(
            context, <span class="string"><span class="delimiter">&quot;</span><span class="content">${message != null}</span><span class="delimiter">&quot;</span></span>, <span class="predefined-type">Boolean</span>.class);
        <span class="predefined-type">Boolean</span> hasMessage = (<span class="predefined-type">Boolean</span>) hasMsgExpr.getValue(context);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Has message: </span><span class="delimiter">&quot;</span></span> + hasMessage); <span class="comment">// Output: true</span>
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-el-6-0-in-facesfacelets">Using EL 6.0 in Faces/Facelets</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xhtml"><span class="comment">&lt;!-- Using the new length property for arrays --&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Total items: #{products.length}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>

<span class="tag">&lt;h:panelGroup</span> <span class="attribute-name">rendered</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{products.length </span></span><span class="error">&gt;</span> 0}&quot;<span class="error">&gt;</span>
    <span class="tag">&lt;ui:repeat</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{products}</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">var</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>
        <span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{product.name}: $#{product.price}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
    <span class="tag">&lt;/ui:repeat&gt;</span>
<span class="tag">&lt;/h:panelGroup&gt;</span>

<span class="comment">&lt;!-- Using Records in EL expressions --&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{productRecord.name}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{productRecord.price}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Total: $#{productRecord.totalValue()}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>

<span class="comment">&lt;!-- Conditional rendering based on array length --&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">No products available</span><span class="delimiter">&quot;</span></span>
              <span class="attribute-name">rendered</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{products.length == 0}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="faces">Faces 4.1</h3>
<div class="paragraph">
<p>Jakarta Faces defines an MVC framework for building user interfaces for web applications, including UI components, state management, event handling, input validation, page navigation, and support for internationalization and accessibility. This release removes references to the SecurityManager, further aligns with CDI where possible, and provides various small enhancements and clarifications.</p>
</div>
<div class="paragraph">
<p><strong>New features in Faces 4.1:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Generic FacesMessage</strong>: Make <code>FacesMessage#VALUES</code> / <code>VALUES_MAP</code> generic for better type safety</p>
</li>
<li>
<p><strong>CDI event firing</strong>: Require firing events for <code>@Initialized</code>, <code>@BeforeDestroyed</code>, <code>@Destroyed</code> for build-in scopes</p>
</li>
<li>
<p><strong>Missing generics</strong>: Add missing generics to API that were missed in Faces 4.0</p>
</li>
<li>
<p><strong>Flow injection</strong>: Support <code>@Inject</code> of current flow like <code>@Inject Flow currentFlow</code></p>
</li>
<li>
<p><strong>UUIDConverter</strong>: Add new converter for UUID types</p>
</li>
<li>
<p><strong>ExternalContext enhancement</strong>: Add <code>setResponseContentLengthLong</code> method for large content</p>
</li>
<li>
<p><strong>UIRepeat enhancement</strong>: Add <code>rowStatePreserved</code> property to UIRepeat, exactly the same as UIData</p>
</li>
<li>
<p><strong>Development mode default</strong>: <code>jakarta.faces.FACELETS_REFRESH_PERIOD</code> default when ProjectStage is Development</p>
</li>
<li>
<p><strong>FacesMessage improvements</strong>: Implement <code>equals()</code>, <code>hashcode()</code>, <code>toString()</code> methods</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Deprecate unused <code>composite.extension</code></p>
</li>
<li>
<p>Remove references to the SecurityManager</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="using-the-new-uuidconverter">Using the new UUIDConverter</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.faces.view.ViewScoped</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Named</span>;
<span class="keyword">import</span> <span class="include">jakarta.faces.convert.UUIDConverter</span>;
<span class="keyword">import</span> <span class="include">java.io.Serializable</span>;
<span class="keyword">import</span> <span class="include">java.util.UUID</span>;

<span class="annotation">@Named</span>
<span class="annotation">@ViewScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">EntityBean</span> <span class="directive">implements</span> <span class="predefined-type">Serializable</span> {

    <span class="directive">private</span> <span class="predefined-type">UUID</span> entityId;
    <span class="directive">private</span> <span class="predefined-type">String</span> entityName;

    <span class="directive">public</span> <span class="type">void</span> init() {
        <span class="comment">// NEW in Faces 4.1: UUIDConverter automatically handles UUID conversion</span>
        <span class="comment">// Generate a new UUID for the entity</span>
        entityId = <span class="predefined-type">UUID</span>.randomUUID();
    }

    <span class="directive">public</span> <span class="type">void</span> saveEntity() {
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Saving entity with ID: </span><span class="delimiter">&quot;</span></span> + entityId);
        <span class="comment">// The UUID is automatically converted to/from String in the view</span>
    }

    <span class="comment">// Getters and setters</span>
    <span class="directive">public</span> <span class="predefined-type">UUID</span> getEntityId() { <span class="keyword">return</span> entityId; }
    <span class="directive">public</span> <span class="type">void</span> setEntityId(<span class="predefined-type">UUID</span> entityId) { <span class="local-variable">this</span>.entityId = entityId; }
    <span class="directive">public</span> <span class="predefined-type">String</span> getEntityName() { <span class="keyword">return</span> entityName; }
    <span class="directive">public</span> <span class="type">void</span> setEntityName(<span class="predefined-type">String</span> entityName) { <span class="local-variable">this</span>.entityName = entityName; }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xhtml"><span class="comment">&lt;!-- The UUID is automatically converted using the new UUIDConverter --&gt;</span>
<span class="tag">&lt;h:form&gt;</span>
    <span class="tag">&lt;h:outputLabel</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Entity ID:</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
    <span class="tag">&lt;h:inputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{entityBean.entityId}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>

    <span class="tag">&lt;h:outputLabel</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Entity Name:</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
    <span class="tag">&lt;h:inputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{entityBean.entityName}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>

    <span class="tag">&lt;h:commandButton</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Save</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">action</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{entityBean.saveEntity}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
<span class="tag">&lt;/h:form&gt;</span>

<span class="comment">&lt;!-- Display UUID --&gt;</span>
<span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Current ID: #{entityBean.entityId}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="injecting-the-current-flow">Injecting the current Flow</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.faces.flow.Flow</span>;
<span class="keyword">import</span> <span class="include">jakarta.faces.view.ViewScoped</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Inject</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Named</span>;
<span class="keyword">import</span> <span class="include">java.io.Serializable</span>;

<span class="annotation">@Named</span>
<span class="annotation">@ViewScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">FlowAwareBean</span> <span class="directive">implements</span> <span class="predefined-type">Serializable</span> {

    <span class="comment">// NEW in Faces 4.1: Direct injection of current Flow</span>
    <span class="annotation">@Inject</span>
    <span class="directive">private</span> Flow currentFlow;

    <span class="directive">public</span> <span class="predefined-type">String</span> getFlowInfo() {
        <span class="keyword">if</span> (currentFlow != <span class="predefined-constant">null</span>) {
            <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">Current flow: </span><span class="delimiter">&quot;</span></span> + currentFlow.getId();
        }
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">Not in a flow</span><span class="delimiter">&quot;</span></span>;
    }

    <span class="directive">public</span> <span class="type">boolean</span> isInFlow() {
        <span class="keyword">return</span> currentFlow != <span class="predefined-constant">null</span>;
    }

    <span class="directive">public</span> <span class="predefined-type">String</span> getFlowId() {
        <span class="keyword">return</span> currentFlow != <span class="predefined-constant">null</span> ? currentFlow.getId() : <span class="predefined-constant">null</span>;
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-rowstatepreserved-in-uirepeat">Using rowStatePreserved in UIRepeat</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xhtml"><span class="comment">&lt;!-- NEW in Faces 4.1: rowStatePreserved property for UIRepeat --&gt;</span>
<span class="comment">&lt;!-- This preserves the state of each row, similar to UIData --&gt;</span>
<span class="tag">&lt;ui:repeat</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{productBean.products}</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">var</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">product</span><span class="delimiter">&quot;</span></span>
           <span class="attribute-name">rowStatePreserved</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>
    <span class="tag">&lt;h:panelGroup&gt;</span>
        <span class="tag">&lt;h:outputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{product.name}: </span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
        <span class="tag">&lt;h:inputText</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{product.quantity}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
        <span class="tag">&lt;h:commandButton</span> <span class="attribute-name">value</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Update</span><span class="delimiter">&quot;</span></span>
                        <span class="attribute-name">action</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">#{productBean.updateProduct(product)}</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
    <span class="tag">&lt;/h:panelGroup&gt;</span>
<span class="tag">&lt;/ui:repeat&gt;</span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-setresponsecontentlengthlong-for-large-files">Using setResponseContentLengthLong for large files</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.faces.context.ExternalContext</span>;
<span class="keyword">import</span> <span class="include">jakarta.faces.context.FacesContext</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Named</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.context.RequestScoped</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.io.InputStream</span>;
<span class="keyword">import</span> <span class="include">java.io.OutputStream</span>;

<span class="annotation">@Named</span>
<span class="annotation">@RequestScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">FileDownloadBean</span> {

    <span class="directive">public</span> <span class="type">void</span> downloadLargeFile() <span class="directive">throws</span> <span class="exception">IOException</span> {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();

        <span class="comment">// Simulate a large file (&gt; 2GB)</span>
        <span class="type">long</span> fileSize = <span class="integer">3</span>_000_000_000L; <span class="comment">// 3GB</span>

        externalContext.responseReset();
        externalContext.setResponseContentType(<span class="string"><span class="delimiter">&quot;</span><span class="content">application/octet-stream</span><span class="delimiter">&quot;</span></span>);
        externalContext.setResponseHeader(<span class="string"><span class="delimiter">&quot;</span><span class="content">Content-Disposition</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">attachment; filename=</span><span class="char">\&quot;</span><span class="content">largefile.bin</span><span class="char">\&quot;</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// NEW in Faces 4.1: setResponseContentLengthLong for files &gt; 2GB</span>
        externalContext.setResponseContentLengthLong(fileSize);

        <span class="keyword">try</span> (<span class="predefined-type">OutputStream</span> output = externalContext.getResponseOutputStream()) {
            <span class="comment">// Write file content</span>
            <span class="comment">// ... (implementation details)</span>
        }

        facesContext.responseComplete();
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="generic-facesmessage-with-improved-type-safety">Generic FacesMessage with improved type safety</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.faces.application.FacesMessage</span>;
<span class="keyword">import</span> <span class="include">jakarta.faces.context.FacesContext</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Named</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.context.RequestScoped</span>;

<span class="annotation">@Named</span>
<span class="annotation">@RequestScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">MessageBean</span> {

    <span class="directive">public</span> <span class="type">void</span> demonstrateGenericMessages() {
        FacesContext context = FacesContext.getCurrentInstance();

        <span class="comment">// NEW in Faces 4.1: FacesMessage.VALUES and VALUES_MAP are now generic</span>
        <span class="comment">// This provides better type safety when working with message severities</span>

        FacesMessage infoMsg = <span class="keyword">new</span> FacesMessage(
            FacesMessage.SEVERITY_INFO,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">Information</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">This is an info message</span><span class="delimiter">&quot;</span></span>
        );

        FacesMessage warnMsg = <span class="keyword">new</span> FacesMessage(
            FacesMessage.SEVERITY_WARN,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">Warning</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">This is a warning message</span><span class="delimiter">&quot;</span></span>
        );

        FacesMessage errorMsg = <span class="keyword">new</span> FacesMessage(
            FacesMessage.SEVERITY_ERROR,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">Error</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">This is an error message</span><span class="delimiter">&quot;</span></span>
        );

        <span class="comment">// NEW in Faces 4.1: FacesMessage now implements equals(), hashCode(), toString()</span>
        <span class="predefined-type">System</span>.out.println(infoMsg.toString());

        <span class="comment">// Compare messages</span>
        FacesMessage anotherInfoMsg = <span class="keyword">new</span> FacesMessage(
            FacesMessage.SEVERITY_INFO,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">Information</span><span class="delimiter">&quot;</span></span>,
            <span class="string"><span class="delimiter">&quot;</span><span class="content">This is an info message</span><span class="delimiter">&quot;</span></span>
        );

        <span class="keyword">if</span> (infoMsg.equals(anotherInfoMsg)) {
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Messages are equal</span><span class="delimiter">&quot;</span></span>);
        }

        context.addMessage(<span class="predefined-constant">null</span>, infoMsg);
        context.addMessage(<span class="predefined-constant">null</span>, warnMsg);
        context.addMessage(<span class="predefined-constant">null</span>, errorMsg);
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="security">Security 4.0</h3>
<div class="paragraph">
<p>Jakarta Security is the overarching API designed to provide a holistic, vendor-neutral security model for the Jakarta EE platform. It simplifies authentication and authorization by leveraging CDI and annotations to manage three core artifacts: Authentication Mechanisms, Identity Stores, and Permission Stores.</p>
</div>
<div class="paragraph">
<p>Jakarta Security 4.0 provides an In-memory Identity Store, which is a developer-defined store of credential information that is used during the Open Liberty authentication and authorization work flow. It provides a quick, simple, and convenient authentication mechanism for Liberty application testing, debugging, demos, and more.</p>
</div>
<div class="paragraph">
<p><strong>New features in Security 4.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Multiple HTTP Authentication Mechanisms (HAMs)</strong>: Multiple HTTP Authentication Mechanisms can now be defined within the same application. These mechanisms can be specified through built-in Jakarta annotations such as <code>@FormAuthenticationMechanismDefinition</code> or through custom implementations of the <code>HttpAuthenticationMechanism</code> interface. Prioritisation of multiple HAMs can be managed by a custom implementation of the <code>HttpAuthenticationMechanismHandler</code> instead of relying on the default algorithm provided by Jakarta Security.</p>
</li>
<li>
<p><strong>Qualifiers for Built-in Authentication Mechanisms</strong>: Built-in authentication mechanisms (BASIC, FORM, Custom FORM, OpenID Connect) now have qualifiers by default, whereas before they were unqualified. This enables programmatic selection and injection of specific authentication mechanisms.</p>
</li>
<li>
<p><strong>In-memory Identity Store</strong>: Provides <code>@InMemoryIdentityStoreDefinition</code> annotation for defining credential stores directly in code. This is designed for testing, debugging, and demos - not recommended for production use.</p>
</li>
<li>
<p><strong>New SecurityContext method</strong>: A new method <code>getAllDeclaredCallerRoles()</code> is added to the <code>SecurityContext</code> interface, which returns a list of all static (declared) application roles that the authenticated caller is in.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals and Breaking Changes:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>All references to the <code>SecurityManager</code> have been removed from the specification</p>
</li>
<li>
<p>Built-in authentication mechanisms now have a qualifier by default, whereas before they were unqualified</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="in-memory-identity-store">In-memory Identity Store</h4>
<div class="paragraph">
<p>Before the introduction of the new identity store specification, Jakarta Security natively supported only two types of identity stores: <strong>database</strong> and <strong>LDAP</strong>, both of which are used for credential validation. While effective for production environments, these options were considered heavyweight for testing, debugging, and demonstration scenarios.</p>
</div>
<div class="paragraph">
<p>The Jakarta Security Specification 4.0 provides details on how to specify credential information to be used during the authentication workflow through the new <code>@InMemoryIdentityStoreDefinition</code> annotation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@InMemoryIdentityStoreDefinition</span> (
    priority = <span class="integer">10</span>,
    priorityExpression = <span class="string"><span class="delimiter">&quot;</span><span class="content">${80/20}</span><span class="delimiter">&quot;</span></span>,
    useFor = {VALIDATE, PROVIDE_GROUPS},
    useForExpression = <span class="string"><span class="delimiter">&quot;</span><span class="content">#{'VALIDATE'}</span><span class="delimiter">&quot;</span></span>,
    value = {
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">jasmine</span><span class="delimiter">&quot;</span></span>, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">secret1</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">caller</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> } )
    }
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>All attributes for the <code>@InMemoryIdentityStoreDefinition</code> annotation are shown in the example. The <code>priority</code>, <code>priorityExpression</code>, <code>useFor</code>, and <code>useForExpression</code> attributes are optional and set to sensible defaults.</p>
</div>
<div class="paragraph">
<p>The <code>@Credentials</code> annotation maps one or more caller names to a password and optional group values. The <code>callerName</code> and <code>password</code> attributes are mandatory. If either one is omitted, a compilation error occurs.</p>
</div>
<div class="paragraph">
<p>The example demonstrates a single caller definition with credential information that uses a plain-text password. However, it is highly recommended that passwords be supplied using an Open Liberty-supported encoding mechanism, as illustrated in the next example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@InMemoryIdentityStoreDefinition</span> (
    value = {
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">jasmine</span><span class="delimiter">&quot;</span></span>,  password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{xor}LDo8LTorbg==</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">caller</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> } ),
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">frank</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> }, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{hash}ARAAA &lt;sequence shortened&gt; Fyyw==</span><span class="delimiter">&quot;</span></span>),
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">sally</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> }, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{aes}ARAFIYJ &lt;sequence shortened&gt; WRQNA==</span><span class="delimiter">&quot;</span></span>)
    }
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Encrypted and encoded passwords can be generated by using the Open Liberty <code>securityUtility</code>, which is included under the <code>wlp/bin/securityUtility</code> path. The following example demonstrates how to encode a text string by using the <code>xor</code> encoding mechanism:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">wlp/bin/securityUtility encode --encoding=xor
Enter text: &lt;enter text to encode&gt;
Re-enter text:
{xor}PTA9Lyg</code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Since this feature is designed for testing and debugging purpose, when an application defines an in‑memory identity store in code, its use must also be explicitly enabled in the server.xml configuration, as shown below. By default, the allowInMemoryIdentityStores attribute is set to false, which instructs the Liberty authentication workflows not to use in‑memory identity stores, even when a custom identity store handler is present.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;server&gt;</span>
    ...
    <span class="tag">&lt;featureManager&gt;</span>
        <span class="tag">&lt;feature&gt;</span>appSecurity-4.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;/featureManager&gt;</span>
    <span class="tag">&lt;appSecurity</span> <span class="attribute-name">allowInMemoryIdentityStores</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span>
    ...
<span class="tag">&lt;/server&gt;</span></code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="multiple-http-authentication-mechanisms">Multiple HTTP Authentication Mechanisms</h4>
<div class="paragraph">
<p>The Jakarta Security 4.0 specification allows multiple HTTP Authentication Mechanisms (HAMs) to be defined within a single application:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@BasicAuthenticationMechanismDefinition</span>(realmName=<span class="string"><span class="delimiter">&quot;</span><span class="content">basicAuth</span><span class="delimiter">&quot;</span></span>)

<span class="annotation">@FormAuthenticationMechanismDefinition</span>(
                loginToContinue = <span class="annotation">@LoginToContinue</span>(errorPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/form-login-error.html</span><span class="delimiter">&quot;</span></span>,
                loginPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/form-login.html</span><span class="delimiter">&quot;</span></span>))

<span class="annotation">@CustomFormAuthenticationMechanismDefinition</span>(
                loginToContinue = <span class="annotation">@LoginToContinue</span>(errorPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/custom-login-error.html</span><span class="delimiter">&quot;</span></span>,
                loginPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/custom-login.html</span><span class="delimiter">&quot;</span></span>))</code></pre>
</div>
</div>
<div class="paragraph">
<p>This example demonstrates how three HTTP Authentication Mechanisms (HAMs) can be defined within a single application.</p>
</div>
<div class="paragraph">
<p>Custom HAMs can also be defined in the same application by implementing the <code>HttpAuthenticationMechanism</code> interface in one or more classes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="comment">// @Priority is optional and used to control selection priority if multiple custom definitions exist</span>
<span class="annotation">@Priority</span>(<span class="integer">100</span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomHAM</span> <span class="directive">implements</span> HttpAuthenticationMechanism {

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthenticationStatus validateRequest(
        HttpServletRequest request,
        HttpServletResponse response,
        HttpMessageContext httpMessageContext) <span class="directive">throws</span> <span class="exception">AuthenticationException</span> {

            <span class="comment">// implement custom logic here, and return an AuthenticationStatus</span>
            <span class="keyword">return</span> AuthenticationStatus.NOT_DONE;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>So a single application can have a mix of both annotation-defined HAMs and custom ones. In the previous two snippets of code, a total of four HAMs are defined (three by annotation and one custom one).</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<code>@Priority</code> must be used to raise or lower the priority of one custom HAM over another. If not specified, then a default priority is assigned. If more than one custom HAM is defined, their priorities need to be explicitly set to unique values. If the priorities are set to the same value or remain unset and inherit the same default value, an error occurs.
</td>
</tr>
</table>
</div>
<div class="sect4">
<h5 id="ham-resolution">HAM Resolution</h5>
<div class="paragraph">
<p>An internal implementation of the Jakarta Security 4.0 <code>HttpAuthenticationMechanismHandler</code> interface (the "internal HAM handler") is provided. When an application defines multiple HAMs, this internal handler selects a single HAM to be used in the authentication flow.</p>
</div>
<div class="paragraph">
<p>The order in which HAMs are considered (when present) is as follows:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Custom (developer-provided) HAMs</p>
<div class="ulist">
<ul>
<li>
<p>If multiple custom HAMs are defined, their relative order is resolved by using <code>@Priority</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>OpenIdAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>CustomFormAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>FormAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>BasicAuthenticationMechanismDefinition</code></p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Given this ordering, the Custom HAM is always selected in the authentication workflow if all five HAM types are defined in the application.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
A developer must provide a custom implementation of the <code>HttpAuthenticationMechanismHandler</code> interface (a "custom HAM handler") if the internal HAM handler does not meet their requirements. A custom handler always takes precedence over the internal HAM handler, allowing any tailored algorithm to select a single HAM from multiple available mechanisms.
</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="qualifiers">Qualifiers</h5>
<div class="paragraph">
<p>HAMs, whether defined through annotations or as custom defined, can also include an optional class-level qualifier to simplify HAM injection into a custom HAM handler. For example, if you want to define qualified HAMs, you would first declare qualifier interfaces such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.METHOD</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.FIELD</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.PARAMETER</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.TYPE</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.RetentionPolicy.RUNTIME</span>;

<span class="keyword">import</span> <span class="include">java.lang.annotation.Retention</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.Target</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Qualifier</span>;

<span class="annotation">@Qualifier</span>
<span class="annotation">@Retention</span>(RUNTIME)
<span class="annotation">@Target</span>({TYPE, METHOD, FIELD, PARAMETER})
<span class="directive">public</span> <span class="annotation">@interface</span> Admin {
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.METHOD</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.FIELD</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.PARAMETER</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.ElementType.TYPE</span>;
<span class="keyword">import</span> <span class="include">static</span> <span class="include">java.lang.annotation.RetentionPolicy.RUNTIME</span>;

<span class="keyword">import</span> <span class="include">java.lang.annotation.Retention</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation.Target</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Qualifier</span>;

<span class="annotation">@Qualifier</span>
<span class="annotation">@Retention</span>(RUNTIME)
<span class="annotation">@Target</span>({TYPE, METHOD, FIELD, PARAMETER})
<span class="directive">public</span> <span class="annotation">@interface</span> User {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now define multiple Basic HTTP Authentication Mechanisms in the main application:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">Admin</span>;
<span class="keyword">import</span> <span class="include">User</span>;

<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.context.ApplicationScoped</span>;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.ApplicationPath</span>;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core.Application</span>;


<span class="annotation">@BasicAuthenticationMechanismDefinition</span>(realmName=<span class="string"><span class="delimiter">&quot;</span><span class="content">admin-realm</span><span class="delimiter">&quot;</span></span>, qualifiers={Admin.class})
<span class="annotation">@BasicAuthenticationMechanismDefinition</span>(realmName=<span class="string"><span class="delimiter">&quot;</span><span class="content">user-realm</span><span class="delimiter">&quot;</span></span>, qualifiers={User.class})

<span class="annotation">@ApplicationScoped</span>
<span class="annotation">@ApplicationPath</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">MultipleHAMsApplication</span> <span class="directive">extends</span> Application {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the example, two Basic HTTP Authentication Mechanisms are defined in the main application. The @BasicAuthenticationMechanismDefinition annotation is used to define the realm name and the qualifier for each mechanism. The qualifiers are used to distinguish between the two mechanisms during injection.</p>
</div>
<div class="paragraph">
<p>Now finally, define an implementation of the <code>HttpAuthenticationMechanismHandler</code> to choose which qualified HAM to use:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">Admin</span>;
<span class="keyword">import</span> <span class="include">User</span>;

<span class="keyword">import</span> <span class="include">jakarta.enterprise.context.ApplicationScoped</span>;
<span class="keyword">import</span> <span class="include">jakarta.enterprise.inject.Default</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Inject</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.AuthenticationException</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.AuthenticationStatus</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanismHandler</span>;
<span class="keyword">import</span> <span class="include">jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;

<span class="annotation">@ApplicationScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomHAMHandler</span> <span class="directive">implements</span> HttpAuthenticationMechanismHandler {

    <span class="annotation">@Inject</span> <span class="annotation">@Admin</span>
    <span class="directive">private</span> HttpAuthenticationMechanism adminHAM;

    <span class="annotation">@Inject</span> <span class="annotation">@User</span>
    <span class="directive">private</span> HttpAuthenticationMechanism userHAM;

    <span class="directive">public</span> CustomHAMHandler() {
    }

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthenticationStatus validateRequest(HttpServletRequest request,
                                                HttpServletResponse response,
                                                HttpMessageContext httpMessageContext) <span class="directive">throws</span> <span class="exception">AuthenticationException</span> {
        <span class="predefined-type">String</span> requestURI = request.getRequestURI();
        <span class="predefined-type">String</span> contextPath = request.getContextPath();

        <span class="predefined-type">String</span> path = requestURI;
        <span class="keyword">if</span> (contextPath != <span class="predefined-constant">null</span> &amp;&amp; !contextPath.isEmpty()) {
            path = requestURI.substring(contextPath.length());
        }

        <span class="keyword">if</span> (path.startsWith(<span class="string"><span class="delimiter">&quot;</span><span class="content">/resource/admin</span><span class="delimiter">&quot;</span></span>)) {
            <span class="keyword">return</span> adminHAM.validateRequest(request, response, httpMessageContext);
        } <span class="keyword">else</span> <span class="keyword">if</span> (path.startsWith(<span class="string"><span class="delimiter">&quot;</span><span class="content">/resource/user</span><span class="delimiter">&quot;</span></span>)) {
            <span class="keyword">return</span> userHAM.validateRequest(request, response, httpMessageContext);
        }
        <span class="keyword">return</span> AuthenticationStatus.SEND_FAILURE;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Note, you can also add a qualifier to custom HTTP Authentication Mechanisms (as you could prior to Jakarta Security 4.0) and inject the custom HAM into your custom HAM handler.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="getalldeclaredcallerroles">getAllDeclaredCallerRoles()</h4>
<div class="paragraph">
<p>To use the new <code>SecurityContext</code> method, inject the <code>SecurityContext</code> implementation into your application and call the method directly:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">    <span class="annotation">@Inject</span>
    <span class="directive">private</span> SecurityContext securityContext;

    <span class="predefined-type">Set</span>&lt;<span class="predefined-type">String</span>&gt; allDeclaredCallerRoles = securityContext.getAllDeclaredCallerRoles();

    <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">All declared caller roles for caller [</span><span class="delimiter">&quot;</span></span>
        + securityContext.getCallerPrincipal().getName()
        + <span class="string"><span class="delimiter">&quot;</span><span class="content">] are </span><span class="delimiter">&quot;</span></span>
        + allDeclaredCallerRoles.toString());

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/info</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
    <span class="directive">public</span> <span class="predefined-type">String</span> getSecureInfo() {
        <span class="predefined-type">String</span> username = securityContext.getUserPrincipal().getName();
        <span class="type">boolean</span> isAdmin = securityContext.isUserInRole(<span class="string"><span class="delimiter">&quot;</span><span class="content">ADMIN</span><span class="delimiter">&quot;</span></span>);

        <span class="keyword">return</span> <span class="predefined-type">String</span>.format(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">{</span><span class="char">\&quot;</span><span class="content">user</span><span class="char">\&quot;</span><span class="content">: </span><span class="char">\&quot;</span><span class="content">%s</span><span class="char">\&quot;</span><span class="content">, </span><span class="char">\&quot;</span><span class="content">isAdmin</span><span class="char">\&quot;</span><span class="content">: %b}</span><span class="delimiter">&quot;</span></span>,
            username,
            isAdmin
        );
    }

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/admin</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Produces</span>(MediaType.TEXT_PLAIN)
    <span class="directive">public</span> <span class="predefined-type">String</span> adminOnly() {
        <span class="keyword">if</span> (!securityContext.isUserInRole(<span class="string"><span class="delimiter">&quot;</span><span class="content">ADMIN</span><span class="delimiter">&quot;</span></span>)) {
            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="exception">SecurityException</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">Admin access required</span><span class="delimiter">&quot;</span></span>);
        }
        <span class="keyword">return</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">Welcome, administrator!</span><span class="delimiter">&quot;</span></span>;
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="servlet">Servlet 6.1</h3>
<div class="paragraph">
<p>Jakarta Servlet defines a server-side API for handling HTTP requests and responses. This release removes references to the SecurityManager and provides various small enhancements and clarifications.</p>
</div>
<div class="paragraph">
<p><strong>New features in Servlet 6.1:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Allow control of status code and response body when sending a redirect</p>
</li>
<li>
<p>Add a query string attribute to error dispatches</p>
</li>
<li>
<p>Add constants for new HTTP status codes</p>
</li>
<li>
<p>Add overloaded methods that use <code>Charset</code> rather than <code>String</code></p>
</li>
<li>
<p>Add <code>ByteBuffer</code> support to <code>ServletInputStream</code> and <code>ServletOutputStream</code></p>
</li>
<li>
<p>Various clarifications throughout the specification</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>All references to the SecurityManager and associated APIs have been removed</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="custom-redirect-with-status-code-control">Custom Redirect with Status Code Control</h4>
<div class="paragraph">
<p>Servlet 6.1 allows you to control the HTTP status code when sending redirects:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.servlet.ServletException</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.annotation.WebServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;

<span class="annotation">@WebServlet</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/redirect-example</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">RedirectServlet</span> <span class="directive">extends</span> HttpServlet {

    <span class="annotation">@Override</span>
    <span class="directive">protected</span> <span class="type">void</span> doGet(HttpServletRequest request, HttpServletResponse response)
            <span class="directive">throws</span> ServletException, <span class="exception">IOException</span> {

        <span class="predefined-type">String</span> action = request.getParameter(<span class="string"><span class="delimiter">&quot;</span><span class="content">action</span><span class="delimiter">&quot;</span></span>);

        <span class="keyword">if</span> (<span class="string"><span class="delimiter">&quot;</span><span class="content">permanent</span><span class="delimiter">&quot;</span></span>.equals(action)) {
            <span class="comment">// Send a 301 Moved Permanently redirect</span>
            response.sendRedirect(<span class="string"><span class="delimiter">&quot;</span><span class="content">/new-location</span><span class="delimiter">&quot;</span></span>, HttpServletResponse.SC_MOVED_PERMANENTLY);
        } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string"><span class="delimiter">&quot;</span><span class="content">temporary</span><span class="delimiter">&quot;</span></span>.equals(action)) {
            <span class="comment">// Send a 307 Temporary Redirect (preserves request method)</span>
            response.sendRedirect(<span class="string"><span class="delimiter">&quot;</span><span class="content">/temp-location</span><span class="delimiter">&quot;</span></span>, HttpServletResponse.SC_TEMPORARY_REDIRECT);
        } <span class="keyword">else</span> {
            <span class="comment">// Default 302 Found redirect</span>
            response.sendRedirect(<span class="string"><span class="delimiter">&quot;</span><span class="content">/default-location</span><span class="delimiter">&quot;</span></span>);
        }
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-charset-methods">Using Charset Methods</h4>
<div class="paragraph">
<p>Servlet 6.1 adds overloaded methods that accept <code>Charset</code> instead of <code>String</code> for better type safety:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.servlet.ServletException</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.annotation.WebServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.io.PrintWriter</span>;
<span class="keyword">import</span> <span class="include">java.nio.charset.StandardCharsets</span>;

<span class="annotation">@WebServlet</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/charset-example</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CharsetServlet</span> <span class="directive">extends</span> HttpServlet {

    <span class="annotation">@Override</span>
    <span class="directive">protected</span> <span class="type">void</span> doGet(HttpServletRequest request, HttpServletResponse response)
            <span class="directive">throws</span> ServletException, <span class="exception">IOException</span> {

        <span class="comment">// Use Charset instead of String for character encoding</span>
        response.setCharacterEncoding(StandardCharsets.UTF_8);
        response.setContentType(<span class="string"><span class="delimiter">&quot;</span><span class="content">text/html</span><span class="delimiter">&quot;</span></span>);

        <span class="predefined-type">PrintWriter</span> writer = response.getWriter();
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;html&gt;&lt;body&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;h1&gt;UTF-8 Encoded Response&lt;/h1&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;Special characters: é, ñ, ü, 中文&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/body&gt;&lt;/html&gt;</span><span class="delimiter">&quot;</span></span>);
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="bytebuffer-support">ByteBuffer Support</h4>
<div class="paragraph">
<p>Servlet 6.1 adds <code>ByteBuffer</code> support to <code>ServletInputStream</code> and <code>ServletOutputStream</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.servlet.ServletException</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.annotation.WebServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.ServletOutputStream</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.nio.ByteBuffer</span>;
<span class="keyword">import</span> <span class="include">java.nio.charset.StandardCharsets</span>;

<span class="annotation">@WebServlet</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/bytebuffer-example</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ByteBufferServlet</span> <span class="directive">extends</span> HttpServlet {

    <span class="annotation">@Override</span>
    <span class="directive">protected</span> <span class="type">void</span> doGet(HttpServletRequest request, HttpServletResponse response)
            <span class="directive">throws</span> ServletException, <span class="exception">IOException</span> {

        response.setContentType(<span class="string"><span class="delimiter">&quot;</span><span class="content">application/octet-stream</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// Create a ByteBuffer with data</span>
        <span class="predefined-type">String</span> data = <span class="string"><span class="delimiter">&quot;</span><span class="content">Binary data using ByteBuffer</span><span class="delimiter">&quot;</span></span>;
        <span class="predefined-type">ByteBuffer</span> buffer = <span class="predefined-type">ByteBuffer</span>.wrap(data.getBytes(StandardCharsets.UTF_8));

        <span class="comment">// Write ByteBuffer directly to ServletOutputStream</span>
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(buffer);
        outputStream.flush();
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="error-dispatch-with-query-string">Error Dispatch with Query String</h4>
<div class="paragraph">
<p>Servlet 6.1 adds a query string attribute to error dispatches:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.servlet.ServletException</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.annotation.WebServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServlet</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletRequest</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.http.HttpServletResponse</span>;
<span class="keyword">import</span> <span class="include">jakarta.servlet.RequestDispatcher</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.io.PrintWriter</span>;

<span class="annotation">@WebServlet</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/error-handler</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ErrorHandlerServlet</span> <span class="directive">extends</span> HttpServlet {

    <span class="annotation">@Override</span>
    <span class="directive">protected</span> <span class="type">void</span> doGet(HttpServletRequest request, HttpServletResponse response)
            <span class="directive">throws</span> ServletException, <span class="exception">IOException</span> {

        response.setContentType(<span class="string"><span class="delimiter">&quot;</span><span class="content">text/html</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">PrintWriter</span> writer = response.getWriter();

        <span class="comment">// Access error attributes including the new query string attribute</span>
        <span class="predefined-type">Integer</span> statusCode = (<span class="predefined-type">Integer</span>) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        <span class="predefined-type">String</span> message = (<span class="predefined-type">String</span>) request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
        <span class="predefined-type">String</span> requestUri = (<span class="predefined-type">String</span>) request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
        <span class="predefined-type">String</span> queryString = (<span class="predefined-type">String</span>) request.getAttribute(RequestDispatcher.ERROR_QUERY_STRING);

        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;html&gt;&lt;body&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;h1&gt;Error Handler&lt;/h1&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;Status Code: </span><span class="delimiter">&quot;</span></span> + statusCode + <span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;Message: </span><span class="delimiter">&quot;</span></span> + message + <span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>);
        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;Request URI: </span><span class="delimiter">&quot;</span></span> + requestUri + <span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// New in Servlet 6.1: Query string is now available in error dispatches</span>
        <span class="keyword">if</span> (queryString != <span class="predefined-constant">null</span>) {
            writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;p&gt;Query String: </span><span class="delimiter">&quot;</span></span> + queryString + <span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/p&gt;</span><span class="delimiter">&quot;</span></span>);
        }

        writer.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">&lt;/body&gt;&lt;/html&gt;</span><span class="delimiter">&quot;</span></span>);
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="restful">RESTful Web Services 4.0</h3>
<div class="paragraph">
<p>Jakarta RESTful Web Services provides a foundational API to develop web services following the Representational State Transfer (REST) architectural pattern. The full details for this release are as follows.</p>
</div>
<div class="paragraph">
<p><strong>New features in RESTful Web Services 4.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>TCK tests for multipart/form-data API</strong>: Added comprehensive tests for multipart form data handling</p>
</li>
<li>
<p><strong>TCK tests for default ExceptionMapper</strong>: Added tests to verify default exception mapping behavior</p>
</li>
<li>
<p><strong>Added containsHeaderString method to a few APIs</strong>: New method added to the APIs -ClientRequestContext, ClientResponseContext, ContainerRequestContext, ContainerResponseContext and HttpHeaders to provide an easy way to check whether a header contains specific values</p>
</li>
<li>
<p><strong>Required TCK for convenience method</strong>: Added required tests for the new convenience method merged in PR 1066</p>
</li>
<li>
<p><strong>Clarified JavaSE support</strong>: Clarified JavaSE support in Section 2.3 of specification</p>
</li>
<li>
<p><strong>Added getMatchedResourceTemplate method to UriInfo</strong>: New method to retrieve the matched resource template</p>
</li>
<li>
<p><strong>Added JSON Merge Patch support</strong>: Support for RFC 7396 JSON Merge Patch</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Remove JAXB dependency</strong>: Jakarta REST no longer depends on JAXB</p>
</li>
<li>
<p><strong>Remove ManagedBean support</strong>: ManagedBean support has been removed from Jakarta REST</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="basic-rest-resource-example">Basic REST Resource Example</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core</span>.*;
<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.ArrayList</span>;

<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/products</span><span class="delimiter">&quot;</span></span>)
<span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
<span class="annotation">@Consumes</span>(MediaType.APPLICATION_JSON)
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProductResource</span> {

    <span class="directive">private</span> <span class="directive">static</span> <span class="predefined-type">List</span>&lt;Product&gt; products = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span>&lt;&gt;();

    <span class="annotation">@GET</span>
    <span class="directive">public</span> Response getAllProducts() {
        <span class="keyword">return</span> Response.ok(products).build();
    }

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{id}</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> Response getProduct(<span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> id) {
        Product product = products.stream()
            .filter(p -&gt; p.getId().equals(id))
            .findFirst()
            .orElse(<span class="predefined-constant">null</span>);

        <span class="keyword">if</span> (product == <span class="predefined-constant">null</span>) {
            <span class="keyword">return</span> Response.status(Response.Status.NOT_FOUND).build();
        }

        <span class="keyword">return</span> Response.ok(product).build();
    }

    <span class="annotation">@POST</span>
    <span class="directive">public</span> Response createProduct(Product product) {
        products.add(product);
        <span class="keyword">return</span> Response.status(Response.Status.CREATED)
            .entity(product)
            .build();
    }

    <span class="annotation">@PUT</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{id}</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> Response updateProduct(<span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> id, Product updatedProduct) {
        Product product = products.stream()
            .filter(p -&gt; p.getId().equals(id))
            .findFirst()
            .orElse(<span class="predefined-constant">null</span>);

        <span class="keyword">if</span> (product == <span class="predefined-constant">null</span>) {
            <span class="keyword">return</span> Response.status(Response.Status.NOT_FOUND).build();
        }

        products.remove(product);
        products.add(updatedProduct);

        <span class="keyword">return</span> Response.ok(updatedProduct).build();
    }

    <span class="annotation">@DELETE</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{id}</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> Response deleteProduct(<span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> id) {
        <span class="type">boolean</span> removed = products.removeIf(p -&gt; p.getId().equals(id));

        <span class="keyword">if</span> (!removed) {
            <span class="keyword">return</span> Response.status(Response.Status.NOT_FOUND).build();
        }

        <span class="keyword">return</span> Response.noContent().build();
    }
}

<span class="type">class</span> <span class="class">Product</span> {
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;
    <span class="directive">private</span> <span class="predefined-type">String</span> name;
    <span class="directive">private</span> <span class="predefined-type">Double</span> price;

    <span class="comment">// Constructors, getters, setters</span>
    <span class="directive">public</span> <span class="predefined-type">Long</span> getId() { <span class="keyword">return</span> id; }
    <span class="directive">public</span> <span class="type">void</span> setId(<span class="predefined-type">Long</span> id) { <span class="local-variable">this</span>.id = id; }
    <span class="directive">public</span> <span class="predefined-type">String</span> getName() { <span class="keyword">return</span> name; }
    <span class="directive">public</span> <span class="type">void</span> setName(<span class="predefined-type">String</span> name) { <span class="local-variable">this</span>.name = name; }
    <span class="directive">public</span> <span class="predefined-type">Double</span> getPrice() { <span class="keyword">return</span> price; }
    <span class="directive">public</span> <span class="type">void</span> setPrice(<span class="predefined-type">Double</span> price) { <span class="local-variable">this</span>.price = price; }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-getmatchedresourcetemplate-new-in-4-0">Using getMatchedResourceTemplate (NEW in 4.0)</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core</span>.*;

<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/api/users</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">UserResource</span> {

    <span class="annotation">@Context</span>
    <span class="directive">private</span> UriInfo uriInfo;

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{userId}/orders/{orderId}</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
    <span class="directive">public</span> Response getUserOrder(
            <span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> userId,
            <span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderId</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> orderId) {

        <span class="comment">// NEW in REST 4.0: getMatchedResourceTemplate method</span>
        <span class="predefined-type">String</span> template = uriInfo.getMatchedResourceTemplate();
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Matched template: </span><span class="delimiter">&quot;</span></span> + template);
        <span class="comment">// Output: /api/users/{userId}/orders/{orderId}</span>

        <span class="comment">// Use the template for logging, metrics, or routing decisions</span>
        <span class="keyword">return</span> Response.ok()
            .entity(<span class="keyword">new</span> Order(orderId, userId))
            .header(<span class="string"><span class="delimiter">&quot;</span><span class="content">X-Resource-Template</span><span class="delimiter">&quot;</span></span>, template)
            .build();
    }
}

<span class="type">class</span> <span class="class">Order</span> {
    <span class="directive">private</span> <span class="predefined-type">Long</span> orderId;
    <span class="directive">private</span> <span class="predefined-type">Long</span> userId;

    <span class="directive">public</span> Order(<span class="predefined-type">Long</span> orderId, <span class="predefined-type">Long</span> userId) {
        <span class="local-variable">this</span>.orderId = orderId;
        <span class="local-variable">this</span>.userId = userId;
    }

    <span class="directive">public</span> <span class="predefined-type">Long</span> getOrderId() { <span class="keyword">return</span> orderId; }
    <span class="directive">public</span> <span class="predefined-type">Long</span> getUserId() { <span class="keyword">return</span> userId; }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="json-merge-patch-support-new-in-4-0">JSON Merge Patch Support (NEW in 4.0)</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.json.Json</span>;
<span class="keyword">import</span> <span class="include">jakarta.json.JsonMergePatch</span>;
<span class="keyword">import</span> <span class="include">jakarta.json.JsonValue</span>;
<span class="keyword">import</span> <span class="include">jakarta.json.bind.Jsonb</span>;
<span class="keyword">import</span> <span class="include">jakarta.json.bind.JsonbBuilder</span>;

<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/customers</span><span class="delimiter">&quot;</span></span>)
<span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
<span class="annotation">@Consumes</span>(MediaType.APPLICATION_JSON)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomerResource</span> {

    <span class="directive">private</span> <span class="directive">static</span> Customer customer = <span class="keyword">new</span> Customer(<span class="integer">1L</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">John Doe</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">john@example.com</span><span class="delimiter">&quot;</span></span>);

    <span class="annotation">@GET</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{id}</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">public</span> Response getCustomer(<span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> id) {
        <span class="keyword">return</span> Response.ok(customer).build();
    }

    <span class="comment">// NEW in REST 4.0: JSON Merge Patch support (RFC 7396)</span>
    <span class="annotation">@PATCH</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/{id}</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Consumes</span>(MediaType.APPLICATION_MERGE_PATCH_JSON)
    <span class="directive">public</span> Response patchCustomer(
            <span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">Long</span> id,
            JsonValue patchJson) {

        <span class="keyword">try</span> (Jsonb jsonb = JsonbBuilder.create()) {
            <span class="comment">// Convert customer to JsonValue</span>
            JsonValue customerJson = jsonb.fromJson(
                jsonb.toJson(customer), JsonValue.class);

            <span class="comment">// Create merge patch manually from the incoming JSON</span>
            JsonMergePatch mergePatch = Json.createMergePatch(patchJson);
            <span class="comment">// Apply the merge patch</span>
            JsonValue patchedJson = mergePatch.apply(customerJson);

            <span class="comment">// Convert back to Customer object</span>
            Customer patchedCustomer = jsonb.fromJson(
                patchedJson.toString(), Customer.class);

            customer = patchedCustomer;

            <span class="keyword">return</span> Response.ok(customer).build();
        } <span class="keyword">catch</span> (<span class="exception">Exception</span> e) {
            <span class="keyword">return</span> Response.status(Response.Status.BAD_REQUEST)
                .entity(<span class="string"><span class="delimiter">&quot;</span><span class="content">Invalid patch: </span><span class="delimiter">&quot;</span></span> + e.getMessage())
                .build();
        }
    }
}

<span class="type">class</span> <span class="class">Customer</span> {
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;
    <span class="directive">private</span> <span class="predefined-type">String</span> name;
    <span class="directive">private</span> <span class="predefined-type">String</span> email;

    <span class="directive">public</span> Customer() {}

    <span class="directive">public</span> Customer(<span class="predefined-type">Long</span> id, <span class="predefined-type">String</span> name, <span class="predefined-type">String</span> email) {
        <span class="local-variable">this</span>.id = id;
        <span class="local-variable">this</span>.name = name;
        <span class="local-variable">this</span>.email = email;
    }

    <span class="comment">// Getters and setters</span>
    <span class="directive">public</span> <span class="predefined-type">Long</span> getId() { <span class="keyword">return</span> id; }
    <span class="directive">public</span> <span class="type">void</span> setId(<span class="predefined-type">Long</span> id) { <span class="local-variable">this</span>.id = id; }
    <span class="directive">public</span> <span class="predefined-type">String</span> getName() { <span class="keyword">return</span> name; }
    <span class="directive">public</span> <span class="type">void</span> setName(<span class="predefined-type">String</span> name) { <span class="local-variable">this</span>.name = name; }
    <span class="directive">public</span> <span class="predefined-type">String</span> getEmail() { <span class="keyword">return</span> email; }
    <span class="directive">public</span> <span class="type">void</span> setEmail(<span class="predefined-type">String</span> email) { <span class="local-variable">this</span>.email = email; }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="multipart-form-data-handling">Multipart Form Data Handling</h4>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core</span>.*;
<span class="keyword">import</span> <span class="include">java.io.InputStream</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="comment">/**
 * Demonstrates multipart/form-data handling in Jakarta REST 4.0
 * using the standard EntityPart API (new in REST 4.0)
 */</span>
<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/upload</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">FileUploadResource</span> {
    <span class="annotation">@POST</span>
    <span class="annotation">@Consumes</span>(MediaType.MULTIPART_FORM_DATA)
    <span class="annotation">@Produces</span>(MediaType.APPLICATION_JSON)
    <span class="directive">public</span> Response uploadFile(
            <span class="annotation">@FormParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">file</span><span class="delimiter">&quot;</span></span>) EntityPart filePart,
            <span class="annotation">@FormParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">description</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> description) {
        <span class="keyword">if</span> (filePart == <span class="predefined-constant">null</span>) {
            <span class="keyword">return</span> Response.status(Response.Status.BAD_REQUEST)
                .entity(<span class="keyword">new</span> UploadResponse(<span class="predefined-constant">null</span>, <span class="integer">0</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">No file provided</span><span class="delimiter">&quot;</span></span>))
                .build();
        }
        <span class="keyword">try</span> {
            <span class="predefined-type">String</span> fileName = filePart.getFileName().orElse(<span class="string"><span class="delimiter">&quot;</span><span class="content">unknown</span><span class="delimiter">&quot;</span></span>);
            <span class="comment">// Read the file content to get size</span>
            <span class="type">long</span> fileSize = <span class="integer">0</span>;
            <span class="keyword">try</span> (<span class="predefined-type">InputStream</span> is = filePart.getContent()) {
                <span class="type">byte</span><span class="type">[]</span> buffer = <span class="keyword">new</span> <span class="type">byte</span>[<span class="integer">8192</span>];
                <span class="type">int</span> bytesRead;
                <span class="keyword">while</span> ((bytesRead = is.read(buffer)) != -<span class="integer">1</span>) {
                    fileSize += bytesRead;
                }
            }
            <span class="comment">// Process the file</span>
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Uploading file: </span><span class="delimiter">&quot;</span></span> + fileName);
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">File size: </span><span class="delimiter">&quot;</span></span> + fileSize);
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Description: </span><span class="delimiter">&quot;</span></span> + description);
            <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Content-Type: </span><span class="delimiter">&quot;</span></span> + filePart.getMediaType());
            <span class="keyword">return</span> Response.ok()
                .entity(<span class="keyword">new</span> UploadResponse(fileName, fileSize, <span class="string"><span class="delimiter">&quot;</span><span class="content">Upload successful</span><span class="delimiter">&quot;</span></span>))
                .build();
        } <span class="keyword">catch</span> (<span class="exception">IOException</span> e) {
            <span class="keyword">return</span> Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(<span class="keyword">new</span> UploadResponse(<span class="predefined-constant">null</span>, <span class="integer">0</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Error processing file: </span><span class="delimiter">&quot;</span></span> + e.getMessage()))
                .build();
        }
    }
}

<span class="type">class</span> <span class="class">UploadResponse</span> {
    <span class="directive">private</span> <span class="predefined-type">String</span> fileName;
    <span class="directive">private</span> <span class="type">long</span> fileSize;
    <span class="directive">private</span> <span class="predefined-type">String</span> message;

    <span class="directive">public</span> UploadResponse(<span class="predefined-type">String</span> fileName, <span class="type">long</span> fileSize, <span class="predefined-type">String</span> message) {
        <span class="local-variable">this</span>.fileName = fileName;
        <span class="local-variable">this</span>.fileSize = fileSize;
        <span class="local-variable">this</span>.message = message;
    }

    <span class="comment">// Getters</span>
    <span class="directive">public</span> <span class="predefined-type">String</span> getFileName() { <span class="keyword">return</span> fileName; }
    <span class="directive">public</span> <span class="type">long</span> getFileSize() { <span class="keyword">return</span> fileSize; }
    <span class="directive">public</span> <span class="predefined-type">String</span> getMessage() { <span class="keyword">return</span> message; }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="persistence">Persistence 3.2</h3>
<div class="paragraph">
<p>Jakarta Persistence defines a standard for management of persistence and object/relational mapping in Java environments.</p>
</div>
<div class="paragraph">
<p><strong>New features in Persistence 3.2:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Java Record Support</strong>: Adds support for Java record types as embeddable classes</p>
</li>
<li>
<p><strong>java.time Support</strong>: Adds support for <code>java.time.Instant</code> and <code>java.time.Year</code> with clarified JDBC mappings for basic types</p>
</li>
<li>
<p><strong>New Query Operators</strong>: Adds <code>union</code>, <code>intersect</code>, <code>except</code>, <code>cast</code>, <code>left</code>, <code>right</code>, and <code>replace</code> operators for Jakarta Persistence QL and criteria queries</p>
</li>
<li>
<p><strong>String Concatenation</strong>: Adds <code>||</code> as a concatenation operator and <code>id</code> and <code>version</code> functions to Jakarta Persistence QL</p>
</li>
<li>
<p><strong>Criteria API Enhancements</strong>: Adds <code>CriteriaSelect</code>, <code>subquery(EntityType)</code> and joins on <code>EntityType</code> to Criteria API</p>
</li>
<li>
<p><strong>Null Precedence</strong>: Adds support for specifying null precedence when ordering Jakarta Persistence QL and criteria queries</p>
</li>
<li>
<p><strong>Query Methods</strong>: Adds <code>getSingleResultOrNull()</code> to <code>Query</code>, <code>TypedQuery</code>, <code>StoredProcedureQuery</code></p>
</li>
<li>
<p><strong>Named Queries</strong>: Adds <code>entities()</code>, <code>classes()</code> and <code>columns()</code> to <code>NamedNativeQuery</code></p>
</li>
<li>
<p><strong>Lock Mode</strong>: Adds <code>lockMode()</code> to <code>EntityResult</code> with the default being <code>OPTIMISTIC</code></p>
</li>
<li>
<p><strong>Transaction Helpers</strong>: Adds <code>runInTransaction()</code> and <code>callInTransaction()</code> convenience methods for executing code within transactions</p>
</li>
<li>
<p><strong>EntityManager Connection Access</strong>: Adds <code>runWithConnection()</code> and <code>callWithConnection()</code> methods for direct JDBC connection access</p>
</li>
<li>
<p><strong>Programmatic Configuration API</strong>: Adds <code>PersistenceConfiguration</code> for programmatic persistence unit configuration</p>
</li>
<li>
<p><strong>Schema Management API</strong>: Adds <code>SchemaManager</code> for schema creation, validation, truncation, and dropping</p>
</li>
<li>
<p><strong>DDL Generation Enhancements</strong>: Adds support for comments, check constraints, table options, and second precision in generated DDL</p>
</li>
<li>
<p><strong>Enum Mapping Enhancements</strong>: Adds <code>@EnumeratedValue</code> for custom enum value mapping</p>
</li>
<li>
<p><strong>Named Query and Graph Factory Access</strong>: Adds APIs for factory-based named query and entity graph creation/access</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Deprecations:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Temporal Types</strong>: Deprecates usage of <code>Calendar</code>, <code>Date</code>, <code>Time</code>, <code>Timestamp</code>, <code>Temporal</code>, <code>MapKeyTemporal</code>, and <code>TemporalType</code> in favor of <code>java.time</code> API</p>
</li>
<li>
<p><strong>multiselect Methods</strong>: Deprecates <code>multiselect</code> methods in <code>CriteriaQuery</code> in favor of <code>array</code> or <code>tuple</code> methods defined in <code>CriteriaBuilder</code></p>
</li>
<li>
<p><strong>Byte[] and Character[] Arrays</strong>: Deprecates use of <code>Byte[]</code> and <code>Character[]</code> arrays for basic attributes, in favor of primitive array types</p>
</li>
<li>
<p><strong>Subgraph Methods for Removal</strong>: Deprecates <code>addSubclassSubgraph()</code> in <code>EntityGraph</code> for removal; <code>addTreatedSubgraph()</code> method should be used as direct replacement</p>
</li>
<li>
<p><strong>Attribute and Class Subgraph Methods</strong>: Deprecates <code>addSubgraph(Attribute, Class)</code> and <code>addKeySubgraph()</code> in <code>Graph</code>/<code>EntityGraph</code>/<code>SubGraph</code> for removal</p>
</li>
<li>
<p><strong>Transaction Methods</strong>: Deprecates <code>jakarta.persistence.spi.PersistenceUnitTransactionType</code> and <code>jakarta.persistence.PersistenceUnitUtil.getTransactionType()</code> methods for removal</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.persistence</span>.*;
<span class="keyword">import</span> <span class="include">java.time.Instant</span>;
<span class="keyword">import</span> <span class="include">java.time.Year</span>;
<span class="keyword">import</span> <span class="include">java.util.List</span>;

<span class="comment">// NEW in 3.2: Java Record as Embeddable</span>
<span class="annotation">@Embeddable</span>
<span class="directive">public</span> record Address(
    <span class="predefined-type">String</span> street,
    <span class="predefined-type">String</span> city,
    <span class="predefined-type">String</span> state,
    <span class="predefined-type">String</span> zipCode
) {}

<span class="annotation">@Entity</span>
<span class="annotation">@Table</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">customers</span><span class="delimiter">&quot;</span></span>)
<span class="annotation">@NamedQuery</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">Customer.findByEmail</span><span class="delimiter">&quot;</span></span>,
            query = <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.email = :email</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Customer</span> {

    <span class="annotation">@Id</span>
    <span class="annotation">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;

    <span class="annotation">@Version</span>
    <span class="directive">private</span> <span class="predefined-type">Long</span> version;

    <span class="annotation">@Column</span>(nullable = <span class="predefined-constant">false</span>)
    <span class="directive">private</span> <span class="predefined-type">String</span> name;

    <span class="annotation">@Column</span>(unique = <span class="predefined-constant">true</span>, nullable = <span class="predefined-constant">false</span>)
    <span class="directive">private</span> <span class="predefined-type">String</span> email;

    <span class="directive">private</span> <span class="predefined-type">String</span> phone;

    <span class="directive">private</span> <span class="predefined-type">String</span> status;  <span class="comment">// e.g., &quot;ACTIVE&quot;, &quot;INACTIVE&quot;</span>

    <span class="directive">private</span> <span class="predefined-type">Integer</span> vipLevel;  <span class="comment">// VIP tier level</span>

    <span class="comment">// NEW in 3.2: Embedded record type</span>
    <span class="annotation">@Embedded</span>
    <span class="directive">private</span> Address address;

    <span class="comment">// NEW in 3.2: java.time.Instant support</span>
    <span class="directive">private</span> Instant createdAt;

    <span class="comment">// NEW in 3.2: java.time.Year support</span>
    <span class="directive">private</span> Year memberSince;

    <span class="annotation">@OneToMany</span>(mappedBy = <span class="string"><span class="delimiter">&quot;</span><span class="content">customer</span><span class="delimiter">&quot;</span></span>, cascade = CascadeType.ALL)
    <span class="directive">private</span> <span class="predefined-type">List</span>&lt;Order&gt; orders;

    <span class="comment">// Constructors, getters, setters</span>
}

<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomerRepository</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="directive">public</span> Customer findById(<span class="predefined-type">Long</span> id) {
        <span class="keyword">return</span> em.find(Customer.class, id);
    }

    <span class="directive">public</span> Customer findByEmail(<span class="predefined-type">String</span> email) {
        <span class="keyword">return</span> em.createNamedQuery(<span class="string"><span class="delimiter">&quot;</span><span class="content">Customer.findByEmail</span><span class="delimiter">&quot;</span></span>, Customer.class)
                 .setParameter(<span class="string"><span class="delimiter">&quot;</span><span class="content">email</span><span class="delimiter">&quot;</span></span>, email)
                 .getSingleResult();
    }

    <span class="comment">// NEW in 3.2: getSingleResultOrNull() method</span>
    <span class="directive">public</span> Customer findByEmailOrNull(<span class="predefined-type">String</span> email) {
        <span class="keyword">return</span> em.createQuery(<span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.email = :email</span><span class="delimiter">&quot;</span></span>, Customer.class)
                 .setParameter(<span class="string"><span class="delimiter">&quot;</span><span class="content">email</span><span class="delimiter">&quot;</span></span>, email)
                 .getSingleResultOrNull();  <span class="comment">// Returns null instead of throwing exception</span>
    }

    <span class="comment">// NEW in 3.2: String concatenation with || operator</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getFullNames() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c.name || ' (' || c.email || ')' FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">String</span>.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: UNION operator</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findActiveAndVIPCustomers() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.status = 'ACTIVE' </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">UNION </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.vipLevel &gt; 5</span><span class="delimiter">&quot;</span></span>,
            Customer.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: Null precedence in ordering</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersOrderedByCity() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c ORDER BY c.address.city NULLS LAST</span><span class="delimiter">&quot;</span></span>,
            Customer.class
        ).getResultList();
    }

    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersWithOrders() {
        <span class="comment">// Using JOIN FETCH for efficient loading</span>
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT DISTINCT c FROM Customer c LEFT JOIN FETCH c.orders</span><span class="delimiter">&quot;</span></span>,
            Customer.class
        ).getResultList();
    }

    <span class="directive">public</span> <span class="type">void</span> save(Customer customer) {
        <span class="keyword">if</span> (customer.getId() == <span class="predefined-constant">null</span>) {
            em.persist(customer);
        } <span class="keyword">else</span> {
            em.merge(customer);
        }
    }
}
<span class="comment">// NEW in 3.2: Criteria API Enhancements</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomerCriteriaRepository</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="comment">// NEW in 3.2: Using CriteriaSelect for type-safe queries</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersByCityUsingCriteria(<span class="predefined-type">String</span> city) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery&lt;Customer&gt; cq = cb.createQuery(Customer.class);
        Root&lt;Customer&gt; customer = cq.from(Customer.class);

        <span class="comment">// CriteriaSelect provides enhanced type safety</span>
        cq.select(customer)
          .where(cb.equal(customer.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">address</span><span class="delimiter">&quot;</span></span>).get(<span class="string"><span class="delimiter">&quot;</span><span class="content">city</span><span class="delimiter">&quot;</span></span>), city));

        <span class="keyword">return</span> em.createQuery(cq).getResultList();
    }

    <span class="comment">// NEW in 3.2: subquery(EntityType) for correlated subqueries</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersWithMultipleOrders() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery&lt;Customer&gt; cq = cb.createQuery(Customer.class);
        Root&lt;Customer&gt; customer = cq.from(Customer.class);

        <span class="comment">// Create a correlated subquery using the new subquery(EntityType) method</span>
        Subquery&lt;<span class="predefined-type">Long</span>&gt; subquery = cq.subquery(<span class="predefined-type">Long</span>.class);
        Root&lt;Order&gt; order = subquery.correlate(customer).join(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders</span><span class="delimiter">&quot;</span></span>);
        subquery.select(cb.count(order));

        cq.select(customer)
          .where(cb.greaterThan(subquery, <span class="integer">1L</span>));

        <span class="keyword">return</span> em.createQuery(cq).getResultList();
    }

    <span class="comment">// NEW in 3.2: Joins on EntityType</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersWithRecentOrders(Instant since) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery&lt;Customer&gt; cq = cb.createQuery(Customer.class);
        Root&lt;Customer&gt; customer = cq.from(Customer.class);

        <span class="comment">// Join directly on EntityType</span>
        Join&lt;Customer, Order&gt; orders = customer.join(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders</span><span class="delimiter">&quot;</span></span>);

        cq.select(customer)
          .where(cb.greaterThanOrEqualTo(orders.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">orderDate</span><span class="delimiter">&quot;</span></span>), since))
          .distinct(<span class="predefined-constant">true</span>);

        <span class="keyword">return</span> em.createQuery(cq).getResultList();
    }
}

<span class="comment">// NEW in 3.2: Advanced Query Operators</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">AdvancedQueryRepository</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="comment">// NEW in 3.2: INTERSECT operator</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findCustomersInBothActiveAndVIP() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.status = 'ACTIVE' </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">INTERSECT </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.vipLevel &gt; 5</span><span class="delimiter">&quot;</span></span>,
            Customer.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: EXCEPT operator (set difference)</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Customer&gt; findActiveCustomersExceptVIP() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.status = 'ACTIVE' </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">EXCEPT </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c WHERE c.vipLevel &gt; 5</span><span class="delimiter">&quot;</span></span>,
            Customer.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: CAST operator for type conversion</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getCustomerIdsAsStrings() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT CAST(c.id AS STRING) FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">String</span>.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: LEFT and RIGHT string functions</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getEmailPrefixes() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT LEFT(c.email, 5) FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">String</span>.class
        ).getResultList();
    }

    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; getEmailDomains() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT RIGHT(c.email, 10) FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">String</span>.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: REPLACE function for string manipulation</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; normalizePhoneNumbers() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT REPLACE(c.phone, '-', '') FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">String</span>.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: id() function to access entity identifier</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Long</span>&gt; getCustomerIds() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT id(c) FROM Customer c WHERE c.status = 'ACTIVE'</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">Long</span>.class
        ).getResultList();
    }

    <span class="comment">// NEW in 3.2: version() function to access entity version</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Object</span><span class="type">[]</span>&gt; getCustomerVersionInfo() {
        <span class="keyword">return</span> em.createQuery(
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c.name, version(c) FROM Customer c</span><span class="delimiter">&quot;</span></span>,
            <span class="predefined-type">Object</span><span class="type">[]</span>.class
        ).getResultList();
    }
}

<span class="comment">// NEW in 3.2: NamedNativeQuery with entities(), classes(), and columns()</span>
<span class="annotation">@Entity</span>
<span class="annotation">@Table</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">products</span><span class="delimiter">&quot;</span></span>)
<span class="annotation">@NamedNativeQuery</span>(
    name = <span class="string"><span class="delimiter">&quot;</span><span class="content">Product.findWithDetails</span><span class="delimiter">&quot;</span></span>,
    query = <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT p.id, p.name, p.price, c.name as category_name </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">FROM products p JOIN categories c ON p.category_id = c.id</span><span class="delimiter">&quot;</span></span>,
    resultSetMapping = <span class="string"><span class="delimiter">&quot;</span><span class="content">ProductDetailsMapping</span><span class="delimiter">&quot;</span></span>
)
<span class="annotation">@SqlResultSetMapping</span>(
    name = <span class="string"><span class="delimiter">&quot;</span><span class="content">ProductDetailsMapping</span><span class="delimiter">&quot;</span></span>,
    entities = <span class="annotation">@EntityResult</span>(
        entityClass = Product.class,
        fields = {
            <span class="annotation">@FieldResult</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>, column = <span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>),
            <span class="annotation">@FieldResult</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>, column = <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>),
            <span class="annotation">@FieldResult</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">price</span><span class="delimiter">&quot;</span></span>, column = <span class="string"><span class="delimiter">&quot;</span><span class="content">price</span><span class="delimiter">&quot;</span></span>)
        },
        <span class="comment">// NEW in 3.2: lockMode() on EntityResult</span>
        lockMode = LockModeType.OPTIMISTIC
    ),
    columns = <span class="annotation">@ColumnResult</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">category_name</span><span class="delimiter">&quot;</span></span>, type = <span class="predefined-type">String</span>.class)
)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Product</span> {

    <span class="annotation">@Id</span>
    <span class="annotation">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;

    <span class="directive">private</span> <span class="predefined-type">String</span> name;
    <span class="directive">private</span> <span class="predefined-type">Double</span> price;

    <span class="annotation">@Version</span>
    <span class="directive">private</span> <span class="predefined-type">Long</span> version;

    <span class="annotation">@ManyToOne</span>
    <span class="annotation">@JoinColumn</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">category_id</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> Category category;

    <span class="comment">// Constructors, getters, setters</span>
}

<span class="annotation">@Entity</span>
<span class="annotation">@Table</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">categories</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Category</span> {

    <span class="annotation">@Id</span>
    <span class="annotation">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;

    <span class="directive">private</span> <span class="predefined-type">String</span> name;

    <span class="annotation">@OneToMany</span>(mappedBy = <span class="string"><span class="delimiter">&quot;</span><span class="content">category</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> <span class="predefined-type">List</span>&lt;Product&gt; products;

    <span class="comment">// Constructors, getters, setters</span>
}

<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProductRepository</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="comment">// NEW in 3.2: Using NamedNativeQuery with enhanced result mapping</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;<span class="predefined-type">Object</span><span class="type">[]</span>&gt; findProductsWithDetails() {
        <span class="keyword">return</span> em.createNamedQuery(<span class="string"><span class="delimiter">&quot;</span><span class="content">Product.findWithDetails</span><span class="delimiter">&quot;</span></span>)
                 .getResultList();
    }

    <span class="comment">// NEW in 3.2: Combining multiple new features in one query</span>
    <span class="comment">// Demonstrates: CAST, LEFT, REPLACE, ||, UNION, NULLS LAST, id()</span>
    <span class="directive">public</span> <span class="predefined-type">List</span>&lt;Product&gt; findFeaturedProducts(<span class="predefined-type">String</span> categoryPrefix) {
        <span class="keyword">return</span> em.createQuery(
            <span class="comment">// First set: Premium products with name manipulation</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT p FROM Product p </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">WHERE CAST(p.price AS STRING) LIKE '%.99' </span><span class="delimiter">&quot;</span></span> +  <span class="comment">// CAST operator</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">AND LEFT(p.category.name, 3) = :prefix </span><span class="delimiter">&quot;</span></span> +     <span class="comment">// LEFT function</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">AND REPLACE(p.name, '-', ' ') IS NOT NULL </span><span class="delimiter">&quot;</span></span> +  <span class="comment">// REPLACE function</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">UNION </span><span class="delimiter">&quot;</span></span> +                                       <span class="comment">// UNION operator</span>
            <span class="comment">// Second set: Discounted products with concatenation</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT p FROM Product p </span><span class="delimiter">&quot;</span></span> +
            <span class="string"><span class="delimiter">&quot;</span><span class="content">WHERE (p.name || ' - ' || p.category.name) LIKE '%Sale%' </span><span class="delimiter">&quot;</span></span> +  <span class="comment">// || concatenation</span>
            <span class="string"><span class="delimiter">&quot;</span><span class="content">ORDER BY p.price NULLS LAST, id(p)</span><span class="delimiter">&quot;</span></span>,           <span class="comment">// NULLS LAST + id() function</span>
            Product.class
        )
        .setParameter(<span class="string"><span class="delimiter">&quot;</span><span class="content">prefix</span><span class="delimiter">&quot;</span></span>, categoryPrefix)
        .getResultList();  <span class="comment">// Returns list of all matching products</span>
    }


<span class="comment">// NEW in 3.2: Transaction Helpers</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">TransactionHelperExample</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="comment">// NEW in 3.2: runInTransaction() - Execute code within a transaction</span>
    <span class="directive">public</span> <span class="type">void</span> processOrderWithTransaction(Order order) {
        em.runInTransaction(entityManager -&gt; {
            <span class="comment">// All operations here run in a transaction</span>
            entityManager.persist(order);

            <span class="comment">// Update inventory</span>
            <span class="keyword">for</span> (OrderItem item : order.getItems()) {
                Product product = entityManager.find(Product.class, item.getProductId());
                product.setStockQuantity(product.getStockQuantity() - item.getQuantity());
                entityManager.merge(product);
            }

            <span class="comment">// No need to explicitly commit - handled automatically</span>
        });
    }

    <span class="comment">// NEW in 3.2: callInTransaction() - Execute code and return a result</span>
    <span class="directive">public</span> Order createOrderWithTransaction(OrderRequest request) {
        <span class="keyword">return</span> em.callInTransaction(entityManager -&gt; {
            Order order = <span class="keyword">new</span> Order();
            order.setCustomerId(request.getCustomerId());
            order.setItems(request.getItems());
            order.setTotal(calculateTotal(request.getItems()));

            entityManager.persist(order);
            <span class="keyword">return</span> order;  <span class="comment">// Return value from transaction</span>
        });
    }

    <span class="directive">private</span> <span class="type">double</span> calculateTotal(<span class="predefined-type">List</span>&lt;OrderItem&gt; items) {
        <span class="keyword">return</span> items.stream()
                   .mapToDouble(item -&gt; item.getPrice() * item.getQuantity())
                   .sum();
    }
}

<span class="comment">// NEW in 3.2: EntityManager Connection Access</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">ConnectionAccessExample</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="comment">// NEW in 3.2: runWithConnection() - Direct JDBC access</span>
    <span class="directive">public</span> <span class="type">void</span> executeBatchUpdate(<span class="predefined-type">List</span>&lt;<span class="predefined-type">Long</span>&gt; productIds) {
        em.runWithConnection(connection -&gt; {
            <span class="predefined-type">String</span> sql = <span class="string"><span class="delimiter">&quot;</span><span class="content">UPDATE products SET last_updated = ? WHERE id = ?</span><span class="delimiter">&quot;</span></span>;
            <span class="keyword">try</span> (<span class="predefined-type">PreparedStatement</span> stmt = connection.prepareStatement(sql)) {
                Instant now = Instant.now();
                <span class="keyword">for</span> (<span class="predefined-type">Long</span> id : productIds) {
                    stmt.setObject(<span class="integer">1</span>, now);
                    stmt.setLong(<span class="integer">2</span>, id);
                    stmt.addBatch();
                }
                stmt.executeBatch();
            }
        });
    }

    <span class="comment">// NEW in 3.2: callWithConnection() - Direct JDBC access with return value</span>
    <span class="directive">public</span> <span class="type">int</span> getProductCount() {
        <span class="keyword">return</span> em.callWithConnection(connection -&gt; {
            <span class="predefined-type">String</span> sql = <span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT COUNT(*) FROM products WHERE active = true</span><span class="delimiter">&quot;</span></span>;
            <span class="keyword">try</span> (<span class="predefined-type">PreparedStatement</span> stmt = connection.prepareStatement(sql);
                 <span class="predefined-type">ResultSet</span> rs = stmt.executeQuery()) {
                <span class="keyword">if</span> (rs.next()) {
                    <span class="keyword">return</span> rs.getInt(<span class="integer">1</span>);
                }
                <span class="keyword">return</span> <span class="integer">0</span>;
            }
        });
    }
}

<span class="comment">// NEW in 3.2: Programmatic Configuration API</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">ProgrammaticConfigurationExample</span> {

    <span class="directive">public</span> EntityManagerFactory createEntityManagerFactory() {
        <span class="comment">// NEW in 3.2: PersistenceConfiguration for programmatic setup</span>
        PersistenceConfiguration config = <span class="keyword">new</span> PersistenceConfiguration(<span class="string"><span class="delimiter">&quot;</span><span class="content">myPersistenceUnit</span><span class="delimiter">&quot;</span></span>);

        config.provider(<span class="string"><span class="delimiter">&quot;</span><span class="content">org.hibernate.jpa.HibernatePersistenceProvider</span><span class="delimiter">&quot;</span></span>)
              .jtaDataSource(<span class="string"><span class="delimiter">&quot;</span><span class="content">java:comp/env/jdbc/MyDataSource</span><span class="delimiter">&quot;</span></span>)
              .managedClass(Product.class)
              .managedClass(Category.class)
              .managedClass(Order.class)
              .property(<span class="string"><span class="delimiter">&quot;</span><span class="content">hibernate.dialect</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">org.hibernate.dialect.PostgreSQLDialect</span><span class="delimiter">&quot;</span></span>)
              .property(<span class="string"><span class="delimiter">&quot;</span><span class="content">hibernate.show_sql</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span>)
              .property(<span class="string"><span class="delimiter">&quot;</span><span class="content">hibernate.format_sql</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span>);

        <span class="keyword">return</span> Persistence.createEntityManagerFactory(config);
    }
}

<span class="comment">// NEW in 3.2: Schema Management API</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">SchemaManagementExample</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="directive">public</span> <span class="type">void</span> manageSchema() {
        <span class="comment">// NEW in 3.2: SchemaManager for schema operations</span>
        SchemaManager schemaManager = em.getSchemaManager();

        <span class="comment">// Create schema</span>
        schemaManager.create(<span class="predefined-constant">false</span>);  <span class="comment">// false = don't drop existing</span>

        <span class="comment">// Validate schema</span>
        schemaManager.validate();

        <span class="comment">// Truncate all tables (useful for testing)</span>
        schemaManager.truncate();

        <span class="comment">// Drop schema</span>
        <span class="comment">// schemaManager.drop();</span>
    }
}

<span class="comment">// NEW in 3.2: Enum Mapping Enhancements with @EnumeratedValue</span>
<span class="directive">public</span> <span class="type">enum</span> OrderStatus {

    PENDING(<span class="string"><span class="delimiter">&quot;</span><span class="content">P</span><span class="delimiter">&quot;</span></span>),
    CONFIRMED(<span class="string"><span class="delimiter">&quot;</span><span class="content">C</span><span class="delimiter">&quot;</span></span>),
    SHIPPED(<span class="string"><span class="delimiter">&quot;</span><span class="content">S</span><span class="delimiter">&quot;</span></span>),
    DELIVERED(<span class="string"><span class="delimiter">&quot;</span><span class="content">D</span><span class="delimiter">&quot;</span></span>),
    CANCELLED(<span class="string"><span class="delimiter">&quot;</span><span class="content">X</span><span class="delimiter">&quot;</span></span>);

    <span class="annotation">@EnumeratedValue</span>
    <span class="directive">private</span> <span class="directive">final</span> <span class="predefined-type">String</span> code;

    OrderStatus(<span class="predefined-type">String</span> code) {
        <span class="local-variable">this</span>.code = code;
    }

    <span class="directive">public</span> <span class="predefined-type">String</span> getCode() {
        <span class="keyword">return</span> code;
    }
}

<span class="annotation">@Entity</span>
<span class="annotation">@Table</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">orders</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">Order</span> {

    <span class="annotation">@Id</span>
    <span class="annotation">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;

    <span class="comment">// NEW in 3.2: Custom enum mapping with @EnumeratedValue</span>
    <span class="annotation">@Enumerated</span>(EnumType.STRING)
    <span class="directive">private</span> OrderStatus status;  <span class="comment">// Stored as &quot;P&quot;, &quot;C&quot;, &quot;S&quot;, &quot;D&quot;, &quot;X&quot; in database</span>

    <span class="directive">private</span> Instant orderDate;
    <span class="directive">private</span> <span class="predefined-type">Double</span> total;

    <span class="annotation">@OneToMany</span>(mappedBy = <span class="string"><span class="delimiter">&quot;</span><span class="content">order</span><span class="delimiter">&quot;</span></span>, cascade = CascadeType.ALL)
    <span class="directive">private</span> <span class="predefined-type">List</span>&lt;OrderItem&gt; items;

    <span class="comment">// Constructors, getters, setters</span>
}

<span class="comment">// NEW in 3.2: DDL Generation Enhancements</span>
<span class="annotation">@Entity</span>
<span class="annotation">@Table</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">products</span><span class="delimiter">&quot;</span></span>,
       comment = <span class="string"><span class="delimiter">&quot;</span><span class="content">Product catalog table</span><span class="delimiter">&quot;</span></span>,  <span class="comment">// NEW in 3.2: Table comments</span>
       options = <span class="string"><span class="delimiter">&quot;</span><span class="content">ENGINE=InnoDB DEFAULT CHARSET=utf8mb4</span><span class="delimiter">&quot;</span></span>)  <span class="comment">// NEW in 3.2: Table options</span>
<span class="annotation">@CheckConstraint</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">price_positive</span><span class="delimiter">&quot;</span></span>,
                constraint = <span class="string"><span class="delimiter">&quot;</span><span class="content">price &gt; 0</span><span class="delimiter">&quot;</span></span>)  <span class="comment">// NEW in 3.2: Check constraints</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">Product</span> {

    <span class="annotation">@Id</span>
    <span class="annotation">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
    <span class="directive">private</span> <span class="predefined-type">Long</span> id;

    <span class="annotation">@Column</span>(nullable = <span class="predefined-constant">false</span>, comment = <span class="string"><span class="delimiter">&quot;</span><span class="content">Product name</span><span class="delimiter">&quot;</span></span>)  <span class="comment">// NEW in 3.2: Column comments</span>
    <span class="directive">private</span> <span class="predefined-type">String</span> name;

    <span class="annotation">@Column</span>(nullable = <span class="predefined-constant">false</span>)
    <span class="annotation">@CheckConstraint</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">price_range</span><span class="delimiter">&quot;</span></span>,
                    constraint = <span class="string"><span class="delimiter">&quot;</span><span class="content">price BETWEEN 0.01 AND 999999.99</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> <span class="predefined-type">Double</span> price;

    <span class="annotation">@Column</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">created_at</span><span class="delimiter">&quot;</span></span>, precision = <span class="integer">6</span>)  <span class="comment">// NEW in 3.2: Second precision for timestamps</span>
    <span class="directive">private</span> Instant createdAt;

    <span class="annotation">@Column</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">updated_at</span><span class="delimiter">&quot;</span></span>, precision = <span class="integer">6</span>)
    <span class="directive">private</span> Instant updatedAt;

    <span class="comment">// Constructors, getters, setters</span>
}

<span class="comment">// NEW in 3.2: Named Query Factory Access</span>
<span class="annotation">@Stateless</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">NamedQueryFactoryExample</span> {

    <span class="annotation">@PersistenceContext</span>
    <span class="directive">private</span> EntityManager em;

    <span class="directive">public</span> <span class="type">void</span> demonstrateNamedQueryFactory() {
        <span class="comment">// NEW in 3.2: Factory-based named query access</span>
        EntityManagerFactory emf = em.getEntityManagerFactory();

        <span class="comment">// Get named query from factory</span>
        TypedQuery&lt;Customer&gt; query = emf.createNamedQuery(<span class="string"><span class="delimiter">&quot;</span><span class="content">Customer.findByEmail</span><span class="delimiter">&quot;</span></span>, Customer.class);
        query.setParameter(<span class="string"><span class="delimiter">&quot;</span><span class="content">email</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">user@example.com</span><span class="delimiter">&quot;</span></span>);
        Customer customer = query.getSingleResultOrNull();

        <span class="comment">// Get named entity graph from factory</span>
        EntityGraph&lt;Customer&gt; graph = emf.createEntityGraph(Customer.class);
        graph.addAttributeNodes(<span class="string"><span class="delimiter">&quot;</span><span class="content">orders</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">address</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// Use the graph in a query</span>
        <span class="predefined-type">List</span>&lt;Customer&gt; customers = em.createQuery(<span class="string"><span class="delimiter">&quot;</span><span class="content">SELECT c FROM Customer c</span><span class="delimiter">&quot;</span></span>, Customer.class)
                                     .setHint(<span class="string"><span class="delimiter">&quot;</span><span class="content">jakarta.persistence.fetchgraph</span><span class="delimiter">&quot;</span></span>, graph)
                                     .getResultList();
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="pages">Pages 4.0</h3>
<div class="paragraph">
<p>Jakarta Pages defines a template engine for web applications. This release removes deprecated code and provides updates necessary to align with changes in the Jakarta Servlet and Expression Language specifications.</p>
</div>
<div class="paragraph">
<p><strong>New features and enhancements in Pages 4.0:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Enhanced ErrorData</strong>: Updated <a href="https://jakarta.ee/specifications/pages/4.0/apidocs/jakarta.servlet.jsp/jakarta/servlet/jsp/errordata"><code>ErrorData</code></a> to add support for new servlet error attributes:</p>
</li>
<li>
<p><code>jakarta.servlet.error.query_string</code> - Access the query string from error pages</p>
</li>
<li>
<p><code>jakarta.servlet.error.method</code> - Access the HTTP method from error pages</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals and deprecations:</strong></p>
</div>
<div class="paragraph">
<p>All code deprecated as of Jakarta Server Pages 3.1 has been removed:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>ELResolver methods</strong>: Removed methods that override <a href="https://jakarta.ee/specifications/expression-language/6.0/apidocs/jakarta.el/jakarta/el/elresolver#getFeatureDescriptors()"><code>ELResolver.getFeatureDescriptors()</code></a> as that method is removed in EL 6.0</p>
</li>
<li>
<p><strong>isThreadSafe directive</strong>: Removed the <code>isThreadSafe</code> page directive attribute as the related Servlet API interface <code>SingleThreadModel</code> has been removed in Servlet 6.0</p>
</li>
<li>
<p><strong>jsp:plugin action</strong>: Removed the <code>jsp:plugin</code> action and related actions as the associated HTML elements are no longer supported by any major browser</p>
</li>
<li>
<p><strong>JspException.getRootCause()</strong>: Removed deprecated <a href="https://jakarta.ee/specifications/pages/3.1/apidocs/jakarta.servlet.jsp/jakarta/servlet/jsp/jspexception#getRootCause()"><code>JspException.getRootCause()</code></a>method</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="enhanced-error-handling-with-errordata">Enhanced Error Handling with ErrorData</h4>
<div class="paragraph">
<p>Jakarta Pages 4.0 adds new error attributes to <a href="https://jakarta.ee/specifications/pages/4.0/apidocs/jakarta.servlet.jsp/jakarta/servlet/jsp/errordata"><code>ErrorData</code></a> for better error page diagnostics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="jsp">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; isErrorPage=&quot;true&quot; %&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Error Page - Jakarta Pages 4.0&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;An Error Occurred&lt;/h1&gt;

    &lt;!-- Standard error attributes --&gt;
    &lt;p&gt;&lt;strong&gt;Status Code:&lt;/strong&gt; ${pageContext.errorData.statusCode}&lt;/p&gt;
    &lt;p&gt;&lt;strong&gt;Request URI:&lt;/strong&gt; ${pageContext.errorData.requestURI}&lt;/p&gt;
    &lt;p&gt;&lt;strong&gt;Servlet Name:&lt;/strong&gt; ${pageContext.errorData.servletName}&lt;/p&gt;

    &lt;!-- NEW in Pages 4.0: Access HTTP method that caused the error --&gt;
    &lt;p&gt;&lt;strong&gt;HTTP Method:&lt;/strong&gt; ${pageContext.request.getAttribute('jakarta.servlet.error.method')}&lt;/p&gt;

    &lt;!-- NEW in Pages 4.0: Access query string from the error request --&gt;
    &lt;p&gt;&lt;strong&gt;Query String:&lt;/strong&gt; ${pageContext.request.getAttribute('jakarta.servlet.error.query_string')}&lt;/p&gt;

    &lt;!-- Exception details if available --&gt;
    &lt;c:if test=&quot;${not empty pageContext.errorData.throwable}&quot;&gt;
        &lt;h2&gt;Exception Details&lt;/h2&gt;
        &lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; ${pageContext.errorData.throwable.class.name}&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Message:&lt;/strong&gt; ${pageContext.errorData.throwable.message}&lt;/p&gt;
    &lt;/c:if&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="websocket">WebSocket 2.2</h3>
<div class="paragraph">
<p>Jakarta WebSocket defines an API for Server and Client Endpoints for the WebSocket protocol (RFC6455). This release removes references to the SecurityManager and provides some minor updates and clarifications.</p>
</div>
<div class="paragraph">
<p><strong>New features in WebSocket 2.2:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Clarified ping/pong responsibilities</strong>: The specification now clearly defines the responsibilities for sending ping and pong messages between client and server</p>
</li>
<li>
<p><strong>New <code>getSession()</code> method</strong>: Added <code>getSession()</code> method to <code>SendResult</code> class to retrieve the session associated with a send operation</p>
</li>
<li>
<p><strong>Clarified <code>maxMessageSize</code> behavior</strong>: Clarified the behavior when <code>@OnMessage.maxMessageSize</code> is set to a value larger than <code>Integer.MAX_VALUE</code></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Removals:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>All references to the SecurityManager have been removed</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="using-the-new-getsession-method-in-sendresult">Using the new getSession() method in SendResult</h4>
<div class="paragraph">
<p>The new <code>getSession()</code> method allows you to retrieve the WebSocket session associated with a send operation, which is particularly useful when handling asynchronous message sending:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.websocket</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.websocket.server.ServerEndpoint</span>;
<span class="keyword">import</span> <span class="include">jakarta.websocket.server.PathParam</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.util.Set</span>;
<span class="keyword">import</span> <span class="include">java.util.concurrent.CopyOnWriteArraySet</span>;
<span class="keyword">import</span> <span class="include">java.util.concurrent.Future</span>;

<span class="annotation">@ServerEndpoint</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/notifications/{userId}</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">NotificationWebSocket</span> {

    <span class="directive">private</span> <span class="directive">static</span> <span class="directive">final</span> <span class="predefined-type">Set</span>&lt;Session&gt; sessions = <span class="keyword">new</span> <span class="predefined-type">CopyOnWriteArraySet</span>&lt;&gt;();

    <span class="annotation">@OnOpen</span>
    <span class="directive">public</span> <span class="type">void</span> onOpen(Session session, <span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> userId) {
        sessions.add(session);
        session.getUserProperties().put(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>, userId);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">User </span><span class="delimiter">&quot;</span></span> + userId + <span class="string"><span class="delimiter">&quot;</span><span class="content"> connected</span><span class="delimiter">&quot;</span></span>);
    }

    <span class="annotation">@OnMessage</span>
    <span class="directive">public</span> <span class="type">void</span> onMessage(<span class="predefined-type">String</span> message, Session session) {
        <span class="predefined-type">String</span> userId = (<span class="predefined-type">String</span>) session.getUserProperties().get(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Received message from </span><span class="delimiter">&quot;</span></span> + userId + <span class="string"><span class="delimiter">&quot;</span><span class="content">: </span><span class="delimiter">&quot;</span></span> + message);

        <span class="comment">// Echo the message back</span>
        sendAsyncMessage(session, <span class="string"><span class="delimiter">&quot;</span><span class="content">Echo: </span><span class="delimiter">&quot;</span></span> + message);
    }

    <span class="annotation">@OnClose</span>
    <span class="directive">public</span> <span class="type">void</span> onClose(Session session, <span class="annotation">@PathParam</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> userId) {
        sessions.remove(session);
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">User </span><span class="delimiter">&quot;</span></span> + userId + <span class="string"><span class="delimiter">&quot;</span><span class="content"> disconnected</span><span class="delimiter">&quot;</span></span>);
    }

    <span class="annotation">@OnError</span>
    <span class="directive">public</span> <span class="type">void</span> onError(Session session, <span class="predefined-type">Throwable</span> throwable) {
        <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">WebSocket error: </span><span class="delimiter">&quot;</span></span> + throwable.getMessage());
        throwable.printStackTrace();
    }

    <span class="comment">// Demonstrates the new getSession() method in SendResult</span>
    <span class="directive">private</span> <span class="type">void</span> sendAsyncMessage(Session session, <span class="predefined-type">String</span> message) {
        <span class="keyword">try</span> {
            RemoteEndpoint.Async asyncRemote = session.getAsyncRemote();
            <span class="predefined-type">Future</span>&lt;<span class="predefined-type">Void</span>&gt; future = asyncRemote.sendText(message);

            <span class="comment">// Add a callback to handle the send result</span>
            future.get(); <span class="comment">// Wait for completion</span>

            <span class="comment">// In a real application, you might use a SendHandler instead</span>
            asyncRemote.sendText(message, <span class="keyword">new</span> SendHandler() {
                <span class="annotation">@Override</span>
                <span class="directive">public</span> <span class="type">void</span> onResult(SendResult result) {
                    <span class="comment">// NEW in WebSocket 2.2: getSession() method</span>
                    Session resultSession = result.getSession();

                    <span class="keyword">if</span> (result.isOK()) {
                        <span class="predefined-type">String</span> userId = (<span class="predefined-type">String</span>) resultSession.getUserProperties().get(<span class="string"><span class="delimiter">&quot;</span><span class="content">userId</span><span class="delimiter">&quot;</span></span>);
                        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Message sent successfully to user: </span><span class="delimiter">&quot;</span></span> + userId);
                    } <span class="keyword">else</span> {
                        <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Failed to send message to session: </span><span class="delimiter">&quot;</span></span> +
                            resultSession.getId());
                        <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Error: </span><span class="delimiter">&quot;</span></span> + result.getException().getMessage());
                    }
                }
            });
        } <span class="keyword">catch</span> (<span class="exception">Exception</span> e) {
            <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Error sending async message: </span><span class="delimiter">&quot;</span></span> + e.getMessage());
        }
    }

    <span class="comment">// Broadcast message to all connected sessions</span>
    <span class="directive">public</span> <span class="type">void</span> broadcastToAll(<span class="predefined-type">String</span> message) {
        sessions.forEach(session -&gt; {
            <span class="keyword">if</span> (session.isOpen()) {
                session.getAsyncRemote().sendText(message, <span class="keyword">new</span> SendHandler() {
                    <span class="annotation">@Override</span>
                    <span class="directive">public</span> <span class="type">void</span> onResult(SendResult result) {
                        <span class="comment">// Use the new getSession() method to identify which session</span>
                        Session resultSession = result.getSession();
                        <span class="keyword">if</span> (!result.isOK()) {
                            <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Failed to broadcast to session: </span><span class="delimiter">&quot;</span></span> +
                                resultSession.getId());
                        }
                    }
                });
            }
        });
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="pingpong-message-handling">Ping/Pong Message Handling</h4>
<div class="paragraph">
<p>WebSocket 2.2 clarifies the responsibilities for ping and pong messages:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.websocket</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.websocket.server.ServerEndpoint</span>;
<span class="keyword">import</span> <span class="include">java.io.IOException</span>;
<span class="keyword">import</span> <span class="include">java.nio.ByteBuffer</span>;

<span class="annotation">@ServerEndpoint</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/ping-pong</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">PingPongWebSocket</span> {

    <span class="annotation">@OnOpen</span>
    <span class="directive">public</span> <span class="type">void</span> onOpen(Session session) {
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">WebSocket opened: </span><span class="delimiter">&quot;</span></span> + session.getId());

        <span class="comment">// Send a ping message to the client</span>
        <span class="keyword">try</span> {
            session.getBasicRemote().sendPing(<span class="predefined-type">ByteBuffer</span>.wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">ping</span><span class="delimiter">&quot;</span></span>.getBytes()));
        } <span class="keyword">catch</span> (<span class="exception">IOException</span> e) {
            e.printStackTrace();
        }
    }

    <span class="annotation">@OnMessage</span>
    <span class="directive">public</span> <span class="type">void</span> onPongMessage(PongMessage pong, Session session) {
        <span class="comment">// Handle pong messages received from the client</span>
        <span class="predefined-type">ByteBuffer</span> data = pong.getApplicationData();
        <span class="type">byte</span><span class="type">[]</span> bytes = <span class="keyword">new</span> <span class="type">byte</span>[data.remaining()];
        data.get(bytes);
        <span class="predefined-type">String</span> pongData = <span class="keyword">new</span> <span class="predefined-type">String</span>(bytes);

        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Received pong from </span><span class="delimiter">&quot;</span></span> + session.getId() + <span class="string"><span class="delimiter">&quot;</span><span class="content">: </span><span class="delimiter">&quot;</span></span> + pongData);
    }

    <span class="annotation">@OnMessage</span>
    <span class="directive">public</span> <span class="type">void</span> onTextMessage(<span class="predefined-type">String</span> message, Session session) {
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">Received text message: </span><span class="delimiter">&quot;</span></span> + message);

        <span class="comment">// Send a pong message in response</span>
        <span class="keyword">try</span> {
            session.getBasicRemote().sendPong(<span class="predefined-type">ByteBuffer</span>.wrap(<span class="string"><span class="delimiter">&quot;</span><span class="content">pong</span><span class="delimiter">&quot;</span></span>.getBytes()));
        } <span class="keyword">catch</span> (<span class="exception">IOException</span> e) {
            e.printStackTrace();
        }
    }

    <span class="annotation">@OnClose</span>
    <span class="directive">public</span> <span class="type">void</span> onClose(Session session) {
        <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">WebSocket closed: </span><span class="delimiter">&quot;</span></span> + session.getId());
    }

    <span class="annotation">@OnError</span>
    <span class="directive">public</span> <span class="type">void</span> onError(Session session, <span class="predefined-type">Throwable</span> throwable) {
        <span class="predefined-type">System</span>.err.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">WebSocket error: </span><span class="delimiter">&quot;</span></span> + throwable.getMessage());
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="validation">Validation 3.1</h3>
<div class="paragraph">
<p>Jakarta Validation defines a metadata model and API for JavaBean and method validation. This release has clarified support for Records introduced by JEP 395.</p>
</div>
<div class="paragraph">
<p><strong>Key changes in Validation 3.1:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>Clarify Java Records support for validation</p>
</li>
<li>
<p>Update dependencies for Jakarta EE 11</p>
</li>
<li>
<p>No removals, deprecations, or backwards incompatible changes</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="keyword">import</span> <span class="include">jakarta.validation.constraints</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.validation.Constraint</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.ConstraintValidator</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.ConstraintValidatorContext</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.Payload</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.Valid</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.Validator</span>;
<span class="keyword">import</span> <span class="include">jakarta.validation.ConstraintViolation</span>;
<span class="keyword">import</span> <span class="include">jakarta.inject.Inject</span>;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs</span>.*;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core.MediaType</span>;
<span class="keyword">import</span> <span class="include">jakarta.ws.rs.core.Response</span>;
<span class="keyword">import</span> <span class="include">java.lang.annotation</span>.*;
<span class="keyword">import</span> <span class="include">java.time.LocalDate</span>;
<span class="keyword">import</span> <span class="include">java.util.List</span>;
<span class="keyword">import</span> <span class="include">java.util.Set</span>;
<span class="keyword">import</span> <span class="include">java.util.stream.Collectors</span>;

<span class="comment">// NEW in 3.1: Java Record with validation support</span>
<span class="directive">public</span> record UserProfile(
    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Size</span>(min = <span class="integer">3</span>, max = <span class="integer">20</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username must be between 3 and 20 characters</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Pattern</span>(regexp = <span class="string"><span class="delimiter">&quot;</span><span class="content">^[a-zA-Z0-9_]+$</span><span class="delimiter">&quot;</span></span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username can only contain letters, numbers, and underscores</span><span class="delimiter">&quot;</span></span>)
    <span class="predefined-type">String</span> username,

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Email is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Email</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Invalid email format</span><span class="delimiter">&quot;</span></span>)
    <span class="predefined-type">String</span> email,

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Age is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Min</span>(value = <span class="integer">18</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Must be at least 18 years old</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Max</span>(value = <span class="integer">120</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Age must be realistic</span><span class="delimiter">&quot;</span></span>)
    <span class="predefined-type">Integer</span> age
) {}

<span class="comment">// Traditional class with validation</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">UserRegistration</span> {

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Size</span>(min = <span class="integer">3</span>, max = <span class="integer">20</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username must be between 3 and 20 characters</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Pattern</span>(regexp = <span class="string"><span class="delimiter">&quot;</span><span class="content">^[a-zA-Z0-9_]+$</span><span class="delimiter">&quot;</span></span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Username can only contain letters, numbers, and underscores</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> <span class="predefined-type">String</span> username;

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Email is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Email</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Invalid email format</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> <span class="predefined-type">String</span> email;

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Password is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Size</span>(min = <span class="integer">8</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Password must be at least 8 characters</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@StrongPassword</span>
    <span class="directive">private</span> <span class="predefined-type">String</span> password;

    <span class="annotation">@NotNull</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Age is required</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Min</span>(value = <span class="integer">18</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Must be at least 18 years old</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Max</span>(value = <span class="integer">120</span>, message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Age must be realistic</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> <span class="predefined-type">Integer</span> age;

    <span class="annotation">@Future</span>(message = <span class="string"><span class="delimiter">&quot;</span><span class="content">Subscription end date must be in the future</span><span class="delimiter">&quot;</span></span>)
    <span class="directive">private</span> LocalDate subscriptionEndDate;

    <span class="comment">// Getters and setters</span>
}

<span class="comment">// Custom validation annotation</span>
<span class="annotation">@Target</span>({<span class="predefined-type">ElementType</span>.FIELD, <span class="predefined-type">ElementType</span>.PARAMETER})
<span class="annotation">@Retention</span>(<span class="predefined-type">RetentionPolicy</span>.RUNTIME)
<span class="annotation">@Constraint</span>(validatedBy = StrongPasswordValidator.class)
<span class="annotation">@Documented</span>
<span class="directive">public</span> <span class="annotation">@interface</span> StrongPassword {
    <span class="predefined-type">String</span> message() <span class="keyword">default</span> <span class="string"><span class="delimiter">&quot;</span><span class="content">Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character</span><span class="delimiter">&quot;</span></span>;
    <span class="predefined-type">Class</span>&lt;?&gt;<span class="type">[]</span> groups() <span class="keyword">default</span> {};
    <span class="predefined-type">Class</span>&lt;? <span class="directive">extends</span> Payload&gt;<span class="type">[]</span> payload() <span class="keyword">default</span> {};
}

<span class="comment">// Custom validator implementation</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">StrongPasswordValidator</span>
        <span class="directive">implements</span> ConstraintValidator&lt;StrongPassword, <span class="predefined-type">String</span>&gt; {

    <span class="annotation">@Override</span>
    <span class="directive">public</span> <span class="type">boolean</span> isValid(<span class="predefined-type">String</span> password, ConstraintValidatorContext context) {
        <span class="keyword">if</span> (password == <span class="predefined-constant">null</span>) {
            <span class="keyword">return</span> <span class="predefined-constant">false</span>;
        }

        <span class="type">boolean</span> hasUppercase = password.chars().anyMatch(<span class="predefined-type">Character</span>::isUpperCase);
        <span class="type">boolean</span> hasLowercase = password.chars().anyMatch(<span class="predefined-type">Character</span>::isLowerCase);
        <span class="type">boolean</span> hasDigit = password.chars().anyMatch(<span class="predefined-type">Character</span>::isDigit);
        <span class="type">boolean</span> hasSpecial = password.chars()
            .anyMatch(ch -&gt; <span class="string"><span class="delimiter">&quot;</span><span class="content">!@#$%^&amp;*()_+-=[]{}|;:,.&lt;&gt;?</span><span class="delimiter">&quot;</span></span>.indexOf(ch) &gt;= <span class="integer">0</span>);

        <span class="keyword">return</span> hasUppercase &amp;&amp; hasLowercase &amp;&amp; hasDigit &amp;&amp; hasSpecial;
    }
}

<span class="comment">// Using validation in a REST endpoint</span>
<span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/users</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">UserResource</span> {

    <span class="annotation">@Inject</span>
    <span class="directive">private</span> <span class="predefined-type">Validator</span> validator;

    <span class="annotation">@POST</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/register</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Consumes</span>(MediaType.APPLICATION_JSON)
    <span class="directive">public</span> Response registerUser(<span class="annotation">@Valid</span> UserRegistration user) {
        <span class="comment">// Validation happens automatically via @Valid</span>
        <span class="comment">// If validation fails, a ConstraintViolationException is thrown</span>

        <span class="comment">// Process registration</span>
        <span class="keyword">return</span> Response.status(Response.Status.CREATED)
                      .entity(<span class="string"><span class="delimiter">&quot;</span><span class="content">User registered successfully</span><span class="delimiter">&quot;</span></span>)
                      .build();
    }

    <span class="comment">// Manual validation example</span>
    <span class="annotation">@POST</span>
    <span class="annotation">@Path</span>(<span class="string"><span class="delimiter">&quot;</span><span class="content">/validate</span><span class="delimiter">&quot;</span></span>)
    <span class="annotation">@Consumes</span>(MediaType.APPLICATION_JSON)
    <span class="directive">public</span> Response validateUser(UserRegistration user) {
        <span class="predefined-type">Set</span>&lt;ConstraintViolation&lt;UserRegistration&gt;&gt; violations =
            validator.validate(user);

        <span class="keyword">if</span> (!violations.isEmpty()) {
            <span class="predefined-type">List</span>&lt;<span class="predefined-type">String</span>&gt; errors = violations.stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());

            <span class="keyword">return</span> Response.status(Response.Status.BAD_REQUEST)
                          .entity(errors)
                          .build();
        }

        <span class="keyword">return</span> Response.ok(<span class="string"><span class="delimiter">&quot;</span><span class="content">Validation passed</span><span class="delimiter">&quot;</span></span>).build();
    }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="getting-started-with-jakarta-ee-11-on-open-liberty">Getting Started with Jakarta EE 11 on Open Liberty</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To use Jakarta EE 11 features in Open Liberty 26.0.0.5, you can enable the Jakarta EE 11 Platform feature in your <code>server.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;server&gt;</span>
    <span class="tag">&lt;featureManager&gt;</span>
        <span class="tag">&lt;feature&gt;</span>jakartaee-11.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;/featureManager&gt;</span>

    ...
<span class="tag">&lt;/server&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You can also enable individual features as needed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;platform&gt;</span>jakartaee-11.0<span class="tag">&lt;/platform&gt;</span>
    <span class="tag">&lt;feature&gt;</span>servlet<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>cdi<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>persistence<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>faces<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>data<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>websocket<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>validation<span class="tag">&lt;/feature&gt;</span>
<span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="sect2">
<h3 id="maven-dependencies">Maven Dependencies</h3>
<div class="paragraph">
<p>Add Jakarta EE 11 dependencies to your <code>pom.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependencies&gt;</span>
    <span class="comment">&lt;!-- Jakarta EE 11 API --&gt;</span>
    <span class="tag">&lt;dependency&gt;</span>
        <span class="tag">&lt;groupId&gt;</span>jakarta.platform<span class="tag">&lt;/groupId&gt;</span>
        <span class="tag">&lt;artifactId&gt;</span>jakarta.jakartaee-api<span class="tag">&lt;/artifactId&gt;</span>
        <span class="tag">&lt;version&gt;</span>11.0.0<span class="tag">&lt;/version&gt;</span>
        <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
    <span class="tag">&lt;/dependency&gt;</span>

    <span class="comment">&lt;!-- Jakarta Data (if using separately) --&gt;</span>
    <span class="tag">&lt;dependency&gt;</span>
        <span class="tag">&lt;groupId&gt;</span>jakarta.data<span class="tag">&lt;/groupId&gt;</span>
        <span class="tag">&lt;artifactId&gt;</span>jakarta.data-api<span class="tag">&lt;/artifactId&gt;</span>
        <span class="tag">&lt;version&gt;</span>1.0.0<span class="tag">&lt;/version&gt;</span>
        <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
    <span class="tag">&lt;/dependency&gt;</span>
<span class="tag">&lt;/dependencies&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Check out <a href="/blog/2026/05/19/26.0.0.5.html">26.0.0.5 blog</a> to learn more on the full details of Jakarta EE 11 on Open Liberty 26.0.0.5.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="integration-with-microprofile">Integration with MicroProfile</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This Open Liberty release provides a great ecosystem of enabling Jakarta EE 11 working with MicroProfile 7.0 and 7.1, allowing you to take advantage of the latest microservices features. You can enable MicroProfile and Jakarta EE 11 features in your <code>server.xml</code> as needed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>microprofile-7.1<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jakartaee-11.0<span class="tag">&lt;/feature&gt;</span>
<span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="spring-boot-4-0">Spring Boot 4.0</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Open Liberty also enables Spring Boot 4.0 applications, allowing you to leverage the latest features and improvements in the Jakarta EE 11 release. You can enable the Spring Boot 4.0 feature in your <code>server.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>springBoot-4.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>servlet-6.1<span class="tag">&lt;/feature&gt;</span>
<span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="migration-considerations">Migration Considerations</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When migrating from Jakarta EE 10 to Jakarta EE 11, review the updated specifications to understand the changes and enhancements. Most applications should migrate smoothly, but it&#8217;s important to test thoroughly, especially if you&#8217;re using features that have been updated.</p>
</div>
<div class="paragraph">
<p>Key areas to review:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Jakarta Data 1.0</strong>: Consider migrating existing DAO/repository code to Jakarta Data for improved productivity and reduced boilerplate</p>
</li>
<li>
<p><strong>Annotations 3.0</strong>: Migrate from <code>@ManagedBean</code> to CDI beans</p>
</li>
<li>
<p><strong>Authentication 3.1</strong>: Remove any SecurityManager dependencies from authentication modules</p>
</li>
<li>
<p><strong>Authorization 3.0</strong>: Switch to use the new <code>PolicyFactory</code> and <code>Policy</code> interfaces for your authorization modules</p>
</li>
<li>
<p><strong>CDI 4.1</strong>: Review dependency injection configurations, especially if using custom interceptors or producers</p>
</li>
<li>
<p><strong>Servlet 6.1</strong>: Remove any SecurityManager dependencies; test redirect handling and error dispatches</p>
</li>
<li>
<p><strong>Persistence 3.2</strong>: Leverage Java records for embeddable types; update queries to use new operators; test java.time types</p>
</li>
<li>
<p><strong>Validation 3.1</strong>: Leverage Java Records for validated data structures</p>
</li>
<li>
<p><strong>Concurrency 3.1</strong>: Test async operations, especially if using Java 21 Virtual Threads</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Jakarta EE 11 in Open Liberty 26.0.0.5 represents a significant advancement in enterprise Java development. The introduction of Jakarta Data 1.0 alone is a game-changer, dramatically reducing boilerplate code and improving developer productivity. Combined with updates across all major specifications, enhanced performance through Java 21 support, and a comprehensive set of modern APIs, Jakarta EE 11 provides a solid foundation for building cloud-native enterprise applications.</p>
</div>
<div class="paragraph">
<p>The combination of Open Liberty&#8217;s lightweight, fast runtime and Jakarta EE 11&#8217;s powerful features makes this an excellent choice for organizations looking to modernize their enterprise Java applications while maintaining compatibility with industry standards.</p>
</div>
<div class="paragraph">
<p>What&#8217;s more? Jakarta EE 11 also works with MicroProfile 7.0 and 7.1 in Open Liberty, allowing you to take advantage of the latest microservices features alongside the core Jakarta EE platform. Whether you&#8217;re building new applications or migrating existing ones, Open Liberty and Jakarta EE 11 provide the tools and capabilities you need to succeed in today&#8217;s fast-paced development environment.</p>
</div>
<div class="paragraph">
<p>If you are using Spring Boot, Open Liberty 26.0.0.5 and later release also enables you to use Spring Boot 4.0 with Jakarta EE 11 features, providing even more flexibility in how you build your applications.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="acknowledgements">Acknowledgements</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Many thanks to the Jakarta EE community for their hard work in bringing Jakarta EE 11 to life and to the Open Liberty teams for ensuring it runs smoothly on Open Liberty. The contributions from the community, including feedback, testing, and code contributions, have been invaluable in making this release a success. I also want to thank my colleagues in the Open Liberty team who provided valuable feedback to this blog: Andrew Rouse, Anija K A, David Webster, Jared Anderson, Nathan Rauh, Paul Nicolucci, Mark Swatosh, Kyle Aure, Jim Krueger, Neena P Jacob, Volodymyr Siedlecki, Phu Dinh and many others.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="learn-more">Learn More</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://jakarta.ee/specifications/platform/11/">Jakarta EE 11 Specifications</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/data/1.0/">Jakarta Data Specification</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/community/">Jakarta EE Community</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/">Open Liberty Documentation</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty">Open Liberty GitHub</a></p>
</li>
</ul>
</div>
<hr>
<div class="paragraph">
<p><strong>Open Liberty is an open-source, lightweight, and fast Java runtime that implements Jakarta EE and MicroProfile specifications. It&#8217;s designed for cloud-native applications and provides a flexible, modular architecture that allows you to use only the features you need.</strong></p>
</div>
</div>
</div>]]></content><author><name>Emily Jiang</name></author><category term="blog" /><summary type="html"><![CDATA[Jakarta EE 11 marks a significant milestone in the evolution of enterprise Java development. This release delivers enhanced developer productivity, improved performance, and modernized APIs that align with the Java LTS releases: Java SE 17, Java SE 21. For more information, see the Jakarta EE Platform 11 specification.]]></summary></entry><entry><title type="html">Jakarta EE 11, Spring Boot 4.0, and more in 26.0.0.5</title><link href="https://openliberty.io/blog/2026/05/19/26.0.0.5.html" rel="alternate" type="text/html" title="Jakarta EE 11, Spring Boot 4.0, and more in 26.0.0.5" /><published>2026-05-19T00:00:00+00:00</published><updated>2026-05-19T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/05/19/26.0.0.5</id><content type="html" xml:base="https://openliberty.io/blog/2026/05/19/26.0.0.5.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This release introduces official support for Jakarta EE 11, Spring Boot 4.0 applications, and updated TLS/SSL cipher handling in Open Liberty, including enhanced Spring Boot deployment support and simplified SSL cipher configuration.</p>
</div>
<div class="paragraph">
<p>In <a href="/">Open Liberty</a> 26.0.0.5:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#jakarta_ee">Jakarta EE 11 Core Profile, Web Profile, and Platform</a></p>
</li>
<li>
<p><a href="#springboot">Spring Boot 4.0</a></p>
</li>
<li>
<p><a href="#ssl">Update to TLS/SSL Cipher support</a></p>
</li>
<li>
<p><a href="#CVEs">Security Vulnerability (CVE) Fixes</a></p>
</li>
<li>
<p><a href="#bugs">Notable bug fixes</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>View the list of fixed bugs in <a href="https://github.com/OpenLiberty/open-liberty/issues?q=label%3Arelease%3A26005+label%3A%22release+bug%22">26.0.0.5</a>.</p>
</div>
<div class="paragraph">
<p>Check out <a href="/blog/?search=release&amp;search!=beta">previous Open Liberty GA release blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="run">Develop and run your apps using 26.0.0.5</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, include the following in your <code>pom.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>, include the following in your <code>build.gradle</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/start/">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="https://stackoverflow.com/tags/open-liberty"><img src="/img/blog/blog_btn_stack.svg" alt="Ask a question on Stack Overflow"></a>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_ee">Jakarta EE 11 Core Profile, Web Profile, and Platform</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Jakarta EE 11 Core Profile, Web Profile and Platform are now officially supported in Open Liberty! We’d like to start by thanking all those who provided feedback throughout our various betas.</p>
</div>
<div class="paragraph">
<p>Jakarta EE 11 marks a major milestone. It is the first Jakarta release to add a new specification to the platform since Java EE 8 in 2017 and, therefore, the first to provide a new component specification since the platform was taken over by the Eclipse Foundation. Among the many updates to existing Java specifications, it also removes all optional specifications and functions from the Platform. Liberty continues to support those optional specifications and functions when combined with Jakarta EE 11 features.</p>
</div>
<div class="paragraph">
<p>The Core Profile specification was introduced in Jakarta EE 10 to provide a profile for lightweight cloud native applications such as MicroProfile-based applications.  With the introduction of Jakarta EE 11 support in this release, the MicroProfile 7.0 and 7.1 features also now work with Jakarta EE 11. You can run your MicroProfile 7 applications using either Jakarta EE 10 or Jakarta EE 11 features.</p>
</div>
<div class="paragraph">
<p>The following specifications make up the Jakarta Platform and the Core and Web profiles:</p>
</div>
<div class="sect2">
<h3 id="jakarta-ee-core-profile-11">Jakarta EE Core Profile 11</h3>
<table id="core_profile" class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 44.4444%;">
<col style="width: 22.2222%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Specification</th>
<th class="tableblock halign-left valign-top">Updates</th>
<th class="tableblock halign-left valign-top">Liberty Feature Documentation</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/annotations/3.0/">Annotations 3.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/cdi-4.1.html">cdi-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/restful-ws/4.0/">RESTful Web Services 4.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/restfulWS-4.0.html">restfulWS-4.0</a>, <a href="/docs/latest/reference/feature/restfulWSClient-4.0.html">restfulWSClient-4.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/cdi/4.1/">Context and Dependency Injection 4.1 Lite</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/cdi-4.1.html">cdi-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/interceptors/2.2/">Interceptors 2.2</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/cdi-4.1.html">cdi-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/dependency-injection/2.0/">Dependency Injection 2.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/cdi-4.1.html">cdi-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/jsonb/3.0/">JSON Binding 3.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/jsonb-3.0.html">jsonb-3.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/jsonp/2.1/">JSON Processing 2.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/jsonp-2.1.html">jsonp-2.1</a></p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="jakarta-ee-web-profile-11">Jakarta EE Web Profile 11</h3>
<table id="web_profile" class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 44.4444%;">
<col style="width: 22.2222%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Specification</th>
<th class="tableblock halign-left valign-top">Updates</th>
<th class="tableblock halign-left valign-top">Liberty Feature Documentation</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/coreprofile/11/">Jakarta EE Core Profile 11</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major Update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">See previous <a href="#core_profile">table</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/data/1.0/">Data 1.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><strong>New</strong></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/data-1.0.html">data-1.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/expression-language/6.0/">Expression Language 6.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/expressionLanguage-6.0.html">expressionLanguage-6.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/pages/4.0/">Pages 4.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/pages-4.0.html">pages-4.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/security/4.0/">Security 4.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/appSecurity-6.0.html">appSecurity-6.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/authentication/3.1/">Authentication 3.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/appAuthentication-3.1.html">appAuthentication-3.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/concurrency/3.1/">Concurrency 3.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/concurrent-3.1.html">concurrent-3.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/cdi/4.1/">Context and Dependency Injection 4.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/cdi-4.1.html">cdi-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/faces/4.1/">Faces 4.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/faces-4.1.html">faces-4.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/persistence/3.2/">Persistence 3.2</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/persistence-3.2.html">persistence-3.2</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/servlet/6.1/">Servlet 6.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/servlet-6.1.html">servlet-6.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/bean-validation/3.1/">Validation 3.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/validation-3.1.html">validation-3.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/websocket/2.2/">WebSocket 2.2</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minor update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/websocket-2.2.html">websocket-2.2</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/debugging/2.0/">Debugging Support for Other Languages 2.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Not applicable</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/enterprise-beans/4.0/">Enterprise Beans 4.0 Lite</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/enterpriseBeansLite-4.0.html">enterpriseBeansLite-4.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/tags/3.0/">Standard Tag Library 3.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/pages-4.0.html">pages-4.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/transactions/2.0/">Transactions 2.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Not applicable</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="jakarta-ee-platform-11">Jakarta EE Platform 11</h3>
<table id="jakarta_ee_platform" class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 44.4444%;">
<col style="width: 22.2222%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Specification</th>
<th class="tableblock halign-left valign-top">Updates</th>
<th class="tableblock halign-left valign-top">Liberty Feature Documentation</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/webprofile/11/">Jakarta EE Web Profile 11</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">See previous <a href="#web_profile">table</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/authorization/3.0/">Authorization 3.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Major update</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/appAuthorization-3.0.html">appAuthorization-3.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/activation/2.1/">Activation 2.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/mail-2.1.html">mail-2.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/batch/2.1/">Batch 2.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/batch-2.1.html">batch-2.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/connectors/2.1/">Connectors 2.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/connectors-2.1.html">connectors-2.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/enterprise-beans/4.0/">Enterprise Beans 4.0</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/enterpriseBeans-4.0.html">enterpriseBeans-4.0</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/mail/2.1/">Mail 2.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/mail-2.1.html">mail-2.1</a></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://jakarta.ee/specifications/messaging/3.1/">Messaging 3.1</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Unchanged</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="/docs/latest/reference/feature/messaging-3.1.html">messaging-3.1</a></p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Enterprise Beans 4.0 is unchanged, but the optional EJB 2.x function is no longer enabled when the enterpriseBeans-4.0 feature is configured with other Jakarta EE 11 features. Users who want to use EJB 2.x APIs must also add the enterpriseBeansHome-4.0 feature.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Liberty provides convenience features for running all of the component specifications that are contained in the Jakarta EE 11 Web Profile (<a href="/docs/latest/reference/feature/webProfile-11.0.html">webProfile-11.0</a>) and the Jakarta EE 11 Platform (<a href="/docs/latest/reference/feature/jakartaee-11.0.html">jakartaee-11.0</a>). These convenience features enable you to rapidly develop applications using all of the APIs contained in their respective specifications. For Jakarta EE 11 features in the application client, use the <a href="/docs/latest/reference/feature/jakartaeeClient-11.0.html">jakartaeeClient-11.0</a> Liberty feature.</p>
</div>
<div class="paragraph">
<p>To enable the Jakarta EE Platform 11 features, add the <code>jakartaee-11.0</code> feature to your <code>server.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jakartaee-11.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Alternatively, to enable the Jakarta EE Web Profile 11 features, add the <code>webProfile-11.0</code> feature to your <code>server.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>webProfile-11.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Although no convenience feature exists for the Core Profile, you can enable its equivalent by adding the following features to your <code>server.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jsonb-3.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jsonp-2.1<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>cdi-4.1<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>restfulWS-4.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To run Jakarta EE 11 features on the Application Client Container, add the following entry in your <code>client.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jakartaeeClient-11.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>For more information reference</strong>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="/docs/latest/reference/diff/jakarta-ee11-diff.html">Differences between Jakarta EE 11 and 10</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/platform/11/">Jakarta EE Platform 11</a>, <a href="https://jakarta.ee/specifications/webprofile/11/">Jakarta EE Web Profile 11</a>, and <a href="https://jakarta.ee/specifications/coreprofile/11/">Jakarta EE Core Profile 11</a> specifications.</p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/platform/11/apidocs/">Jakarta EE 11 Javadoc</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="springboot">Spring Boot 4.0</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Open Liberty currently supports running Spring Boot 1.5.x, 2.x, and 3.x applications. With the introduction of the new <code>springBoot-4.0</code> feature, users can now deploy Spring Boot 4.x applications. While Liberty has consistently supported Spring Boot applications packaged as <code>WAR</code> files, this enhancement extends support to both <code>JAR</code> and <code>WAR</code> formats for Spring Boot 4.x applications.</p>
</div>
<div class="paragraph">
<p>The <code>springBoot-4.0</code> feature provides complete support for running a Spring Boot 4.x application on Open Liberty, as well as the ability to thin the application when building containerized applications.</p>
</div>
<div class="paragraph">
<p>To use this feature, users must be running Java 17 or later with Jakarta EE 11 features enabled. If the application uses servlets, it must be configured to use <code>servlet-6.1</code>. Include the following features in your <code>server.xml</code> file to configure the server.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;featureManager&gt;</span>
   <span class="tag">&lt;feature&gt;</span>springBoot-4.0<span class="tag">&lt;/feature&gt;</span>
   <span class="tag">&lt;feature&gt;</span>servlet-6.1<span class="tag">&lt;/feature&gt;</span>
<span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>server.xml</code> configuration for deploying a Spring Boot application follows the same approach used in earlier Liberty Spring Boot versions.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;springBootApplication</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">spring-boot-app</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">location</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">spring-boot-app-0.1.0.jar</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">spring-boot-app</span><span class="delimiter">&quot;</span></span> <span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As in earlier versions, the Spring Boot application JAR can be deployed by placing it in the <code>/dropins/spring</code> folder. The <code>springBootApplication</code> configuration in the <code>server.xml</code> file can be omitted when this deployment method is used.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ssl">Update to TLS/SSL Cipher support</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Liberty now uses the effective cipher list from the JDK for SSL configuration. The <code>securityLevel</code> attribute in the SSL configuration is not used anymore. In addition, the <code>enabledCiphers</code> attribute in the SSL config is updated to customize the SSL ciphers in a more flexible way.</p>
</div>
<div class="paragraph">
<p>Liberty&#8217;s <code>securityLevel</code> based cipher categories no longer provide meaningful value. The <code>MEDIUM</code> and <code>LOW</code> categories contain no remaining ciphers.</p>
</div>
<div class="paragraph">
<p>The <code>enabledCiphers</code> attribute now has two mutually exclusive modes: (1) Specify a custom list of ciphers separated by spaces, or (2) Specify filter criteria to add (+) or remove (-) cipher suites from the effective JDK cipher list. If the value set in <code>enabledCiphers</code> contains a static entry and a +/- entry, an error is logged, and the server ignores the <code>enabledCiphers</code> value by returning the effective JDK cipher list.</p>
</div>
<div class="paragraph">
<p><strong>Existing Usage:</strong> A user sets <code>securityLevel</code> as <code>HIGH</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">securityLevel</span>=<span class="attribute-value">HIGH</span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>securityLevel</code> attribute is now ignored, so the previous <code>&lt;ssl&gt;</code> configuration is treated equivalently to the configuration shown here where there is no <code>securityLevel</code> attribute configured.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Existing Usage:</strong> A user specifies all ciphers from the effective JDK list, excluding all TLS_RSA ciphers except for one (TLS_RSA_WITH_AES_128_GCM_SHA256)</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">securityLevel</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">CUSTOM</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">enabledCiphers</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_256_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_128_GCM_SHA256</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Example with new syntax:</strong> Use wildcards to achieve the same logic</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">enabledCiphers</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">-TLS_RSA* +TLS_RSA_WITH_AES_128_GCM_SHA256</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To learn more about Transport Security, see <a href="/docs/modules/reference/23.0.0.6/com.ibm.websphere.appserver.api.ssl_1.5-javadoc/com/ibm/websphere/ssl/Constants.html">SSL Constants Javadoc</a>, <a href="/docs/modules/reference/23.0.0.6/com.ibm.websphere.appserver.api.ssl_1.5-javadoc/com/ibm/websphere/ssl/JSSEProvider.html">JSSEProvider Javadoc</a>, and <a href="/docs/latest/reference/config/ssl.html">SSL Configuration Reference</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="CVEs">Security vulnerability (CVE) fixes in this release</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">CVE</th>
<th class="tableblock halign-left valign-top">CVSS Score</th>
<th class="tableblock halign-left valign-top">Vulnerability Assessment</th>
<th class="tableblock halign-left valign-top">Versions Affected</th>
<th class="tableblock halign-left valign-top">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2026-3621">CVE-2026-3621</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">7.5</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Identity spoofing</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.4</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>For a list of past security vulnerability fixes, reference the <a href="/docs/latest/security-vulnerabilities.html">Security vulnerability (CVE) list</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bugs">Notable bugs fixed in this release</h2>
<div class="sectionbody">
<div class="paragraph">
<p>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  <a href="https://github.com/OpenLiberty/open-liberty/issues?q=label%3Arelease%3A26005+label%3A%22release+bug%22">full list of bugs fixed in 26.0.0.5</a>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34716">Subject Leak When a feature that use Security Service is Enabled, and appSecurity Disabled (CVE-2026-3621)</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34664">java.lang.ClassCastException: org.apache.jasper.runtime.JspApplicationContextImpl in multi module JSP Application</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/pull/34657">Fix classloading conflict with cached JspApplicationContext</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34642">PKCE is being enforced by the oauthProvider even when the Authorization Code Grant isn&#8217;t used</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-open-liberty-26-0-0-5-now">Get Open Liberty 26.0.0.5 now</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Available through <a href="#run">Maven, Gradle, Docker, and as a downloadable archive</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><category term="announcements" /><category term="release" /><category term="security" /><category term="spring" /><category term="jakarta-ee" /></entry><entry><title type="html">Enabling hardware cryptography for Liberty for Linux on distributed environment with Semeru Java 17</title><link href="https://openliberty.io/blog/2026/05/19/configuration-steps-for-hardware-cryptography-for-liberty-for-linux.html" rel="alternate" type="text/html" title="Enabling hardware cryptography for Liberty for Linux on distributed environment with Semeru Java 17" /><published>2026-05-19T00:00:00+00:00</published><updated>2026-05-19T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/05/19/configuration-steps-for-hardware-cryptography-for-liberty-for-linux</id><content type="html" xml:base="https://openliberty.io/blog/2026/05/19/configuration-steps-for-hardware-cryptography-for-liberty-for-linux.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Hardware cryptography, through hardware security modules (HSM), provides enhanced security for applications that are running on Liberty on distributed Linux environments. The following instructions to integrate Liberty on distributed Linux with hardware cryptographic devices were successfully tested by a customer who was using a Thales Luna HSM with Semeru Java 17. These instructions should also be applicable to other Java versions; see <a href="#java-version-considerations">Java version considerations</a> for potential modifications needed. The configuration should be adaptable to other hardware security modules that support the PKCS#11 interface.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="example-use-cases">Example use cases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The HSM integration with Liberty enables enhanced security for various cryptographic operations. Here are some example use cases:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Secure Web Services with HSM-Protected Keys</strong>: Liberty serves as a secure web service endpoint using TLS with the server’s private key stored in the HSM. The TLS handshake uses the HSM-protected key, ensuring the private key never leaves the secure hardware boundary, even if the Liberty server is compromised.</p>
</li>
<li>
<p><strong>Mutual TLS Authentication for Microservices</strong>: Liberty stores client certificates in the HSM for outbound connections requiring mutual TLS. The HSM handles the cryptographic operations for client authentication, protecting the private key material.</p>
</li>
<li>
<p><strong>Digital Signature Services</strong>: Applications can use the HSM to sign data (like JSON Web Tokens) with keys that are generated and stored within the HSM. The signing operation occurs inside the hardware, maintaining the integrity of the signing process.</p>
</li>
<li>
<p><strong>Secure Data Encryption and Decryption</strong>: Encryption keys are securely stored in the HSM, and encryption/decryption operations are performed by the hardware, providing an additional layer of security for sensitive data.</p>
</li>
<li>
<p><strong>Certificate Authority Operations</strong>: Organizations running their own CA can store root and intermediate certificate private keys in the HSM, with certificate signing operations performed within the hardware to ensure the highest level of protection for these critical security assets.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="steps">Steps</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Install HSM client libraries that enable communication between your application and the HSM.</p>
</li>
<li>
<p>Configure authentication between the Linux server and the HSM to establish secure communication.
Create and register a self-signed client certificate, establishing mutual TLS as the authentication method.</p>
</li>
<li>
<p>Create a PKCS#11 configuration file that defines how Java will interact with the HSM through the PKCS#11 interface.</p>
<div class="paragraph">
<p>For example, the following PKCS11Config.cfg file was created with assistance from Luna Support and saved in the Liberty server configuration directory:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>description = Thales Luna HSM/PKCS11 Configuration
 name = PKCS11Config
 library = /opt/safenet/lunaclient/lib/libCryptoki2_64.so
 slot = 0
 showInfo = true
 attributes(*,CKO_SECRET_KEY,*) = {
 CKA_CLASS=4
 CKA_PRIVATE= true
 CKA_KEY_TYPE = 21
 CKA_SENSITIVE= true
 CKA_ENCRYPT= true
 CKA_DECRYPT= true
 CKA_WRAP= true
 CKA_UNWRAP= true
 }
 attributes(*,CKO_PRIVATE_KEY,*) = {
 CKA_CLASS=3
 CKA_LABEL=true
 CKA_PRIVATE = true
 CKA_DECRYPT=true
 CKA_SIGN=true
 CKA_UNWRAP=true
 }
 attributes(*,CKO_PUBLIC_KEY,*) = {
 CKA_CLASS=2
 CKA_LABEL=true
 CKA_ENCRYPT = true
 CKA_VERIFY=true
 CKA_WRAP=true
 }</pre>
</div>
</div>
</li>
<li>
<p>Update the Java security configuration to include the PKCS#11 provider.<br></p>
<div class="paragraph">
<p>For example, create a <code>java.security</code> file in the same directory as the <code>server.xml</code> file with the following content to add the SunPKCS11 provider pointing to the <code>PKCS11Config.cfg</code> file:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>security.provider.1=SunPKCS11 ${server.config.dir}/PKCS11Config.cfg
security.provider.2=SUN
security.provider.3=SunRsaSign
security.provider.4=SunEC
security.provider.5=SunJSSE
security.provider.6=SunJCE
security.provider.7=SunJGSS
security.provider.8=SunSASL
security.provider.9=XMLDSig
security.provider.10=SunPCSC
security.provider.11=JdkLDAP
security.provider.12=JdkSASL</pre>
</div>
</div>
<div class="paragraph">
<p>Also create, a <code>jvm.options</code> file in the same directory with the following content to point to the custom <code>java.security</code> file:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>-Djava.security.properties=${server.config.dir}/java.security</pre>
</div>
</div>
</li>
<li>
<p>Set up a hardware-based keystore/truststore, a file-based keystore/truststore, or both:</p>
<div class="ulist">
<ul>
<li>
<p>For a HSM (hardware-based keystore/truststore), create or import the necessary certificates for your application&#8217;s security requirements. (i.e. TLS server certificate and client certificates for various use cases).</p>
</li>
<li>
<p>For a file-based keystore/truststore, create a standard file-based truststore to hold trusted CA certificates. For example, a traditional PKCS12 file-based keystore can be created to serve as the general "truststore" in Liberty, which includes the CA certificates to be trusted.</p>
</li>
</ul>
</div>
</li>
<li>
<p>Edit the Liberty <code>server.xml</code> file to use the HSM-backed keystore, the file-based truststore, or both by adding the relevant configuration snippets:</p>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Standard file-based truststore --&gt;
 &lt;keyStore
     id="defaultTrustStore"
     location="${server.config.dir}/truststore.p12"
     password="truststore_password"
     type="PKCS12"
 /&gt;</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Luna HSM-backed PKCS11 keystore --&gt;
&lt;keyStore
    id="defaultKeyStore"
    fileBased="false"
    readOnly="true"
    location="${server.config.dir}/PKCS11Config.cfg"
    password="crypto_user_pwd_for_luna_hsm_partition"
    type="PKCS11"
/&gt;</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Enable SSL feature --&gt;
&lt;feature&gt;transportSecurity-1.0&lt;/feature&gt;
&lt;!-- or --&gt;
&lt;feature&gt;ssl-1.0&lt;/feature&gt;</pre>
</div>
</div>
</li>
<li>
<p>Configure the  Liberty server environment variables.</p>
<div class="paragraph">
<p>For example, edit the Liberty <code>server.env</code> file with the following configuration:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>JAVA_HOME=/etc/alternatives/jre_17
 JVM_ARGS="--add-exports=jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED"</pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Where: The <code>--add-exports</code> JVM argument is necessary because in Java 17, the Java module system restricts access to internal packages. The PKCS#11 implementation requires access to classes in the <code>sun.security.pkcs11</code> package, which is part of the <code>jdk.crypto.cryptoki</code> module. This argument explicitly allows Liberty to access these internal classes that are needed for the HSM integration to work properly.</p>
<div class="ulist">
<ul>
<li>
<p>Similar JVM arguments might be required for other Java versions that use the module system (Java 9 and later). The specific arguments might vary slightly depending on the Java version.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<li>
<p>Configure any JVM options that are required for your specific HSM compatibility. For example, to disable unsupported algorithms, add the list of algorithms to the Liberty <code>jvm.options</code> file:</p>
<div class="literalblock">
<div class="content">
<pre>-Djdk.tls.disabledAlgorithms=RSASSA-PSS,RSAPSS</pre>
</div>
</div>
</li>
<li>
<p>Start the Liberty server and verify that the HSM integration is working correctly.</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="troubleshooting">Troubleshooting</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you encounter issues with your HSM integration, consider enabling PKCS#11 debug logging by adding to jvm.options:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>-Djava.security.debug=sunpkcs11,pkcs11</pre>
</div>
</div>
<div class="paragraph">
<p>For specific HSM-related issues, consult your HSM vendor&#8217;s documentation and support resources.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="java-version-considerations">Java version considerations</h2>
<div class="sectionbody">
<div class="paragraph">
<p>While these steps were written and tested with Semeru Java 17, they should be applicable to other Java versions with potential minor adjustments:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The module-related JVM arguments (<code>--add-exports</code>) may vary depending on the Java version</p>
</li>
<li>
<p>The exact path to the Java installation will differ</p>
</li>
<li>
<p>Security provider configurations may have slight differences between Java versions</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="related-articles">Related articles</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/server-configuration-overview.html">Server configuration overview</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/feature-overview.html">Feature Overview</a></p>
</li>
<li>
<p><a href="https://www.ibm.com/support/pages/enabling-hardware-cryptography-liberty-zos-using-java-11-java-17-or-java-21?view=full">Enabling hardware cryptography for Liberty for z/OS using Java 11, Java 17, or Java 21</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="acknowledgements">Acknowledgements</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We would like to sincerely thank a valued customer who, while choosing to remain anonymous, generously shared his expertise with us. Access to this level of specialized hardware and configuration experience is rare. Through collaboration, persistence, and thoughtful experimentation, he helped validate solutions and provided the practical steps documented here.
His willingness to share these insights will benefit many others in the WAS community facing similar challenges. We deeply appreciate his generosity and the meaningful impact of his contribution.</p>
</div>
</div>
</div>]]></content><author><name>Hiroko Takamiya</name></author><category term="blog" /><summary type="html"><![CDATA[Hardware cryptography, through hardware security modules (HSM), provides enhanced security for applications that are running on Liberty on distributed Linux environments. The following instructions to integrate Liberty on distributed Linux with hardware cryptographic devices were successfully tested by a customer who was using a Thales Luna HSM with Semeru Java 17. These instructions should also be applicable to other Java versions; see [Java version considerations] for potential modifications needed. The configuration should be adaptable to other hardware security modules that support the PKCS#11 interface.]]></summary></entry><entry><title type="html">Enabling hardware cryptography for Liberty for Linux on distributed environment with Semeru Java 17</title><link href="https://openliberty.io/blog/2026/05/11/configuration-steps-for-hardware-cryptography-for-liberty-for-linux.html" rel="alternate" type="text/html" title="Enabling hardware cryptography for Liberty for Linux on distributed environment with Semeru Java 17" /><published>2026-05-11T00:00:00+00:00</published><updated>2026-05-11T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/05/11/configuration-steps-for-hardware-cryptography-for-liberty-for-linux</id><content type="html" xml:base="https://openliberty.io/blog/2026/05/11/configuration-steps-for-hardware-cryptography-for-liberty-for-linux.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Hardware cryptography, through hardware security modules (HSM), provides enhanced security for applications that are running on Liberty on distributed Linux environments. The following instructions to integrate Liberty on distributed Linux with hardware cryptographic devices were successfully tested by a customer who was using a Thales Luna HSM with Semeru Java 17. These instructions should also be applicable to other Java versions; see <a href="#java-version-considerations">Java version considerations</a> for potential modifications needed. The configuration should be adaptable to other hardware security modules that support the PKCS#11 interface.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="example-use-cases">Example use cases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The HSM integration with Liberty enables enhanced security for various cryptographic operations. Here are some example use cases:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Secure Web Services with HSM-Protected Keys</strong>: Liberty serves as a secure web service endpoint using TLS with the server’s private key stored in the HSM. The TLS handshake uses the HSM-protected key, ensuring the private key never leaves the secure hardware boundary, even if the Liberty server is compromised.</p>
</li>
<li>
<p><strong>Mutual TLS Authentication for Microservices</strong>: Liberty stores client certificates in the HSM for outbound connections requiring mutual TLS. The HSM handles the cryptographic operations for client authentication, protecting the private key material.</p>
</li>
<li>
<p><strong>Digital Signature Services</strong>: Applications can use the HSM to sign data (like JSON Web Tokens) with keys that are generated and stored within the HSM. The signing operation occurs inside the hardware, maintaining the integrity of the signing process.</p>
</li>
<li>
<p><strong>Secure Data Encryption and Decryption</strong>: Encryption keys are securely stored in the HSM, and encryption/decryption operations are performed by the hardware, providing an additional layer of security for sensitive data.</p>
</li>
<li>
<p><strong>Certificate Authority Operations</strong>: Organizations running their own CA can store root and intermediate certificate private keys in the HSM, with certificate signing operations performed within the hardware to ensure the highest level of protection for these critical security assets.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="steps">Steps</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Install HSM client libraries that enable communication between your application and the HSM.</p>
</li>
<li>
<p>Configure authentication between the Linux server and the HSM to establish secure communication.
Create and register a self-signed client certificate, establishing mutual TLS as the authentication method.</p>
</li>
<li>
<p>Create a PKCS#11 configuration file that defines how Java will interact with the HSM through the PKCS#11 interface.</p>
<div class="paragraph">
<p>For example, the following PKCS11Config.cfg file was created with assistance from Luna Support and saved in the Liberty server configuration directory:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>description = Thales Luna HSM/PKCS11 Configuration
 name = PKCS11Config
 library = /opt/safenet/lunaclient/lib/libCryptoki2_64.so
 slot = 0
 showInfo = true
 attributes(*,CKO_SECRET_KEY,*) = {
 CKA_CLASS=4
 CKA_PRIVATE= true
 CKA_KEY_TYPE = 21
 CKA_SENSITIVE= true
 CKA_ENCRYPT= true
 CKA_DECRYPT= true
 CKA_WRAP= true
 CKA_UNWRAP= true
 }
 attributes(*,CKO_PRIVATE_KEY,*) = {
 CKA_CLASS=3
 CKA_LABEL=true
 CKA_PRIVATE = true
 CKA_DECRYPT=true
 CKA_SIGN=true
 CKA_UNWRAP=true
 }
 attributes(*,CKO_PUBLIC_KEY,*) = {
 CKA_CLASS=2
 CKA_LABEL=true
 CKA_ENCRYPT = true
 CKA_VERIFY=true
 CKA_WRAP=true
 }</pre>
</div>
</div>
</li>
<li>
<p>Update the Java security configuration to include the PKCS#11 provider.<br></p>
<div class="paragraph">
<p>For example, create a <code>java.security</code> file in the same directory as the <code>server.xml</code> file with the following content to add the SunPKCS11 provider pointing to the <code>PKCS11Config.cfg</code> file:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>security.provider.1=SunPKCS11 ${server.config.dir}/PKCS11Config.cfg
security.provider.2=SUN
security.provider.3=SunRsaSign
security.provider.4=SunEC
security.provider.5=SunJSSE
security.provider.6=SunJCE
security.provider.7=SunJGSS
security.provider.8=SunSASL
security.provider.9=XMLDSig
security.provider.10=SunPCSC
security.provider.11=JdkLDAP
security.provider.12=JdkSASL</pre>
</div>
</div>
<div class="paragraph">
<p>Also create, a <code>jvm.options</code> file in the same directory with the following content to point to the custom <code>java.security</code> file:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>-Djava.security.properties=${server.config.dir}/java.security</pre>
</div>
</div>
</li>
<li>
<p>Set up a hardware-based keystore/truststore, a file-based keystore/truststore, or both:</p>
<div class="ulist">
<ul>
<li>
<p>For a HSM (hardware-based keystore/truststore), create or import the necessary certificates for your application&#8217;s security requirements. (i.e. TLS server certificate and client certificates for various use cases).</p>
</li>
<li>
<p>For a file-based keystore/truststore, create a standard file-based truststore to hold trusted CA certificates. For example, a traditional PKCS12 file-based keystore can be created to serve as the general "truststore" in Liberty, which includes the CA certificates to be trusted.</p>
</li>
</ul>
</div>
</li>
<li>
<p>Edit the Liberty <code>server.xml</code> file to use the HSM-backed keystore, the file-based truststore, or both by adding the relevant configuration snippets:</p>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Standard file-based truststore --&gt;
 &lt;keyStore
     id="defaultTrustStore"
     location="${server.config.dir}/truststore.p12"
     password="truststore_password"
     type="PKCS12"
 /&gt;</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Luna HSM-backed PKCS11 keystore --&gt;
&lt;keyStore
    id="defaultKeyStore"
    fileBased="false"
    readOnly="true"
    location="${server.config.dir}/PKCS11Config.cfg"
    password="crypto_user_pwd_for_luna_hsm_partition"
    type="PKCS11"
/&gt;</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>&lt;!-- Enable SSL feature --&gt;
&lt;feature&gt;transportSecurity-1.0&lt;/feature&gt;
&lt;!-- or --&gt;
&lt;feature&gt;ssl-1.0&lt;/feature&gt;</pre>
</div>
</div>
</li>
<li>
<p>Configure the  Liberty server environment variables.</p>
<div class="paragraph">
<p>For example, edit the Liberty <code>server.env</code> file with the following configuration:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>JAVA_HOME=/etc/alternatives/jre_17
 JVM_ARGS="--add-exports=jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED"</pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>Where: The <code>--add-exports</code> JVM argument is necessary because in Java 17, the Java module system restricts access to internal packages. The PKCS#11 implementation requires access to classes in the <code>sun.security.pkcs11</code> package, which is part of the <code>jdk.crypto.cryptoki</code> module. This argument explicitly allows Liberty to access these internal classes that are needed for the HSM integration to work properly.</p>
<div class="ulist">
<ul>
<li>
<p>Similar JVM arguments might be required for other Java versions that use the module system (Java 9 and later). The specific arguments might vary slightly depending on the Java version.</p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<li>
<p>Configure any JVM options that are required for your specific HSM compatibility. For example, to disable unsupported algorithms, add the list of algorithms to the Liberty <code>jvm.options</code> file:</p>
<div class="literalblock">
<div class="content">
<pre>-Djdk.tls.disabledAlgorithms=RSASSA-PSS,RSAPSS</pre>
</div>
</div>
</li>
<li>
<p>Start the Liberty server and verify that the HSM integration is working correctly.</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="troubleshooting">Troubleshooting</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you encounter issues with your HSM integration, consider enabling PKCS#11 debug logging by adding to jvm.options:</p>
</div>
<div class="literalblock">
<div class="content">
<pre>-Djava.security.debug=sunpkcs11,pkcs11</pre>
</div>
</div>
<div class="paragraph">
<p>For specific HSM-related issues, consult your HSM vendor&#8217;s documentation and support resources.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="java-version-considerations">Java version considerations</h2>
<div class="sectionbody">
<div class="paragraph">
<p>While these steps were written and tested with Semeru Java 17, they should be applicable to other Java versions with potential minor adjustments:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The module-related JVM arguments (<code>--add-exports</code>) may vary depending on the Java version</p>
</li>
<li>
<p>The exact path to the Java installation will differ</p>
</li>
<li>
<p>Security provider configurations may have slight differences between Java versions</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="related-articles">Related articles</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/server-configuration-overview.html">Server configuration overview</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/feature-overview.html">Feature Overview</a></p>
</li>
<li>
<p><a href="https://www.ibm.com/support/pages/enabling-hardware-cryptography-liberty-zos-using-java-11-java-17-or-java-21?view=full">Enabling hardware cryptography for Liberty for z/OS using Java 11, Java 17, or Java 21</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="acknowledgements">Acknowledgements</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We would like to sincerely thank a valued customer who, while choosing to remain anonymous, generously shared his expertise with us. Access to this level of specialized hardware and configuration experience is rare. Through collaboration, persistence, and thoughtful experimentation, he helped validate solutions and provided the practical steps documented here.
His willingness to share these insights will benefit many others in the WAS community facing similar challenges. We deeply appreciate his generosity and the meaningful impact of his contribution.</p>
</div>
</div>
</div>]]></content><author><name>Hiroko Takamiya</name></author><category term="blog" /><summary type="html"><![CDATA[Hardware cryptography, through hardware security modules (HSM), provides enhanced security for applications that are running on Liberty on distributed Linux environments. The following instructions to integrate Liberty on distributed Linux with hardware cryptographic devices were successfully tested by a customer who was using a Thales Luna HSM with Semeru Java 17. These instructions should also be applicable to other Java versions; see [Java version considerations] for potential modifications needed. The configuration should be adaptable to other hardware security modules that support the PKCS#11 interface.]]></summary></entry><entry><title type="html">Updates to MCP Server and TLS/SSL Cipher Support in 26.0.0.5-beta</title><link href="https://openliberty.io/blog/2026/05/05/26.0.0.5-beta.html" rel="alternate" type="text/html" title="Updates to MCP Server and TLS/SSL Cipher Support in 26.0.0.5-beta" /><published>2026-05-05T00:00:00+00:00</published><updated>2026-05-05T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/05/05/26.0.0.5-beta</id><content type="html" xml:base="https://openliberty.io/blog/2026/05/05/26.0.0.5-beta.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This beta release updates the <code>mcpServer-1.0</code> feature and simplifies SSL cipher configuration by using the effective JDK cipher list by default and flexible <code>enabledCiphers</code> syntax.</p>
</div>
<div class="paragraph">
<p>The <a href="/">Open Liberty</a> 26.0.0.5-beta includes the following beta features (along with <a href="/docs/latest/reference/feature/feature-overview.html">all GA features</a>):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#mcp">Updates to <code>mcpServer-1.0</code></a></p>
</li>
<li>
<p><a href="#ssl">Update to TLS/SSL Cipher support</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>See also <a href="/blog/?search=beta&amp;key=tag">previous Open Liberty beta blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="mcp">Updates to <code>mcpServer-1.0</code></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> is an open standard that enables AI applications to access real-time information from external sources. The Liberty MCP Server feature <code>mcpServer-1.0</code> allows developers to expose the business logic or data from their applications, allowing it to be integrated into agentic AI workflows.</p>
</div>
<div class="paragraph">
<p>This beta release of Liberty includes important updates to the <code>mcpServer-1.0</code> feature, including configurable endpoint paths and notable bug fixes.</p>
</div>
<div class="sect2">
<h3 id="prerequisites">Prerequisites</h3>
<div class="paragraph">
<p>To use the <code>mcpServer-1.0</code> feature, it is required to have Java 17 or later installed on your system.</p>
</div>
</div>
<div class="sect2">
<h3 id="configure-custom-mcp-endpoint-paths">Configure custom MCP endpoint paths</h3>
<div class="paragraph">
<p>Previously, the MCP endpoint was hard-coded to <code>/mcp</code> under the web application context root. You can now configure custom endpoint paths to better suit your application architecture and naming conventions.</p>
</div>
<div class="sect3">
<h4 id="single-application-configuration">Single Application Configuration</h4>
<div class="paragraph">
<p>For a single application, configure the endpoint path directly in the <code>&lt;mcpServer&gt;</code> element:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;server</span> <span class="attribute-name">description</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Configurable Mcp Path Liberty server</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>

  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>servlet-6.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>cdi-4.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;feature&gt;</span>mcpServer-1.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span>

  <span class="tag">&lt;application</span> <span class="attribute-name">location</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">configurableMcpPathTests.war</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>
          <span class="tag">&lt;mcpServer</span> <span class="attribute-name">path</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">/custom-mcp</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span>
  <span class="tag">&lt;/application&gt;</span>

<span class="tag">&lt;/server&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>With this configuration, MCP server can be accessed at <code>/custom-mcp</code> instead of the default <code>/mcp</code> path.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="configuration-options">Configuration Options</h3>
<div class="paragraph">
<p>The <code>&lt;mcpServer&gt;</code> element supports the following attributes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>path</strong> (single app): The custom path for the MCP endpoint (for example, <code>/custom-mcp</code>)</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="notable-bug-fixes-in-this-release-for-mcpserver-1-0">Notable bug fixes in this release for <code>mcpServer-1.0</code></h3>
<div class="sect3">
<h4 id="1-structured-content-output-schemas-now-comply-with-mcp-specification">1) Structured content output schemas now comply with MCP specification</h4>
<div class="paragraph">
<p>The MCP specification requires that structured content output schemas must have an object type at the root level. Previously, when arrays of objects are returned, the schema incorrectly placed the array at the root level.</p>
</div>
<div class="paragraph">
<p><strong>Previous Behavior (Incorrect):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="json">{
  <span class="key"><span class="delimiter">&quot;</span><span class="content">outputSchema</span><span class="delimiter">&quot;</span></span>: {
    <span class="key"><span class="delimiter">&quot;</span><span class="content">description</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">Returns list of person object</span><span class="delimiter">&quot;</span></span>,
    <span class="key"><span class="delimiter">&quot;</span><span class="content">type</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">array</span><span class="delimiter">&quot;</span></span>,
    <span class="key"><span class="delimiter">&quot;</span><span class="content">items</span><span class="delimiter">&quot;</span></span>: {
      <span class="key"><span class="delimiter">&quot;</span><span class="content">$ref</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">#/$defs/Person</span><span class="delimiter">&quot;</span></span>
    }
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Current Behavior (Correct):</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="json">{
  <span class="key"><span class="delimiter">&quot;</span><span class="content">outputSchema</span><span class="delimiter">&quot;</span></span>: {
    <span class="key"><span class="delimiter">&quot;</span><span class="content">description</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">Returns Persons object</span><span class="delimiter">&quot;</span></span>,
    <span class="key"><span class="delimiter">&quot;</span><span class="content">type</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">object</span><span class="delimiter">&quot;</span></span>,
    <span class="key"><span class="delimiter">&quot;</span><span class="content">properties</span><span class="delimiter">&quot;</span></span>: {
      <span class="key"><span class="delimiter">&quot;</span><span class="content">persons</span><span class="delimiter">&quot;</span></span>: {
        <span class="key"><span class="delimiter">&quot;</span><span class="content">type</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">array</span><span class="delimiter">&quot;</span></span>,
        <span class="key"><span class="delimiter">&quot;</span><span class="content">items</span><span class="delimiter">&quot;</span></span>: {
          <span class="key"><span class="delimiter">&quot;</span><span class="content">$ref</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">#/$defs/Person</span><span class="delimiter">&quot;</span></span>
        }
      }
    }
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This helps ensure that all structured content responses comply with the <a href="https://modelcontextprotocol.io/specification/2025-11-25/server/tools#structured-content">MCP structured content specification</a> and improves compatibility with MCP clients and conformance tests.</p>
</div>
</div>
<div class="sect3">
<h4 id="2-authentication-failures-now-return-correct-http-status-code">2) Authentication failures now return correct HTTP status code</h4>
<div class="paragraph">
<p>Previously, failed authentication attempts returned a <code>403 Forbidden</code> response, which might be confusing as it typically indicates authorization (permission) failures rather than authentication (identity verification) failures.</p>
</div>
<div class="paragraph">
<p>Now, failed authentication attempts correctly return a <code>401 Unauthorized</code> response, making it immediately clear that the issue is with authentication credentials rather than permissions. This behavior follows HTTP specification best practices and makes it easier to troubleshoot authentication configuration problems.</p>
</div>
</div>
<div class="sect3">
<h4 id="3-fixed-encoder-bean-isolation-in-multi-application-deployments">3) Fixed encoder bean isolation in multi-application deployments</h4>
<div class="paragraph">
<p>Previously, encoder beans from multiple applications were stored in a static list within <code>McpCdiExtension</code>, causing beans from different applications to interfere with each other. This behavior could result in encoder beans from one application being incorrectly called in another application, unpredictable behavior in multi-application deployments, and potential security issues with cross-application bean access.</p>
</div>
<div class="paragraph">
<p>This has been fixed to ensure proper isolation of encoder beans per application, preventing cross-application interference and helping ensure that each application uses only its own encoder beans.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ssl">Update to TLS/SSL Cipher support</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Liberty now uses the effective cipher list from the JDK for SSL configuration. The <code>securityLevel</code> attribute in the SSL configuration is not used anymore. In addition, the <code>enabledCiphers</code> attribute in the SSL config is updated to customize the SSL ciphers in a more flexible way.</p>
</div>
<div class="paragraph">
<p>Liberty&#8217;s <code>securityLevel</code> based cipher categories no longer provide meaningful value. The <code>MEDIUM</code> and <code>LOW</code> categories contain no remaining ciphers.</p>
</div>
<div class="paragraph">
<p>The <code>enabledCiphers</code> attribute now has two mutually exclusive modes: (1) Specify a custom list of ciphers separated by spaces, or (2) Specify filter criteria to add (+) or remove (-) cipher suites from the effective JDK cipher list. If the value set in <code>enabledCiphers</code> contains a static entry and a +/- entry, an error is logged, and the server ignores the <code>enabledCiphers</code> value by returning the effective JDK cipher list.</p>
</div>
<div class="paragraph">
<p><strong>Existing Usage:</strong> A user sets <code>securityLevel</code> as <code>HIGH</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">securityLevel</span>=<span class="attribute-value">HIGH</span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>securityLevel</code> attribute is now ignored, so the previous <code>&lt;ssl&gt;</code> configuration is treated equivalently to the configuration shown here where there is no <code>securityLevel</code> attribute configured.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Existing Usage:</strong> A user specifies all ciphers from the effective JDK list, excluding all TLS_RSA ciphers except for one (TLS_RSA_WITH_AES_128_GCM_SHA256)</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">securityLevel</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">CUSTOM</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">enabledCiphers</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_256_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_128_GCM_SHA256</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Example with new syntax:</strong> Use wildcards to achieve the same logic</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ssl</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">defaultSSL</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">enabledCiphers</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">-TLS_RSA* +TLS_RSA_WITH_AES_128_GCM_SHA256</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>To learn more about Transport Security, see <a href="https://openliberty.io/docs/modules/reference/23.0.0.6/com.ibm.websphere.appserver.api.ssl_1.5-javadoc/com/ibm/websphere/ssl/Constants.html">SSL Constants Javadoc</a>, <a href="https://openliberty.io/docs/modules/reference/23.0.0.6/com.ibm.websphere.appserver.api.ssl_1.5-javadoc/com/ibm/websphere/ssl/JSSEProvider.html">JSSEProvider Javadoc</a>, and <a href="https://openliberty.io/docs/latest/reference/config/ssl.html">SSL Configuration Reference</a>.</p>
</div>
<div class="sect2">
<h3 id="run">Try it now</h3>
<div class="paragraph">
<p>To try out these features, update your build tools to pull the Open Liberty All Beta Features package instead of the main release. To enable the MCP server feature, follow the instructions from <a href="https://openliberty.io/blog/2025/10/23/mcp-standalone-blog.html">MCP standalone blog</a>. The beta works with Java SE 25, Java SE 21, Java SE 17, Java SE 11, and Java SE 8.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, you can install the All Beta Features package using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;configuration&gt;</span>
        <span class="tag">&lt;runtimeArtifact&gt;</span>
          <span class="tag">&lt;groupId&gt;</span>io.openliberty.beta<span class="tag">&lt;/groupId&gt;</span>
          <span class="tag">&lt;artifactId&gt;</span>openliberty-runtime<span class="tag">&lt;/artifactId&gt;</span>
          <span class="tag">&lt;version&gt;</span>26.0.0.5-beta<span class="tag">&lt;/version&gt;</span>
          <span class="tag">&lt;type&gt;</span>zip<span class="tag">&lt;/type&gt;</span>
        <span class="tag">&lt;/runtimeArtifact&gt;</span>
    <span class="tag">&lt;/configuration&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You must also add dependencies to your <code>pom.xml</code> file for the beta version of the APIs that are associated with the beta features that you want to try. For example, the following block adds dependencies for two example beta APIs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>org.example.spec<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>exampleApi<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>7.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;type&gt;</span>pom<span class="tag">&lt;/type&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span>
<span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>example.platform<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>example.example-api<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>11.0.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[26.0.0.5-beta,)'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty:beta</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/downloads/#runtime_betas">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="https://openliberty.io/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="paragraph">
<p>For more information on using a beta release, refer to the <a href="docs/latest/installing-open-liberty-betas.html">Installing Open Liberty beta releases</a> documentation.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="feedback">We welcome your feedback</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let us know what you think on <a href="https://groups.io/g/openliberty">our mailing list</a>. If you hit a problem, <a href="https://stackoverflow.com/questions/tagged/open-liberty">post a question on StackOverflow</a>. If you hit a bug, <a href="https://github.com/OpenLiberty/open-liberty/issues">please raise an issue</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><category term="announcements" /><category term="release" /><category term="beta" /><category term="security" /><summary type="html"><![CDATA[This beta release updates the mcpServer-1.0 feature and simplifies SSL cipher configuration by using the effective JDK cipher list by default and flexible enabledCiphers syntax.]]></summary></entry><entry><title type="html">Enhanced JWT validation, Java 26 support, and more in 26.0.0.4</title><link href="https://openliberty.io/blog/2026/04/21/26.0.0.4.html" rel="alternate" type="text/html" title="Enhanced JWT validation, Java 26 support, and more in 26.0.0.4" /><published>2026-04-21T00:00:00+00:00</published><updated>2026-04-21T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/04/21/26.0.0.4</id><content type="html" xml:base="https://openliberty.io/blog/2026/04/21/26.0.0.4.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This release introduces support for selecting JWT signature algorithms from JOSE headers and adds Java 26 support. It also removes the default LTPA keys password for enhanced security, and includes file transfer restrictions and security vulnerability fixes.</p>
</div>
<div class="paragraph">
<p>In <a href="/">Open Liberty</a> 26.0.0.4:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#file_transfer">Blocklist added to <code>FileService</code> MBean</a></p>
</li>
<li>
<p><a href="#ltpa">Default LTPA keys password removal</a></p>
</li>
<li>
<p><a href="#jwt">Support selecting JWT signature and decryption algorithms from JOSE header</a></p>
</li>
<li>
<p><a href="#java_26">Support for Java 26</a></p>
</li>
<li>
<p><a href="#displayCustomizedExceptionText">Documentation for <code>displayCustomizedExceptionText</code> property in Web Container</a></p>
</li>
<li>
<p><a href="#CVEs">Security Vulnerability (CVE) Fixes</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>View the list of fixed bugs in <a href="https://github.com/OpenLiberty/open-liberty/issues?q=label%3Arelease%3A26004+label%3A%22release+bug%22">26.0.0.4</a>.</p>
</div>
<div class="paragraph">
<p>Check out <a href="/blog/?search=release&amp;search!=beta">previous Open Liberty GA release blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="run">Develop and run your apps using 26.0.0.4</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, include the following in your <code>pom.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>, include the following in your <code>build.gradle</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/start/">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a>, or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="https://openliberty.io/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging, and application management all from within your IDE.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="https://stackoverflow.com/tags/open-liberty"><img src="/img/blog/blog_btn_stack.svg" alt="Ask a question on Stack Overflow"></a>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="file_transfer">Blocklist added to <code>FileService</code> MBean</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <code>FileService</code> MBean provided by the <code>restConnector-2.0</code> feature in Liberty now includes a <code>blocklist</code> attribute. This attribute is configured by the <code>&lt;blockDir&gt;</code> config element in the <code>server.xml</code> file. The default value of this attribute is <code>${server.output.dir}/resources/security</code>. This enhancement resolves the security vulnerability <a href="https://github.com/advisories/GHSA-c39w-6qgm-5cp7">CVE-2025-14915</a> by restricting file transfer access to <code>${server.output.dir}/resources/security</code> by default.</p>
</div>
<div class="paragraph">
<p>If FileTransfer access to <code>${server.output.dir}/resources/security</code> is required, the original behavior can be restored by setting an empty blocklist.</p>
</div>
<div class="paragraph">
<p>For more information, see the <a href="https://www.ibm.com/docs/en/was-liberty/nd?topic=manually-file-transfer">documentation</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ltpa">Default LTPA keys password removal</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The default LTPA keys password is removed to resolve the security vulnerability <a href="https://www.ibm.com/support/pages/node/7266845">CVE-2025-14917</a>.</p>
</div>
<div class="paragraph">
<p>Previously, a default password for the LTPA keys was used when the <code>keysPassword</code> attribute was not defined in the <code>&lt;ltpa /&gt;</code> element. With this change, a default password is no longer used when the <code>keysPassword</code> attribute is not set.</p>
</div>
<div class="paragraph">
<p>For existing servers, if the LTPA keys password is not configured in the <code>server.xml</code> file, the <code>keystore_password</code> in the <code>server.env</code> file is used. This value re-encrypts the LTPA keys in the <code>ltpa.keys</code> file. The LTPA keys themselves are not impacted. The <code>keystore_password</code> is configured in the <code>server.env</code> file during server creation unless the <code>--no-password</code> option is used with the <code>server create</code> command.</p>
</div>
<div class="paragraph">
<p>If a <code>keysPassword</code> is not defined in the <code>&lt;ltpa /&gt;</code> element in the <code>server.xml</code> file and a <code>keystore_password</code> is not defined in the <code>server.env</code> file, the LTPA service fails.
The following error message is displayed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="text">CWWKS4118E: LTPA configuration error. A keysPassword attribute is not configured on the &lt;ltpa /&gt; element, the 'ltpa_keys_password' environment variable is not set, and the 'keystore_password' environment variable is not set.</code></pre>
</div>
</div>
<div class="paragraph">
<p>Confirm that an LTPA keys password is set up by doing the following steps:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Check to see whether a <code>keysPassword</code> attribute is provided for the <code>&lt;ltpa /&gt;</code> element in the <code>server.xml</code> file (for example, <code>&lt;ltpa keysPassword="myKeysPassword" /&gt;</code>).</p>
<div class="ulist">
<ul>
<li>
<p>If it is provided, this update does not affect you and no further action is needed.</p>
</li>
<li>
<p>If it is not provided, do <strong>not</strong> add it and proceed to the next step.</p>
</li>
</ul>
</div>
</li>
<li>
<p>Check to see whether the <code>keystore_password</code> environment variable exists in the <code>server.env</code> file (for example, <code>keystore_password=myKeystorePassword</code>).</p>
<div class="ulist">
<ul>
<li>
<p>If it exists, then the <code>keystore_password</code> is used to reencrypt the LTPA keys that were previously encrypted with the default <code>keysPassword</code> when the server starts.</p>
</li>
<li>
<p>If it does not exist, proceed to the next step.</p>
</li>
</ul>
</div>
</li>
<li>
<p>Add the following environment variable to the <code>server.env</code> file (ensure you use the <code>keystore_password</code> here and <strong>not</strong> the <code>ltpa_keys_password</code> described in the next section for new servers):</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="properties">keystore_password=your-desired-password</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p>The <code>keystore_password</code> is used to reencrypt the LTPA keys that were previously encrypted with the default <code>keysPassword</code> when the server starts.</p>
</li>
</ul>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>For new servers, an <code>ltpa_keys_password</code> value is randomly generated during server creation. It is stored in the <code>server.env</code> file unless the <code>--no-password</code> option is specified with the <code>server create</code> command. The randomly generated <code>ltpa_keys_password</code> is used if the <code>keysPassword</code> attribute is not defined for the <code>&lt;ltpa /&gt;</code> element.</p>
</div>
<div class="paragraph">
<p>For more information, see the <a href="https://openliberty.io/docs/latest/reference/config/ltpa.html">LTPA</a> configuration element.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jwt">Support selecting JWT signature and decryption algorithms from JOSE header</h2>
<div class="sectionbody">
<div class="paragraph">
<p>JSON Web Tokens (JWTs) can be signed by using various cryptographic signature algorithms. With this release, the JWT Consumer, MicroProfile JWT, OpenID Connect Client, and Social Media Login features support selecting the JWT signature algorithm from the JOSE header. This support allows different signature algorithms to be used based on the token header.</p>
</div>
<div class="paragraph">
<p>Previously, only one single signature algorithm (for example, <code>RS256</code>) was able to be configured for each configuration in the <code>server.xml</code> file. If the incoming JWT was signed with a different algorithm, validation would fail. This update allows the signature algorithm from the JWT header to be used for validation. It provides the flexibility of using different signature algorithms within a single configuration.</p>
</div>
<div class="sect2">
<h3 id="how-to-use">How to use</h3>
<div class="paragraph">
<p>To enable signature algorithm selection from the header, set the <code>signatureAlgorithm</code> attribute to <code>FROM_HEADER</code> and optionally configure the <code>allowedSignatureAlgorithms</code> attribute to specify which algorithms are permitted.</p>
</div>
<div class="paragraph">
<p>If <code>allowedSignatureAlgorithms</code> is not configured, the default list contains all Open Liberty-supported signature algorithms: <code>RS256, RS384, RS512, HS256, HS384, HS512, ES256, ES384</code>, and <code>ES512</code>.</p>
</div>
<div class="paragraph">
<p>When using <code>FROM_HEADER</code> with asymmetric algorithms and a truststore setup, the aliases for the corresponding public keys must be prefixed with their corresponding algorithm (e.g., <code>RS256_keyalias</code>) for automatic selection. The remainder of the alias name does not matter as long as it begins with the signature algorithm string. During validation, the server searches the truststore for an alias that begins with the algorithm specified in the JWT&#8217;s header. If no algorithm-prefixed alias is found, the client falls back to using the alias specified by the <code>trustedAlias</code> attribute (for <code>jwtConsumer</code>) or <code>trustAliasName</code> attribute (for <code>openidConnectClient</code>, <code>oidcLogin</code> and <code>mpJwt</code>), if configured. If multiple aliases with the signature algorithm prefix exist within the truststore, Liberty uses the first one found.</p>
</div>
<div class="paragraph">
<p>See the following <code>server.xml</code> file configurations for examples on how to apply these settings to the supported elements:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;jwtConsumer</span>
    <span class="attribute-name">signatureAlgorithm</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">FROM_HEADER</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">allowedSignatureAlgorithms</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">RS256, ES384, HS512</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">...</span>
<span class="tag">/&gt;</span>

<span class="tag">&lt;mpJwt</span>
    <span class="attribute-name">signatureAlgorithm</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">FROM_HEADER</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">allowedSignatureAlgorithms</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">RS256, ES384, HS512</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">...</span>
<span class="tag">/&gt;</span>

<span class="tag">&lt;openidConnectClient</span>
    <span class="attribute-name">signatureAlgorithm</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">FROM_HEADER</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">allowedSignatureAlgorithms</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">RS256, ES384, HS512</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">...</span>
<span class="tag">/&gt;</span>

<span class="tag">&lt;oidcLogin</span>
    <span class="attribute-name">signatureAlgorithm</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">FROM_HEADER</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">allowedSignatureAlgorithms</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">RS256, ES384, HS512</span><span class="delimiter">&quot;</span></span>
    <span class="attribute-name">...</span>
<span class="tag">/&gt;</span></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="learn-more">Learn more</h3>
<div class="paragraph">
<p><strong>Server configurations:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/openidConnectClient.html">openidConnectClient</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/jwtConsumer.html">jwtConsumer</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/mpJwt.html">mpJwt</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/config/oidcLogin.html">oidcLogin</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Documentation:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/openidConnectClient-1.0.html">OpenID Connect Client 1.0</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/jwt-1.0.html">JSON Web Token 1.0</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/mpJwt-2.1.html">MicroProfile JWT 2.1</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/reference/feature/socialLogin-1.0.html">Social Media Login 1.0</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="java_26">Support for Java 26</h3>
<div class="paragraph">
<p>Java 26 is a recent Java release that introduces new features and enhancements over earlier versions that can be useful to review. This release is not a long-term support (LTS) release.</p>
</div>
<div class="paragraph">
<p>There are 10 new features (JEPs) in <a href="https://openjdk.org/projects/jdk/26/">Java 26</a>. Five are test features and five are fully delivered.</p>
</div>
<div class="paragraph">
<p><strong>Test Features:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>524: <a href="https://openjdk.org/jeps/524">PEM Encodings of Cryptographic Objects (Second Preview)</a></p>
</li>
<li>
<p>525: <a href="https://openjdk.org/jeps/525">Structured Concurrency (Sixth Preview)</a></p>
</li>
<li>
<p>526: <a href="https://openjdk.org/jeps/526">Lazy Constants (Second Preview)</a></p>
</li>
<li>
<p>529: <a href="https://openjdk.org/jeps/529">Vector API (Eleventh Incubator)</a></p>
</li>
<li>
<p>530: <a href="https://openjdk.org/jeps/530">Primitive Types in Patterns, instanceof, and switch (Fourth Preview)</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Delivered Features:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>500: <a href="https://openjdk.org/jeps/500">Prepare to Make Final Mean Final</a></p>
</li>
<li>
<p>504: <a href="https://openjdk.org/jeps/504">Remove the Applet API</a></p>
</li>
<li>
<p>516: <a href="https://openjdk.org/jeps/516">Ahead-of-Time Object Caching with Any GC</a></p>
</li>
<li>
<p>517: <a href="https://openjdk.org/jeps/517">HTTP/3 for the HTTP Client API</a></p>
</li>
<li>
<p>522: <a href="https://openjdk.org/jeps/522">G1 GC: Improve Throughput by Reducing Synchronization</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>A new change JEP 500 ("Prepare to Make Final Mean Final") in Java 26 starts enforcing true immutability of final fields by restricting their mutation when using deep reflection.
In Java 26, such mutations still work but trigger runtime warnings by default, preparing developers for stricter enforcement.
Future releases would likely throw exceptions instead, making the final truly nonmutable.</p>
</div>
<div class="paragraph">
<p>Developers can opt in early to this stricter behavior by using a JVM flag (for example, <code>--illegal-final-field-mutation=deny</code>) to detect issues sooner.
This change improves program correctness, security, and JVM optimizations.</p>
</div>
<div class="paragraph">
<p>Take advantage of these changes now to gain more time to evaluate how your applications
and microservices behave on Java 26.</p>
</div>
<div class="paragraph">
<p>Get started today by downloading the latest release of  <a href="https://developer.ibm.com/languages/java/semeru-runtimes/downloads/">IBM Semeru Runtime 26</a> or <a href="https://adoptium.net/temurin/releases/?version=26">Temurin 26</a>, then download and install the Open Liberty <a href="/start/#runtime_releases">26.0.0.4</a>. Update your Liberty server&#8217;s <a href="/docs/latest/reference/config/server-configuration-overview.html#server-env">server.env</a> file with <code>JAVA_HOME</code> set to your Java 26 installation directory and start testing.</p>
</div>
<div class="paragraph">
<p>For more information on Java 26, see the Java 26 <a href="https://jdk.java.net/26/release-notes">release notes page</a> and <a href="https://docs.oracle.com/en/java/javase/26/docs/api/index.html">API Javadoc page</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="displayCustomizedExceptionText">Documentation for <code>displayCustomizedExceptionText</code> property in Web Container</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This release adds documentation for the <code>displayCustomizedExceptionText</code> attribute in the <code>&lt;webContainer&gt;</code> configuration, which allows users to override Liberty’s default error messages (such as SRVE0218E: Forbidden and SRVE0232E: An exception occurred) with clearer, user-defined messages.</p>
</div>
<div class="paragraph">
<p>The feature is enabled through simple <code>server.xml</code> file configuration, where custom messages can be mapped to specific HTTP status codes (<code>403</code> and <code>500</code>).</p>
</div>
<div class="paragraph">
<p>Testing ensures that these custom messages correctly replace Liberty’s defaults across all supported platforms, confirming that the configured text is returned consistently in all scenarios.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;webContainer</span> <span class="attribute-name">displayCustomizedExceptionText</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">Custom error message</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="CVEs">Security vulnerability (CVE) fixes in this release</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">CVE</th>
<th class="tableblock halign-left valign-top">CVSS Score</th>
<th class="tableblock halign-left valign-top">Vulnerability Assessment</th>
<th class="tableblock halign-left valign-top">Versions Affected</th>
<th class="tableblock halign-left valign-top">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2025-14915">CVE-2025-14915</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">6.5</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Privilege escalation</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Affects the <code>restConnector-2.0</code> feature</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2025-14917">CVE-2025-14917</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">6.7</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Weaker security</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Affects the <code>appSecurity-1.0</code>, <code>appSecurity-2.0</code>, <code>appSecurity-3.0</code>, <code>appSecurity-4.0</code>, and <code>appSecurity-5.0</code> features</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2026-1561">CVE-2026-1561</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">5.4</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Server-side request forgery</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Affects the <code>samlWeb-2.0</code> feature</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2026-29063">CVE-2026-29063</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">8.7</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Prototype pollution</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.3</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Affects the <code>openapi-3.1</code>, <code>mpOpenAPI-1.0</code>, <code>mpOpenAPI-1.1</code>, <code>mpOpenAPI-2.0</code>, <code>mpOpenAPI-3.0</code>, <code>mpOpenAPI-3.1</code>, <code>mpOpenAPI-4.0</code> and <code>mpOpenAPI-4.1</code> features</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>For a list of past security vulnerability fixes, reference the <a href="/docs/latest/security-vulnerabilities.html">Security vulnerability (CVE) list</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-open-liberty-26-0-0-4-now">Get Open Liberty 26.0.0.4 now</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Available through <a href="#run">Maven, Gradle, Docker, and as a downloadable archive</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><category term="announcements" /><category term="microprofile" /><category term="java-se" /><category term="release" /><category term="security" /><summary type="html"><![CDATA[This release introduces support for selecting JWT signature algorithms from JOSE headers and adds Java 26 support. It also removes the default LTPA keys password for enhanced security, and includes file transfer restrictions and security vulnerability fixes.]]></summary></entry><entry><title type="html">Jakarta EE 11 Platform, Java 26, and more in 26.0.0.4-beta</title><link href="https://openliberty.io/blog/2026/04/07/26.0.0.4-beta.html" rel="alternate" type="text/html" title="Jakarta EE 11 Platform, Java 26, and more in 26.0.0.4-beta" /><published>2026-04-07T00:00:00+00:00</published><updated>2026-04-07T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/04/07/26.0.0.4-beta</id><content type="html" xml:base="https://openliberty.io/blog/2026/04/07/26.0.0.4-beta.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This beta introduces Jakarta EE 11 Platform and Web Profile, and delivers enhancements across Jakarta Authentication 3.1, Jakarta Authorization 3.0, and Jakarta Security 4.0. In addition, it provides a preview of Jakarta Data 1.1 M2, support for Java 26, MCP Server updates, and Jandex index improvements.</p>
</div>
<div class="paragraph">
<p>The <a href="/">Open Liberty</a> 26.0.0.4-beta includes the following beta features (along with <a href="/docs/latest/reference/feature/feature-overview.html">all GA features</a>):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#jakarta_ee">Jakarta EE 11 Platform and Web Profile</a></p>
<div class="ulist">
<ul>
<li>
<p><a href="#jakarta_auth">Application Authentication 3.1 (Jakarta Authentication 3.1)</a></p>
</li>
<li>
<p><a href="#jakarta_authz">Application Authorization 3.0 (Jakarta Authorization 3.0)</a></p>
</li>
<li>
<p><a href="#jakarta_security">Application Security 6.0 (Jakarta Security 4.0)</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><a href="#java_26">Beta support for Java 26</a></p>
</li>
<li>
<p><a href="#mcp">Updates to <code>mcpServer-1.0</code></a></p>
</li>
<li>
<p><a href="#jakarta_data">Preview of some Jakarta Data 1.1 M2 capability</a></p>
</li>
<li>
<p><a href="#jandex_index">Support for Reading Jandex Indexes from WEB-INF/classes in Web Modules</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>See also <a href="/blog/?search=beta&amp;key=tag">previous Open Liberty beta blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_ee">Jakarta EE 11 Platform and Web Profile</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Open Liberty 24.0.0.11-beta was one of the ratifying implementations for Jakarta EE 11 Core Profile. Building on that foundation, the 26.0.0.4-beta release completes Jakarta EE 11 beta support, including Jakarta EE Application Client 11.0, Jakarta EE Web Profile 11.0, and Jakarta EE Platform 11.0.</p>
</div>
<div class="paragraph">
<p>Liberty provides convenience features that bundle all component specifications in the Jakarta EE Web Profile and Jakarta EE Platform. The <code>webProfile-11.0</code> Liberty feature includes all Jakarta EE Web Profile functions, including the new Jakarta Data 1.0 component. The <code>jakartaee-11.0</code> Liberty feature provides the Jakarta EE Platform version 11 implementation. For Jakarta EE 11 features in the application client, use the <code>jakartaeeClient-11.0</code> Liberty feature.</p>
</div>
<div class="paragraph">
<p>These convenience features enable developers to rapidly develop applications using all APIs in the Web Profile and Platform specifications. Unlike the <code>jakartaee-10.0</code> Liberty feature, the <code>jakartaee-11.0</code> Liberty feature does not enable the Managed Beans specification function any longer, as this specification was removed from the platform which includes removal of the <code>jakarta.annotation.ManagedBean</code> annotation API. Applications relying on the <code>@ManagedBean</code> annotation must migrate to use CDI annotations.</p>
</div>
<div class="paragraph">
<p>The Jakarta EE 11 Platform specification removes all optional specifications from the platform, meaning Jakarta SOAP with Attachments, XML Binding, XML Web Services, and Enterprise Beans 2.x APIs functions are not included with the <code>jakartaee-11.0</code> Liberty feature. To use these capabilities, explicitly add Liberty features to your <code>server.xml</code> feature list: <code>xmlBinding-4.0</code> for XML Binding, <code>xmlWS-4.0</code> for SOAP with Attachments and XML Web Services, and <code>enterpriseBeansHome-4.0</code> for Jakarta Enterprise Beans 2.x APIs. Alternatively, use the equivalent versionless features with the <code>jakartaee-11.0</code> platform.</p>
</div>
<div class="paragraph">
<p>When using the Liberty application client with the <code>jakartaeeClient-11.0</code> feature, Jakarta SOAP with Attachments, XML Binding, and XML Web Services client functions are not available. To continue using these functions in your Liberty application client, enable the <code>xmlBinding-4.0</code> and the new <code>xmlWSClient-4.0</code> features in your <code>client.xml</code> file.</p>
</div>
<div class="paragraph">
<p>To enable the Jakarta EE 11 beta features in your Liberty server&#8217;s <code>server.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>jakartaee-11.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or you can add the Web Profile convenience feature to enable all of the Jakarta EE 11 Web Profile beta features at once:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">  <span class="tag">&lt;featureManager&gt;</span>
    <span class="tag">&lt;feature&gt;</span>webProfile-11.0<span class="tag">&lt;/feature&gt;</span>
  <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You can enable the Jakarta EE 11 features on the Application Client Container in the <code>client.xml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"> <span class="tag">&lt;featureManager&gt;</span>
       <span class="tag">&lt;feature&gt;</span>jakartaeeClient-11.0<span class="tag">&lt;/feature&gt;</span>
 <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For more information, see the <a href="https://jakarta.ee/specifications/platform/11/jakarta-platform-spec-11.0">Jakarta EE Platform 11 Specification</a> and the <a href="https://jakarta.ee/specifications/webprofile/11/jakarta-webprofile-spec-11.0">Jakarta EE Web Profile 11 Specification</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_auth">Application Authentication 3.1 (Jakarta Authentication 3.1)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Jakarta Authentication defines a general SPI for authentication mechanisms, which are controllers that interact with a caller and the container&#8217;s environment to obtain and validate the caller&#8217;s credentials. It then passes an authenticated identity (such as name and groups) to the container.</p>
</div>
<div class="paragraph">
<p>The 3.1 version of the Jakarta Authentication API removes the deprecated Permission-related fields in the <code>jakarta.security.auth.message.config.AuthConfigFactory</code> class. The methods in the class no longer do permission checking also. These changes are part of Jakarta EE 11&#8217;s removal of SecurityManager support.</p>
</div>
<div class="paragraph">
<p>You can enable the Jakarta Authentication 3.1 feature by adding the <code>appAuthentication-3.1</code> Liberty feature in the <code>server.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">    <span class="tag">&lt;featureManager&gt;</span>
        <span class="tag">&lt;feature&gt;</span>appAuthentication-3.1<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For more information, see the Jakarta Authentication 3.1 <a href="https://jakarta.ee/specifications/authentication/3.1/jakarta-authentication-spec-3.1">specification</a> and <a href="https://jakarta.ee/specifications/authentication/3.1/apidocs/jakarta.security.auth.message/module-summary.html">javadoc</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_authz">Application Authorization 3.0 (Jakarta Authorization 3.0)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Jakarta Authorization defines an SPI for authorization modules, which are repositories of permissions that facilitate subject-based security by determining whether a subject has a specific permission. It also defines algorithms that transform security constraints for specific containers, such as Jakarta Servlet or Jakarta Enterprise Beans, into these permissions.</p>
</div>
<div class="paragraph">
<p>The 3.0 API introduces the new <code>jakarta.security.jacc.PolicyFactory</code> and <code>jakarta.security.jacc.Policy</code> classes for doing authorization decisions. These classes are added to remove the dependency on the <code>java.security.Policy</code> class, which is deprecated in newer versions of Java. With the new <code>PolicyFactory</code> API, now you can have a <code>Policy</code> per policy context instead of having a global policy. This design allows separate policies to be maintained for each application.</p>
</div>
<div class="paragraph">
<p>Additionally, the 3.0 specification defines a mechanism to define <code>PolicyConfigurationFactory</code> and <code>PolicyFactory</code> classes in your application by using a <code>web.xml</code> file. This design allows for an application to have a different configuration than the server-scoped one, but still allow for it to delegate to a server scoped factory for any other applications. Authorization modules can do this delegation by using decorator constructors for both <code>PolicyConfigurationFactory</code> and <code>PolicyFactory</code> classes.</p>
</div>
<div class="paragraph">
<p>To configure your authorization modules in your application&#8217;s <code>web.xml</code> file, add specification defined context parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="preprocessor">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
<span class="tag">&lt;web-app</span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://jakarta.ee/xml/ns/jakartaee</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">xmlns:xsi</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://www.w3.org/2001/XMLSchema-instance</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">xsi:schemaLocation</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd</span><span class="delimiter">&quot;</span></span>
  <span class="attribute-name">version</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">6.1</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>

  <span class="tag">&lt;context-param&gt;</span>
    <span class="tag">&lt;param-name&gt;</span>jakarta.security.jacc.PolicyConfigurationFactory.provider<span class="tag">&lt;/param-name&gt;</span>
    <span class="tag">&lt;param-value&gt;</span>com.example.MyPolicyConfigurationFactory<span class="tag">&lt;/param-value&gt;</span>
  <span class="tag">&lt;/context-param&gt;</span>

  <span class="tag">&lt;context-param&gt;</span>
    <span class="tag">&lt;param-name&gt;</span>jakarta.security.jacc.PolicyFactory.provider<span class="tag">&lt;/param-name&gt;</span>
    <span class="tag">&lt;param-value&gt;</span>com.example.MyPolicyFactory<span class="tag">&lt;/param-value&gt;</span>
  <span class="tag">&lt;/context-param&gt;</span>

<span class="tag">&lt;/web-app&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Due to Jakarta Authorization 3.0 no longer using the <code>java.security.Policy</code> class and introducing a new configuration mechanism for authorization modules, the <code>com.ibm.wsspi.security.authorization.jacc.ProviderService</code> Liberty API is no longer available with the appAuthorization-3.0 feature. If a Liberty user feature configures authorization modules, the OSGi service that provided a <code>ProviderService</code> implementation must be updated to use the <code>PolicyConfigurationFactory</code> and <code>PolicyFactory</code> <code>set</code> methods. These methods configure the modules in the OSGi service. Alternatively you can use a Web Application Bundle (WAB) in your user feature to specify your security modules in a <code>web.xml</code> file.</p>
</div>
<div class="paragraph">
<p>Finally, the 3.0 API adds a new <code>jakarta.security.jacc.PrincipalMapper</code> class that you can obtain from the <code>PolicyContext</code> class when authorization processing is done in your <code>Policy</code> implementation. From this class, you can obtain the roles that are associated with a specific Subject to be able to determine whether the Subject is in the required role.</p>
</div>
<div class="paragraph">
<p>You can use the <code>PrincipalMapper</code> class in your <code>Policy</code> implementation&#8217;s <code>impliesByRole</code> (or <code>implies</code>) method, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">    <span class="directive">public</span> <span class="type">boolean</span> impliesByRole(<span class="predefined-type">Permission</span> p, <span class="predefined-type">Subject</span> subject) {
        <span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">PermissionCollection</span>&gt; perRolePermissions =
            PolicyConfigurationFactory.get().getPolicyConfiguration(contextID).getPerRolePermissions();
        PrincipalMapper principalMapper = PolicyContext.get(PolicyContext.PRINCIPAL_MAPPER);

        <span class="comment">// Check to see if the Permission is in the all authenticated users role (**)</span>
        <span class="keyword">if</span> (!principalMapper.isAnyAuthenticatedUserRoleMapped() &amp;&amp; !subject.getPrincipals().isEmpty()) {
            <span class="predefined-type">PermissionCollection</span> rolePermissions = perRolePermissions.get(<span class="string"><span class="delimiter">&quot;</span><span class="content">**</span><span class="delimiter">&quot;</span></span>);
            <span class="keyword">if</span> (rolePermissions != <span class="predefined-constant">null</span> &amp;&amp; rolePermissions.implies(p)) {
                <span class="keyword">return</span> <span class="predefined-constant">true</span>;
            }
        }

        <span class="comment">// Check to see if the roles for the Subject provided imply the permission</span>
        <span class="predefined-type">Set</span>&lt;<span class="predefined-type">String</span>&gt; mappedRoles = principalMapper.getMappedRoles(subject);
        <span class="keyword">for</span> (<span class="predefined-type">String</span> mappedRole : mappedRoles) {
            <span class="predefined-type">PermissionCollection</span> rolePermissions = perRolePermissions.get(mappedRole);
            <span class="keyword">if</span> (rolePermissions != <span class="predefined-constant">null</span> &amp;&amp; rolePermissions.implies(p)) {
                <span class="keyword">return</span> <span class="predefined-constant">true</span>;
            }
        }
        <span class="keyword">return</span> <span class="predefined-constant">false</span>;
    }</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can enable the Jakarta Authorization 3.0 feature by adding the <code>appAuthorization-3.0</code> Liberty feature in the <code>server.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml">    <span class="tag">&lt;featureManager&gt;</span>
        <span class="tag">&lt;feature&gt;</span>appAuthorization-3.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;/featureManager&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>For more information, see the Jakarta Authorization 3.0 <a href="https://jakarta.ee/specifications/authorization/3.0/jakarta-authorization-spec-3.0">specification</a> and <a href="https://jakarta.ee/specifications/authorization/3.0/apidocs/jakarta.security.jacc/module-summary.html">javadoc</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_security">Application Security 6.0 (Jakarta Security 4.0)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>An In-memory Identity Store is a developer-defined store of credential information that is used during the Open Liberty authentication and authorization work flow. It provides a quick, simple, and convenient authentication mechanism for Liberty application testing, debugging, demos, and more.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Multiple HTTP Authentication Mechanisms (HAMs) can now be defined within the same application. These mechanisms can be specified through built-in Jakarta annotations such as <code>@FormAuthenticationMechanismDefinition</code> or through custom implementations of the <code>HttpAuthenticationMechanism</code> interface.</p>
</li>
<li>
<p>A new method is added to the <code>SecurityContext</code> interface called <code>getAllDeclaredCallerRoles()</code>, which returns a list of all static (declared) application roles that the authenticated caller is in.</p>
</li>
<li>
<p>References to the <code>IdentityStorePermission</code> class are removed as it was previously deprecated.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>All features are available as part of <code>appSecurity-6.0</code>.</p>
</div>
<div class="paragraph">
<p>All feature use cases are targeted towards Jakarta EE web application developers who want to move to a modern Jakarta version and use implementations of the Jakarta Security 4.0 specification.</p>
</div>
<div class="sect2">
<h3 id="in-memory-identity-store">In-memory identity store</h3>
<div class="paragraph">
<p>Before the introduction of the new identity store specification, Jakarta Security natively supported only two types of identity stores: <strong>database</strong> and <strong>LDAP</strong>, both of which are used for credential validation. While effective for production environments, these options were considered heavyweight for testing, debugging, and demonstration scenarios.</p>
</div>
<div class="paragraph">
<p>Developers can also implement custom identity stores, but doing so requires bespoke code to collect, validate, and manage credential information. This effort can divert attention from core application development. A built-in, in-memory identity store reduces this burden for non-production use cases such as testing, debugging, and demonstrations.</p>
</div>
</div>
<div class="sect2">
<h3 id="multiple-hams">Multiple HAMs</h3>
<div class="paragraph">
<p>It is now possible for multiple authentication mechanisms to logically act as a single HTTP Authentication Mechanism (HAM), providing a more flexible, dynamic, and configurable approach to the authentication workflow.</p>
</div>
<div class="paragraph">
<p>Access to the <code>HttpAuthenticationMechanism</code> is now abstracted by an internal implementation of the new Jakarta <code>HttpAuthenticationMechanismHandler</code> interface. This implementation prioritizes custom‑defined HAMs first and then falls back to the built‑in (annotation‑defined) HAMs, in the following order: OpenID, custom form, form, and basic authentication mechanisms.</p>
</div>
<div class="paragraph">
<p>Developers are free to provide their own implementation of the <code>HttpAuthenticationMechanismHandler</code>, allowing them to define a custom prioritization strategy for selecting HAMs. They <strong>must</strong> supply such an implementation if any built‑in HAMs are defined with the optional <code>qualifiers</code> attribute set.</p>
</div>
</div>
<div class="sect2">
<h3 id="getalldeclaredcallerroles">getAllDeclaredCallerRoles()</h3>
<div class="paragraph">
<p>The <code>SecurityContext</code> interface implementation is updated to include a new method, <code>getAllDeclaredCallerRoles()</code>, which returns a list of the declared application roles for an authenticated caller. If the caller is not authenticated or has no declared roles, the method returns an empty list.</p>
</div>
</div>
<div class="sect2">
<h3 id="how-to-use">How to use</h3>
<div class="paragraph">
<p>Enable Jakarta Security 4.0 features within the <code>server.xml</code> file by adding the <code>appSecurity-6.0</code> feature as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;server</span> <span class="attribute-name">description</span> = <span class="string"><span class="delimiter">&quot;</span><span class="content">Liberty server</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span>

    . . .

    <span class="tag">&lt;featureManager&gt;</span>
        <span class="tag">&lt;feature&gt;</span>appSecurity-6.0<span class="tag">&lt;/feature&gt;</span>
    <span class="tag">&lt;/featureManager&gt;</span>

    <span class="tag">&lt;webAppSecurity</span> <span class="attribute-name">allowInMemoryIdentityStores</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span>

    . . .</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
As shown in the preceding example, when an application defines an in‑memory identity store in code, its use must also be explicitly enabled in the <code>server.xml</code> configuration. By default, the <code>allowInMemoryIdentityStores</code> attribute is set to false, which instructs the Liberty authentication workflows not to use in‑memory identity stores, even when a custom identity store handler is present. For applications that rely on multiple HAMs, the <code>allowInMemoryIdentityStores</code> attribute does not need to be set.
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="in-memory-identity-store-2">In-Memory Identity Store</h4>
<div class="paragraph">
<p>The <a href="https://jakarta.ee/specifications/security/4.0/jakarta-security-spec-4.0#in-memory-annotation">Jakarta Security Specification 4.0</a> provides details on how to specify credential information to be used during the authentication workflow through the new <code>@InMemoryIdentityStoreDefinition</code> annotation, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">. . .

<span class="annotation">@InMemoryIdentityStoreDefinition</span> (
    priority = <span class="integer">10</span>,
    priorityExpression = <span class="string"><span class="delimiter">&quot;</span><span class="content">${80/20}</span><span class="delimiter">&quot;</span></span>,
    useFor = {VALIDATE, PROVIDE_GROUPS},
    useForExpression = <span class="string"><span class="delimiter">&quot;</span><span class="content">#{'VALIDATE'}</span><span class="delimiter">&quot;</span></span>,
    value = {
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">jasmine</span><span class="delimiter">&quot;</span></span>, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">secret1</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">caller</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> } )
    }
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>All attributes for the <code>@InMemoryIdentityStoreDefinition</code> annotation are shown in the example. The <code>priority</code>, <code>priorityExpression</code>, <code>useFor</code>, and <code>useForExpression</code> attributes are optional and set to sensible defaults.</p>
</div>
<div class="paragraph">
<p>The <code>@Credentials</code> annotation maps one or more caller names to a password and optional group values. The <code>callerName</code> and <code>password</code> attributes are mandatory. If either one is omitted, a compilation error occurs.</p>
</div>
<div class="paragraph">
<p>The example demonstrates a single caller definition with credential information that uses a plain-text password. However, it is highly recommended that passwords be supplied that uses an Open Liberty–supported encoding mechanism, as illustrated in the next example.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@InMemoryIdentityStoreDefinition</span> (
    value = {
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">jasmine</span><span class="delimiter">&quot;</span></span>,  password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{xor}LDo8LTorbg==</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">caller</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> } ),
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">frank</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> }, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{hash}ARAAA &lt;sequence shortened&gt; Fyyw==</span><span class="delimiter">&quot;</span></span>),
        <span class="annotation">@Credentials</span>(callerName = <span class="string"><span class="delimiter">&quot;</span><span class="content">sally</span><span class="delimiter">&quot;</span></span>, groups = { <span class="string"><span class="delimiter">&quot;</span><span class="content">user</span><span class="delimiter">&quot;</span></span> }, password = <span class="string"><span class="delimiter">&quot;</span><span class="content">{aes}ARAFIYJ &lt;sequence shortened&gt; WRQNA==</span><span class="delimiter">&quot;</span></span>)
    }
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Encrypted and encoded passwords can be generated by using the Open Liberty <code>securityUtility</code>, which is included under the <code>wlp/bin/securityUtility</code> path. The following example demonstrates how to encode a text string by using the <code>xor</code> encoding mechanism.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">wlp/bin/securityUtility encode --encoding=xor
Enter text: &lt;enter text to encode&gt;
Re-enter text:
{xor}PTA9Lyg</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="multiple-hams-2">Multiple HAMs</h4>
<div class="sect4">
<h5 id="application-specification">Application Specification</h5>
<div class="paragraph">
<p>The <a href="https://jakarta.ee/specifications/security/4.0/jakarta-security-spec-4.0#handling-multiple-authentication-mechanisms">Jakarta Security 4.0</a> specification allows multiple HTTP Authentication Mechanisms (HAMs) to be defined within a single application, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@BasicAuthenticationMechanismDefinition</span>(realmName=<span class="string"><span class="delimiter">&quot;</span><span class="content">basicAuth</span><span class="delimiter">&quot;</span></span>)

<span class="annotation">@FormAuthenticationMechanismDefinition</span>(
                loginToContinue = <span class="annotation">@LoginToContinue</span>(errorPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/form-login-error.html</span><span class="delimiter">&quot;</span></span>,
                loginPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/form-login.html</span><span class="delimiter">&quot;</span></span>))

<span class="annotation">@CustomFormAuthenticationMechanismDefinition</span>(
                loginToContinue = <span class="annotation">@LoginToContinue</span>(errorPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/custom-login-error.html</span><span class="delimiter">&quot;</span></span>,
                loginPage = <span class="string"><span class="delimiter">&quot;</span><span class="content">/custom-login.html</span><span class="delimiter">&quot;</span></span>))</code></pre>
</div>
</div>
<div class="paragraph">
<p>This example demonstrates how three HTTP Authentication Mechanisms (HAMs) can be defined within a single application.</p>
</div>
<div class="paragraph">
<p>Custom HAMs can also be defined in the same application by implementing the <code>HttpAuthenticationMechanism</code> interface in one or more classes, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="comment">// @Priority is optional and used to control selection priority if multiple custom definitions exist</span>
<span class="annotation">@Priority</span>(<span class="integer">100</span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomHAM</span> <span class="directive">implements</span> HttpAuthenticationMechanism {

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthenticationStatus validateRequest(
        HttpServletRequest request,
        HttpServletResponse response,
        HttpMessageContext httpMessageContext) <span class="directive">throws</span> <span class="exception">AuthenticationException</span> {

            <span class="comment">// implement custom logic here, and return an AuthenticationStatus</span>
            <span class="keyword">return</span> AuthenticationStatus.NOT_DONE;
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>So a single application can have a mix of both annotation-defined HAMs and custom ones. In the previous two snippets of code, a total of four HAMs are defined (three by annotation and one custom one).</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<code>@Priority</code> must be used to raise or lower the priority of one custom HAM over another. If not specified, then a default priority is assigned. If more than one custom HAM is defined, their priorities need to be explicitly set to unique values. If the priorities are set to the same value or remain unset and inherit the same default value, an error occurs.
</td>
</tr>
</table>
</div>
<hr>
</div>
<div class="sect4">
<h5 id="ham-resolution">HAM resolution</h5>
<div class="paragraph">
<p>An internal implementation of the Jakarta Security 4.0 <code>HttpAuthenticationMechanismHandler</code> interface (the "internal HAM handler") is provided. When an application defines multiple HAMs, this internal handler selects a single HAM to be used in the authentication flow.</p>
</div>
<div class="paragraph">
<p>The order in which HAMs are considered (when present) is as follows:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Custom (developer‑provided) HAMs</p>
<div class="ulist">
<ul>
<li>
<p>If multiple custom HAMs are defined, their relative order is resolved by using <code>@Priority</code>.</p>
</li>
</ul>
</div>
</li>
<li>
<p><code>OpenIdAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>CustomFormAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>FormAuthenticationMechanismDefinition</code></p>
</li>
<li>
<p><code>BasicAuthenticationMechanismDefinition</code></p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Given this ordering, the Custom HAM is always selected in the authentication workflow if all five HAM types are defined in the application.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
A developer must provide a custom implementation of the <code>HttpAuthenticationMechanismHandler</code> interface (a "custom HAM handler") if the internal HAM handler does not meet their requirements. A custom handler always takes precedence over the internal HAM handler, allowing any tailored algorithm to select a single HAM from multiple available mechanisms. Additional information about creating and using a custom HAM handler is provided in a later section.
</td>
</tr>
</table>
</div>
<hr>
</div>
<div class="sect4">
<h5 id="qualifiers">Qualifiers</h5>
<div class="paragraph">
<p>HAMs - whether defined through annotations or as custom defined - can also include an optional class‑level qualifier to simplify HAM injection into a custom HAM handler. For example, if you want to define qualified HAMs, you would first declare qualifier interfaces such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Qualifier</span>
<span class="annotation">@Retention</span>(RUNTIME)
<span class="annotation">@Target</span>({TYPE, METHOD, FIELD, PARAMETER})
<span class="directive">public</span> <span class="annotation">@interface</span> Admin {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><em>(Not shown is the qualifier definitions for <code>User</code>, <code>Fallback</code> which follow an identical pattern, and are used below).</em></p>
</div>
<div class="paragraph">
<p>Then, import and use the qualifiers in your in-built or custom HAM specifications, such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@CustomFormAuthenticationMechanismDefinition</span>(&lt;existing details not shown&gt;, qualifiers={Admin.class})

<span class="annotation">@BasicAuthenticationMechanismDefinition</span>(&lt;existing details not shown&gt;, qualifiers={User.class})</code></pre>
</div>
</div>
<div class="paragraph">
<p>and</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="annotation">@Fallback</span>   <span class="comment">// add custom qualifier to be used during injection</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomHAM</span> <span class="directive">implements</span> HttpAuthenticationMechanism {

    <span class="annotation">@Override</span>
    <span class="directive">public</span> AuthenticationStatus validateRequest (. . .)  {
         . . .
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The three HAMs defined in the example are available for injection into a custom HAM handler based on their qualifier names. This scenario represents the typical use case for applications that define multiple HAMs.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
If qualifiers are specified in any of the built-in HAM definitions, a custom HAM handler must be provided; otherwise, an error is raised. This requirement comes directly from the Jakarta Security specification. Details about creating and using a custom HAM handler are explained in the next section.
</td>
</tr>
</table>
</div>
<hr>
<div class="paragraph">
<p>To implement a custom HAM handler, define a public class that is annotated with <code>@ApplicationScoped</code> that implements the <code>HttpAuthenticationMechanismHandler</code> interface. Also, inject the qualified HAMs by using standard CDI syntax, as shown in the following section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Default</span>
<span class="annotation">@ApplicationScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">CustomHAMHandler</span> <span class="directive">implements</span> HttpAuthenticationMechanismHandler {

    <span class="annotation">@Inject</span> <span class="annotation">@Admin</span> <span class="comment">// this will be the FormHAM</span>
    <span class="directive">private</span> HttpAuthenticationMechanism adminHAM;

    <span class="annotation">@Inject</span> <span class="annotation">@User</span> <span class="comment">// this will be the BasicHAM</span>
    <span class="directive">private</span> HttpAuthenticationMechanism userHAM;

    <span class="annotation">@Inject</span> <span class="annotation">@Fallback</span> <span class="comment">// this will be the Custom HAM</span>
    <span class="directive">private</span> HttpAuthenticationMechanism fallbackHAM;

    <span class="directive">public</span> AuthenticationStatus validateRequest(
        HttpServletRequest request,
        HttpServletResponse response,
        HttpMessageContext context
    ) <span class="directive">throws</span> <span class="exception">AuthenticationException</span> {

        <span class="predefined-type">String</span> path = request.getRequestURI();

        <span class="comment">// route to appropriate mechanism based on path, default to my-realm</span>
        <span class="keyword">if</span> (path.startsWith(<span class="string"><span class="delimiter">&quot;</span><span class="content">/admin</span><span class="delimiter">&quot;</span></span>)) { <span class="comment">// FormHAM</span>
            <span class="keyword">return</span> adminHAM.validateRequest(request, response, context);
        } <span class="keyword">else</span> <span class="keyword">if</span> (path.startsWith(<span class="string"><span class="delimiter">&quot;</span><span class="content">/user</span><span class="delimiter">&quot;</span></span>)) { <span class="comment">// BasicHAM</span>
            <span class="keyword">return</span> userHAM.validateRequest(request, response, context);
        } <span class="keyword">else</span> { <span class="comment">// Custom HAM</span>
            <span class="keyword">return</span> fallbackHAM.validateRequest(request, response, context);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This custom HAM handler takes priority over the internal HAM handler, allowing a different prioritisation algorithm to be implemented.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="getalldeclaredcallerroles-2">getAllDeclaredCallerRoles()</h4>
<div class="paragraph">
<p>To use the new <code>SecurityContext</code> method, inject the <code>SecurityContext</code> implementation into your application and call the method directly, as shown in the following section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">    <span class="annotation">@Inject</span>
    <span class="directive">private</span> SecurityContext securityContext;

. . .

    Set&lt;<span class="predefined-type">String</span>&gt; allDeclaredCallerRoles = securityContext.getAllDeclaredCallerRoles();

    <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">&quot;</span><span class="content">All declared caller roles for caller [</span><span class="delimiter">&quot;</span></span>
        + securityContext.getCallerPrincipal().getName()
        + <span class="string"><span class="delimiter">&quot;</span><span class="content">] are </span><span class="delimiter">&quot;</span></span>
        + allDeclaredCallerRoles.toString());</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="learn-more">Learn more</h3>
<div class="paragraph">
<p>Further information can be found in the Jakarta Security Specification 4.0:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://jakarta.ee/specifications/security/4.0/jakarta-security-spec-4.0#in-memory-annotation">in-memory identity store</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/security/4.0/jakarta-security-spec-4.0#handling-multiple-authentication-mechanisms">multiple-hams</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/security/4.0/jakarta-security-spec-4.0#retrieving-and-testing-for-caller-data">getAllDeclaredCallerRoles()</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>For more information about the <code>securityUtility</code> command, see the <a href="https://www.ibm.com/docs/en/was-liberty/base?topic=applications-securityutility-command">WAS Liberty base topic</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="java_26">Beta support for Java 26</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Java 26 is a recent Java release that introduces new features and enhancements over earlier versions that can be useful to review. This release is not a long-term support (LTS) release.</p>
</div>
<div class="paragraph">
<p>There are 10 new features (JEPs) in <a href="https://openjdk.org/projects/jdk/26/">Java 26</a>. Five are test features and five are fully delivered.</p>
</div>
<div class="paragraph">
<p><strong>Test Features:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>524: <a href="https://openjdk.org/jeps/524">PEM Encodings of Cryptographic Objects (Second Preview)</a></p>
</li>
<li>
<p>525: <a href="https://openjdk.org/jeps/525">Structured Concurrency (Sixth Preview)</a></p>
</li>
<li>
<p>526: <a href="https://openjdk.org/jeps/526">Lazy Constants (Second Preview)</a></p>
</li>
<li>
<p>529: <a href="https://openjdk.org/jeps/529">Vector API (Eleventh Incubator)</a></p>
</li>
<li>
<p>530: <a href="https://openjdk.org/jeps/530">Primitive Types in Patterns, instanceof, and switch (Fourth Preview)</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Delivered Features:</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>500: <a href="https://openjdk.org/jeps/500">Prepare to Make Final Mean Final</a></p>
</li>
<li>
<p>504: <a href="https://openjdk.org/jeps/504">Remove the Applet API</a></p>
</li>
<li>
<p>516: <a href="https://openjdk.org/jeps/516">Ahead-of-Time Object Caching with Any GC</a></p>
</li>
<li>
<p>517: <a href="https://openjdk.org/jeps/517">HTTP/3 for the HTTP Client API</a></p>
</li>
<li>
<p>522: <a href="https://openjdk.org/jeps/522">G1 GC: Improve Throughput by Reducing Synchronization</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>A new change JEP 500 ("Prepare to Make Final Mean Final") in Java 26 starts enforcing true immutability of final fields by restricting their mutation when using deep reflection.
In Java 26, such mutations still work but trigger runtime warnings by default, preparing developers for stricter enforcement.
Future releases would likely throw exceptions instead, making the final truly nonmutable.</p>
</div>
<div class="paragraph">
<p>Developers can opt in early to this stricter behavior by using a JVM flag (for example, <code>--illegal-final-field-mutation=deny</code>) to detect issues sooner.
This change improves program correctness, security, and JVM optimizations.</p>
</div>
<div class="paragraph">
<p>Take advantage of these changes now to gain more time to evaluate how your applications
and microservices behave on Java 26.</p>
</div>
<div class="paragraph">
<p>Get started today by downloading the latest release of  <a href="https://developer.ibm.com/languages/java/semeru-runtimes/downloads/">IBM Semeru Runtime 26</a> or <a href="https://adoptium.net/temurin/releases/?version=26">Temurin 26</a>, then download and install the Open Liberty <a href="/downloads/#runtime_betas">26.0.0.4-beta</a>. Update your Liberty server&#8217;s <a href="/docs/latest/reference/config/server-configuration-overview.html#server-env">server.env</a> file with <code>JAVA_HOME</code> set to your Java 26 installation directory and start testing.</p>
</div>
<div class="paragraph">
<p>For more information on Java 26, see the Java 26 <a href="https://jdk.java.net/26/release-notes">release notes page</a> and <a href="https://docs.oracle.com/en/java/javase/26/docs/api/index.html">API Javadoc page</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="mcp">Updates to <code>mcpServer-1.0</code></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> is an open standard that enables AI applications to access real-time information from external sources. The Liberty MCP Server feature <code>mcpServer-1.0</code> allows developers to expose the business logic of their applications, allowing it to be integrated into agentic AI workflows.</p>
</div>
<div class="paragraph">
<p>This beta release of Open Liberty includes updates to the <code>mcpServer-1.0</code> feature including dynamic registration of tools and support for version <code>2025-11-25</code> of the protocol.</p>
</div>
<div class="sect2">
<h3 id="dynamically-register-mcp-tools">Dynamically register MCP tools</h3>
<div class="paragraph">
<p>Tools can now be registered dynamically through an API. This capability allows the set of available tools on the server to be adjusted based on configuration or environment.</p>
</div>
<div class="paragraph">
<p>Tools can be registered by injecting <code>ToolManager</code> and calling its methods to add, remove, and list the available tools on the server. The full Javadoc for <code>ToolManager</code> can be found within the Liberty beta in <code>dev/api/ibm/javadoc/io.openliberty.mcp_1.0-javadoc.zip</code>.</p>
</div>
<div class="paragraph">
<p>Tools can be registered when the application starts through the CDI <code>Startup</code> event. See the following example where the <code>Startup</code> event is used to register a weather forecast tool only if a <code>WeatherClient</code> bean is available.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">    <span class="annotation">@Inject</span>
    ToolManager toolManager;

    <span class="annotation">@Inject</span>
    Instance&lt;WeatherClient&gt; weatherClientInstance;

    <span class="directive">private</span> <span class="type">void</span> createWeatherTool(<span class="annotation">@Observes</span> Startup startup) {
        <span class="keyword">if</span> (weatherClientInstance.isResolvable()) {
            WeatherClient weatherClient = weatherClientInstance.get();
            toolManager.newTool(<span class="string"><span class="delimiter">&quot;</span><span class="content">getForecast</span><span class="delimiter">&quot;</span></span>)
                       .setTitle(<span class="string"><span class="delimiter">&quot;</span><span class="content">Weather Forecast Provider</span><span class="delimiter">&quot;</span></span>)
                       .setDescription(<span class="string"><span class="delimiter">&quot;</span><span class="content">Get weather forecast for a location</span><span class="delimiter">&quot;</span></span>)
                       .addArgument(<span class="string"><span class="delimiter">&quot;</span><span class="content">latitude</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Latitude of the location</span><span class="delimiter">&quot;</span></span>, <span class="predefined-constant">true</span>, <span class="predefined-type">Double</span>.class)
                       .addArgument(<span class="string"><span class="delimiter">&quot;</span><span class="content">longitude</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">Longitude of the location</span><span class="delimiter">&quot;</span></span>, <span class="predefined-constant">true</span>, <span class="predefined-type">Double</span>.class)
                       .setHandler(toolArguments -&gt; {
                           <span class="predefined-type">Double</span> latitude = (<span class="predefined-type">Double</span>) toolArguments.args().get(<span class="string"><span class="delimiter">&quot;</span><span class="content">latitude</span><span class="delimiter">&quot;</span></span>);
                           <span class="predefined-type">Double</span> longitude = (<span class="predefined-type">Double</span>) toolArguments.args().get(<span class="string"><span class="delimiter">&quot;</span><span class="content">longitude</span><span class="delimiter">&quot;</span></span>);
                           <span class="predefined-type">String</span> result = weatherClient.getForecast(
                                     latitude,
                                     longitude,
                                     <span class="integer">4</span>,
                                     <span class="string"><span class="delimiter">&quot;</span><span class="content">temperature_2m,snowfall,rain,precipitation,precipitation_probability</span><span class="delimiter">&quot;</span></span>);
                           <span class="keyword">return</span> ToolResponse.success(result);
                       })
                       .register();
        }
    }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Tool registration starts with <code>newTool()</code>, then the information about the tool is added, including its title, description, and arguments. The handler supplies the code to run when the tool is called. It has one parameter, which is a <code>ToolArguments</code> object, which provides access to the arguments and other information about the tool call request. The handler must always create and return a <code>ToolResponse</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="accept-the-2025-11-25-mcp-protocol">Accept the 2025-11-25 MCP protocol</h3>
<div class="paragraph">
<p>The <code>mcpServer-1.0</code> feature now accepts version <code>2025-11-25</code> of the Model Context Protocol (MCP), although it does not support any new features introduced in this version of the protocol.</p>
</div>
<div class="paragraph">
<p>Supporting MCP 2025-11-25 introduces two notable behavior changes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Tool name restrictions</strong>: Tool names are now limited to ASCII letters ('A–Z, a–z'), digits ('0–9'), and the underscore ('_'), hyphen ('-'), and dot ('.') characters.</p>
</li>
<li>
<p><strong>Improved handling of invalid tool arguments</strong>: Invalid arguments passed when invoking a tool are now treated as <em>tool execution errors</em> rather than <em>protocol errors</em>. This change allows the LLM to receive feedback that the tool was called incorrectly and to retry with corrected arguments.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="paginated-results">Paginated results</h3>
<div class="paragraph">
<p>The result of a <code>tools/list</code> call is now paginated with a page size of 20. This means that if there are more than 20 tools deployed on the server, clients make several small calls to retrieve the list of tools, rather than one large call.</p>
</div>
</div>
<div class="sect2">
<h3 id="bug-fixes">Bug fixes</h3>
<div class="ulist">
<ul>
<li>
<p>During cancellation of a tool call, we check that both the session id and the authenticated user match the session id and the user that made the tool call. Previously only the session id was checked.</p>
</li>
<li>
<p>Messages that are returned to the MCP client no longer contain Open Liberty message codes.</p>
</li>
<li>
<p>Structured content is only returned when client is using protocol version <code>2025-06-18</code> or later.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="further-information">Further information</h3>
<div class="ulist">
<ul>
<li>
<p>For more information about the <code>mcpServer-1.0</code> feature and how to get started using the beta, see our <a href="/blog/2025/10/23/mcp-standalone-blog.html">dedicated blog post</a>.</p>
</li>
<li>
<p>For more information about the Model Context Protocol, see <a href="https://modelcontextprotocol.io/docs/getting-started/intro">modelcontextprotocol.io</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jakarta_data">Preview of some Jakarta Data 1.1 M2 capability</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Previews some new capability at the Jakarta Data 1.1 Milestone 2 level: <code>Constraint</code> subtype parameters for repository methods that constrain to repository <code>@Find</code> operations and limited use of <code>Restriction</code> with repository <code>@Find</code> operations. Also included from the prior beta are: retrieving a subset/projection of entity attributes and the <code>@Is</code> annotation.</p>
</div>
<div class="paragraph">
<p>Previously, parameter-based <code>@Find</code> reposotory methods could filter results only using equality conditions. This limitation has now been removed, allowing additional filtering options to be defined.</p>
</div>
<div class="paragraph">
<p>Filtering can now be specified in several ways. One approach is to define the repository method parameter as a <code>Constraint</code> subtype, or to indicate the constraint subtype by using the <code>@Is</code> annotation. Alternatively, a repository <code>@Find</code> method can include a special parameter of type <code>Restriction</code>, allowing the application to supply one or more restrictions dynamically at runtime when the method is invoked.</p>
</div>
<div class="paragraph">
<p>In Jakarta Data, you define simple Java objects called <code>entities</code> to represent data, and interfaces called <code>repositories</code> to define data operations. You inject a repository into your application and use it. The implementation of the repository is automatically provided.</p>
</div>
<div class="paragraph">
<p>Start by defining an entity class that corresponds to your data. With relational databases, the entity class corresponds to a database table and the entity properties (public methods and fields of the entity class) generally correspond to the columns of the table. An entity class can be:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>annotated with <code>jakarta.persistence.Entity</code> and related annotations from Jakarta Persistence</p>
</li>
<li>
<p>a Java class without entity annotations, in which case the primary key is inferred from an entity property named <code>id</code> or ending with <code>Id</code> and an entity property named <code>version</code> designates an automatically incremented version column.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Here&#8217;s a simple entity:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Entity</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">Product</span> {
    <span class="annotation">@Id</span>
    <span class="directive">public</span> <span class="type">long</span> id;

    <span class="directive">public</span> <span class="predefined-type">String</span> name;

    <span class="directive">public</span> <span class="type">double</span> price;

    <span class="directive">public</span> <span class="type">double</span> weight;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>After you define the entity to represent the data, it is usually helpful to have your IDE generate a static metamodel class for it. By convention, static metamodel classes begin with the underscore ('_') character, followed by the entity name.</p>
</div>
<div class="paragraph">
<p>Because this beta is available well before the release of Jakarta Data 1.1, IDE support for this generation cannot be expected yet. However, a static metamodel class can be provided in the same form that an IDE would generate for the <code>Product</code> entity.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@StaticMetamodel</span>(Product.class)
<span class="directive">public</span> <span class="type">interface</span> <span class="class">_Product</span> {
    <span class="predefined-type">String</span> ID = <span class="string"><span class="delimiter">&quot;</span><span class="content">id</span><span class="delimiter">&quot;</span></span>;
    <span class="predefined-type">String</span> NAME = <span class="string"><span class="delimiter">&quot;</span><span class="content">name</span><span class="delimiter">&quot;</span></span>;
    <span class="predefined-type">String</span> PRICE = <span class="string"><span class="delimiter">&quot;</span><span class="content">price</span><span class="delimiter">&quot;</span></span>;
    <span class="predefined-type">String</span> WEIGHT = <span class="string"><span class="delimiter">&quot;</span><span class="content">weight</span><span class="delimiter">&quot;</span></span>;

    NumericAttribute&lt;Product, <span class="predefined-type">Long</span>&gt; id = NumericAttribute.of(
            Product.class, ID, <span class="type">long</span>.class);
    <span class="predefined-type">TextAttribute</span>&lt;Product&gt; name = <span class="predefined-type">TextAttribute</span>.of(
            Product.class, NAME);
    NumericAttribute&lt;Product, <span class="predefined-type">Double</span>&gt; price = NumericAttribute.of(
            Product.class, PRICE, <span class="type">double</span>.class);
    NumericAttribute&lt;Product, <span class="predefined-type">Double</span>&gt; weight = NumericAttribute.of(
            Product.class, WEIGHT, <span class="type">double</span>.class);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first half of the static metamodel class includes constants for each of the entity attribute names so that you don&#8217;t need to otherwise hardcode string values into your application. The second half of the static metamodel class provides a special instance for each entity attribute, from which you can build restrictions and sorting to apply to queries at run time. This capability enables many powerful operations with repository queries, but those features are deferred to a future beta.</p>
</div>
<div class="paragraph">
<p>A repository defines operations that are related to the Product entity. Your repository interface can inherit from built-in interfaces such as <code>BasicRepository</code> and <code>CrudRepository</code> to gain various general-purpose repository methods for inserting, updating, deleting, and querying for entities. In addition to these capabilities, the repository can define custom operations by using the static metamodel and annotations such as @Find or @Delete.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Repository</span>(dataStore = <span class="string"><span class="delimiter">&quot;</span><span class="content">java:app/jdbc/my-example-data</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">interface</span> <span class="class">Products</span> <span class="directive">extends</span> CrudRepository&lt;Product, <span class="predefined-type">Long</span>&gt; {

    <span class="comment">// Filtering is pre-defined at development time by the @Is annotation,</span>
    <span class="annotation">@Find</span>
    <span class="annotation">@OrderBy</span>(_Product.PRICE)
    <span class="annotation">@OrderBy</span>(_Product.NAME)
    <span class="predefined-type">List</span>&lt;Product&gt; costingUpTo(<span class="annotation">@By</span>(_Product.PRICE) <span class="annotation">@Is</span>(AtMost.class) <span class="type">double</span> maxPrice);

    <span class="comment">// Constraint types, such as Like, can also pre-define filtering.</span>
    <span class="comment">// Allow additional filtering at run time by including a Restriction,</span>
    <span class="annotation">@Find</span>
    Page&lt;Product&gt; named(<span class="annotation">@By</span>(_Product.Name) Like namePattern,
                        Restriction&lt;Product&gt; filter,
                        Order&lt;Product&gt; sorting,
                        PageRequest pageRequest);

    <span class="comment">// Retrieve a single entity attribute, identified by @Select,</span>
    <span class="annotation">@Find</span>
    <span class="annotation">@Select</span>(_Product.PRICE)
    Optional&lt;<span class="predefined-type">Double</span>&gt; priceOf(<span class="annotation">@By</span>(_Product.ID) <span class="type">long</span> productNum);

    <span class="comment">// Retrieve multiple entity attributes as a Java record,</span>
    <span class="annotation">@Find</span>
    <span class="annotation">@Select</span>(_Product.WEIGHT)
    <span class="annotation">@Select</span>(_Product.NAME)
    Optional&lt;WeightInfo&gt; weightAndNameOf(<span class="annotation">@By</span>(_Product.ID) <span class="type">long</span> productNum);

    <span class="directive">static</span> record WeightInfo(<span class="type">double</span> itemWeight, <span class="predefined-type">String</span> itemName) {}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is an example of using the repository and static metamodel:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DataSourceDefinition</span>(name = <span class="string"><span class="delimiter">&quot;</span><span class="content">java:app/jdbc/my-example-data</span><span class="delimiter">&quot;</span></span>,
                      className = <span class="string"><span class="delimiter">&quot;</span><span class="content">org.postgresql.xa.PGXADataSource</span><span class="delimiter">&quot;</span></span>,
                      databaseName = <span class="string"><span class="delimiter">&quot;</span><span class="content">ExampleDB</span><span class="delimiter">&quot;</span></span>,
                      serverName = <span class="string"><span class="delimiter">&quot;</span><span class="content">localhost</span><span class="delimiter">&quot;</span></span>,
                      portNumber = <span class="integer">5432</span>,
                      user = <span class="string"><span class="delimiter">&quot;</span><span class="content">${example.database.user}</span><span class="delimiter">&quot;</span></span>,
                      password = <span class="string"><span class="delimiter">&quot;</span><span class="content">${example.database.password}</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="type">class</span> <span class="class">MyServlet</span> <span class="directive">extends</span> HttpServlet {
    <span class="annotation">@Inject</span>
    Products products;

    <span class="directive">protected</span> <span class="type">void</span> doGet(HttpServletRequest req, HttpServletResponse resp)
            <span class="directive">throws</span> ServletException, <span class="exception">IOException</span> {
        <span class="comment">// Insert:</span>
        Product prod = ...
        prod = products.insert(prod);

        <span class="comment">// Filter by supplying values only:</span>
        <span class="predefined-type">List</span>&lt;Product&gt; found = products.costingUpTo(<span class="float">50.0</span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">%keyboard%</span><span class="delimiter">&quot;</span></span>);

        <span class="comment">// Compute filtering at run time,</span>
        Page&lt;Product&gt; page1 = products.named(
                        Like.suffix(<span class="string"><span class="delimiter">&quot;</span><span class="content">printer</span><span class="delimiter">&quot;</span></span>),
                        Restrict.all(_Product.price.times(taxRate).plus(shipping).lessThan(<span class="float">250.0</span>),
                                     _Product.weight.times(kgToPounds).between(<span class="float">10.0</span>, <span class="float">20.0</span>)),
                        Order.by(_Product.price.desc(),
                                 _Product.name.asc(),
                                 _Product.id.asc()),
                        PageRequest.ofSize(<span class="integer">10</span>));

        <span class="comment">// Find one entity attribute:</span>
        price = products.priceOf(prod.id).orElseThrow();

        <span class="comment">// Find multiple entity attributes as a Java record:</span>
        WeightInfo info = products.getWeightAndName(prod.id);
        <span class="predefined-type">System</span>.out.println(info.itemName() + <span class="string"><span class="delimiter">&quot;</span><span class="content"> weighs </span><span class="delimiter">&quot;</span></span> + info.itemWeight() + <span class="string"><span class="delimiter">&quot;</span><span class="content"> kg.</span><span class="delimiter">&quot;</span></span>);

        ...
    }
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="learn-more-2">Learn more</h3>
<div class="ulist">
<ul>
<li>
<p><a href="https://jakarta.ee/specifications/data/1.1/apidocs">Jakarta Data 1.1 API Javadoc</a></p>
</li>
<li>
<p><a href="https://jakarta.ee/specifications/data/1.1/">Jakarta Data 1.1 overview page</a></p>
</li>
<li>
<p>Maven coordinates for Data 1.1 Milestone 2:</p>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>jakarta.data<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>jakarta.data-api<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>1.1.0-M2<span class="tag">&lt;/version&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This beta includes only the Data 1.1 features for entity subsets and projections, the @Is annotation, and Constraint subtypes as parameters for repository Find methods. It also includes limited support for Restriction as a special parameter for repository Find methods. The <code>navigate</code> operations are not implemented for restrictions and constraints. Other new Data 1.1 features are not included in this beta.
</td>
</tr>
</table>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jandex_index">Support for Reading Jandex Indexes from WEB-INF/classes in Web Modules.</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Previously, Jandex indexes across all module types were read only from <code>META-INF/jandex.idx</code>. This update extends support for web modules to also read indexes from <code>WEB-INF/classes/META-INF/jandex.idx</code>, bringing it in line with industry practices.</p>
</div>
<div class="paragraph">
<p>Jandex indexes are read from the original or the new location only when Jandex use is enabled. For compatibility with earlier versions, reading indexes from the new location must be enabled using a new application property.</p>
</div>
<div class="sect2">
<h3 id="benefits">Benefits</h3>
<div class="ulist">
<ul>
<li>
<p>Improved Compatibility: Improve compatibility with industry standard practices.</p>
</li>
<li>
<p>Faster Startup Times: Continue to benefit from Jandex&#8217;s efficient annotation indexing.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="target-persona">Target Persona</h3>
<div class="paragraph">
<p>Customers who wish to place Jandex indexes at industry standard locations in their web modules.</p>
</div>
</div>
<div class="sect2">
<h3 id="target-features">Target Features</h3>
<div class="paragraph">
<p>This update applies to the scanning of classes within applications. Class scanning (which includes annotation scanning) is a general purpose function that is used by many features.</p>
</div>
</div>
<div class="sect2">
<h3 id="problem-description">Problem Description</h3>
<div class="paragraph">
<p>To gather information about application classes, including annotation data, the classes must be scanned. This scanning process is expensive because the entire application must be read and processed.</p>
</div>
<div class="paragraph">
<p>To improve performance, class scanning can be performed during application packaging, with the scan results stored in an index within the application. Jandex provides this capability.</p>
</div>
<div class="paragraph">
<p>Jandex scan results are typically written to <code>META-INF/jandex.idx</code>.</p>
</div>
<div class="paragraph">
<p>For JAR files, this path is relative to the root of the JAR. For WAR files, however, the <code>META-INF/jandex.idx</code> path can have two interpretations: it can be relative to the root of the WAR file, or relative to the <code>WEB-INF/classes</code> directory.</p>
</div>
<div class="paragraph">
<p>Before this update, Liberty used the location relative to the root of the WAR file. However, industry-standard practices place the location relative to the <code>WEB-INF/classes</code> directory. This update enables Liberty to also use the <code>WEB-INF/classes</code> location.</p>
</div>
</div>
<div class="sect2">
<h3 id="how-to-use-2">How to Use</h3>
<div class="paragraph">
<p>Support for reading Jandex indexes under <code>WEB-INF/classes</code> is enabled by a new property on the <code>application</code> and <code>applicationManager</code> elements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Property: <strong>useJandexUnderClasses</strong></p>
</li>
<li>
<p>Description: Enables use of a Jandex index for WAR <code>WEB-INF/classes</code> under <code>/WEB-INF/classes/META-INF/jandex.idx</code>.</p>
</li>
<li>
<p>Possible values: true | false</p>
</li>
<li>
<p>Default value: false</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Example: applicationManager</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;applicationManager</span> <span class="attribute-name">useJandex</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">useJandexUnderClasses</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Example: application</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;application</span> <span class="attribute-name">location</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">TestServlet40.ear</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">useJandex</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">useJandexUnderClasses</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>When the new property is placed on an application manager element, it applies to all web modules of all applications. When the new property is placed on an application element, the property applies to only that application. If the property is set on both the application manager element and an application element, the value on the application element overrides the value on the application manager element for that application.</p>
</div>
</div>
<div class="sect2">
<h3 id="limitation">Limitation</h3>
<div class="paragraph">
<p>Jandex index support requires explicit enablement. See the <code>useJandex</code> property  on <code>applicationManager</code> and on <code>application</code> elements. The new <code>useJandexUnderClasses</code> property is meaningful only if the <code>useJandex</code> property is <code>true</code>.</p>
</div>
<div class="paragraph">
<p>For compatibility with earlier versions, reads of Jandex from the new location require explicit enablement. See the new <strong>useJandexUnderClasses</strong> property, as documented previously. Explicit enablement is required to prevent applications from accidentally reading an out of date Jandex index from the new location. An out of date Jandex index might cause application errors that are hard to detect.</p>
</div>
<div class="paragraph">
<p>The name of the new property, <strong>useJandexUnderClasses</strong>, is subject to revision.</p>
</div>
</div>
<div class="sect2">
<h3 id="learn-more-3">Learn More</h3>
<div class="ulist">
<ul>
<li>
<p><a href="https://smallrye.io/jandex/jandex/3.5.3/index.html">Jandex Documentation</a></p>
</li>
<li>
<p><a href="/docs/latest/reference/config/applicationManager.html">Application Manager Documentation</a></p>
</li>
<li>
<p><a href="/docs/latest/reference/config/application.html">Application Documentation</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/32438">Look for jandex index under WEB-INF/classes</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="run">Try it now</h3>
<div class="paragraph">
<p>To try out these features, update your build tools to pull the Open Liberty All Beta Features package instead of the main release. To enable the MCP server feature, follow the instructions from <a href="https://openliberty.io/blog/2025/10/23/mcp-standalone-blog.html">MCP standalone blog</a>. The beta works with Java SE 25, Java SE 21, Java SE 17, Java SE 11, and Java SE 8.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, you can install the All Beta Features package using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;configuration&gt;</span>
        <span class="tag">&lt;runtimeArtifact&gt;</span>
          <span class="tag">&lt;groupId&gt;</span>io.openliberty.beta<span class="tag">&lt;/groupId&gt;</span>
          <span class="tag">&lt;artifactId&gt;</span>openliberty-runtime<span class="tag">&lt;/artifactId&gt;</span>
          <span class="tag">&lt;version&gt;</span>26.0.0.4-beta<span class="tag">&lt;/version&gt;</span>
          <span class="tag">&lt;type&gt;</span>zip<span class="tag">&lt;/type&gt;</span>
        <span class="tag">&lt;/runtimeArtifact&gt;</span>
    <span class="tag">&lt;/configuration&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You must also add dependencies to your <code>pom.xml</code> file for the beta version of the APIs that are associated with the beta features that you want to try. For example, the following block adds dependencies for two example beta APIs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>org.example.spec<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>exampleApi<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>7.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;type&gt;</span>pom<span class="tag">&lt;/type&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span>
<span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>example.platform<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>example.example-api<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>11.0.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[26.0.0.4-beta,)'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty:beta</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/downloads/#runtime_betas">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="paragraph">
<p>For more information on using a beta release, refer to the <a href="docs/latest/installing-open-liberty-betas.html">Installing Open Liberty beta releases</a> documentation.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="feedback">We welcome your feedback</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let us know what you think on <a href="https://groups.io/g/openliberty">our mailing list</a>. If you hit a problem, <a href="https://stackoverflow.com/questions/tagged/open-liberty">post a question on StackOverflow</a>. If you hit a bug, <a href="https://github.com/OpenLiberty/open-liberty/issues">please raise an issue</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><category term="announcements" /><category term="java-se" /><category term="release" /><category term="beta" /><category term="security" /><category term="jakarta-ee" /><summary type="html"><![CDATA[This beta introduces Jakarta EE 11 Platform and Web Profile, and delivers enhancements across Jakarta Authentication 3.1, Jakarta Authorization 3.0, and Jakarta Security 4.0. In addition, it provides a preview of Jakarta Data 1.1 M2, support for Java 26, MCP Server updates, and Jandex index improvements.]]></summary></entry><entry><title type="html">UserRegistry Attribute Reader Enhancement, Jandex Index Format Support Update and more in 26.0.0.3</title><link href="https://openliberty.io/blog/2026/03/24/26.0.0.3.html" rel="alternate" type="text/html" title="UserRegistry Attribute Reader Enhancement, Jandex Index Format Support Update and more in 26.0.0.3" /><published>2026-03-24T00:00:00+00:00</published><updated>2026-03-24T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/03/24/26.0.0.3</id><content type="html" xml:base="https://openliberty.io/blog/2026/03/24/26.0.0.3.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This release introduces UserRegistry Attribute Reader APIs for retrieving user attributes from LDAP and custom registries, and adds support for Jandex index formats 11-13 to improve application startup performance. Other highlights include changes to AES encoding and fixes for security vulnerabilities and bugs.</p>
</div>
<div class="paragraph">
<p>In <a href="/">Open Liberty</a> 26.0.0.3:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#userregistry">UserRegistry Attribute Reader Enhancement</a></p>
</li>
<li>
<p><a href="#jandex">Jandex Index Format Support Update</a></p>
</li>
<li>
<p><a href="#aes">AES encoding changes</a></p>
</li>
<li>
<p><a href="#CVEs">Security Vulnerability (CVE) Fixes</a></p>
</li>
<li>
<p><a href="#bugs">Notable bug fixes</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>View the list of fixed bugs in <a href="https://github.com/OpenLiberty/open-liberty/issues?q=label%3Arelease%3A26003+label%3A%22release+bug%22">26.0.0.3</a>.</p>
</div>
<div class="paragraph">
<p>Check out <a href="/blog/?search=release&amp;search!=beta">previous Open Liberty GA release blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="run">Develop and run your apps using 26.0.0.3</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, include the following in your <code>pom.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>, include the following in your <code>build.gradle</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/start/">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="https://openliberty.io/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="https://stackoverflow.com/tags/open-liberty"><img src="/img/blog/blog_btn_stack.svg" alt="Ask a question on Stack Overflow"></a>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="userregistry">UserRegistry Attribute Reader Enhancement</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The UserRegistry API provides applications with the ability to retrieve specific user attributes and search for users based on attribute values directly from LDAP and custom user registries.</p>
</div>
<div class="sect2">
<h3 id="whats-new">What&#8217;s New?</h3>
<div class="paragraph">
<p>Previously, the UserRegistry API supported only basic user lookups such as searching by <code>userSecurityName</code> or by using wildcard pattern matching. However, it did not allow applications to retrieve specific user attributes or search for users based on attribute values. These capabilities were available in traditional WebSphere through the VMM API. The introduction of these two new APIs now enables applications to search for users and retrieve attributes from LDAP and custom user registries.</p>
</div>
<div class="paragraph">
<p>With this enhancement, you can now retrieve specific attributes for a user by using the <code>getAttributesForUser()</code> method. This method supports retrieval of individual attributes such as email or phoneNumber, or "*" to retrieve all available attributes for a user.</p>
</div>
<div class="paragraph">
<p>You can also search for users based on attribute values by using the <code>getUsersByAttribute()</code> method, enabling applications to locate users by matching specific attribute criteria. These methods are supported for LDAP user registries. Custom user registry implementations can also implement and support these methods.</p>
</div>
</div>
<div class="sect2">
<h3 id="how-to-use-it">How to Use It</h3>
<div class="paragraph">
<p>New API methods added to <code>UserRegistry.java</code>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>default Map&lt;String, Object&gt; getAttributesForUser(String userSecurityName, Set&lt;String&gt; attributeNames)</code></p>
<div class="paragraph">
<p><strong>Description:</strong> Returns a Map&lt;String, Object&gt; containing the specified <code>attributesNames</code> and their corresponding values for the given <code>userSecurityName</code>, as configured in the LDAP user registry.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">UserRegistry ur = RegistryHelper.getUserRegistry(<span class="string"><span class="delimiter">&quot;</span><span class="content">realmName</span><span class="delimiter">&quot;</span></span>);

<span class="comment">// valid ldap attribute names</span>
<span class="predefined-type">Set</span>&lt;<span class="predefined-type">String</span>&gt; attributeNames = <span class="predefined-type">Set</span>.of(<span class="string"><span class="delimiter">&quot;</span><span class="content">telephoneNumber</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">uid</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">mail</span><span class="delimiter">&quot;</span></span>);  <span class="comment">// or &quot;*&quot; for retrieving all attributes</span>
<span class="predefined-type">Map</span>&lt;<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>&gt; result = ur.getAttributesForUser(<span class="string"><span class="delimiter">&quot;</span><span class="content">testuser</span><span class="delimiter">&quot;</span></span>, attributeNames);

<span class="comment">// output in trace.log</span>
&gt; getAttributesForUser Entry
  testuser
  [telephoneNumber, uid, mail]

&lt; getAttributesForUser Exit
  {uid=testuser, mail=testuser<span class="annotation">@example</span>.com, telephoneNumber=&lt;telephone-number&gt;}</code></pre>
</div>
</div>
</li>
<li>
<p><code>default SearchResult getUsersByAttribute(String attributeName, String value, int limit)</code></p>
<div class="paragraph">
<p><strong>Description:</strong> Returns a <code>SearchResult</code> object that contains a list of users that match an <code>attributeName</code> with specified <code>value</code> that is configured in the LDAP user registry.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java">UserRegistry ur = RegistryHelper.getUserRegistry(<span class="string"><span class="delimiter">&quot;</span><span class="content">realmName</span><span class="delimiter">&quot;</span></span>);

<span class="predefined-type">SearchResult</span> searchResult = ur.getUsersByAttribute(<span class="string"><span class="delimiter">&quot;</span><span class="content">email</span><span class="delimiter">&quot;</span></span>, <span class="string"><span class="delimiter">&quot;</span><span class="content">testuser@example.com</span><span class="delimiter">&quot;</span></span>, <span class="integer">1</span>);

<span class="comment">// output in trace.log</span>
&gt; getUsersByAttribute Entry
  email
  testuser<span class="annotation">@example</span>.com
  <span class="integer">1</span>

&lt; getUsersByAttribute Exit
  <span class="predefined-type">SearchResult</span> hasMore=<span class="predefined-constant">false</span> [testuser]</code></pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>To learn more about the user registry, see the following resources:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.ibm.com/docs/en/was-liberty/base?topic=infrastructure-customizing-user-registries-repositories-liberty">Open Liberty Custom User Registry documentation</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/modules/reference/24.0.0.3/com.ibm.websphere.appserver.api.securityClient_1.1-javadoc/com/ibm/websphere/security/UserRegistry.html">UserRegistry API Javadoc</a></p>
</li>
<li>
<p><a href="https://openliberty.io/docs/latest/user-registries-application-security.html#_federated_user_registries">Federated User Registry configuration</a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jandex">Jandex Index Format Support Update</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Open Liberty now supports the latest Jandex index formats, allowing the use of Jandex indexes that are created with Jandex version 3.1.0 and later. Jandex is a space‑efficient Java annotation indexer and offline reflection library that improves application startup performance by pre‑indexing class metadata.</p>
</div>
<div class="paragraph">
<p>This feature targets application developers and DevOps engineers who work with Jakarta EE and MicroProfile applications. It is designed to optimize application startup times and reduce runtime costs.</p>
</div>
<div class="sect2">
<h3 id="whats-new-2">What&#8217;s New?</h3>
<div class="paragraph">
<p>With this update, Open Liberty supports Jandex index formats <strong>11, 12, and 13</strong>. Jandex versions 3.1.0 through the current latest version, 3.5.3, generate indexes that use these formats.</p>
</div>
<div class="paragraph">
<p>Previously, Open Liberty supported Jandex index format versions only up to and including version 10. This index format limitation meant that applications packaged with Jandex indexes could not use index formats beyond version 10 while retaining the expected performance improvements. To preserve these performance benefits, build tooling had to continue using a Jandex version prior to 3.1.0, or explicitly configure Jandex index generation to produce indexes that use index format up to and including version 10.</p>
</div>
</div>
<div class="sect2">
<h3 id="benefits">Benefits</h3>
<div class="paragraph">
<p>The added support for Jandex index formats has several benefits:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Improved Compatibility:</strong> Works seamlessly with the latest versions of build tools and frameworks that use newer Jandex versions</p>
</li>
<li>
<p><strong>Faster Startup Times:</strong> Continue to benefit from Jandex&#8217;s efficient annotation indexing without version constraints</p>
</li>
<li>
<p><strong>Reduced Maintenance:</strong> No need to maintain multiple Jandex versions or regenerate indexes for compatibility</p>
</li>
<li>
<p><strong>Future-Proof:</strong> Stay current with the Jandex ecosystem as it evolves</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="limitations">Limitations</h3>
<div class="paragraph">
<p><strong>When using Jandex indexes, ensure that the index files are kept up to date with the application classes.</strong> If a Jandex index is not synchronized with the application classes, it can contain incorrect annotation data, which can cause the application to function incorrectly. Out‑of‑date Jandex indexes cannot be reliably detected.</p>
</div>
<div class="paragraph">
<p>Open Liberty does not read index formats higher than index format 13. If a new version of Jandex adds a higher index format version, Open Liberty requires an update before it can read Jandex indexes that use the higher index format version.</p>
</div>
<div class="paragraph">
<p>The Open Liberty feature <code>mpGraphQL</code>, which is obtained from an external source, is limited to reading Jandex index formats no higher than index format 10. If the feature <code>mpGraphQL</code> is used, Jandex indexes should be generated by using index format 10. This limitation applies to all current versions of <code>mpGraphQL</code>, including the current highest version, <code>mpGraphQL-2.0</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="lifting-restriction">Lifting Restriction</h3>
<div class="paragraph">
<p>Previously, when Jandex usage was enabled and an application included Jandex indexes that used formats 11 through 13, Open Liberty displayed an error message and ignored those indexes. In such cases, annotation scanning was performed by using Liberty’s internal annotation scanning mechanism.</p>
</div>
<div class="paragraph">
<p>With the addition of support for Jandex index formats 11 through 13, Open Liberty now uses indexes that are generated in these formats. These indexes must be kept up to date with the application classes. Ensure that the Jandex indexes packaged with your application accurately reflect the current application classes.</p>
</div>
</div>
<div class="sect2">
<h3 id="how-to-use">How to Use</h3>
<div class="paragraph">
<p>No configuration changes are required in the <code>server.xml</code> file to enable support for the new Jandex index formats. Open Liberty automatically detects the Jandex index format and selects the appropriate index reader for that format. It reads indexes that use formats 11, 12, and 13, and continues to support indexes that use format up to version 10.</p>
</div>
<div class="paragraph">
<p>By default, Jandex usage is disabled. To enable Jandex, set the <code>useJandex</code> property to <code>true</code> in either an <code>application</code> element or the <code>applicationManager</code> element.</p>
</div>
<div class="paragraph">
<p>If you are generating Jandex indexes as part of your build process, you can now use the latest Jandex Maven plugin or Gradle plugin versions:</p>
</div>
<div class="paragraph">
<p><strong>Maven Example:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.smallrye<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>jandex-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.5.3<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;executions&gt;</span>
        <span class="tag">&lt;execution&gt;</span>
            <span class="tag">&lt;id&gt;</span>make-index<span class="tag">&lt;/id&gt;</span>
            <span class="tag">&lt;goals&gt;</span>
                <span class="tag">&lt;goal&gt;</span>jandex<span class="tag">&lt;/goal&gt;</span>
            <span class="tag">&lt;/goals&gt;</span>
        <span class="tag">&lt;/execution&gt;</span>
    <span class="tag">&lt;/executions&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Gradle Example:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">plugins {
    id 'org.kordamp.gradle.jandex' version '2.3.0'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Open Liberty automatically detects and recognizes the generated <code>META-INF/jandex.idx</code> file for any supported index format version.</p>
</div>
<div class="paragraph">
<p>For more information, see the <a href="https://smallrye.io/jandex/jandex/3.5.3/index.html">Jandex Documentation</a></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="aes">AES encoding changes</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <code>securityUtility encode</code> command now requires a key to be specified for AES encodings. Specify a key by including one of the following options: <code>--key</code>, <code>--base64Key</code>, <code>--aesConfigFile</code>, or <code>--keyring</code>. This update resolves <a href="https://www.ibm.com/support/pages/node/7261638">CVE-2025-14923</a>.</p>
</div>
<div class="paragraph">
<p>The following command now results in an error because a key is not specified in the command.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">securityUtility encode --encoding=aes &quot;password&quot;

When using AES encoding, one of the following arguments must be specified: --key, --base64Key, --aesConfigFile, or --keyring.</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following is an example of a valid usage with the new requirement.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">securityUtility encode --encoding=aes --key=mycustomkey &quot;password&quot;
{aes}ARArmkfkgGyE1eiN8mKKjnkUDrFUFuuq2FlpDqrJKBgTAEYeN+PLP45wChLvIhlnWEDHnSA4zJI9KA3k5R9e/QDC+O2tzr3EwD3IMMAfvfOYxxqNPmoXqIeRVPD8TPZWnIIPmCnvyROw5A8=</code></pre>
</div>
</div>
<div class="paragraph">
<p>For more information, see the full documentation for <a href="https://openliberty.io/docs/latest/reference/command/securityUtility-encode.html">securityUtility encode</a> and <a href="https://openliberty.io/docs/latest/password-encryption.html">password encryption</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="CVEs">Security vulnerability (CVE) fixes in this release</h2>
<div class="sectionbody">
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">CVE</th>
<th class="tableblock halign-left valign-top">CVSS Score</th>
<th class="tableblock halign-left valign-top">Vulnerability Assessment</th>
<th class="tableblock halign-left valign-top">Versions Affected</th>
<th class="tableblock halign-left valign-top">Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2025-14923">CVE-2025-14923</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">4.7</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Weaker security</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">17.0.0.3-26.0.0.2</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://www.cve.org/CVERecord?id=CVE-2024-29371">CVE-2024-29371</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">7.5</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Denial of service</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">21.0.0.3-26.0.0.2</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Affects the <code>openidConnectClient-1.0</code>, <code>socialLogin-1.0</code>, <code>mpJwt-1.2</code>, <code>mpJwt-2.0</code>, <code>mpJwt-2.1</code>, and <code>jwt-1.0</code> features</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>For a list of past security vulnerability fixes, reference the <a href="/docs/latest/security-vulnerabilities.html">Security vulnerability (CVE) list</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bugs">Notable bugs fixed in this release</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We’ve spent some time fixing bugs. The following sections describe some of the issues resolved in this release. If you’re interested, here’s the <a href="https://github.com/OpenLiberty/open-liberty/issues?q=label%3Arelease%3A26003+label%3A%22release+bug%22">full list of bugs fixed in 26.0.0.3</a>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34017">Missing error message in JMX REST client</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34052">NullPointerException resurfaced in Open Liberty due to removal of EclipseLink 2.7.16 fix</a></p>
</li>
<li>
<p><a href="https://github.com/OpenLiberty/open-liberty/issues/34171">IBM WebSphere Application Server Liberty is affected by a denial of service due to jose4j (CVE-2024-29371 CVSS 7.5)</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-open-liberty-26-0-0-3-now">Get Open Liberty 26.0.0.3 now</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Available through <a href="#run">Maven, Gradle, Docker, and as a downloadable archive</a>.</p>
</div>
</div>
</div>]]></content><author><name>Navaneeth S Nair</name></author><category term="blog" /><category term="announcements" /><category term="microprofile" /><category term="java-se" /><category term="release" /><category term="security" /><category term="jakarta-ee" /><category term="performance-enhancements" /></entry><entry><title type="html">Model Context Protocol Server 1.0 updates in 26.0.0.3-beta</title><link href="https://openliberty.io/blog/2026/03/10/26.0.0.3-beta.html" rel="alternate" type="text/html" title="Model Context Protocol Server 1.0 updates in 26.0.0.3-beta" /><published>2026-03-10T00:00:00+00:00</published><updated>2026-03-10T00:00:00+00:00</updated><id>https://openliberty.io/blog/2026/03/10/26.0.0.3-beta</id><content type="html" xml:base="https://openliberty.io/blog/2026/03/10/26.0.0.3-beta.html"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>The 26.0.0.3-beta release updates the <code>mcpServer-1.0</code> feature with response encoders and provides request ID access to tools for logging and auditing.</p>
</div>
<div class="paragraph">
<p>The <a href="/">Open Liberty</a> 26.0.0.3-beta includes the following beta features (along with <a href="/docs/latest/reference/feature/feature-overview.html">all GA features</a>):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#encoders">Encode responses with ContentEncoder and ToolResponseEncoder</a></p>
</li>
<li>
<p><a href="#requestid">Access the request ID from a tool</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> is an open standard that enables AI applications to access real-time information from external sources. The Liberty MCP Server feature (<code>mcpServer-1.0</code>) allows developers to expose the business logic of their applications, allowing it to be integrated into agentic AI workflows.</p>
</div>
<div class="paragraph">
<p>See also <a href="/blog/?search=beta&amp;key=tag">previous Open Liberty beta blog posts</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="encoders">Encode responses with ContentEncoder and ToolResponseEncoder</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When a client calls a tool, the object that is returned by the tool method is converted to a <code>ToolResponse</code>, which usually contains one or more <code>Content</code> objects. The <code>ToolResponse</code> maps directly to the result returned in the response.</p>
</div>
<div class="paragraph">
<p>If you need more control over the response from your tool, you can return a <code>ToolResponse</code> or <code>Content</code> object directly. Now, it is also possible to register encoders to control how other objects are converted into a response.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Use <code>ToolResponseEncoder</code> to convert an object into a <code>ToolResponse</code>, which gives you complete control over the whole response.</p>
</li>
<li>
<p>Use <code>ContentEncoder</code> when you only need to convert an object into a <code>Content</code> that is included in the response. You can also return a list of objects, and each object is individually converted into a <code>Content</code> and included in the response.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If a tool method returns an object for which no encoder is provided, JSON-B encodes the object as JSON, which is returned as text content.</p>
</div>
<div class="sect2">
<h3 id="example">Example</h3>
<div class="paragraph">
<p>Consider a tool that does a search over some datastore:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Tool</span>(description = <span class="string"><span class="delimiter">&quot;</span><span class="content">search the data store</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="predefined-type">SearchResult</span> search(<span class="annotation">@ToolArg</span>(name=<span class="string"><span class="delimiter">&quot;</span><span class="content">query</span><span class="delimiter">&quot;</span></span>, description=<span class="string"><span class="delimiter">&quot;</span><span class="content">the query to run</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> query) {
    <span class="predefined-type">SearchResult</span> result = datastore.runQuery(query);
    <span class="keyword">return</span> result;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this scenario, the <code>SearchResult</code> object encapsulates a list of individual results. Each entry contains a summary, associated metadata, and a relevance score that indicates its relationship to the query.</p>
</div>
<div class="paragraph">
<p>By default, the <code>SearchResult</code> object that is returned is encoded as JSON by using JSON-B and placed into a <code>TextContent</code>. However, to return each search result summary as an individual <code>TextContent</code> and map the relevance score to a priority annotation, a <code>ToolResponseEncoder</code> is to be used.</p>
</div>
<div class="paragraph">
<p>Create a CDI bean that implements the <code>ToolResponseEncoder</code> interface and implement:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <code>encode</code> method to create a <code>ToolResponse</code> from a <code>SearchResult</code></p>
</li>
<li>
<p>the <code>supports</code> method to indicate that your encoder can be used for any <code>SearchResult</code></p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@ApplicationScoped</span>
<span class="directive">public</span> <span class="type">class</span> <span class="class">SearchResultEncoder</span> <span class="directive">implements</span> ToolResponseEncoder&lt;<span class="predefined-type">SearchResult</span>&gt; {

    <span class="directive">public</span> <span class="type">boolean</span> supports(<span class="predefined-type">Class</span>&lt;?&gt; runtimeType) {
        <span class="comment">// This encoder can encode SearchResult and any subtypes</span>
        <span class="keyword">return</span> <span class="predefined-type">SearchResult</span>.class.isAssignableFrom(runtimeType);
    }

    <span class="directive">public</span> ToolResponse encode(<span class="predefined-type">SearchResult</span> searchResult) {
        <span class="keyword">if</span> (searchResult.results().isEmpty()) {
            <span class="keyword">return</span> ToolResponse.error(<span class="string"><span class="delimiter">&quot;</span><span class="content">No results</span><span class="delimiter">&quot;</span></span>);
        }

        <span class="predefined-type">ArrayList</span>&lt;TextContent&gt; contents = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span>&lt;&gt;();
        <span class="keyword">for</span> (<span class="type">var</span> result : searchResult.results()) {
            <span class="comment">// Set the priority annotation based on the relevance</span>
            Annotations annotations = <span class="keyword">new</span> Annotations(<span class="predefined-constant">null</span>, <span class="predefined-constant">null</span>, result.relevance());
            <span class="comment">// Create a TextContent from the summary, and add the annotations</span>
            contents.add(<span class="keyword">new</span> TextContent(result.summary(), <span class="predefined-constant">null</span>, annotations));
        }

        <span class="comment">// Create a successful response, using the created TextContents</span>
        <span class="keyword">return</span> ToolResponse.success(contents);
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This encoder is used for any tools in the application that return a <code>SearchResult</code></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="requestid">Access the request ID from a tool</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Each request from an MCP client includes a unique session-based Request ID, which is now accessible to tools and is useful for logging and auditing.</p>
</div>
<div class="paragraph">
<p>To access the ID, add a <code>RequestId</code> parameter to the tool method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Tool</span>(description = <span class="string"><span class="delimiter">&quot;</span><span class="content">search the data store</span><span class="delimiter">&quot;</span></span>)
<span class="directive">public</span> <span class="predefined-type">SearchResult</span> search(<span class="annotation">@ToolArg</span>(name=<span class="string"><span class="delimiter">&quot;</span><span class="content">query</span><span class="delimiter">&quot;</span></span>) <span class="predefined-type">String</span> query, RequestId requestId) {
    logger.log(INFO, <span class="string"><span class="delimiter">&quot;</span><span class="content">Running search (</span><span class="delimiter">&quot;</span></span> + requestId.asString() + <span class="string"><span class="delimiter">&quot;</span><span class="content">) for query: </span><span class="delimiter">&quot;</span></span> + query);
    <span class="comment">// ....</span>
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="notable-bug-fixes">Notable bug fixes</h3>
<div class="paragraph">
<p>The following bugs have been fixed:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Output schemas were not generated correctly for asynchronous tool methods which have <code>structuredContent = true</code></p>
</li>
<li>
<p>Omitting the <code>arguments</code> object when calling a tool would result in an error. The <code>arguments</code> object is optional if the tool does not require any arguments</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="run">Try it now</h3>
<div class="paragraph">
<p>To try out these features, update your build tools to pull the Open Liberty All Beta Features package instead of the main release. To enable the MCP server feature, follow the instructions from <a href="https://openliberty.io/blog/2025/10/23/mcp-standalone-blog.html">MCP standalone blog</a>. The beta works with Java SE 25, Java SE 21, Java SE 17, Java SE 11, and Java SE 8.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="/guides/maven-intro.html">Maven</a>, you can install the All Beta Features package using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;plugin&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>io.openliberty.tools<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>liberty-maven-plugin<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>3.12.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;configuration&gt;</span>
        <span class="tag">&lt;runtimeArtifact&gt;</span>
          <span class="tag">&lt;groupId&gt;</span>io.openliberty.beta<span class="tag">&lt;/groupId&gt;</span>
          <span class="tag">&lt;artifactId&gt;</span>openliberty-runtime<span class="tag">&lt;/artifactId&gt;</span>
          <span class="tag">&lt;version&gt;</span>26.0.0.3-beta<span class="tag">&lt;/version&gt;</span>
          <span class="tag">&lt;type&gt;</span>zip<span class="tag">&lt;/type&gt;</span>
        <span class="tag">&lt;/runtimeArtifact&gt;</span>
    <span class="tag">&lt;/configuration&gt;</span>
<span class="tag">&lt;/plugin&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You must also add dependencies to your <code>pom.xml</code> file for the beta version of the APIs that are associated with the beta features that you want to try. For example, the following block adds dependencies for two example beta APIs:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>org.example.spec<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>exampleApi<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>7.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;type&gt;</span>pom<span class="tag">&lt;/type&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span>
<span class="tag">&lt;dependency&gt;</span>
    <span class="tag">&lt;groupId&gt;</span>example.platform<span class="tag">&lt;/groupId&gt;</span>
    <span class="tag">&lt;artifactId&gt;</span>example.example-api<span class="tag">&lt;/artifactId&gt;</span>
    <span class="tag">&lt;version&gt;</span>11.0.0<span class="tag">&lt;/version&gt;</span>
    <span class="tag">&lt;scope&gt;</span>provided<span class="tag">&lt;/scope&gt;</span>
<span class="tag">&lt;/dependency&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Or for <a href="/guides/gradle-intro.html">Gradle</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="gradle">buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:3.10.0'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[26.0.0.3-beta,)'
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or if you&#8217;re using <a href="/docs/latest/container-images.html">container images</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code>FROM icr.io/appcafe/open-liberty:beta</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or take a look at our <a href="/downloads/#runtime_betas">Downloads page</a>.</p>
</div>
<div class="paragraph">
<p>If you&#8217;re using <a href="https://plugins.jetbrains.com/plugin/14856-liberty-tools">IntelliJ IDEA</a>, <a href="https://marketplace.visualstudio.com/items?itemName=Open-Liberty.liberty-dev-vscode-ext">Visual Studio Code</a> or <a href="https://marketplace.eclipse.org/content/liberty-tools">Eclipse IDE</a>, you can also take advantage of our open source <a href="https://openliberty.io/docs/latest/develop-liberty-tools.html">Liberty developer tools</a> to enable effective development, testing, debugging and application management all from within your IDE.</p>
</div>
<div class="paragraph">
<p>For more information on using a beta release, refer to the <a href="docs/latest/installing-open-liberty-betas.html">Installing Open Liberty beta releases</a> documentation.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="feedback">We welcome your feedback</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let us know what you think on <a href="https://groups.io/g/openliberty">our mailing list</a>. If you hit a problem, <a href="https://stackoverflow.com/questions/tagged/open-liberty">post a question on StackOverflow</a>. If you hit a bug, <a href="https://github.com/OpenLiberty/open-liberty/issues">please raise an issue</a>.</p>
</div>
</div>
</div>]]></content><author><name>Ismath Badsha</name></author><category term="blog" /><category term="announcements" /><category term="release" /><category term="beta" /><summary type="html"><![CDATA[The 26.0.0.3-beta release updates the mcpServer-1.0 feature with response encoders and provides request ID access to tools for logging and auditing.]]></summary></entry></feed>