10

Getting started with Jakarta RESTful Services

 2 years ago
source link: http://www.mastertheboss.com/jboss-frameworks/resteasy/getting-started-with-jakarta-restful-services/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The latest release of RESTEasy (6.1.0) provides an overview of what’s coming with Jakarta RESTful Web Services 3.1. which will be a core component of Jakarta EE 10. Let’s review through this article the upcoming features using RESTEasy (6.1.0) implementation.

What’s new in Jakarta RESTful Web Services 3.1

There is currently a list of proposal to be finalized in the upcoming Jakarta EE 10 specification with regards to RESTful Services. Some of the core additions include:

  • Java SE Bootstrap API
  • Support for multipart media type
  • Deprecation of @Context in preparation for better alignment with CDI

In this article we will start covering the items which already have an available test implementation, for instance the Java SE REST Bootstrap API.

Getting Started with the SeBootstrap API

This API provides a standard way of publishing a REST application on Java SE using a bootstrapping code that is completely portable across vendors and products.

In a Java SE environment, it is a common practice to publish an application using an embedded HTTP server. With this new API, an application can invoke the jakarta.ws.rs.SeBootstrap.start(app, config) method with an implementation of Application and a Configuration. For example:

// Bootstrap JAX-RS
SeBootstrap.Configuration requestedConfiguration = SeBootstrap.Configuration.builder().from(config)
.build();
// Bootstrap JAX-RS
SeBootstrap.Configuration requestedConfiguration = SeBootstrap.Configuration.builder().from(config)
	                .build();

The builder is created by SeBootstrap.Configuration.builder() and assembles all information needed to configure the embedded Web server using properties. After that, let’s build a full example application which uses the SeBootstrap API.

Firstly, you need a basic Java project, you can use any Maven archetype to get started, for example the maven-archetype-quickstart.

After that, we will be adding a minimal REST Service:

package com.sample.jaxws_bootstrap;
import java.util.Collections;
import java.util.Set;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Application;
@Path("greet")
public class HelloWorld extends Application {
* Returns the sole, singleton resource.
@Override
public Set<Class<?>> getClasses() {
return Collections.singleton(HelloWorld.class);
* @return {@code "Hello, World!"}
public String greeting() {
return "Hello World!";
package com.sample.jaxws_bootstrap;

import java.util.Collections;
import java.util.Set;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Application;


@Path("greet")
public class HelloWorld extends Application {

    /**
     * Returns the sole, singleton resource.
     */
    @Override
    public Set<Class<?>> getClasses() {
        return Collections.singleton(HelloWorld.class);
    }

    /**
     * @return {@code "Hello, World!"}
     */
    @GET
    public String greeting() {
        return "Hello World!";
    }

}

As you can see, the above HelloWorld class exposes just a single Resource through a GET method. In addition, let’s add the Bootstrap Class:

package com.sample.jaxws_bootstrap;
import java.net.URI;
import jakarta.ws.rs.SeBootstrap;
import jakarta.ws.rs.core.Application;
public class App {
public static void main(final String[] args) throws InterruptedException {
final Application application = new HelloWorld();
SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().port(8080).build();
final SeBootstrap.Configuration requestedConfiguration = SeBootstrap.Configuration.builder().from(config)
.build();
SeBootstrap.start(application, requestedConfiguration).thenAccept(instance -> {
instance.stopOnShutdown(stopResult ->
System.out.printf("Stop result: %s [Native stop result: %s].%n", stopResult,
stopResult.unwrap(Object.class)));
final URI uri = instance.configuration().baseUri();
System.out.printf("Instance %s running at %s [Native handle: %s].%n", instance, uri,
instance.unwrap(Object.class));
System.out.println("Send SIGKILL to shutdown.");
Thread.currentThread().join();
package com.sample.jaxws_bootstrap;

import java.net.URI;
import jakarta.ws.rs.SeBootstrap;
import jakarta.ws.rs.core.Application;

public class App {

	 public static void main(final String[] args) throws InterruptedException {
	       final Application application = new HelloWorld();

	    	SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().port(8080).build();

	        final SeBootstrap.Configuration requestedConfiguration = SeBootstrap.Configuration.builder().from(config)
	                .build();

	        SeBootstrap.start(application, requestedConfiguration).thenAccept(instance -> {
	            instance.stopOnShutdown(stopResult ->
	                    System.out.printf("Stop result: %s [Native stop result: %s].%n", stopResult,
	                            stopResult.unwrap(Object.class)));
	            final URI uri = instance.configuration().baseUri();
	            System.out.printf("Instance %s running at %s [Native handle: %s].%n", instance, uri,
	                    instance.unwrap(Object.class));
	            System.out.println("Send SIGKILL to shutdown.");
	        });

	        Thread.currentThread().join();
	    }
}

In short, here is what we are doing here:

Firstly, we are referencing our REST Application through the jakarta.ws.rs.core.Application. This is much like extending the Application Class to initialize REST Services on the server side.

Next, we are using the SeBootstrap.Configuration Class to provide some custom configuration. In our case, we are building a Configuration which uses port 8080 using the fluent builder API.

Then, we are starting the REST Server with the SeBootstrap.start which receives as input the Application and the Configuration. Likewise, a Shutdown listener will take care to do some clean-up if you send a signal to kill the server.

Finally, we are using the Thread.join() API to put our application in a wait state. Otherwise the server will just shutdown.

After that, you can optionally add a Test Class, for example with REST Assured APi to test your service:

public class AppTest
@Test
public void checkSiteIsUp() {
RestAssured.baseURI = "http://localhost:8080";
RequestSpecification httpRequest = RestAssured.given();
Response response = httpRequest.get("/greet");
String body = response.getBody().asString();
assertEquals(body,"Hello World!");
public class AppTest 
{
	 @Test
	 public void checkSiteIsUp() {
		    RestAssured.baseURI = "http://localhost:8080";
		    RequestSpecification httpRequest = RestAssured.given();
		    
		    Response response = httpRequest.get("/greet");
		    
		    String body = response.getBody().asString();
		    assertEquals(body,"Hello World!");
		              
	 }
}

Building and Running the application

Next, to build our application, at the time of writing there is still no official Jakarta EE 10 API. Therefore you will need to add the following dependencies:

<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0.RC1-jbossorg-1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-core</artifactId>
<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
	<groupId>jakarta.ws.rs</groupId>
	<artifactId>jakarta.ws.rs-api</artifactId>
	<version>3.1.0.RC1-jbossorg-1</version>
</dependency>
<dependency>
	<groupId>org.jboss.resteasy</groupId>
	<artifactId>resteasy-core</artifactId>
	<version>6.1.0.Alpha1</version>
</dependency>
<dependency>
	<groupId>org.jboss.resteasy</groupId>
	<artifactId>resteasy-undertow</artifactId>
	<version>6.1.0.Alpha1</version>
</dependency>
  • The jakarta.ws.rs-api dependency adds the main Jakarta RESTful Web Services 3.1.0 dependency.
  • The resteasy-core dependency is the actual RESTful implementation that RESTEasy provides
  • The resteasy-undertow provides RESTEasy Undertow Integration.

In addition, until the above API is available on the Maven Central, make sure you have in your settings.xml (or pom.xml) the JBoss Public Repository:

<repositories>
<repository>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<id>jboss-public-repository-group</id>
<name>JBoss Public Repository Group</name>
<url>https://repository.jboss.org/nexus/content/groups/public/</url>
<layout>default</layout>
</repository>
</repositories>
<repositories>
	<repository>
		<releases>
			<enabled>true</enabled>
			<updatePolicy>never</updatePolicy>
		</releases>
		<snapshots>
			<enabled>true</enabled>
			<updatePolicy>never</updatePolicy>
		</snapshots>
		<id>jboss-public-repository-group</id>
		<name>JBoss Public Repository Group</name>
		<url>https://repository.jboss.org/nexus/content/groups/public/</url>
		<layout>default</layout>
	</repository>
</repositories>

Next, run the main class, the Undertow server will start and the Rest Endpoint as well:

Jakarta REST Services

Advanced Bootstrap Configuration

Within our example application, we are using a minimal configuration which only sets the HTTP Port. We will show how to use some other configuration options with the fluent API.

Using a different protocol

SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().from(config).protocol("HTTPS")
.build();
SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().from(config).protocol("HTTPS")
                .build();

Configuring Client Authentication

SeBootstrap.Configuration.builder().protocol("HTTPS")
.sslClientAuthentication(SSLClientAuthentication.MANDATORY).build();
SeBootstrap.Configuration.builder().protocol("HTTPS")
                .sslClientAuthentication(SSLClientAuthentication.MANDATORY).build(); 

Using custom Host, Port and Document Root

SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().protocol("HTTP").host("localhost").rootPath("/").port(8080).build();
SeBootstrap.Configuration config = SeBootstrap.Configuration.builder().protocol("HTTP").host("localhost").rootPath("/").port(8080).build();

Finally, the source code for this article is available here: https://github.com/fmarchioni/mastertheboss/tree/master/jax-rs/jaxrs-bootstrap

Multipart/form-data support

The multipart/form-data media type enables requests to send multiple entities (called Parts) as a single entity. The Jakarta EE 8/9 does not provide a specific support for multpart/form. Therefore, each provider came up with a different option to handle it:

LibraryJerseyCFXResteasyMultipart param@FormDataParam@Multipart@MultipartForm + @FormData + @PartTypeMultipart bodyMultiPartMultipartBodyMultipartInputBody PartAttachmentBodyPartInputPart

There are mainly two options to consume multiple parts:

  • Declare your resource with a List of EntityPart. This is the only option to access the headers for a particular part. Example:
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postWidget(List<EntityPart> parts) {
for (EntityPart part : parts) {
String name = part.getName();
Optional<String> fileName = part.getFileName();
InputStream is = part.getContent();
MultivaluedMap<String, String> partHeaders = part.getHeaders();
MediaType mediaType = part.getMediaType();
doSomethingWithPart(name, fileName, is, partHeaders, mediaType);
return Response.ok().build();
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postWidget(List<EntityPart> parts) {
for (EntityPart part : parts) {
    String name = part.getName();
    Optional<String> fileName = part.getFileName();
    InputStream is = part.getContent();
    MultivaluedMap<String, String> partHeaders = part.getHeaders();
    MediaType mediaType = part.getMediaType();
    doSomethingWithPart(name, fileName, is, partHeaders, mediaType);
}
return Response.ok().build();
}

Use multiple @FormParam parameters. In this case, the value in the annotation corresponds to the name of the part. The parameter type may be a jakarta.ws.rs.core.EntityPart, a java.io.InputStream, or a String. Example:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postWidget(@FormParam("part1Name") String part1,
@FormParam("part2Name") InputStream part2,
@FormParam("part3Name") EntityPart part3) {...}
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response postWidget(@FormParam("part1Name") String part1,
                       @FormParam("part2Name") InputStream part2,
                       @FormParam("part3Name") EntityPart part3) {...}

Be aware that String parameter types will load the entire content of the Part into the Java heap. That might be an issue for very large Parts.

Deprecation of Context Injection

The JAX-RS API specification provides the @Context annotation to inject a set of object instances related to the context of HTTP requests. Example:

@Produces(MediaType.APPLICATION_JSON)
public Response getHeaders(final @Context HttpServletRequest request){
// Code here that uses HttpServletRequest
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getHeaders(final @Context HttpServletRequest request){
    // Code here that uses HttpServletRequest
    }
}

The @Context annotation is deprecated and future versions of Jakarta RESTful services will no longer support it. The same stands for related types such as ContextResolver. As much as possible, all injection tasks will be delegated to Jakarta CDI for a better integration into the Jakarta EE ecosystem.

References:

https://resteasy.dev/2022/04/19/resteasy-releases/

https://jakarta.ee/specifications/restful-ws/3.1/jakarta-restful-ws-spec-3.1.html

Post Views: 69

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK