Chapter 3. Just enough Application Server for microservices

This chapter covers

  • What is Just enough Application Server?
  • What is MicroProfile?
  • What runtimes support JeAS?
  • How do JeAS runtimes compare?

This chapter explores the ideas behind Just enough Application Server (JeAS) and the runtime options that we as developers have for developing Enterprise Java microservices using JeAS. We’ll begin by defining JeAS and how it compares to Java EE. To aid in the discussion, a hypothetical microservice, requiring several specifications, will be described so its needs may be evaluated against what the various JeAS runtimes offer. As part of the comparison, we’ll detail each of the JeAS runtimes and how they differ as we develop a Beach Vacation shopping application.

3.1. Just enough Application Server

The term Just enough Application Server has been used occasionally over the years, but usually in relation to customizing a full application server by removing functionality manually. Only since the popularity of microservices has JeAS become crucial for Enterprise Java. This section covers what JeAS means, its benefits, and what an example developed in each of the JeAS runtimes looks like.

3.1.1. What does JeAS mean?

Say you need to develop a microservice that interacts with an enterprise information system (EIS), such as SAP, to retrieve Human Resources (HR) information on employees. For this microservice, you’ve chosen to use JAX-RS, CDI, and JMS. If you were to develop such a microservice for deployment onto a typical Java EE application server, it’d most likely be done against the full Java EE platform, as illustrated in figure 3.1.

Figure 3.1. Microservice specification usage of full Java EE platform

As you can see, there are lots of specifications within the full platform that you’re not using, but they’re still there even though you don’t require them. The full platform has 33 JSRs included within it. That’s a lot of specifications that you may not always need.

Maybe there’s a Java EE profile you can use to slim it down? You have only one option right now. Let’s try the Web Profile and see how that works; see figure 3.2.

Figure 3.2. Microservice specification usage of Java EE Web Profile

That results in fewer unused specifications, but now you have the problem that JMS isn’t part of the Web Profile. You can still add an implementation of JMS to your microservice as part of the deployment, but it’s no longer automatically part of the stack and may require additional configuration that you didn’t need with the full platform.

What’s the answer? Can JeAS help? And what exactly is Just enough Application Server? In a nutshell, JeAS inverts the relationship between an application server and an application, ensuring that you package only the parts of an application server that your application requires. In the case of our preceding microservice example, you know that you need JMS, so you choose the full platform of an application server. But you know your application will never use large parts of that application server.

Java EE profiles

Since Java EE 6, we’ve had one profile and the full platform available for developers to choose as their application server. Although you may not be familiar with these options, the following provides an overview of which specifications the full platform and Web Profile contain.

Feature

Web Profile

Full platform

EJB (Local)
JTS/JTA
Clustering
Servlet
JSF
JPA
JBDC
CDI
Bean validation
JAX-RS
JSON-P
EJB (remote)  
JCA  
JAX-WS  
JAXB  
JMS  
JavaMail  
JAX-RPC  
JAXR  

Many application servers provide the flexibility to slim down their distribution by removing components and their associated configuration. I’ve worked with many customers in the past who have taken this approach. But finding the correct combination that still ensures that the application server functions properly requires somewhat of a trial-and-error approach. In some cases, there may even be a component that you’d like to remove but can’t, usually because that component is a key part of the application server.

Customizing an application server for many different applications quickly devolves into a complex set of differing configurations that need to be managed and maintained. In these situations, developers typically prefer to simplify their lives and choose the full platform as opposed to spending time trying to slim the application server. They opt to accept the extra overhead that comes with not using all components of the application server.

Over the years, several application servers, such as WildFly, have worked to reduce the footprint of components that aren’t being used. Though the dependencies required for various components are still on the classpath, the application server is clever enough to not load those classes into memory if the deployed applications don’t require them. This can go only so far, unfortunately, because many components are too central to the functioning of the application server, no matter what an application might require.

So where does JeAS fit in with your microservices architecture from figure 1.4? Take a look at figure 3.3.

Figure 3.3. JeAS as runtime for microservices architecture

As you can see, the focus of JeAS is on the runtime needed for a microservice. A JeAS runtime aims to provide a whittled-down application server for microservices, but the way it’s packaged can differ among implementations.

JeAS runtimes provide a simple and manageable way to include only the parts of an application server that your application requires. Some runtimes are more flexible than others in terms of what’s included, and we’ll cover those details shortly.

Which JeAS runtime is chosen can impact the packaging available to a microservice. The driving factor, however, should always be about what’s supported by a JeAS runtime and not how it needs to be packaged.

3.1.2. What are the benefits?

In your SAP microservice from the previous section, you saw how painful it can be when your application relies on the smaller-footprint Web Profile. It requires you to bring in additional libraries and configure them to work with the rest of the application server.

As developers, we want to spend our time effectively, developing new features or fixing bugs. We don’t want to spend it endlessly configuring application servers based on differing requirements between applications. More often than not, we’d choose the full platform for the sake of simplicity.

What’s the big deal with using the full platform? Sure, there are lots of parts you may not use today, but you plan to one day, right? Certainly, in some cases an application will grow to include the use of one, or maybe two, additional specifications that weren’t part of the original design. It’s highly doubtful that an application would suddenly grow to include all specifications of the full platform. If it does, there’s likely a need to redesign the application, because it contains too many features for a single application. So that leaves a large part of a full platform application server unused.

Wouldn’t it be nice if application servers weren’t one size fits all? This is one of the use cases that JeAS aims to solve, by allowing the developer to choose which features, or specifications, of an application server are required for a given application.

Why does that matter? If an application needs only servlets, it can be deployed to a JeAS runtime that has only servlets available for an application. With a JeAS runtime, if an application needs to add a feature, such as JAX-RS, a developer can choose to add that feature into the JeAS runtime as a standalone piece. It’s no longer necessary to choose between only two Java EE options, or attempt to customize the application server yourself.

This flexibility means that JeAS runtimes have great benefits:

  • Reduced package sizeWhen compared against an application bundled with the application server it’s deployed to.
  • Reduction in allocated memoryHow reduced will depend on many factors, such as the number of classes that are no longer being loaded.
  • Reduced security footprintFewer ports are being opened for various features, and fewer services are running. In addition, you have a significantly reduced surface area for potential critical vulnerabilities (CVEs).
  • Greater separation between applicationsMany applications were usually deployed to a single application server.
  • Simplified upgradesThe upgrade impacts a single application only.

Greater separation between applications can mean a great many things, so it warrants additional explanation. Over the years that Enterprise Java applications have been deployed into production, an application server rarely would contain a single application. Typically, an application server would be running anywhere from a handful to dozens of applications in a single instance.

As you can see in figure 3.4, JeAS runtimes provide a greater isolation between different microservices than applications in a traditional Java EE application server.

Figure 3.4. Traditional Java EE vs. JeAS runtimes

Why was this the case? Historically, the biggest reason is cost—and not just the cost of the application server, which usually wasn’t cheap, but all the physical hardware that was required to run a single application server. Certainly, over the last decade, with improved virtual machines and the rise of containers in recent years, the amount of required physical hardware for production environments has dropped significantly—and along with it, the cost of production environments for enterprises.

With JeAS runtimes, it’s possible, ignoring containers for now, to run many instances of them on a single piece of physical hardware. Each JeAS runtime running in its own process is isolated from the others, preventing a common problem of collocated applications in an application server: namely, that one application failure causes the whole application server, and all applications running on it, to fail in an unrecoverable manner.

3.1.3. Eclipse MicroProfile

For anyone following developments in the landscape of Enterprise Java and microservices over the last couple of years, you’ve likely heard about Eclipse MicroProfile. It’s a community initiative to “optimize Enterprise Java for microservices” that was formed with collaboration from Red Hat, IBM, Tomitribe, Payara, and the London Java Community. Since its initial formation, the community has moved to the Eclipse Foundation.

From the first release with JAX-RS, CDI, and JSON-P forming the base Java EE technologies, we’ve now surpassed version 1.3, including eight new MicroProfile specifications over those versions so far. Table 3.1 details the specifications included in each MicroProfile release.

Table 3.1. MicroProfile specifications in each release

Specification

1.0 (Sep 2016)

1.1 (July 2017)

1.2 (Sep 2017)

1.3 (Jan 2018)

JAX-RS
CDI
JSON-P
Config  
Fault tolerance    
JWT propagation    
Metrics    
Health check    
Open tracing      
Open API      
Type-safe REST client      

The community has an aim of providing a new release roughly every quarter. The project has done well to hold closely to that schedule, though a delay occurred after the 1.0 release for the project submission to the Eclipse Foundation. Time was needed for all existing project code and documentation to be reviewed by the Eclipse Foundation, as is required by the foundation.

Eclipse MicroProfile creates specifications for Enterprise Java microservices, with the benefit that microservices become portable between JeAS runtimes that support Eclipse MicroProfile. There will certainly be JeAS runtimes that don’t implement the specifications, and those that provide more flexibility than is defined. The goal isn’t to cover every possible use case for Enterprise Java microservice development, but to collaborate on what an opinionated stack should contain that covers the majority of use cases.

Over the last 18 months, the MicroProfile community has delivered functionality for solving the problems of Enterprise Java, microservices, and the cloud. It has done so in a collaborative and inclusive manner, with more individual contributors and vendors joining the effort as it moves forward.

3.2. Choosing Just enough Application Server

Now it’s time to evaluate a handful of the most popular runtimes for Enterprise Java microservices. You’ll follow the development of a simple microservice example application to show the differences among the frameworks, both in the way the code differs and in the sets of features that each framework brings to the table.

The full code of the example application for each runtime is available in the source code for this book (https://github.com/kenfinnigan/ejm-samples).

3.2.1. Beach Vacation example application

Our Beach Vacation example application will be a simple shopping cart that has a RESTful interface and a single class representing an item in the cart. You’ll pre-populate the contents of the shopping cart with items that everyone needs on a beach vacation! To keep it simple, you’ll store only a name and quantity in your CartItem.

Listing 3.1. CartItem
public class CartItem {
    private String itemName;                                    1

    private Integer itemQuantity;                               2

    public CartItem(String name, Integer qty) {                 3
        this.itemName = name;
        this.itemQuantity = qty;
    }

    public String getItemName() {
        return itemName;
    }

    public CartItem itemName(String itemName) {
        this.itemName = itemName;
        return this;
    }

    public Integer getItemQuantity() {
        return itemQuantity;
    }

    public CartItem itemQuantity(Integer itemQuantity) {
        this.itemQuantity = itemQuantity;
        return this;
    }

    public CartItem increaseQuantity(Integer itemQuantity) {   4
        this.itemQuantity = this.itemQuantity + itemQuantity;
        return this;
    }
}

  • 1 Name of the item.
  • 2 Quantity to be bought.
  • 3 Construct an instance of CartItem with the provided name and quantity.
  • 4 Convenience method for increasing the quantity by a specified amount

The other piece you need is your RESTful interface. In keeping it simple, you won’t be using a database to store items; the data will be held in memory only. Your CartController will initialize a list of items for you to use as a base for the shopping cart. Listing 3.2 is your controller code at its simplest so you can see exactly what each framework requires within the class and methods later. It provides three methods that you’ll make available over REST: all(), addOrUpdateItem(), and getItem(). The addOrUpdateItem() method is the most complicated, because it handles adding quantity to an existing item in the cart or adding an entirely new item.

Listing 3.2. CartController
public class CartController {
    private static List<CartItem> items = new ArrayList<>();

    static {                                                              1
        items.add(new CartItem("sunscreen", 3));
        items.add(new CartItem("towel", 1));
        items.add(new CartItem("hat", 5));
        items.add(new CartItem("umbrella", 1));
    }

    public List<CartItem> all() throws Exception {
        return items;                                                     2
    }

    public String addOrUpdateItem(String itemName, Integer qty) throws
     Exception {
        Optional<CartItem> item = items.stream()
                .filter(i -> i.getItemName().equalsIgnoreCase(itemName))
                .findFirst();                                             3

        if (item.isPresent()) {                                           4
            Integer total =
     item.get().increaseQuantity(qty).getItemQuantity();
            return "Updated quantity of '" + itemName + "' to " + total;
        }

        items.add(new CartItem(itemName, qty));                           5
        return "Added '" + itemName + "' to shopping cart";
    }

    public CartItem getItem(String itemName) throws Exception {
        return items.stream()
                .filter(i -> i.getItemName().equalsIgnoreCase(itemName))
                .findFirst()
                .get();                                                   6
    }
}

  • 1 Populates the cart with commonly required items for a beach vacation
  • 2 Returns all the current items in the cart
  • 3 Streams all the cart items to find one whose name matches
  • 4 Checks whether you found an item by name. If yes, then update the quantity.
  • 5 Item wasn’t found in cart, so add it.
  • 6 Filters all cart items to find the one whose name matches

Now you’ve covered the two main classes you’ll need for your Beach Vacation shopping application. In the following sections, you’ll update these two classes for each JeAS runtime based on their particular requirements.

Note

The following examples don’t always follow the proper use of REST HTTP verbs and semantics. The examples illustrate a comparison of the runtimes, rather than proper REST patterns.

3.2.2. Dropwizard—the original opinionated Microservice runtime

Dropwizard provides a small JeAS runtime by being opinionated about what developers need in order to build a microservice. For Dropwizard, that means the following:

  • Eclipse Jetty as an HTTP server
  • Jersey for RESTful endpoints
  • Jackson for transforming data to/from JSON
  • Hibernate Validator
  • Dropwizard Metrics to provide insight into code behavior in production

Dropwizard provides additional libraries to make it easier to develop a microservice, in addition to the preceding ones. Check out www.dropwizard.io for the full list.

If your application requires libraries that Dropwizard doesn’t include for you, you need to add the necessary Maven dependencies to your project, adding whatever configuration those libraries might require as well.

Note

Dropwizard began in early 2011 and was the first project to put together an opinionated JeAS runtime for microservices. Dropwizard has now surpassed version 1.3.0.

Let’s go back to the sample microservice we talked about earlier that uses JAX-RS, CDI, and JMS. Figure 3.5 shows what the microservice looks like with Dropwizard.

Figure 3.5. Microservice usage in Dropwizard

When developing microservices that use more than just JAX-RS from Java EE, it’s obvious that everything else you need must be added and integrated. Though this is possible, it may not be the most practical option because it requires a lot more initial project setup before being able to develop any code.

It’s for this reason that Thorntail, not Dropwizard, is my preferred runtime for Enterprise Java microservices. Dropwizard covers only a small portion of what would be required. This is especially true when converting existing Enterprise Java applications into microservices, because you don’t want to have to rewrite all the code to use different technologies. Ideally, you want to take an existing application and package it in a different manner for use with a JeAS runtime.

Back to our Beach Vacation shopping cart, let’s create your Dropwizard project by generating a project with the Maven archetype:

mvn archetype:generate -DarchetypeGroupId=io.dropwizard.archetypes
-DarchetypeArtifactId=java-simple -DarchetypeVersion=1.0.9

Now that you have your project, let’s modify the basic code so it can be used with Dropwizard. The first change is easy: adding a default constructor to your CartItem bean.

Your CartController needs modification to make it RESTful. First you need to define the RESTful path that your controller will be accessible from.

Listing 3.3. CartController RESTful path
@Path("/")
public class CartController {
}

Now you need to add the JAX-RS annotations to your methods.

Listing 3.4. CartController methods with annotations
@GET                                                               1
@Produces(MediaType.APPLICATION_JSON)
public List<CartItem> all() throws Exception {}

@GET
@Path("/add")                                                      2
public String addOrUpdateItem(
    @QueryParam("item") String itemName,
    @QueryParam("qty") Integer qty) throws Exception {             3
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/get/{itemName}")
public CartItem getItem(
    @PathParam("itemName") String itemName) throws Exception {     4
}

  • 1 Indicates that the method supports only HTTP GET requests
  • 2 Endpoint is accessible as /add.
  • 3 Method parameters that will be passed on the URL, such as /add?item=hat&qty=2
  • 4 Parameter defined as part of the URL path, /get/hat.

The JAX-RS annotations you’ve added have no surprises, because they’re regularly used for RESTful endpoints. Your three methods are all annotated with @GET. The all() and getItem() methods both produce JSON output, so you’ve added @Produces to indicate the correct media type for JSON. The addOrUpdateItem() method is accessible with a URL path of /add, and you add the necessary @QueryParam definitions to your method parameters. It maps URL query string parameters into your method parameters on invocation based on the name you pass to @QueryParam. Finally, getItem() specifies a URL path that defines the path parameter @Path("/get/{itemName}"), which is then passed to your method by setting @PathParam("itemName") on the parameter.

Note

CartController.addOrUpdateItem() is defined with @GET, which does break normal RESTful semantics; it isn’t an idempotent operation, because you’re modifying data. But I’ve taken this route purely for simplicity, because your object model has only two fields and it enables you to call the endpoint directly from a browser URL, removing the need to use curl or browser extensions to POST data for testing.

Now you need to add custom classes so that Dropwizard knows what to run and how it’s configured. First, you need to create a configuration class that specifies any environment-specific parameters that your application requires. In this example, you’re not worried about environment parameters, so the class can be empty.

Listing 3.5. Chapter3Configuration
public class Chapter3Configuration extends Configuration {
}

Finally, you need to extend Application from Dropwizard so you can specify what needs to be run.

Listing 3.6. Chapter3Application
public class Chapter3Application extends Application<Chapter3Configuration> {

    public static void main(final String[] args) throws Exception {            1
        new Chapter3Application().run(args);
    }

    @Override
    public String getName() {
        return "chapter3";
    }

    @Override
    public void initialize(final Bootstrap<Chapter3Configuration> bootstrap) { 2
    }

    @Override
    public void run(final Chapter3Configuration configuration,
                    final Environment environment) {
        final CartController resource = new CartController();
        environment.jersey().register(resource);                               3
    }
}

  • 1 Used by Dropwizard to start your application.
  • 2 Configure any parts of the application that need to be set up before it’s run.
  • 3 Register an instance of your RESTful endpoint with Jersey.

Now that you’ve built the application, how can you run it? By creating the project with the Maven archetype, it has added the necessary plugins to build an uber jar. The only thing you need to do is ensure that the plugins in pom.xml reference the application class you created on any configuration that needs mainClass. Now build the application:

mvn clean package

and run the application:

java -jar target/chapter3-dropwizard-1.0-SNAPSHOT.jar server

It’s now possible to access the application by going to http://localhost:8080/ in a browser. This returns the list of current items in the cart. You can look at the details of hat in your cart by navigating to http://localhost:8080/get/hat. Update the quantity of an existing item with http://localhost:8080/add?item=towel&qty=1 or add a new item to your cart with http://localhost:8080/add?item=kite&qty=2.

Dropwizard has many other features that we didn’t cover here, such as Metrics and Health Checks, so check out www.dropwizard.io/1.0.0/docs/index.html for further information.

3.2.3. Payara Micro—slimmed Java EE app server in a JAR

Payara Micro is similar to Dropwizard in that it provides an opinionated JeAS runtime where the stack is defined. Any additional libraries that you want to be used need to be added to the application directly.

Payara Micro also has a different deployment model than the other runtimes you’ll look at, as Payara provides a distribution that can be executed directly. Payara’s distribution is like a prebuilt application server, except that it can be started with java -jar payara-micro.jar and given a WAR to deploy with --deploy myApp.war as part of the same command. Without --deploy, the distribution starts up just as a normal application server, but with nothing deployed.

Note

Payara Micro came out of the work that Payara was doing in providing fixes and enhancements to GlassFish v4.x via its Payara Server. Payara Micro was first released in May 2015 as a subset of Payara Server. Payara Micro has now surpassed version 5.181.

There are certainly advantages to having a distribution that you deploy your application to, as in a traditional application server. The biggest advantage is that the Payara Micro distribution can be used as a Docker layer. This makes it possible to create a Docker image containing that layer, which can then be used many times to package different applications with Docker.

The major downside to this type of JeAS runtime is that it isn’t possible to remove additional pieces. For instance, if your application requires only servlets, there’s no way to remove parts such as JAX-RS. The advantages of this approach might outweigh such a downside, but that’s a decision for an enterprise to make based on its situation. We’ll cover a more flexible approach to JeAS a bit later.

What does Payara Micro provide? Figure 3.6 compares the Payara Micro distribution with the Web Profile.

Figure 3.6. Payara Micro compared to Web Profile

Let’s take a look at how your JAX-RS, CDI, JMS microservice uses Payara Micro; see figure 3.7.

Figure 3.7. Microservice usage in Payara Micro

Because Payara Micro doesn’t include the JMS specification, you need to add an implementation to your microservice yourself. This isn’t a major issue, but needing to include additional implementations is easier if they’re already provided in a distribution. But then you’re back to the problem of application server pieces being present but not used.

To create your Payara Micro project, you create a regular Maven WAR project as if you were developing a Java EE application that was being deployed to an application server. You can add a Maven dependency

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-web-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

and gain access to all the APIs that your application may require.

Because you also want to use JAXB, with Jackson, you need to add the following dependency:

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-jackson</artifactId>
  <version>2.23.1</version>
</dependency>

Now that you have your project, let’s modify the basic code so it can be used with Payara Micro. For the CartItem bean, you need to identify it as being mappable to JAXB, create a default constructor, and use properly named setter methods.

Listing 3.7. CartItem with JAXB mappings
@XmlRootElement                                                1
public class CartItem {
    public CartItem() {
    }
    ...
    public CartItem setItemName(String itemName) {
        this.itemName = itemName;
        return this;
    }
    ...
    public CartItem setItemQuantity(Integer itemQuantity) {    2
        this.itemQuantity = itemQuantity;
        return this;
    }
}

  • 1 Enables Java class as JAXB mapping element
  • 2 Method changed from itemQuantity() to setItemQuantity()
Note

Payara Micro requires that a bean use proper setter methods, as you have in listing 3.7. A bean that contains Builder pattern–type named setter methods won’t correctly marshal to JSON.

Your CartController, as shown in listing 3.8, needs the same modifications to make it RESTful as you made for Dropwizard. Both use the JAX-RS APIs for RESTful endpoints.

Listing 3.8. CartController with Payara
@Path("/")
public class CartController {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<CartItem> all() throws Exception {}

    @GET
    @Path("/add")                                                      1
    public String addOrUpdateItem(
        @QueryParam("item") String itemName,
        @QueryParam("qty") Integer qty) throws Exception {             2
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/get/{itemName}")
    public CartItem getItem(
        @PathParam("itemName") String itemName) throws Exception {     3
    }
}

  • 1 Endpoint is accessible as /add.
  • 2 Method parameters that will be passed on the URL, such as /add?item=hat&qty=2
  • 3 Parameter defined as part of the URL path, /get/hat.

Now that your RESTful endpoint is defined, you need to tell the runtime that you want to make it available. With Payara Micro, you do that with a custom JAX-RS Application class that registers your resource.

Listing 3.9. JaxrsApplication with Payara
@ApplicationPath("/")
public class JaxrsApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new HashSet<>();
        resources.add(CartController.class);
        return resources;
    }
}

You specify a URL path for the whole application and then add your CartController class to a set of classes that the application makes available to the JAX-RS runtime for instantiation.

Now that you’ve developed the application, let’s run it. Before you can run it, you need to download the Payara Micro runtime from www.payara.fish/downloads.

Note

After downloading the runtime, it’s worth renaming the file to payara-micro.jar and removing the version information. You don’t need this information for running the file locally, and the omission makes the command line easier to read.

Because it’s a regular Maven WAR project, you build it as usual:

mvn clean package

and run the application:

java -jar payara-micro.jar --deploy target/chapter3.war

It’s now possible to access the application at http://localhost:8080/chapter3/ in a browser. This returns the list of current items in the cart. You can look at the details of hat in your cart by navigating to http://localhost:8080/chapter3/get/hat. Update the quantity of an existing item with http://localhost:8080/chapter3/add?item=towel&qty=1 or add a new item to your cart with http://localhost:8080/chapter3/add?item=kite&qty=2.

3.2.4. Spring Boot—opinionated Spring microservices

Spring Boot came about from a desire to remove the need for boilerplate configuration by following conventions instead. Annotations were also introduced to provide a means of enabling various parts of Spring Boot without needing configuration to do so.

Spring Boot provides many starters as dependencies for your project that combine related libraries and frameworks and configuration for many features that you may require when developing a microservice. For example, the spring-boot-starter-data-jpa dependency brings in all that’s required to use Spring and JPA for accessing a database. A full list of all the available starters can be found in the GitHub repository: http://mng.bz/cuQ3. Or take a look at http://start.spring.io, where you can create a Maven project based on the starters that you need for your application.

Figure 3.8 shows how your JAX-RS, CDI, JMS microservice uses Spring Boot. The biggest challenge with this microservice would be rewriting existing code that uses CDI to use Spring dependency injection instead. Options are available to make CDI work inside Spring, but if you’re looking for the project to remain as a Spring-based project, rewriting it to use Spring injection makes better sense.

Figure 3.8. Microservice usage in Spring Boot

With starters, Spring Boot is able to provide a flexible JeAS runtime that can be expanded or contracted as required based on the evolving requirements of the application. Modifying the application’s functionality is only a matter of adding or removing Spring Starter dependencies and rebuilding the application.

If you’re not sure what specific starters you might need, head over to http://start.spring.io to look at the options. The website contains a project generator that’s a great place to see the entire landscape of available starters and the types of functionality they provide, or the use cases they might solve. Starters are available for regular development tasks such as database access, but also for microservice programming patterns such as circuit breaking and service discovery.

Note

Spring Boot started in October 2012, and it has surpassed version 1.5.10.

You can use http://start.spring.io to create a project that includes the Web starter. This should give you a pom.xml that contains the following dependency:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Now that you have your project, let’s modify the basic code so it can be used with Spring Boot. For the CartItem bean, you need to add only the @XmlRootElement to it. With your CartController, you need to add the necessary annotations to make it a RESTful endpoint. This is similar to your JAX-RS-based annotations, but the names are slightly different.

Listing 3.10. CartController with Spring Boot
@RestController                                                 1
public class CartController {
    @RequestMapping(                                            2
      method = RequestMethod.GET,
      path = "/",
      produces = "application/json")
    public List<CartItem> all() throws Exception {}

    @RequestMapping(
      method = RequestMethod.GET,
      path = "/add",
      produces = "application/json")
    public String addOrUpdateItem(
        @RequestParam("item") String itemName,                  3
        @RequestParam("qty") Integer qty) throws Exception {
    }

    @RequestMapping(
      method = RequestMethod.GET,
      path = "/get/{itemName}",                                 4
      produces = "application/json")
    public CartItem getItem(
        @PathVariable("itemName") String itemName)              5
        throws Exception {
    }
}

  • 1 Indicates to Spring that the class will provide RESTful endpoint methods
  • 2 Method will be available on a URL path of / for HTTP GET requests.
  • 3 A URL query parameter called item will be mapped into this method parameter.
  • 4 URL path variable is expected after /get/ for this endpoint.
  • 5 URL path variable will be mapped to this method parameter.

Each of the methods on your controller provide the same details as your other JAX-RS examples but in a single annotation. @RequestMapping holds all the information of your JAX-RS examples that used @GET, @Produces, and @Path. The other difference is that @QueryParam from JAX-RS is @RequestParam with Spring, and @PathParam from JAX-RS is @PathVariable with Spring.

Note

Spring offers shortcuts for @RequestMapping as well. Instead of @RequestMapping(method = RequestMethod.GET, path = "/", produces = "application/json"), you could use @GetMapping(path = "/", produces = "application/json").

Your RESTful endpoint has now been defined, so last of all you create your Spring Boot application class.

Listing 3.11. Chapter3SpringBootApplication
@SpringBootApplication
public class Chapter3SpringBootApplication {

    public static void main(String[] args) {
           SpringApplication.run(Chapter3SpringBootApplication.class, args);
    }
}

All you’re doing here is saying that main() should activate @SpringBootApplication. It’s in this class that you’d add additional annotations for various parts of Spring Boot that you want to activate.

Now that you’ve developed the application, let’s run it. With Spring Boot, you have a couple of options for running your application:

  • Running from the command line
  • Building the project and running an uber jar

Providing multiple execution options allows developers to choose what’s best for their situation. For instance, when doing lots of iterative development, running from the command line may be faster because it doesn’t require the project to be built on every change. But when a developer wants to verify production-like behavior, running the uber jar will provide a more accurate reflection of production. Not that there’s anything broken with one method as opposed to another, but it’s always preferable to verify applications prior to production deployment in an environment and manner that reflects the way it’ll be executed in production.

To run from the command line without having built your application with Maven, you can start the Spring Boot server with the following:

mvn spring-boot:run

This uses the Maven plugin from Spring Boot to execute the application as if it had been packaged into an uber jar.

The alternative approach is to construct an uber jar. You build the Maven project as usual:

mvn clean package

and run the application:

java -jar target/chapter3-spring-boot-1.0-SNAPSHOT.jar

It’s now possible to access the application at http://localhost:8080/ in a browser. This will return the list of current items in the cart. You can look at the details of hat in your cart by navigating to http://localhost:8080/get/hat. Update the quantity of an existing item with http://localhost:8080/add?item=towel&qty=1 or add a new item to your cart with http://localhost:8080/add?item=kite&qty=2.

3.2.5. Thorntail—the most flexible JeAS runtime

Thorntail was born out of the desire to take advantage of the modularization within the WildFly application server. That effort enables different groups of modules to be gathered and installed into the server for use. This also enables Thorntail to be the most flexible JeAS runtime available for Java EE. Choosing a single piece of Java EE functionality to use with your application is now super simple.

Thorntail defines each dependency that can be included by your application, such as JPA, JAX-RS, and most parts of Java EE. In addition to Java EE dependencies, Thorntail provides dependencies for libraries that can assist with developing Enterprise Java microservices such as Swagger, Keycloak, and other frameworks and libraries.

If you’re unsure of what Thorntail dependencies might be needed by your application, you have a couple of options. You can generate a skeleton project by visiting http://wildfly-swarm.io/generator and selecting the types of functionality that you need for your microservice. There are options for Java EE features and non-Java EE features such as Eclipse MicroProfile, Hibernate Search, fault tolerance, and security, to name a few.

The other option for developing your Thorntail application, if you’re unsure of what dependencies you need, is to add the Maven plugin to your pom.xml and allow the plugin to autodetect dependencies. Auto Detect inspects your application code to determine which APIs are being used, and therefore which dependencies are required. This is usually the simplest means of using Thorntail, especially when converting from an existing Java EE application; it allows the rest of the application to remain the same because you added only a new plugin into pom.xml.

Note

Though Auto Detect is easy for getting started, it does mean the plugin is a bit slower to package an application than specifying dependencies directly.

After a developer is more familiar with the available dependencies or requires dependencies that can’t be detected by the plugin, switching to using direct Maven dependencies is easy. An easy way to see what dependencies are detected by the plugin is to look at the log output from building the project. It’s then possible to use that list as a set of Maven dependencies that need to be added.

Note

Thorntail was founded in February 2015, and has now surpassed version 2.2.0.Final. The project was renamed from WildFly Swarm to Thorntail in May 2018.

Figure 3.9 illustrates how your JAX-RS, CDI, JMS microservice uses specifications within Thorntail. Here you can see that Thorntail provides exactly what your microservice needs—no more and no less. Thorntail provides the ideal JeAS runtime because it always gives you just enough for your microservice. No other JeAS runtime can match your application’s requirements so closely. The other runtimes have unused portions or require you to include additional libraries in your application.

Figure 3.9. Microservice usage in Thorntail

To create your project, you’ll use a basic Maven WAR project and add the following plugin definition.

Listing 3.12. Plugin configuration
<plugin>
  <groupId>io.thorntail</groupId>
  <artifactId>thorntail-maven-plugin</artifactId>    1
  <version>2.2.0.Final</version>                     2
  <executions>
    <execution>
      <goals>
        <goal>package</goal>                         3
      </goals>
    </execution>
  </executions>
</plugin>

  • 1 Artifact ID for the Thorntail plugin
  • 2 Version of Thorntail
  • 3 Execute plugin during package phase.

And then you add the Java EE Web APIs in the provided scope:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-web-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

Now that you have your project, let’s modify the basic code so it can be run with the Thorntail JeAS runtime.

For the CartItem bean, you need to add only the @XmlRootElement to it. The CartController needs the same JAX-RS annotations as you added for Payara Micro and Dropwizard. Finally, you need an Application class to activate JAX-RS.

Listing 3.13. JaxrsApplication with Thorntail
@ApplicationPath("/")
public class JaxrsApplication extends Application {
}

Now that you’ve developed the application, let’s run it. With Thorntail, you have a couple of options for running your application:

  • Running from the command line
  • Building the project and running an uber jar

As with Spring Boot, Thorntail provides flexibility as to how a developer might prefer to run an application based on their requirements. Without having built your application with Maven, you can start the Thorntail JeAS runtime with this:

mvn thorntail:run

This uses the Maven plugin from Thorntail to execute the application as if it had been packaged into an uber jar.

The other approach is to construct an uber jar. You build the Maven project as usual:

mvn clean package

and run the application:

java -jar target/chapter3-thorntail.jar

It’s now possible to access the application at http://localhost:8080/ in a browser. This returns the list of current items in the cart. You can look at the details of hat in your cart by navigating to http://localhost:8080/get/hat. Update the quantity of an existing item with http://localhost:8080/add?item=towel&qty=1 or add a new item to your cart with http://localhost:8080/add?item=kite&qty=2.

3.2.6. How do they compare?

You’ve taken a look at some JeAS runtimes and the way the code for each differs for a simple application that exposes a few RESTful endpoints. Let’s compare some of the features of the JeAS runtimes in table 3.2.

Table 3.2. JeAS runtime comparison

Feature

Dropwizard

Payara Micro

Spring Boot

Thorntail

Dependency injection (DI)  
Uber jar packaging  
WAR deployment  
Maven plugin run    
Project generator  
Auto Detect dependencies      
Java EE APIs    

When it comes to choosing the best JeAS runtime for your application or enterprise, many factors play a role. Some of the more critical factors are as follows:

  • Is there experience and knowledge of either Java EE or Spring?
  • What’s the preferred packaging method for production?
  • Is there prior experience of either of the non-JeAS runtimes of the frameworks?

These are just some of the factors that will influence which is the preferred JeAS framework for an application. It may be that Thorntail is the preferred choice for developers with previous Java EE experience, but developers looking for a simple stack that doesn’t require many Java EE APIs may choose Dropwizard instead.

Summary

  • JeAS enables the packaging of just enough runtime along with a microservice. Of the runtimes covered in this chapter, Thorntail is the most customizable JeAS runtime.
  • You choose pieces of an Enterprise Java application server by using a JeAS runtime, selecting only what you need.
  • JeAS runtimes are the perfect deployment method for RESTful microservices.
  • MicroProfile offers critical features for cloud native microservice development.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.163.250