back to all blogsSee all blog posts

Jakarta EE 11 previews and InstantOn support for IBM MQ in 24.0.0.5-beta

image of author
David Mueller on May 7, 2024
Post available in languages:

The 24.0.0.5-beta release introduces previews for several Jakarta EE 11 features, including Jakarta Contexts and Dependency Injection 4.1, Jakarta Concurrency 3.1, Jakarta Data 1.0, Jakarta Expression Language 6.0, Jakarta Pages 4.0, and Jakarta Servlet 6.1. This release also includes support for using InstantOn with IBM MQ messaging.

The Open Liberty 24.0.0.5-beta includes the following beta Jakarta EE 11 features (along with all GA features):

In 24.0.0.5-beta, you can also use IBM® MQ messaging with InstantOn support.

Jakarta CDI 4.1

CDI allows objects to be bound to lifecycle contexts, to be injected, to be associated with interceptors and decorators, and to interact in a loosely coupled fashion by firing and observing events. Highlights of CDI 4.1 include the ability for interceptors to inspect the annotations that are used to bind them. This update also brings in new Jakarta EE 11 versions of the Jakarta Annotations and Jakarta Interceptors APIs.

Retrieve interceptor binding information

Support is added to allow interceptors to retrieve and inspect the annotations that are used to bind them. Interceptors can now call InvocationContext.getInterceptorBindings() or one of the related methods to retrieve the annotations so that they can read values from them. This capability is particularly useful when some of the fields of an interceptor binding annotation are annotated with @Nonbinding. These fields are not used to restrict which interceptor is bound and can now be read by the interceptor when the interceptor method is called.

For example, a binding annotation for a logging interceptor might look like this:

@InterceptorBinding
public @interface Logged {
    @Nonbinding String value();
}

This annotation might be applied to a method on a managed bean like this:

@Logged("myName")
public void myMethod() {
   // ....
}

Then, an interceptor like this can read the myName value from the annotation and include it in the log message:

@Interceptor
@Logged("")
public class LoggedInterceptor {

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

    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        Logged binding = ctx.getInterceptorBinding(Logged.class);
        LOG.info("Method called: " + binding.value());
        return ctx.proceed();
    }
}

The new methods added to InvocationContext make it easier to retrieve the annotations. These methods return the correct binding annotations whether they’re applied to the class or the method and reflect any changes that are made to the annotations by a CDI extension.

Method invoker support

Support is added for extensions to create method invokers, which are used to call a method on a CDI-managed bean indirectly and can support looking up the object instance and the method arguments from CDI. This feature is intended for people who want to build frameworks that build on or integrate with CDI.

For more information on how to create and use an Invoker, see the CDI specification blog.

Deprecated methods in BeanManager

The BeanManager.getELResolver and BeanManager.wrapExpressionFactory methods are deprecated and will be removed in the next major version so that the CDI API won’t need to depend on the Expression Language (EL) API. The same methods are added to ELAwareBeanManager, which extends BeanManager and lives in a new API jar.

These methods were mostly used by EL implementations, but any users who want to continue using these methods must switch to using ELAwareBeanManager and include the new API artifact in their dependencies. For example, by adding it to their maven pom.xml file:

<dependency>
    <groupId>jakarta.enterprise</groupId>
    <artifactId>jakarta.enterprise.cdi-el-api</artifactId>
    <scope>provided</provided>
    <version>4.1.0</version>
</dependency>
in this beta, ELAwareBeanManager cannot be injected directly, but a BeanManager can be injected and then cast to an ELAwareBeanManager. This limitation will be fixed before release.

Remove references to Managed Beans specification

Jakarta EE 11 does not include the Managed Beans specification, which was separate from the CDI specification but did many of the same things. As a result, the associated @ManagedBean annotation is removed from the Jakarta Annotations API.

Changes to EE integration

The parts of the CDI specification that define how CDI integrates with other Jakarta EE technologies moved into the Jakarta EE 11 Platform, Web Profile, and Core Profile specifications. This has no effect on users, but if you use the CDI specification as a documentation reference, you will find that some parts of it are now in the Plaform, Web Profile and Core Profile specifications instead.

To use the CDI 4.1 feature, add cdi-4.1 to your server.xml file:

<featureManager>
  <feature>cdi-4.1</feature>
</featureManager>

Further references:

Jakarta Concurrency 3.1

Jakarta Concurrency allows applications to use concurrency while maintaining the benefits of running on a Jakarta EE runtime. Jakarta Concurrency 3.1 is the new version for the upcoming Jakarta EE 11 release, and it adds support for new features of the latest Java SE releases as well as some usability improvements.

The new features in Jakarta Concurrency 3.1 are improved support for the Java Flow (Reactive Streams) APIs and the use of virtual threads in managed executors and managed thread factories. Support is also included for using @Inject in place of @Resource and the option to run asynchronous methods on a schedule by using the new @Schedule annotation.

To support the Flow APIs, ContextService has two new methods, contextualSubscriber, which provides context in a Flow.Subscriber, and contextualProcessor, which provides context in a Flow.Processor. These new methods allow for context in a flow when using a Flow.Publisher, which does not allow configuring a ManagedExecutor.

publisher.subscribe(contextService.contextualSubscriber(subscriber));

Virtual threads are available in Jakarta Concurrency 3.1 by specifying virtual = true on a ManagedExecutorDefinition, ManagedScheduledExecutorDefinition, or ManagedThreadFactory instance. Virtual threads are a new feature of Java 21, so when running on Java 17 and ‘virtual = true’ is specified, platform threads are provided instead of virtual threads.

@ManagedExecutorDefinition(name = "java:module/concurrent/virtual-executor",
                           virtual = true)

The new @Schedule annotation allows asynchronous methods to run on a schedule. In this example, after the method is called, it runs asynchronously at 10 AM on every day of every month where the day of the week is a Monday, Wednesday, or Friday, according to the provided cron string.

@Asynchronous(runAt = @Schedule(cron = "0 10 * * MON,WED,FRI", zone = "America/Chicago"))
void runAt10MWF() {
    ...
    if (neverRunAgain)
        Asynchronous.Result.complete(null);
}

To try out Jakarta Concurrency 3.1, check out the sample application.

To use this feature, add the following code to your server.xml file:

<featureManager>
     <feature>concurrent-3.1</feature>
</featureManager>

Jakarta Data (Milestone 4)

Jakarta Data is a new Jakarta EE specification being developed in the open that aims to standardize the popular Data Repository pattern across various providers. Open Liberty includes the Jakarta Data 1.0 Milestone 4 release, which adds the Jakarta Data Query Language (JDQL) and enhances the Static Metamodel.

The Open Liberty beta includes a test implementation of Jakarta Data that we are using to experiment with proposed specification features. You can try out these features and provide feedback to influence the Jakarta Data 1.0 specification as it continues to be developed. The test implementation currently works with relational databases and operates by redirecting repository operations to the built-in Jakarta Persistence provider.

Jakarta Data 1.0 Milestone 4 introduces Jakarta Data Query Language (JDQL), which is a subset of Jakarta Persistence Query Language (JPQL). JDQL allows basic comparison and update operations on a single entity (an entity identifier variable is not used), as well as the ability to perform deletion. Find operations in JDQL consist of SELECT, FROM, WHERE, and ORDER BY clauses, all of which are optional. The static metamodel, which allows for more type-safe usage, is simplified in Milestone 4 to allow all fields to be pre-initialized. To use these capabilities, you need an Entity and a Repository.

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:

  • annotated with jakarta.persistence.Entity and related annotations from Jakarta Persistence

  • a Java class without entity annotations, in which case the primary key is inferred from an entity property that is named id or ending with Id and an entity property that is named version designates an automatically incremented version column.

You define one or more repository interfaces for an entity, annotate those interfaces as @Repository, and inject them into components by using @Inject. The Jakarta Data provider supplies the implementation of the repository interface for you.

The following example shows a simple entity:

@Entity
public class Product {
    @Id
    public long id;

    public boolean isDiscounted;

    public String name;

    public float price;

    @Version
    public long version;
}

The following example shows a repository that defines operations that relate to the entity. Your repository interface can inherit from built-in interfaces such as BasicRepository and CrudRepository to gain various general-purpose repository methods for inserting, updating, deleting, and querying for entities. You can add methods to further customize it.

@Repository(dataStore = "java:app/jdbc/my-example-data")
public interface Products extends BasicRepository<Product, Long> {
    @Insert
    Product add(Product newProduct);

    // query-by-method name pattern:
    List<Product> findByNameIgnoreCaseContains(String searchFor, Order<Product> orderBy);

    // parameter based query that does not require -parameters because it explicitly specifies the name
    @Find
    Page<Product> find(@By("isDiscounted") boolean onSale,
                       PageRequest<Product> pageRequest);

    // find query in JDQL that requires compilation with -parameters to preserve parameter names
    @Query("SELECT price FROM Product WHERE id=:productId")
    Optional<Float> getPrice(long productId);

    // update query in JDQL:
    @Query("UPDATE Product SET price = price - (?2 * price), isDiscounted = true WHERE id = ?1")
    boolean discount(long productId, float discountRate);

    // delete query in JDQL:
    @Query("DELETE FROM Product WHERE name = ?1")
    int discontinue(String name);
}

Observe that the repository interface includes type parameters in PageRequest<Product> and Order<Product>. These parameters help ensure that the page request and sort criteria are for a Product entity rather than some other entity. To accomplish this, you can optionally define a static metamodel class for the entity (or various IDEs might generate one for you after the 1.0 specification is actually released). Here is one that can be used with the Product entity:

@StaticMetamodel(Product.class)
public class _Product {
    public static final String ID = "id";
    public static final String IS_DISCOUNTED = "isDiscounted";
    public static final String NAME = "name";
    public static final String PRICE = "price";
    public static final String VERSION = "version";

    public static final SortableAttribute<Product> id = new SortableAttributeRecord(ID);
    public static final SortableAttribute<Product> isDiscounted = new SortableAttributeRecord(IS_DISCOUNTED);
    public static final TextAttribute<Product> name = new TextAttributeRecord(NAME);
    public static final SortableAttribute<Product> price = new SortableAttributeRecord(PRICE);
    public static final SortableAttribute<Product> version = new SortableAttributeRecord(VERSION);
}

The following example shows the repository and static metamodel being used,

@DataSourceDefinition(name = "java:app/jdbc/my-example-data",
                      className = "org.postgresql.xa.PGXADataSource",
                      databaseName = "ExampleDB",
                      serverName = "localhost",
                      portNumber = 5432,
                      user = "${example.database.user}",
                      password = "${example.database.password}")
public class MyServlet extends HttpServlet {
    @Inject
    Products products;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // Insert:
        Product prod = ...
        prod = products.add(prod);

        // Find the price of one product:
        price = products.getPrice(productId).orElseThrow();

        // Find all, sorted:
        List<Product> all = products.findByNameIgnoreCaseContains(searchFor, Order.by(
                                     _Product.price.desc(),
                                     _Product.name.asc(),
                                     _Product.id.asc()));

        // Find the first 20 most expensive products on sale:
        Page<Product> page1 = products.find(onSale, Order.by(_Product.price.desc(),
                                                             _Product.name.asc(),
                                                             _Product.id.asc())
                                                         .pageSize(20));
        ...
    }
}

To use this feature, add the following code to your server.xml file:

<featureManager>
     <feature>data-1.0</feature>
</featureManager>

Jakarta Expression Language 6.0

The expressionLanguage-6.0 feature is an implementation of the Expression Language 6.0 Specification for Jakarta EE 11. The Expression Language 6.0 specification includes a number of new features and specification clarifications.

Support for java.util.Optional (not enabled by default) and java.lang.Record is added. Another new feature is the addition of the length property for Arrays. For more information and the change history of the specification between Expression Language 5.0 and Expression Language 6.0, see Changes between 6.0 and 5.0.

To use this feature, add the following code to your server.xml file:

<featureManager>
     <feature>expressionLanguage-6.0</feature>
</featureManager>

Jakarta Pages 4.0

The pages-4.0 feature is an implementation of the Pages 4.0 Specification for Jakarta EE 11. The Pages 4.0 specification includes a couple of new features and the removal of previously deprecated functions.

The jakarta.servlet.jsp.ErrorData class was updated to add support for the new jakarta.servlet.error.method and jakarta.servlet.error.query_string attributes. The following deprecated classes, methods, and actions were removed:

  • jakarta.servlet.jsp.JspException.getRootCause()

  • Classes in jakarta.servlet.jsp.el.

  • isThreadSafe page directive

  • jsp:plugin action and related actions

  • jakarta.servet.jsp.tagext.BodyTag.EVAL_BODY_TAG constant

  • Any methods that implemented jakarta.el.ELResolver.getFeatureDescriptors() were removed as the getFeatureDescriptors() method was removed in Expression Language 6.0.

For more information and the change history of the specification between Pages 3.1 and Pages 4.0, see Changes between 3.1 and 4.0.

To use this feature, add the following code to your server.xml file:

<featureManager>
     <feature>pages-4.0</feature>
</featureManager>

For more information, see the Jakarta Pages 4.0 Specification.

Jakarta Servlet 6.1

The Open Liberty servlet-6.1 feature is an implementation of the Servlet 6.1 specification for Jakarta EE 11 . It includes a number of new features, specification clarifications, and deprecates some existing servlet features.

Before Servlet 6.1, there was no way for an application to control the response data when doing a send redirect, as well as the response status code, which was always set to 302. An application could not easily retrieve the initial request’s query string or request HTTP method during an error handling dispatch. It also could not set the character encoding for a request or a response using the java.nio.charset.Charset; the only available option was using a String. During the read or write of servlet data, the jakarta.servlet.ServletInputStream or jakarta.servlet.ServletOutputStream only supports a byte array.

Servlet 6.1 provides servlet APIs that allow the send redirect to include an optional response data or set a compliant status code, instead of the default 302. Additional request attributes are available during the error handling process to easily retrieve the initial request’s query string or method. Furthermore, international applications can now set the character encoding using a Charset instead of a String. The ServletInputStream or ServletOutputStream can use java.nio.ByteBuffer to read or write the data.

Several clarifications are added to the behavior of the existing servlet APIs. For example, the getParameter family from the jakarta.servlet.ServletRequest is now throwing the runtime java.lang.IllegalStateException when an error occurs during the parsing of the request’s parameters. While processing an error-handling dispatch, the HTTP GET method is always used instead of the original request’s HTTP method.

To use this feature, add the following code to your server.xml file:

<featureManager>
   <feature>servlet-6.1</feature>
</featureManager>

For more information, see the Jakarta Servlet 6.1 specification.

Use InstantOn with IBM® MQ in Liberty

The 24.0.0.5-beta release introduces InstantOn feature support for Jakarta Messaging, Jakarta Connectors, and Jakarta Enterprise Beans Message-Driven Beans (MDB). InstantOn now provides blazing fast startup times for applications that use resource adapters to access external EIS resources, including applications that use the Jakarta Messaging API to access external messaging systems, like IBM® MQ. These "messaging client" applications can also manage message delivery to endpoints with message-driven bean listeners.

Here’s a server configuration snippet to deploy applications that use the JakartaEE-10 Messaging API and require the IBM® MQ resource adapter to access external messaging resources. This configuration automatically includes the connectors-2.1 feature, which supports the resource adapter configuration element.

<featureManager>
   <feature>messaging-3.1</feature>
   <feature>mdb-4.0</feature>
   <feature>servlet-6.0</feature>
<featureManager/>

<resourceAdapter id="mqJms" location="${server.config.dir}/wmq.jakarta.jmsra-9.3.5.0.rar"/>

You can use the MQ in Container image to stand up an MQ server that provides the following resources: queue manager QM1, queue DEV.QUEUE.1, channel DEV.APP.SVRCONN, and listener SYSTEM.LISTENER.TCP.1 on port 1414. These resources can support a simple point-to-point messaging scenario and are named within the messaging feature configuration elements shown in the following example.

InstantOn can dynamically update the messaging and connectors configuration elements to enable connections to external resources that are made available in any environment where a server is restored from checkpoint.

In the following example, the messaging configuration elements use Liberty variables named MQ_HOSTNAME and MQ_PORT to specify the hostname and IP port of the system that the MQ queue manager runs on. You can define these variables in the server.env file of a checkpointed server. When the server is restored, the queue connection factory and message endpoint activation configurations update to the environment-specific hostname and port values for the MQ queue manager.

   <jmsQueue id="jms/queue1" jndiName="jms/queue1">
      <properties.mqJms  baseQueueName="DEV.QUEUE.1"  baseQueueManagerName="QM1"/>
   </jmsQueue>

   <variable name="MQ_PORT" value="1414"/>
   <variable name="MQ_HOSTNAME" value="localhost"/>

   <jmsQueueConnectionFactory jndiName="jms/qcf1" connectionManagerRef="ConMgr7">
      <properties.mqJms  hostName="${MQ_HOSTNAME}"  port="${MQ_PORT}"
            channel="DEV.APP.SVRCONN"  queueManager="QM1"/>
   </jmsQueueConnectionFactory>

   <jmsConnectionFactory jndiName="jms/cf1" connectionManagerRef="ConMgr1">
      <properties.mqJms  hostName="${MQ_HOSTNAME}"  port="${MQ_PORT}"
            channel="DEV.APP.SVRCONN"  queueManager="QM1"/>
   </jmsConnectionFactory>
    <connectionManager id="ConMgr1" maxPoolSize="10"/>

   <jmsActivationSpec id="myapp/mymdb/FVTMessageDrivenBean">
      <properties.mqJms  destinationRef="jms/queue1"  destinationType="jakarta.jms.Queue"
            transportType="CLIENT"  hostName="${MQ_HOSTNAME}"  port="${MQ_PORT}"
            channel="DEV.APP.SVRCONN"  queueManager="QM1"/>
   </jmsActivationSpec>

Enjoy the time savings and stay tuned for upcoming announcements regarding InstantOn support for Jakarta features.

Try it now

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 22, Java SE 21, Java SE 17, Java SE 11, and Java SE 8.

If you’re using Maven, you can install the All Beta Features package using:

<plugin>
    <groupId>io.openliberty.tools</groupId>
    <artifactId>liberty-maven-plugin</artifactId>
    <version>3.10.2</version>
    <configuration>
        <runtimeArtifact>
          <groupId>io.openliberty.beta</groupId>
          <artifactId>openliberty-runtime</artifactId>
          <version>24.0.0.5-beta</version>
          <type>zip</type>
        </runtimeArtifact>
    </configuration>
</plugin>

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:

<dependency>
    <groupId>org.example.spec</groupId>
    <artifactId>exampleApi</artifactId>
    <version>7.0</version>
    <type>pom</type>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>example.platform</groupId>
    <artifactId>example.example-api</artifactId>
    <version>11.0.0</version>
    <scope>provided</scope>
</dependency>

Or for Gradle:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:3.8.2'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[24.0.0.5-beta,)'
}

Or if you’re using container images:

FROM icr.io/appcafe/open-liberty:beta

Or take a look at our Downloads page.

If you’re using IntelliJ IDEA, Visual Studio Code or Eclipse IDE, you can also take advantage of our open source Liberty developer tools to enable effective development, testing, debugging, and application management all from within your IDE.

For more information on using a beta release, refer to the Installing Open Liberty beta releases documentation.

We welcome your feedback

Let us know what you think on our mailing list. If you hit a problem, post a question on StackOverflow. If you hit a bug, raise an issue.