Jakarta EE Cookbook - Second Edition

5 (1 reviews total)
By Elder Moraes
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. New Features and Improvements

About 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.

Publication date:
May 2020
Publisher
Packt
Pages
380
ISBN
9781838642884

 

New Features and Improvements

Jakarta EE 8 is a big release, desired and anticipated by the global community for many years. More than ever before, the whole platform is now even more robust, mature, and stable.

This chapter will cover the main APIs that we can highlight for Jakarta EE 8. Not that they are the only topics covered by this release—far from it—but they have a big role in the enterprise context and are worthy of a careful look inside.

Also, this chapter (as the whole book) will work perfectly with Jakarta EE 9. 

In this chapter, we will cover the following recipes:

  • Running your first Jakarta Bean Validation 2.0 code 
  • Running your first Jakarta CDI 2.0 code 
  • Running your first JAX-RS 2.1 code 
  • Running your first JSF 2.3 code 
  • Running your first JSON-P 1.1 code 
  • Running your first JSON-B 1.0 
  • Running your first Jakarta Servlet 4.0 code 
  • Running your first Jakarta Security 1.0 
  • Running your first MVC 1.0 code 

 

Running your first Jakarta Bean Validation 2.0 code

Jakarta Bean Validation is a Java specification that basically helps you to protect your data. Through its API, you can validate fields and parameters, express constraints using annotations, and extend your customs validation rules.

It can be used both with Java SE and Jakarta EE.

In this recipe, you will have a glimpse of Jakarta Bean Validation 2.0. It doesn't matter whether you are new to it or are already using version 1.1; this content will help to familiarize you with some of its new features.

Getting ready

First, you need to add the right Jakarta Bean Validation dependency to your project, as follows:

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.15.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b11</version>
</dependency>
</dependencies>

How to do it...

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

  1. First, we need to create an object with some fields to be validated:
public class User {

@NotBlank
private String name;

@Email
private String email;

@NotEmpty
private List<@PositiveOrZero Integer> profileId;


public User(String name, String email, List<Integer> profileId) {
this.name = name;
this.email = email;
this.profileId = profileId;
}
}
  1. Then, we create a UserTest class to validate those constraints:
public class UserTest {

private static Validator validator;

@BeforeClass
public static void setUpClass() {
validator = Validation.buildDefaultValidatorFactory()
.getValidator();
}

@Test
public void validUser() {
User user = new User(
"elder",
"[email protected]",
asList(1,2));

Set<ConstraintViolation<User>> cv = validator
.validate(user);
assertTrue(cv.isEmpty());
}

@Test
public void invalidName() {
User user = new User(
"",
"[email protected]",
asList(1,2));

Set<ConstraintViolation<User>> cv = validator
.validate(user);
assertEquals(1, cv.size());
}

@Test
public void invalidEmail() {
User user = new User(
"elder",

"elder-eldermoraes_com",
asList(1,2));

Set<ConstraintViolation<User>> cv = validator
.validate(user);
assertEquals(1, cv.size());
}

@Test
public void invalidId() {
User user = new User(
"elder",
"[email protected]",
asList(-1,-2,1,2));

Set<ConstraintViolation<User>> cv = validator
.validate(user);
assertEquals(2, cv.size());
}
}

After this, let's see how the recipe works.

How it works...

Our User class uses four of the new constraints introduced by Jakarta Bean Validation 2.0:

  • @NotBlank: This ensures that the value is not null, empty, or an empty string (it trims the value before evaluation, to make sure there aren't spaces).
  • @Email: This allows only a valid email format. Forget those crazy JavaScript functions!
  • @NotEmpty: This ensures that a list has at least one item.
  • @PositiveOrZero: This guarantees that a number is equal to or greater than zero.

Then, we create a test class (using JUnit) to test our validations. It first instantiates Validator, as follows:

@BeforeClass
public static void setUpClass() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}

Validator is an API that validates beans according to the constraints defined for them.

Our first test method tests a valid user, which is a User object that has the following:

  • Name not empty
  • Valid email
  • A profileId list only with integers greater than zero

This is shown in the following code snippet:

User user = new User(
"elder",
"[email protected]",
asList(1,2));

And finally, we have the validation:

Set<ConstraintViolation<User>> cv = validator.validate(user);

The validate() method from Validator returns a set of constraint violations found, if any, or an empty set if there are no violations at all.

So, for a valid user, it should return an empty set:

assertTrue(cv.isEmpty());

For the other methods that work with variations around this model, we have the following:

  • invalidName(): Uses an empty name
  • invalidEmail(): Uses a malformed email
  • invalidId(): Adds some negative numbers to the list

Note that the invalidId() method adds two negative numbers to the list:

asList(-1,-2,1,2));

So, we expect two constraint violations:

assertEquals(2, cv.size());

In other words, Validator checks not only the constraints violated, but how many times they are violated.

See also

 

Running your first Jakarta CDI 2.0 code

Jakarta Contexts and Dependency Injection (CDI) is certainly one of the most important APIs for the Jakarta EE platform. In version 2.0, it also works with Java SE.

Nowadays, CDI has an impact on many other APIs in the Jakarta EE platform as said in an interview for Java EE 8 – The Next Frontier project:

"If there was CDI by the time we created JSF, it would be made completely different."
                                                                                                     – Ed Burns, JSF Spec Lead

There are a lot of new features in CDI 2.0. This recipe will cover observer ordering to give you a quick start.

Getting ready

We need to add the right CDI 2.0 dependency to your project. To make things easier at this point, we are going to use CDI SE, the dependency that allows you to use CDI without a Jakarta EE server:

<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>3.1.0.Final</version>
</dependency>

How to do it...

This recipe will show you one of the main features introduced by CDI 2.0: ordered observers. Now, you can turn the observer's job into something predictable:

  1. First, let's create an event to be observed:
public class MyEvent {

private final String value;

public MyEvent(String value){
this.value = value;
}

public String getValue(){
return value;
}
}
  1. Now, we build our observers and the server that will fire them:
public class OrderedObserver {

public static void main(String[] args){
try(SeContainer container =
SeContainerInitializer.newInstance().initialize()){
container
.getBeanManager()
.fireEvent(new MyEvent("event: " +
System.currentTimeMillis()));
}
}

public void thisEventBefore(
@Observes @Priority(Interceptor.Priority
.APPLICATION - 200)
MyEvent event){

System.out.println("thisEventBefore: " + event.getValue());
}

public void thisEventAfter(
@Observes @Priority(Interceptor.Priority
.APPLICATION + 200)
MyEvent event){

System.out.println("thisEventAfter: " + event.getValue());
}
}

  1. Also, don't forget to add the beans.xml file to the META-INF folder:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
  1. Once you run it, you should see a result like this:
INFO: WELD-ENV-002003: Weld SE container 
353db40d-e670-431d-b7be-4275b1813782 initialized

thisEventBefore: event -> 1501818268764
thisEventAfter: event -> 1501818268764

Now, let's see how this works.

How it works...

First, we are building a server to manage our event and observers:

public static void main(String[] args){
try(SeContainer container =
SeContainerInitializer.newInstance().initialize()){
container
.getBeanManager()
.fireEvent(new ExampleEvent("event: "
+ System.currentTimeMillis()));
}
}

This will give us all of the resources needed to run the recipe as if it were a Jakarta EE server.

Then, we build an observer, as follows:

public void thisEventBefore(
@Observes @Priority(Interceptor.Priority.APPLICATION - 200)
MyEvent event){

System.out.println("thisEventBefore: " + event.getValue());
}

So, we have three important topics:

  • @Observes: This annotation is used to tell the server that it needs to watch the events fired with MyEvent.
  • @Priority: This annotation informs in which priority order this observer needs to run; it receives an int parameter, and the execution order is ascendant.
  • MyEvent event: This is the event being observed.

In the thisEventBefore method and thisEventAfter, we only changed the @Priority value and the server took care of running it in the right order.

There's more...

The behavior would be exactly the same in a Jakarta EE 8 server. You just wouldn't need SeContainerInitializer and would need to change the dependencies to the following:

        <dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
</dependency>

See also

 

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

 

Running your first JSF 2.3 code 

Jakarta Server Faces (JSF) is the Java technology made to simplify the process of building UIs.

With JSF, you can build components and use (or reuse) them in the UI in an extensible way. You can also use other powerful APIs, such as Jakarta CDI and Jakarta Bean Validation, to improve your application and its architecture.

In this recipe, we will use the Validator and Converter interfaces with the new feature introduced by version 2.3, which is the possibility of using them with generic parameters.

Getting ready

We need to add the required dependencies:

        <dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
</dependency>

How to do it...

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

  1. Let's create a User class as the main object of our recipe:
public class User implements Serializable {

private String name;
private String email;

public User(String name, String email) {
this.name = name;
this.email = email;
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM
}
  1. Now, we create a UserBean class to manage our UI:
@Named
@ViewScoped
public class UserBean implements Serializable {

private User user;

public UserBean(){
user = new User("Elder Moraes", "[email protected]");
}

public void userAction(){
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Name|Password welformed"));
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM
}
  1. Now, we implement the Converter interface with a User parameter:
@FacesConverter("userConverter")
public class UserConverter implements Converter<User> {

@Override
public String getAsString(FacesContext fc, UIComponent uic,
User user) {
return user.getName() + "|" + user.getEmail();
}

@Override
public User getAsObject(FacesContext fc, UIComponent uic,
String string) {
return new User(string.substring(0, string.indexOf("|")),
string.substring(string.indexOf("|") + 1));
}

}
  1. Now, we implement the Validator interface with a User parameter:
@FacesValidator("userValidator")
public class UserValidator implements Validator<User> {

@Override
public void validate(FacesContext fc, UIComponent uic,
User user)
throws ValidatorException {
if(!user.getEmail().contains("@")){
throw new ValidatorException(new FacesMessage(null,
"Malformed e-mail"));
}
}
}
  1. And then, we create our UI using all of them:
<h:body>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel value="Name|E-mail:"
for="userNameEmail"/>
<h:inputText id="userNameEmail"
value="#{userBean.user}"
converter="userConverter" validator="userValidator"/>
<h:message for="userNameEmail"/>
</h:panelGrid>
<h:commandButton value="Validate"
action="#{userBean.userAction()}"/>
</h:form>
</h:body>

Don't forget to run it in a Jakarta EE 8 server.

How it works...

From the code in the preceding section, we see that the UserBean class manages the communication between the UI and the server. Once you instantiate the user object, it is available for both of them.

That's why, when you run it, Name | E-mail is already filled (the user object is instantiated when the UserBean class is created by the server).

We associated the userAction() method from the UserBean class with the Validate button of the UI:

<h:commandButton value="Validate" action="#{userBean.userAction()}"/>

You can create other methods in UserBean and do the same to empower your application.

The whole core of our recipe is represented by just a single line in the UI:

<h:inputText id="userNameEmail" value="#{userBean.user}" converter="userConverter" validator="userValidator"/>

So, our two implemented interfaces used here are userConverter and userValidator.

Basically, the UserConverter class (with the getAsString and getAsObject methods) converts an object into/from a string and vice versa, according to the logic defined by you.

We have just mentioned it in the preceding code snippet:

value="#{userBean.user}"

The server uses the userConverter object, calls the getAsString method, and prints the result using the preceding expression language.

Finally, the UserValidator class is automatically called when you submit the form, by calling its validate method, and applying the rules defined by you.

There's more...

You could increase the validators by adding a Jakarta Bean Validation on it and, for example, defining the email property from User with an @Email constraint.

See also

 

Running your first JSON-P 1.1 code 

Jakarta JSON Processing is the API for JSON processing. By processing, we mean generating, transforming, parsing, and querying JSON strings and/or objects.

In this recipe, you will learn how to use a JSON Pointer to get a specific value from a JSON message very easily.

Getting ready

Let's get our dependency:

<dependencies>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>runtime</runtime>
</dependency>
</dependencies>

How to do it...

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

  1. First, we define a JSON message to represent the User object:
{
"user": {
"email": "[email protected]",
"name": "Elder",
"profile": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
}
}
  1. Now, we create a method to read it and print the values we want:
public class JPointer {

public static void main(String[] args) throws IOException{
try (InputStream is =
JPointer.class.getClassLoader().getResourceAsStream("user.json");
JsonReader jr = Json.createReader(is)) {

JsonStructure js = jr.read();
JsonPointer jp = Json.createPointer("/user/profile");
JsonValue jv = jp.getValue(js);
System.out.println("profile: " + jv);
}
}
}
  1. The execution of this code prints the following:
profile: [{"id":1},{"id":2},{"id":3}]

How it works...

The JSON Pointer is a standard defined by the Internet Engineering Task Force (IETF) under Request for Comments (RFC) 6901. The standard basically says that a JSON Pointer is a string that identifies a specific value in a JSON document.

Without a JSON Pointer, you would need to parse the whole message and iterate through it until you find the desired value—probably lots of if and else instances and things like that.

So, a JSON Pointer helps you to decrease the written code dramatically by doing this kind of operation very elegantly.

See also

 

Running your first JSON-B 1.0 code 

Jakarta JSON Binding is an API for converting Java objects into/from JSON messages in a standardized way. It defines a default mapping algorithm to convert Java classes into JSON and still lets you customize your own algorithms.

With JSON-B (JSON-Binding), Jakarta EE has a complete set of tools to work with JSON, such as the JSON API and JSON-P (JSON-Padding). No third-party frameworks are needed anymore (although you are still free to use them).

This quick recipe will show you how to use JSON-B to convert a Java object into and from a JSON message.

Getting ready

Let's add our dependencies to the project:

<dependencies>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
</dependency>
</dependencies>

How to do it...

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

  1. Let's create a User class as a model for our JSON message:
public class User {

private String name;
private String email;

public User(){
}

public User(String name, String email) {
this.name = name;
this.email = email;
}

@Override
public String toString() {
return "User{" + "name=" + name + ", email=" + email + '}';
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM

}
  1. Then, let's create a class to use JSON-B to transform an object:
public class JsonBUser {

public static void main(String[] args) throws Exception {
User user = new User("Elder", "[email protected]");

Jsonb jb = JsonbBuilder.create();
String jsonUser = jb.toJson(user);
User u = jb.fromJson(jsonUser, User.class);

jb.close();
System.out.println("json: " + jsonUser);
System.out.println("user: " + u);

}
}
  1. The result printed is as follows:
 json: {"email":"[email protected]","name":"Elder"}
user: User{name=Elder, [email protected]}

The first line is the object transformed into a JSON string. The second is the same string converted into an object.

How it works...

It uses the getters and setters defined in the User class to transform both ways and that's why they are so important.

See also

 

Running your first Jakarta Servlet 4.0 code 

Jakarta Servlet 4.0 is one the of biggest APIs of Jakarta EE 8. Since the very beginning of the Java EE platform (the old J2EE), the Servlet specification has always played a key role.

The coolest additions of this version are surely HTTP/2.0 and Server Push. Both of them bring performance improvements to your application.

This recipe will use Server Push to do one of the most basic tasks on a web page—load an image.

Getting ready

Let's add the dependencies that we need:

        <dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
</dependency>

How to do it...

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

  1. We will create a servlet:
@WebServlet(name = "ServerPush", urlPatterns = {"/ServerPush"})
public class ServerPush extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse
response) throws ServletException, IOException {

PushBuilder pb = request.newPushBuilder();
if (pb != null) {
pb.path("images/javaee-logo.png")
.addHeader("content-type", "image/png")
.push();
}

try (PrintWriter writer = response.getWriter();) {
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append("<center>");
html.append("<img src='images/javaee-logo.png'><br>");
html.append("<h2>Image pushed by ServerPush</h2>");
html.append("</center>");
html.append("</html>");
writer.write(html.toString());
}
}
}

  1. To try it, run the project in a Jakarta EE 8 server and open this URL:
https://localhost:8181/ch01-servlet/ServerPush

Let's now see how this works.

How it works...

We use the PushBuilder object to send an image to the client before it is requested by the img src tag. In other words, the browser doesn't need to do another request (what it usually does with img src) to have an image available for rendering.

It might seem as if it doesn't make too much difference for a single image, but it would with dozens, hundreds, or thousands of images. There's less traffic for your client and from your server. That's better performance for all!

There's more...

If you are using JSF, you can get the benefits from Server Push for free! You don't even need to rewrite a single line of your code, as JSF relies on the Server Push specification.

Just make sure that you run it under the HTTPS protocol, as HTTP/2.0 only works under it.

See also

 

Running your first Jakarta Security code 

Security is one of the top concerns when you build an enterprise application. Luckily, the Jakarta EE platform now has this API that handles many of the enterprise requirements in a standardized way.

In this recipe, you will learn how to define roles and give them the right authorization based on rules defined in the methods that manage sensitive data.

Getting ready

We start by adding our dependencies to the project:

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-core</artifactId>
<version>7.0.4</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

How to do it...

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

  1. We first create a User entity:
@Entity 
public class User implements Serializable{

@Id
private Long id;
private String name;
private String email;

public User(){
}

public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM
}
  1. Here, we create a class to store our security roles:
public class Roles {
public static final String ADMIN = "ADMIN";
public static final String OPERATOR = "OPERATOR";
}
  1. Then, we create a stateful bean to manage our user operations:
@Stateful
public class UserBean {

@PersistenceContext(unitName = "ch01-security-pu",
type = PersistenceContextType.EXTENDED)
private EntityManager em;

@RolesAllowed({Roles.ADMIN, Roles.OPERATOR})
public void add(User user){
em.persist(user);
}

@RolesAllowed({Roles.ADMIN})
public void remove(User user){
em.remove(user);
}

@RolesAllowed({Roles.ADMIN})
public void update(User user){
em.merge(user);
}

@PermitAll
public List<User> get(){
Query q = em.createQuery("SELECT u FROM User as u ");
return q.getResultList();
}
  1. Now, we need to create an executor for each role:
public class RoleExecutor {

public interface Executable {
void execute() throws Exception;
}

@Stateless
@RunAs(Roles.ADMIN)
public static class AdminExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}

@Stateless
@RunAs(Roles.OPERATOR)
public static class OperatorExecutor {
public void run(Executable executable) throws Exception {
executable.execute();
}
}
}
  1. And finally, we create a test class to try our security rules.
  2. Our code uses three test methods: asAdmin(), asOperator(), and asAnonymous().
  3. First, it tests asAdmin():
    //Lot of setup code before this point

@Test
public void asAdmin() throws Exception {
adminExecutor.run(() -> {
userBean.add(new User(1L, "user1", "[email protected]"));
userBean.add(new User(2L, "user2", "[email protected]"));
userBean.add(new User(3L, "user3", "[email protected]"));
userBean.add(new User(4L, "user4", "[email protected]"));

List<User> list = userBean.get();

list.forEach((user) -> {
userBean.remove(user);
});

Assert.assertEquals("userBean.get()", 0,
userBean.get().size());
});
}
  1. Then, it tests asOperator():
    @Test
public void asOperator() throws Exception {

operatorExecutor.run(() -> {
userBean.add(new User(1L, "user1", "[email protected]"));
userBean.add(new User(2L, "user2", "[email protected]"));
userBean.add(new User(3L, "user3", "[email protected]"));
userBean.add(new User(4L, "user4", "[email protected]"));

List<User> list = userBean.get();

list.forEach((user) -> {
try {
userBean.remove(user);
Assert.fail("Operator was able to remove user " +
user.getName());
} catch (EJBAccessException e) {
}
});
Assert.assertEquals("userBean.get()", 4,
userBean.get().size());
});
}
  1. And, finally, it tests asAnonymous():
    @Test
public void asAnonymous() {

try {
userBean.add(new User(1L, "elder",
"[email protected]"));
Assert.fail("Anonymous user should not add users");
} catch (EJBAccessException e) {
}

try {
userBean.remove(new User(1L, "elder",
"[email protected]"));
Assert.fail("Anonymous user should not remove users");
} catch (EJBAccessException e) {
}

try {
userBean.get();
} catch (EJBAccessException e) {
Assert.fail("Everyone can list users");
}
}

This class is huge! For the full source code, check the link at the end of this recipe.

How it works...

The whole point in this recipe is to do with the @RolesAllowed, @RunsAs, and @PermitAll annotations. They define what operations each role can do and what happens when a user tries an operation using the wrong role.

There's more...

What we did here is called programmatic security, that is, we defined the security rules and roles through our code (the program). There's another approach called declarative security, where you declare the rules and roles through application and server configurations.

One good step up for this recipe is to evolve the roles management to a source outside the application, such as a database or a service.

See also

 

Running your first MVC 1.0 code 

If you have been following Jakarta EE 8 history for a while, you may now be wondering: why is MVC 1.0 here if it was dropped from the Jakarta EE 8 umbrella?

Yes, it is true. MVC 1.0 doesn't belong to the Jakarta EE 8 release. But that doesn't reduce the importance of this great API and I'm sure it will change the way some other APIs work in future releases (for example, JSF).

So, why not cover it here? You will use it anyway.

This recipe will show you how to use a Controller (the C) to inject a Model (the M) into the View (the V). It also brings some Jakarta CDI and JAX-RS to the party.

Getting ready

Add the proper dependencies to your project:

<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.mvc</groupId>
<artifactId>javax.mvc-api</artifactId>
<version>1.0-pr</version>
</dependency>
</dependencies>

How to do it...

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

  1. Start by creating a root for your JAX-RS endpoints:
@ApplicationPath("webresources")
public class AppConfig extends Application{
}
  1. Create a User class (this will be your Model):
public class User {

private String name;
private String email;

public User(String name, String email) {
this.name = name;
this.email = email;
}

//DON'T FORGET THE GETTERS AND SETTERS
//THIS RECIPE WON'T WORK WITHOUT THEM
}
  1. Now, create a session bean, which will be injected later in your Controller:
@Stateless
public class UserBean {

public User getUser(){
return new User("Elder", "[email protected]");
}
}
  1. Then, create the Controller:
@Controller
@Path("userController")
public class UserController {

@Inject
Models models;

@Inject
UserBean userBean;

@GET
public String user(){
models.put("user", userBean.getUser());
return "/user.jsp";
}
}
  1. And finally, create the web page (the View):
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8">
<title>User MVC</title>
</head>
<body>
<h1>${user.name}/${user.email}</h1>
</body>
  1. Run it on a Jakarta EE 8 server and access this URL:
http://localhost:8080/ch01-mvc/webresources/userController

How it works...

The main actor in this whole scenario is the Models class injected into the Controller, shown as follows:

@Inject
Models models;

It's a class from MVC 1.0 API that has the responsibility, in this recipe, of letting the User object to be available for the View layer. It's injected (using CDI) and uses another injected bean, userBean, to do it:

models.put("user", userBean.getUser());

So, the View can easily access the values from the User object using expression language as follows:

<h1>${user.name}/${user.email}</h1>

See also

About the Author

  • Elder Moraes

    Elder Moraes helps Jakarta EE developers build and deliver secure, fast, and available applications so that they are able to work on great projects. He is passionate about content sharing; he does it by speaking at international events, blogging, and writing articles.

    He has been working with Java since 2002 and has developed applications for different industries. As a board member at SouJava, he led the Java EE 8 - The Next Frontier initiative, interviewing some world-class Java EE experts

    Browse publications by this author

Latest Reviews

(1 reviews total)
Very useful recipts that can be applied in my personal and professional opportunity

Recommended For You

Book Title
Unlock this book and the full library for FREE
Start free trial