Monday, November 29, 2021 - 05:00
  • Share this article:

The Jakarta RESTful Web Services 3.1 specification that will be released with Jakarta EE 10 includes dozens of small improvements and clarifications as well as two major new features: a Java SE Bootstrap API and a Multipart API. It’s also fully backwards-compatible with previous releases of the specification. 

In the remainder of this article, we’ll summarize the improvements, and we’ll refer to Jakarta RESTful Web Services simply as Jakarta REST.

Enhancements Across All Areas of the API

It’s difficult to cover all of the enhancements in Jakarta REST 3.1, but here are some of the most important ones:

  • Numerous improvements and clarifications to the Server-Sent Events (SSE) API, including:
    • A new close method on SseBroadcaster 
    • Better description of onClose behavior
    • Clarification of SSE client re-connections, as well as the onError and onComplete methods 
    • Exception handling in onEvent
  • A new service-based extension to load providers. Jakarta REST providers can now be loaded using the standard Java Service Loader mechanism. This provides an alternative way to set up the appropriate pipeline for request-response processing, and is primarily for Jakarta REST extensions.
  • Better support for HTTP cookies, including a new fluent API for creating them.
  • The default exception mapper now attempts to map an exception to a response without developer intervention. Typically, developers will want to define a mapper, but if it is not defined, a mapper will be provided by the REST implementation.
  • Better support for AutoClosable and Serializable.
  • Deprecated types and methods in preparation for version 4.0. These include JAXB-related types as well as the getSingletons method in Application which is typically not used due to similar support provided by Jakarta Contexts and Dependency Injection (CDI).

Java SE Bootstrap API for Porting Jakarta REST Applications Developed With Java SE

Previously, some Jakarta REST implementations supported Java SE environments, but you couldn’t port a Jakarta REST application across vendors. The Java SE Bootstrap API requires Jakarta REST implementations to include an embedded HTTP server that interacts with resources with no need for an application server or other similar component to enable this portability.

With the advent of microservices, use of Jakarta REST in Java SE applications has grown significantly, increasing the need for the Bootstrap API. Although MicroProfile also supports this use case, it does so somewhat differently, and in a way that’s more suitable for applications that rely on other specifications from the MicroProfile ecosystem.

To start a Jakarta REST application in Java SE, you need to define an Application subclass and, optionally, some basic configuration, as shown in the example below. 

 

// Create application instance

Application application = new HelloWorld();

// Start using default configuration 

SeBootstrap.start(application).thenAccept(instance -> { 

    instance.stopOnShutdown(stopResult -> System.out.printf(”Instance stopped”));

    URI uri = instance.configuration().baseUri(); 

    System.out.printf("Instance %s running at %s", instance, uri); 

    System.out.println("Send SIGKILL to shutdown.");

});

 

In the example above, the start method returns a CompletionStage upon which additional actions can be registered. In this case, an action is associated with the instance shutdown and some information is printed on the standard output after startup is complete.

The next example shows how to use an Application subclass instead of an instance, and how to provide additional configuration to enable HTTPS in the Jakarta REST application.

 

// Set up HTTPS 
SeBootstrap.Configuration config = SeBootstrap.Configuration.builder()
    .protocol("HTTPS") 
    .sslContext(getSslContext())
    .build();
 
// Start using the provided configuration
SeBootstrap.start(HelloWorld.class, config).thenAccept(instance -> { 
    // use instance
});

 

Because the Bootstrap API is inherently asynchronous, it enables concurrent initialization of other application services while the embedded HTTP server is started and the Jakarta REST resources are being registered.

Multipart API for Producing and Consuming Parts of Entities 

Support for multipart entities (multipart/form-data) has been requested for some time. Most Jakarta REST implementations provide some support for this media type, but similar to the situation described above, portability was not previously possible. 

With multipart support, an entity comprises multiple parts, each with its own set of headers and media type. Although typically used together with HTML forms, the media type can be used on its own to send or receive multiple parts at the same time — for example, JSON content with attachments.

The new API introduces the EntityPart type and supports producing and consuming List<EntityPart>. Each entity part has a media type, a name, a file name, headers, and some content. 

The example below demonstrates how to produce a multipart entity as the return type of a GET resource.

@GET
@Produces(MediaType.MULTIPART_FORM_DATA)
List<EntityPart> getFiles(@QueryParam("dirName") String dirName) { 
    File dir = getDirectoryIfExists(dirName); 
    List<EntityPart> parts = new ArrayList<>(); 
    for (File f : dir.listFiles()) { 
        parts.add(EntityPart.withFileName(f.getName())
            .content(new FileInputStream(f))
            .mediaType("application/pdf")
            .build()); 
    } 
    return parts;
}

There’s a simple and intuitive fluent API to create each EntityPart, and they are all collected in a list that is returned by the resource method. Multiparts can be consumed by accepting a List<EntityPart>, or by binding each part individually (and ignoring some parts) using the @FormParam annotation. The parameter to this annotation is the part name as shown below.

@Path("/apply")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response applyForJob(
    @FormParam("name") String name, 
    @FormParam("recentPhoto") InputStream photoStream, 
    @FormParam("resume") EntityPart resume) 

    processApplication(name, photoStream, resume);
    return Response.ok("Application received").build();
}

Binding parts in a resource method using @FormParm when the parameter type is anything other than EntityPart prevents access to attributes such as headers and file names, but it can be simpler, and it helps with code readability. As with normal entities, message body readers and writers are triggered to convert a part based on its media and parameter types. 

Looking Ahead to Jakarta REST 4.0

Jakarta REST 4.0 will be the first backward-incompatible release of the specification, and will be proposed for inclusion in Jakarta EE 11. 

This version will define much tighter integration with Jakarta CDI, and will end support for @Context and related artifacts such as context resolvers. It will define new CDI-based scopes for all Jakarta REST types by mapping the existing per-request and per-application scopes, and use CDI for bean discovery in all environments, including Java SE. It will also eliminate the compile-time dependency for JAXB types, and provide better integration with Jakarta Concurrency and other popular reactive APIs. Stay tuned.

Get Involved in Jakarta RESTful Web Services 

We welcome everyone who is interested in helping evolve the Jakarta REST specification to join the team. There are many different ways to contribute. For more information:

About the Author

Santiago Pericas-Geertsen

Santiago Pericas-Geertsen

Santiago Pericas-Geertsen is the project lead for Jakarta RESTful Web Services.