Book Image

Jakarta EE Cookbook - Second Edition

By : Elder Moraes
Book Image

Jakarta EE Cookbook - Second Edition

By: Elder Moraes

Overview of this book

Jakarta EE is widely used around the world for developing enterprise applications for a variety of domains. With this book, Java professionals will be able to enhance their skills to deliver powerful enterprise solutions using practical recipes. This second edition of the Jakarta EE Cookbook takes you through the improvements introduced in its latest version and helps you get hands-on with its significant APIs and features used for server-side development. You'll use Jakarta EE for creating RESTful web services and web applications with the JAX-RS, JSON-P, and JSON-B APIs and learn how you can improve the security of your enterprise solutions. Not only will you learn how to use the most important servers on the market, but you'll also learn to make the best of what they have to offer for your project. From an architectural point of view, this Jakarta book covers microservices, cloud computing, and containers. It allows you to explore all the tools for building reactive applications using Jakarta EE and core Java features such as lambdas. Finally, you'll discover how professionals can improve their projects by engaging with and contributing to the community. By the end of this book, you'll have become proficient in developing and deploying enterprise applications using Jakarta EE.
Table of Contents (14 chapters)

Running your first JAX-RS 2.1 code

Jakarta RESTful Web Service, previously known as JAX-RS, is an API designed to give a portable and standard way of building RESTful web services in Java. This is one of the most widely used technologies for transporting data between different applications that use a network (the internet included) for communication.

One of the coolest features introduced by the 2.1 release is Server-Sent Events (SSE), which will be covered in this recipe. SSE is a specification created by HTML5, where it has established a channel between server and client, one way only from server to client. It is a protocol that transports a message containing some data.

Getting ready

Let's start by adding the right dependency to our project:

<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<version>2.28</version>
</dependency>
</dependencies>

You surely noticed that we are using Jersey here. Why? Because Jersey is one of the reference implementations for JAX-RS, which means that it implements all JAX-RS specifications.

Moreover, with Jersey, we can use Grizzly to start a small local server, which will be useful for this recipe, as we need just a few server features to show the SSE behavior.

Further on in this book, we will use a full GlassFish to build more JAX-RS recipes.

How to do it...

We need to perform the following steps to try this recipe:

  1. First, we create a class that will be our server:
public class ServerMock {

public static final URI CONTEXT =
URI.create("http://localhost:8080/");
public static final String BASE_PATH = "ssevents";

public static void main(String[] args) {
try {
final ResourceConfig resourceConfig = new
ResourceConfig(SseResource.class);

final HttpServer server =
GrizzlyHttpServerFactory.createHttpServer(CONTEXT,
resourceConfig, false);
server.start();

System.out.println(String.format("Mock Server started
at %s%s", CONTEXT, BASE_PATH));

Thread.currentThread().join();
} catch (IOException | InterruptedException ex) {
System.out.println(ex.getMessage());
}
}
}
  1. Then, we create a JAX-RS endpoint to send the events to the clients:
@Path(ServerMock.BASE_PATH)
public class SseResource {

private static volatile SseEventSink SINK = null;

@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public void getMessageQueue(@Context SseEventSink sink) {
SseResource.SINK = sink;
}

@POST
public void addMessage(final String message, @Context Sse sse)
throws IOException {
if (SINK != null) {
SINK.send(sse.newEventBuilder()
.name("sse-message")
.id(String.valueOf(System.currentTimeMillis()))
.data(String.class, message)
.comment("")
.build());
}
}
}
  1. Then, we create a client class to consume the events generated from the server:
public class ClientConsumer {

public static final Client CLIENT = ClientBuilder.newClient();
public static final WebTarget WEB_TARGET =
CLIENT.target(ServerMock.CONTEXT
+ BASE_PATH);

public static void main(String[] args) {
consume();
}

private static void consume() {

try (final SseEventSource sseSource =
SseEventSource
.target(WEB_TARGET)
.build()) {

sseSource.register(System.out::println);
sseSource.open();

for (int counter=0; counter < 5; counter++) {
System.out.println(" ");
for (int innerCounter=0; innerCounter < 5;
innerCounter++) {
WEB_TARGET.request().post(Entity.json("event "
+ innerCounter));
}
Thread.sleep(1000);
}

CLIENT.close();
System.out.println("\n All messages consumed");
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}

}
}
  1. For you to try it out, first run the ServerMock class and then the ClientConsumer class. If everything worked well, you should see something like this:
 InboundEvent{name='sse-message', id='1502228257736', comment='',   data=event 0}
InboundEvent{name='sse-message', id='1502228257753', comment='', data=event 1}
InboundEvent{name='sse-message', id='1502228257758', comment='', data=event 2}
InboundEvent{name='sse-message', id='1502228257763', comment='', data=event 3}
InboundEvent{name='sse-message', id='1502228257768', comment='', data=event 4}

These are the messages sent from the server to the client.

How it works...

This recipe is made up of three parts:

  • The server, represented by the ServerMock class
  • The SSE engine, represented by the SseResource class
  • The client, represented by the ClientConsumer class

So, once ServerMock is instantiated, it registers the SseResource class:

final ResourceConfig resourceConfig = new ResourceConfig(SseResource.class);
final HttpServer server = GrizzlyHttpServerFactory.createHttpServer(CONTEXT, resourceConfig, false);
server.start();

Then, two key methods from SseResource take place. The first one adds messages to the server queue:

addMessage(final String message, @Context Sse sse)

The second one consumes this queue and sends the messages to the clients:

@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
public void getMessageQueue(@Context SseEventSink sink)

Note that this one has a media type, SERVER_SENT_EVENTS, introduced in this version for this very purpose. And finally, we have our client. In this recipe, it is both posting and consuming messages.

It consumes here:

sseSource.register(System.out::println);
sseSource.open();

It posts here:

ServerMock.WEB_TARGET.request().post(Entity.json("event " + innerCounter));

See also