Top 7 Features in Jakarta EE 10 Release

Java EE was the dominant force behind all the enterprise development in Java. Lately, it was renamed Jakarta EE, transitioning from JCP to Eclipse Foundation.

This transition opened the door to open governess, open compatibly testing (earlier Technology Compatibility Kit -TCK was closed source), and of course, open-source. That means no specific vendor has more influence than others; it’s more distributed now.

Jakarta EE is an integral part of the Java ecosystem. 25-35% of java application runs on JakartaEE application servers. WildFly, JBoss EAP, Payara, WebSphere/Liberty, WebLogic, etc., are some examples of application servers. These application servers are developed and maintained by a different vendor. However, as a technology, the Jakarta EE remains vendor-neutral, meaning the same application code should be run in any application servers since all vendors adhere to Jakarta EE specifications.

70-80% of java applications use Jakarta EE APIs one way or another. eg Tomcat, Hibernate, ActiveMQ, Jetty, CXF, Jersey, RESTEasy, Quarkus, Microprofile, Spring. If we dig a little deeper, we will find Jakarta EE APIs behind Java applications.

Jakarta EE is a lively ecosystem. We have Jakarta EE 9, Jakarta EE 9, MicroProfile, and Helidon. Quarkus, introduced after Micronaut and Helidon, remain a popular framework. Many applications are yet to migrate; that’s why Java EE 8 is still around. However, Jakarta EE 9 may be considered a foundation for innovation to drive new features in Jakarta EE 10, which will be released soon.

Jakarta EE 9 moves all relevant specifications javax to the Jakarta namespace. Unfortunately, that causes some breaking changes, but the migration rather seems easy. There are tools to do that. It also removes older deprecated technologies and moves Java 8 to Java 11.

Among many features making their way to Jakarta EE 10, I will discuss the top 7.

1. @ManagedExecutorDefinition

One of the most commonly used services in the Jakarta Concurrency API is the ManagedExecutorService. It is used to create a thread pool in the Jakarta EE environment to spawn threads in a more manageable way. Although it is pretty much the same as ExecutorService, we employ it in the Java SE environment.

To configure the thread pool, we had a vendor-specific setting. Now it can be defined with just annotation. This is a more standard way of configuring the thread pool.

package ca.bazlur;

import static jakarta.enterprise.concurrent.ContextServiceDefinition.APPLICATION;
import static jakarta.enterprise.concurrent.ContextServiceDefinition.SECURITY;

import jakarta.annotation.Resource;
import jakarta.ejb.Asynchronous;

import jakarta.enterprise.concurrent.ContextServiceDefinition;
import jakarta.enterprise.concurrent.ManagedExecutorDefinition;
import jakarta.enterprise.concurrent.ManagedExecutorService;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
import java.time.Duration;
import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;

@Named
@ApplicationScoped
@ManagedExecutorDefinition(
    name = "java:module/concurrent/MyExecutor",
    context = "java:module/concurrent/MyExecutorContext",
    hungTaskThreshold = 120000,
    maxAsync = 5)
@ContextServiceDefinition(
    name = "java:module/concurrent/MyExecutorContext",
    propagated = {SECURITY, APPLICATION})
public class WorkService {
  
  @Resource(name = "java:app/concurrent/MyExecutorService")
  private ManagedExecutorService executor;

  @Asynchronous
  public CompletableFuture<Long> hoursWorked(LocalDate from, LocalDate to) {

    return CompletableFuture.supplyAsync(() -> heavyAPICall(from, to), executor);
  }

  private static long heavyAPICall(LocalDate from, LocalDate to) {

    return Duration.between(from, to).toMillis();
  }
}

2. @Asynchronous

There is a similar annotation available in EJB. A method annotated with this annotation is supposed to run asynchronously. Unfortunately, however, the EJB one is a bit out of date.

Also, in EJB, we can’t specify the thread pool. It used to use the app server’s default thread pool. Now the new annotation jakarta.enterprise.concurrent.Asynchronous

comes with Jakarta EE Concurrency 3.0, which doesn’t require using EJB, and we can specify the thread pool. It can be used with any CDI bean. Each asynchronous method execution corresponds to a managed java.util.concurrent.CompletableFuture instance that is backed by jakarta.enterprise.concurrent.ManagedExecutorService as its default asynchronous execution facility.

package ca.bazlur;

import jakarta.enterprise.concurrent.Asynchronous;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

@Named
@ApplicationScoped
public class PaymentService {

  @Asynchronous(executor = "java:app/concurrent/MyExecutorService")
  public CompletableFuture<Confirmation> processPayment(final Order order) {
    try {
      var status = processOrder(order);
      return CompletableFuture.completedFuture(status);
    } catch (PaymentException ex) {
      throw new CompletionException(ex);
    }
  }

  private Confirmation processOrder(Order order) {
    return new Confirmation();
  }
}

class PaymentException extends RuntimeException {}
record Confirmation() {}
record Order() {}

3. Bootstrap API:

If you are invoking a rest service outside of a Jakarta EE environment, eg unit tests, integration tests, etc., this is good to have a standalone API to do that. With this, you don’t need to run the entire container. Thus, it becomes very convenient.

code example

4. Multipart/Form-Data

Jakarta RESTful Web Service is one of the popular features among developers to expose restful endpoint. However, it didn’t reasonably support multipart form data in a standard way. That’s why, for years, developers needed either Servlets or a vendor-specific REST API to handle the multipart media type. With the Jakarta REST 3.1, we no longer have to deal with such an issue, and it adds full support for multipart form data, which is standard.

package ca.bazlur;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.EntityPart;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.io.InputStream;

@Path("/job")
public class FileResource {

  @Path("/apply")
  @POST
  @Consumes(MediaType.MULTIPART_FORM_DATA)
  public Response applyForJob(
      @FormParam("name") String name, @FormParam("recentPhoto") EntityPart photo,
      @FormParam("resume") EntityPart resume) {
    processApplication(name,
        photo.getMediaType(), photo.getContent(), resume.getMediaType(), resume.getContent());

    return Response.ok("Application received").build();
  }

  private void processApplication(String name, MediaType mediaType, InputStream content,
      MediaType mediaType1, InputStream content1) {
  }
}

5. @OpenIdAuthenticationDefinition

Well, for many years, we have been using usernames and passwords for authenticating users in a web application. However, in modern applications, it has become widespread to log in using other services, eg Facebook, Google, Twitter, etc. The OpenID Connect protocol powers this kind of authentication. With the Jakarta Security 3.0, we now have a standard way to adhere to OpenID Connect by using just an annotation, which is @OpenIdAuthenticationDefinition. Life is much simpler now; specify the required properties, such as Provider URI, clientId, clientSecret, redirect URI, etc.

@OpenIdAuthenticationDefinition(
       providerURI = "https://sample-openid-server.com",
       clientId = "87068hgfg5675htfv6mrucov57bknst.apps.sample.com",
       clientSecret = "{my-secret}",
       redirectURI = "${baseURL}/callback",
       extraParameters = {
            "testKey=testValue", 
            "testKey2=testValue2"
       }
)

public class SecurityConfig {
}

6. UUID Key

Jakarta Persistence 3.1 brings java.util.UUID is to be used as a basic type of field, which is very convenient for entity IDs for the cloud environment since many databases don’t automatically generate UUID.

package ca.bazlur;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.util.UUID;

@Entity
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.UUID)
  private UUID id;

  private String username;
  private String password;
}

Besides, it brings several functions to the query language and Criteria API. The new functions fall into three categories:

  • Numeric functions (CEILING, EXP, FLOOR, LN, POWER, ROUND, SIGN)
  • Functions that return the current local date and time using java.time types (LOCAL DATE, LOCAL DATETIME, LOCAL TIME)
  • The EXTRACT function extracts a numeric part from a given date (YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND)

7. Jakarta Faces Views With Pure Java

Jakarta Faces 4.0 brings a new API to define the full View (HTML Pages) using only Java Code. So if you are particularly not interested in writing any kind of tagging language whatsoever, you can now write your UI code entirely in Java with the Pure Java faces View. I love it.

Java with the Pure Java faces View

Now a bit about Jakarta EE ambassadors: We are an independent grassroots group of people committed to moving Jakarta EE forward through active community participation and advocacy. If you want to join and contribute, find out more here: https://jakartaee-ambassadors.io/.The final release of the entire Jakarta 10 platform will be later in the next month. Find out more here: Jakarta EE 10 release plan.
Finally, if you want to know more about what’s going on with Jakarta EE, please watch this session by Reza Rahman.

That’s all for today.

.

Leave a Comment