Jakarta MVC In Jakarta EE 10

Photo of Luqman Saeed by Luqman Saeed

Jakarta EE has action and component based frameworks for building web applications using the model view controller architecture pattern. The much older Jakarta Faces is a component based framework while the much newer Jakarta MVC is an action based one. This blog starts by defining the two types, then takes a look at Jakarta MVC, what it is and how to get started.

MVC - What Is It?

Before defining the two types of web frameworks mentioned above, let us first look at the MVC architecture pattern. The model, or the M, refers to the underlying application data. InJakarta EE, this is generally a Jakarta Persistence entity or its projected instance (DTOs etc). The view or V, refers to the presentation of the model data to the user. Depending on the framework, there are different view technologies that can be used. For instanceJakarta Faces uses Facelets as its view technology, whileJakarta MVC supports both Facelets and Jakarta Server Pages (which is the default). 

The controller, or C, refers to the unit that manages user input, connects that input to the model and returns output to the user. The returned output could be another view, a redirect, a file download, or anything at all, depending on the application. You can think of the controller as the central, coordinating piece of the application that liaises user actions to different parts of the application.

Action Vs Component Based Frameworks

An action based web framework is one in which the application code creates an explicit controller that accepts requests and maps them to actions. Component based web frameworks on the hand, have the controllers owned and managed by the framework itself. The controller is transparent to the developer in a component based framework.

What Is Jakarta MVC?

Jakarta MVC is an action based web application development framework built on top of Jakarta REST. It is an alternative way to build traditional web applications on the Jakarta EE Platform. Jakarta MVC is an optional standalone specification that is not part of Jakarta EE by default. As a Jakarta REST based framework, it makes all the features and options for developing REST services available for developing much more traditional web applications. 

Setting Up

To start with Jakarta MVC, you will need to add the specification and an implementation to your application dependencies. The current release is 2.1, withEclipse Krazo as the implementation. This setup is shown below. 

       <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>${jakartaee-api.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>jakarta.mvc</groupId>
            <artifactId>jakarta.mvc-api</artifactId>
            <version>${jakarta.mvc-api.version}</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.krazo</groupId>
            <artifactId>krazo-jersey</artifactId>
            <version>${krazo.version}</version>
        </dependency>

With the dependencies in place, you are ready to start with Jakarta MVC

The Controller

A typical Jakarta MVC application has three parts - the view, the controller and a model. The model doesn't have to necessarily be a database entity. I consider any data that the UI displays as the model. With your above setup, a typical Hello, World will look as follows. First let's see the controller.

@Path("app")
@Controller
public class AppController {

    @Inject
    Models models;


    @GET
    public String sayHello() {


        models.put("greet", "Hello, World! Jakarta MVC");
        models.put("platform", "Jakarta EE 10 on Payara 6 Community");
        models.put("date",  LocalDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        models.put("message", "Your Jakarta MVC application is running!");


        return "greet.xhtml";
    }

}

The AppController is a Jakarta REST resource class, identified as such by the @Path annotation. The @Controller annotation is from Jakarta MVC, marking all methods in this class as controller methods. A controller method is one that returns a view in the form of a string, as done by the sayHello() method. Or a jakarta.ws.rs.core.Response object that wraps a string object, resolvable to a veiw. 

The sayHello() method creates the models for the UI by using the jakarta.mvc.Models map. The Models is injected into the controller and then the method populates it accordingly with data for the UI to display. In this example, the injected Models instance, models, is populated with a few strings identified by their keys. The Models is essentially a map that makes its values available to the UI through its keys. All of this is done automatically on your behalf by Jakarta MVC runtime.

The last line of the sayHello() method returns greet.xhtml. This is the UI that will be rendered when the user navigates to the sayHello() method. For the sample application, the full path is http://localhost:8080/jakarta-mvc/mvc/app/, where jakarta-mvc is the context path, mvc is the Jakarta REST root resource path and app is the path to the sayHello() method. The Models map instance is automatically available to the view and as such it can access all the values we put into it. Now let's look at the view.

The View

There are two view technology options you can use with Jakarta MVC. The first and default is the Jakarta Server Pages or optionally Jakarta Faces. As Jakarta Server Pages is no longer a popular technology, this blog will stick to the much popular Jakarta Faces option. The easiest way to tell Jakarta MVC to use Jakarta Faces as the default view technology is to create an empty faces-config.xml in the WEB-INF folder. With that done, the greet.xhml facelet is shown next.

<!DOCTYPE html>
<html lang="en"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

<h:head>
    <title>Jakarta MVC</title>
</h:head>
<h:body>
    <h1>#{greet}</h1>

    <p>#{message}</p>
    <p>This application is running on #{platform}, deployed on #{date}</p>

</h:body>
</html>

The greet.xhml view is a very simple facelet file that is accessing the models to display to the user. The models that were put in the Models map instance are being accessed through the #{} expression, using the key of each value. For instance the #{greet} will return Hello, World! Jakarta MVC, as was put in the map. Accessinghttp://localhost:8080/jakarta-mvc/mvc/app/ gives us the response shown below. 

image-png-Jan-05-2023-03-39-51-1193-PM

Models

So far we have seen how we can pass models, or data to the view for display through the Models map. Another way is through the use ofCDI. First let's introduce our model, this time as a Plain Old Java Object, garnished with two CDI annotations, shown below.

@Named
@RequestScoped
public class Salutation {

    private String greet;
    private String platform;
    private String greetingDate;
    private String message;

}

Class Salutation is a simple Java class with some fields. These are the same fields we passed to the first view through the Models map. Salutation is annotated @Named and @RequestScoped. @Named is a CDI qualifier that makes CDI managed instances of the class available in an Expression Language context - as used in the facelet files. The @RequestScoped will cause a new instance of Salutation to be created for each injection point. With the model in place, let's look at the amended controller and how the model is instantiated and populated.

@Path("app")
@Controller
public class AppController {

    @Inject
    Salutation salutation;

    @GET
    @Path("salute")
    public String salute() {

String formattedDate = LocalDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        this.salutation
                .setGreet("Hello, World! Jakarta MVC")
                .setPlatform("Jakarta EE 10 on Payara 6 Community")
                .setGreetingDate(formattedDate)
                .setMessage("Your Jakarta MVC application is running!");
        return "salute.xhtml";
    }

}

The AppController controller has a new method, salute(), hosted at the path /salute, that populates a CDI injected instance of class Salutation. This method returns the salute.xhtml view to render the data. As you can see, the Models map is not used anywhere at all. The injected Salutation instance is automatically available to the view thanks to the @Named annotation. The salute.xhtml is shown next.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns:h="http://xmlns.jcp.org/jsf/html">

<h:head>
    <title>Title</title>
</h:head>

<h:body>

    <h1>#{salutation.greet}</h1>

    <p>#{salutation.message}</p>
    <p>This application is running on #{salutation.platform}, deployed on #{salutation.greetingDate}</p>

</h:body>

</html>

The salue.xhtml uses the same #{} expression to access the model. This time around it calls the getter methods of the various fields. The salutation instance is what is CDI makes available automatically. This way, the view has access to the model without explicitly using the Models map. The salute method is hosted at http://localhost:8080/jakarta-mvc/mvc/app/salute, which returns the following.

Screenshot_20230105_154213

Conclusion

Jakarta MVC is a much simpler web action based web framework for developing traditional web applications. In a future post, we will take a look at data binding and how the framework helps you validate user input.Make sure you are subscribed to our blogso you don't miss out! 

Please also check out our further Jakarta EE 10 resources:

Comments