Build High Performance JVM Microservices with Ratpack & Spring Boot

From: https://www.infoq.com/articles/Ratpack-and-Spring-Boot

Ratpack and Spring Boot are a match made in microservice heaven. Each is a developer-centric web framework for the JVM, focused on productivity, efficiency, and lightweight deployments. They have their respective benefits in the area of microservice development, in that they bring different offerings to the table. Ratpack brings a reactive programming model with a high throughput, non-blocking web layer, and a convenient handler chain for defining application structure and HTTP request processing; Spring Boot brings an integration to the entire Spring ecosystem, and simplistic way to configure and autowire components into an application. For building cloud-native and data-driven microservices, they are a compliment that is unparalleled.

Ratpack ships with no opinion on an application’s underlying dependency injection framework. Instead, it allows applications to access service layer components through its DI abstraction, known as the Registry. Ratpack’s Registry is an integral aspect of its infrastructure, providing an interface for DI providers to participate in the component resolution sequence through a registry backing.

Related Vendor Content

Building Reactive Microservices in Java (By O’Reilly) - Download Now

Introducing Istio Service Mesh for Microservices (By O’Reilly)

The Value of Measuring End-User Experience from a Global Point of Presence

How to Switch From Oracle to the World’s First Engagement Database

APM in the AWS Cloud

Related Sponsor

 

Continuously build, test and monitor your microservices for optimal performance

Out of the box, Ratpack ships with registry backings for both Guice and Spring Boot, giving the flexibility of implementation back to the developer.

In this post, we will demonstrate building a RESTful data-driven Ratpack and Spring Boot microservice that leverages Spring Data behind the scenes.

The best way to get started with a Ratpack project is by creating a Gradle build script and the standard Java project structure. Gradle is the supported build system for Ratpack, but since Ratpack is simply a collection of JVM libraries, it can really be built by any build system (although your mileage may vary). The easiest way to get Gradle installed if you don’t already have it is through the Groovy enVironment Manager. Our project’s buildscript is depicted in Listing 1.

Listing 1

<span style="color:#000000"><code class="language-java">buildscript <span style="color:#999999">{</span>
  repositories <span style="color:#999999">{</span>
    <span style="color:#dd4a68">jcenter</span><span style="color:#999999">(</span><span style="color:#999999">)</span>
  <span style="color:#999999">}</span>
  dependencies <span style="color:#999999">{</span>
    classpath <span style="color:#669900">'io.ratpack:ratpack-gradle:0.9.18'</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>

apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'io.ratpack.ratpack-java'</span>
apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'idea'</span>
apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'eclipse'</span>

repositories <span style="color:#999999">{</span>
  <span style="color:#dd4a68">jcenter</span><span style="color:#999999">(</span><span style="color:#999999">)</span>
<span style="color:#999999">}</span>

dependencies <span style="color:#999999">{</span>
  compile ratpack<span style="color:#999999">.</span><span style="color:#dd4a68">dependency</span><span style="color:#999999">(</span><span style="color:#669900">'spring-boot'</span><span style="color:#999999">)</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
<span style="color:#999999">}</span>

mainClassName <span style="color:#a67f59">=</span> <span style="color:#669900">"springpack.Main"</span> <span style="color:#999999">(</span><span style="color:#990055">2</span><span style="color:#999999">)</span>

eclipse <span style="color:#999999">{</span>
  classpath <span style="color:#999999">{</span>
    containers<span style="color:#999999">.</span><span style="color:#dd4a68">remove</span><span style="color:#999999">(</span><span style="color:#669900">'org.eclipse.jdt.launching.JRE_CONTAINER'</span><span style="color:#999999">)</span>
    containers <span style="color:#669900">'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>

The buildscript imports the Ratpack Spring Boot integration through the use of the Ratpack Gradle plugin’s ratpack.dependency(..) capability at (1). With the buildscript and project structure in place, we can create a "main class", which will be the runnable class to start and run our application. Note that at (2) we are specifying the main class name so that the command line tooling will work better. This means that our main class must correspond to this, so we’ll create a springpack.Main class in the src/main/java tree of the project.

Within the main class, we build an instance of a RatpackServer through a factory method, start, to which we supply the definition of our application. Within this definition will exist our RESTful HTTP API handler chain. As an initial demonstration, consider the main class shown in Listing 2. Note that Ratpack requires Java 8.

Listing 2

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>server<span style="color:#999999">.</span>RatpackServer<span style="color:#999999">;</span>

<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> Main <span style="color:#999999">{</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#0077aa">throws</span> Exception <span style="color:#999999">{</span>
    RatpackServer<span style="color:#999999">.</span><span style="color:#dd4a68">start</span><span style="color:#999999">(</span>spec <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> spec
      <span style="color:#999999">.</span><span style="color:#dd4a68">handlers</span><span style="color:#999999">(</span>chain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">chain</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
          <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">"api"</span><span style="color:#999999">,</span> pchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">pchain</span> <span style="color:#999999">(</span><span style="color:#990055">2</span><span style="color:#999999">)</span>
            <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">ctx</span> <span style="color:#999999">(</span><span style="color:#990055">3</span><span style="color:#999999">)</span>
              <span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">method</span> <span style="color:#999999">(</span><span style="color:#990055">4</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received GET request"</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">post</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received POST request"</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received PUT request"</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">delete</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received DELETE request"</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
              <span style="color:#999999">)</span>
            <span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
      <span style="color:#999999">)</span>
    <span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>

If we dissect the application definition within the main class, we can identify a few key areas that are worth explaining for those unfamiliar with Ratpack. The first notable point is that HTTP requests in Ratpack flow through a handler chain as defined by the handlers section of the definition at (1). Handlers are defined within the chain that describe the type of request they are capable of satisfying. Specifically, at (2) we define a prefix handler type, and specify that it should bind to the "api" HTTP route. The prefix handler, in turn created a new chain that will be delegated to for incoming requests that match the "/api" endpoint. At (3) we use the all handler type to specify that all incoming requests should be run through the provided handler, and at (4) we use Ratpack’s byMethod mechanism to bind get, post, put, and delete handlers to their respective HTTP methods.

We can now run the application from the command line by simply issuing the gradle “run” command at the root of the project. This will start and bind the webserver on port 5050. To demonstrate the existing functionality of the project and to make sure the handler structure is working as expected, we can run a few test with curl from the command line:

As you can see, the application handler chain is properly routing the request, and we have the structure for our RESTful API in place. Now we need to make it do something…​

For the sake of demonstration, let’s keep it simple and make this microservice responsible for the CRUD operations related to a User domain object. Through the REST endpoint, clients should be able to:

  • request a specific user account through a GET request with the username as a path variable;
  • list all users through a GET request when no username is specified;
  • create a user by POSTing a JSON encoded user object;
  • update the email address of a user by issuing a PUT request with the username as a path variable;
  • delete a user by issuing a DELETE request with the the username as a path variable.

Most of the infrastructure for handling these requirements is already in place based on the handlers we defined in the prior section, but the requirements mean that we will need to change things slightly. For example, we will now need to bind handlers that accept the username path variable. The updated code in Listing 3 shows the main class, now with handlers to match the requirements.

Listing 3

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>server<span style="color:#999999">.</span>RatpackServer<span style="color:#999999">;</span>

<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> Main <span style="color:#999999">{</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#0077aa">throws</span> Exception <span style="color:#999999">{</span>
    RatpackServer<span style="color:#999999">.</span><span style="color:#dd4a68">start</span><span style="color:#999999">(</span>spec <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> spec
      <span style="color:#999999">.</span><span style="color:#dd4a68">handlers</span><span style="color:#999999">(</span>chain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> chain
        <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">"api/users"</span><span style="color:#999999">,</span> pchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">pchain</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
          <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">":username"</span><span style="color:#999999">,</span> uchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">uchain</span> <span style="color:#999999">(</span><span style="color:#990055">2</span><span style="color:#999999">)</span>
            <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span> <span style="color:#999999">(</span><span style="color:#990055">3</span><span style="color:#999999">)</span>
              String username <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getPathTokens</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#669900">"username"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              ctx<span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">method</span> <span style="color:#999999">(</span><span style="color:#990055">4</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request for user: "</span> <span style="color:#a67f59">+</span> username<span style="color:#999999">)</span><span style="color:#999999">)</span>
                                               <span style="color:#999999">.</span><span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                  String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received update request for user: "</span> <span style="color:#a67f59">+</span> username <span style="color:#a67f59">+</span> <span style="color:#669900">", JSON: "</span> <span style="color:#a67f59">+</span> json<span style="color:#999999">)</span><span style="color:#999999">;</span>
                <span style="color:#999999">}</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">delete</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received delete request for user: "</span> <span style="color:#a67f59">+</span> username<span style="color:#999999">)</span><span style="color:#999999">)</span>
              <span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">}</span><span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
          <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">ctx</span> <span style="color:#999999">(</span><span style="color:#990055">5</span><span style="color:#999999">)</span>
            <span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> method
              <span style="color:#999999">.</span><span style="color:#dd4a68">post</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span> <span style="color:#999999">(</span><span style="color:#990055">6</span><span style="color:#999999">)</span>
                String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request to create a new user with JSON: "</span> <span style="color:#a67f59">+</span> json<span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:#999999">}</span><span style="color:#999999">)</span>
              <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request to list all users"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">(</span><span style="color:#990055">7</span><span style="color:#999999">)</span>
            <span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
        <span style="color:#999999">)</span>
      <span style="color:#999999">)</span>
    <span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>

The API has now been restructured to follow a more resource-oriented pattern centralized around our user domain object with the following changes:

  • at (1), we change the entry-level prefix to /api/users;
  • at (2), we bind a new prefix handler, this time on the :username path variable. Any value present in the incoming request path will be translated and made accessible to the Ratpack handler via the ctx.getPathTokens() map;
  • at (3), we bind a handler for all traffic following the /api/users/:username URI pattern;
  • and at (4) we use the byMethod mechanism to attach handlers to the HTTP GET, PUT, and DELETE methods. These handlers allow us to understand the intention of the client’s operation against a given user. Within the PUT handler, we make the ctx.getRequest().getBody().getText() call to capture the JSON from the incoming request;
  • at (5), we attach a handler to match all incoming requests to the /api/users endpoint;
  • at (6), we leverage the byMethod mechanism again within the /api/users handler to attach a POST handler that called when creating new users. We again make a call to capture the JSON from the incoming request;
  • and finally, at (7), we attach the GET handler that will be called when a client desires a list of all users.

If you again start the application and make another series of curl command line calls, we can test that the endpoints are operating as intended:

Now that we have the scaffolding in place that represents the requirements for our API, we need to make it do something useful. We can start by setting up the dependencies for our service layer. In this example, we will leverage the Spring Data JPA project for our data access object; the changes to the buildscript are reflected in Listing 4.

Listing 4

<span style="color:#000000"><code class="language-java">buildscript <span style="color:#999999">{</span>
  repositories <span style="color:#999999">{</span>
    <span style="color:#dd4a68">jcenter</span><span style="color:#999999">(</span><span style="color:#999999">)</span>
  <span style="color:#999999">}</span>
  dependencies <span style="color:#999999">{</span>
    classpath <span style="color:#669900">'io.ratpack:ratpack-gradle:0.9.18'</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>

apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'io.ratpack.ratpack-java'</span>
apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'idea'</span>
apply plugin<span style="color:#a67f59">:</span> <span style="color:#669900">'eclipse'</span>

repositories <span style="color:#999999">{</span>
  <span style="color:#dd4a68">jcenter</span><span style="color:#999999">(</span><span style="color:#999999">)</span>
<span style="color:#999999">}</span>

dependencies <span style="color:#999999">{</span>
  compile ratpack<span style="color:#999999">.</span><span style="color:#dd4a68">dependency</span><span style="color:#999999">(</span><span style="color:#669900">'spring-boot'</span><span style="color:#999999">)</span>
  compile <span style="color:#669900">'org.springframework.boot:spring-boot-starter-data-jpa:1.2.4.RELEASE'</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
  compile <span style="color:#669900">'com.h2database:h2:1.4.187'</span> <span style="color:#999999">(</span><span style="color:#990055">2</span><span style="color:#999999">)</span>
<span style="color:#999999">}</span>

mainClassName <span style="color:#a67f59">=</span> <span style="color:#669900">"springpack.Main"</span>

eclipse <span style="color:#999999">{</span>
  classpath <span style="color:#999999">{</span>
    containers<span style="color:#999999">.</span><span style="color:#dd4a68">remove</span><span style="color:#999999">(</span><span style="color:#669900">'org.eclipse.jdt.launching.JRE_CONTAINER'</span><span style="color:#999999">)</span>
    containers <span style="color:#669900">'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>

 

The only changes are at (1), we now include the Spring Boot Spring Data JPA dependency, and at (2) we bring in the H2 embedded database dependency. When H2 is found on the classpath, Spring Boot will autoconfigure Spring Data to use it as an in-memory data source. Configuring and working with Spring Data datasources is well documented on the project page.

With the new dependencies in place, the first thing we must do is start by modeling our microservice’s domain object: the user. The User class can be fairly simple for the sake of demonstration, and the code in Listing 5 shows a properly modeled JPA domain entity. We place this in the src/main/java/springpack/model/User.java class file within the project.

Listing 5

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">.</span>model<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> javax<span style="color:#999999">.</span>persistence<span style="color:#999999">.</span>Column<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> javax<span style="color:#999999">.</span>persistence<span style="color:#999999">.</span>Entity<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> javax<span style="color:#999999">.</span>persistence<span style="color:#999999">.</span>GeneratedValue<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> javax<span style="color:#999999">.</span>persistence<span style="color:#999999">.</span>Id<span style="color:#999999">;</span>

@Entity
<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> User <span style="color:#999999">{</span>
  <span style="color:#0077aa">private</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">final</span> <span style="color:#0077aa">long</span> serialVersionUID <span style="color:#a67f59">=</span> 1l<span style="color:#999999">;</span>

  @Id
  @GeneratedValue
  <span style="color:#0077aa">private</span> Long id<span style="color:#999999">;</span>

  @<span style="color:#dd4a68">Column</span><span style="color:#999999">(</span>nullable <span style="color:#a67f59">=</span> <span style="color:#990055">false</span><span style="color:#999999">)</span>
  <span style="color:#0077aa">private</span> String username<span style="color:#999999">;</span>

  @<span style="color:#dd4a68">Column</span><span style="color:#999999">(</span>nullable <span style="color:#a67f59">=</span> <span style="color:#990055">false</span><span style="color:#999999">)</span>
  <span style="color:#0077aa">private</span> String email<span style="color:#999999">;</span>

  <span style="color:#0077aa">public</span> Long <span style="color:#dd4a68">getId</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">return</span> id<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">setId</span><span style="color:#999999">(</span>Long id<span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">this</span><span style="color:#999999">.</span>id <span style="color:#a67f59">=</span> id<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">public</span> String <span style="color:#dd4a68">getUsername</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">return</span> username<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">setUsername</span><span style="color:#999999">(</span>String username<span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">this</span><span style="color:#999999">.</span>username <span style="color:#a67f59">=</span> username<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">public</span> String <span style="color:#dd4a68">getEmail</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">return</span> email<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">setEmail</span><span style="color:#999999">(</span>String email<span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">this</span><span style="color:#999999">.</span>email <span style="color:#a67f59">=</span> email<span style="color:#999999">;</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>

We can make use of the javax.persistence.* annotations since Spring Data is now on the project’s compile-time classpath. Spring Boot makes it a seamless process to get up-and-running with data access objects, so we can model our DAO around the Repository service type, as afforded to us by Spring Data. Since our API follows relatively straight-forward CRUD operations, we can utilize the CrudRepository fixture provided by Spring Data to minimize the code necessary for the UserRepository DAO implementation.

Listing 6

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">.</span>model<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> org<span style="color:#999999">.</span>springframework<span style="color:#999999">.</span>data<span style="color:#999999">.</span>repository<span style="color:#999999">.</span>CrudRepository<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> org<span style="color:#999999">.</span>springframework<span style="color:#999999">.</span>stereotype<span style="color:#999999">.</span>Repository<span style="color:#999999">;</span>

@Repository
<span style="color:#0077aa">public</span> <span style="color:#0077aa">interface</span> UserRepository <span style="color:#0077aa">extends</span> CrudRepository<span style="color:#a67f59"><</span>User<span style="color:#999999">,</span> Long<span style="color:#a67f59">></span> <span style="color:#999999">{</span>

  User <span style="color:#dd4a68">findByUsername</span><span style="color:#999999">(</span>String username<span style="color:#999999">)</span><span style="color:#999999">;</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
<span style="color:#999999">}</span></code></span>

Amazingly, the UserRepository DAO implementation shown in Listing 6 is all that is necessary for us to have a fully formed service layer for our User domain object. The Repository interface offered by Spring Data allows us to create "helper" lookup methods based on the convention of the entity we are searching against. Based on the requirements, we know that our API layer will need to lookup users by their username, so we can add the findByUsername method at <1>. We place the UserRepository into the src/main/java/springpack/model/UserRepository.java class file within the project.

Before we can dig in to modifying the API to make use of the UserRepository, we first must define our Spring Boot application class. This class acts as a configuration entry point into the Spring Boot autoconfiguration engine and constructs a Spring ApplicationContext that we can use as a registry backing within our Ratpack application. Listing 7 depicts the Spring Boot configuration class.

Listing 7

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> com<span style="color:#999999">.</span>fasterxml<span style="color:#999999">.</span>jackson<span style="color:#999999">.</span>databind<span style="color:#999999">.</span>ObjectMapper<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> org<span style="color:#999999">.</span>springframework<span style="color:#999999">.</span>boot<span style="color:#999999">.</span>autoconfigure<span style="color:#999999">.</span>SpringBootApplication<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> org<span style="color:#999999">.</span>springframework<span style="color:#999999">.</span>context<span style="color:#999999">.</span>annotation<span style="color:#999999">.</span>Bean<span style="color:#999999">;</span>

@SpringBootApplication
<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> SpringBootConfig <span style="color:#999999">{</span>

  @Bean
  ObjectMapper <span style="color:#dd4a68">objectMapper</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
    <span style="color:#0077aa">return</span> <span style="color:#0077aa">new</span> ObjectMapper<span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>

The stunningly small amount of code required for our SpringBootConfig class goes into the src/main/java/springpack/SpringBootConfig.java class file. In this, we are explicitly wiring in a bean definition for the Jackson ObjectMapper. We’ll use this within our API layer to read and write JSON.

The @SpringBootApplication annotation does the majority of the heavy lifting here. When we initialize the Spring Boot registry backing, we provide this class as the entry point. Its infrastructure will then use that annotation to scan the classpath for any available components, autowire them into the application context, and autoconfigure them according to the conventional rules of Spring Boot. For example, the mere presence of the UserRepository class (annotated with @Repository) on our application’s classpath will cause Spring Boot to proxy that interface through the Spring Data engine, which will also be configured to work with the H2 embedded database, which is also on the classpath. At this point, nothing more is needed from the Spring Boot side of things.

The next thing that we must do before we can implement our API layer is instruct Ratpack to use our Spring Boot application as a registry. Ratpack’s Spring Boot integration provides a fixture to seamlessly translate a Spring Boot application to a registry backing, making it a single line of code to merge the two worlds. The code in Listing 8 shows an updated main class, this time with the SpringBootConfig class stood up as a registry for our API layer.

Listing 8

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>server<span style="color:#999999">.</span>RatpackServer<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>spring<span style="color:#999999">.</span>Spring<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> springpack<span style="color:#999999">.</span>config<span style="color:#999999">.</span>SpringBootConfig<span style="color:#999999">;</span>

<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> Main <span style="color:#999999">{</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#0077aa">throws</span> Exception <span style="color:#999999">{</span>
    RatpackServer<span style="color:#999999">.</span><span style="color:#dd4a68">start</span><span style="color:#999999">(</span>spec <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> spec
      <span style="color:#999999">.</span><span style="color:#dd4a68">registry</span><span style="color:#999999">(</span>Spring<span style="color:#999999">.</span><span style="color:#dd4a68">spring</span><span style="color:#999999">(</span>SpringBootConfig<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">(</span><span style="color:#990055">1</span><span style="color:#999999">)</span>
      <span style="color:#999999">.</span><span style="color:#dd4a68">handlers</span><span style="color:#999999">(</span>chain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> chain
        <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">"api/users"</span><span style="color:#999999">,</span> pchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> pchain
          <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">":username"</span><span style="color:#999999">,</span> uchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> uchain
            <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
              String username <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getPathTokens</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#669900">"username"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              ctx<span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> method
                <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request for user: "</span> <span style="color:#a67f59">+</span> username<span style="color:#999999">)</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                  String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received update request for user: "</span> <span style="color:#a67f59">+</span> username <span style="color:#a67f59">+</span> <span style="color:#669900">", JSON: "</span> <span style="color:#a67f59">+</span> json<span style="color:#999999">)</span><span style="color:#999999">;</span>
                <span style="color:#999999">}</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">delete</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received delete request for user: "</span> <span style="color:#a67f59">+</span> username<span style="color:#999999">)</span><span style="color:#999999">)</span>
              <span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">}</span><span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
          <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx
            <span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> method
              <span style="color:#999999">.</span><span style="color:#dd4a68">post</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request to create a new user with JSON: "</span> <span style="color:#a67f59">+</span> json<span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:#999999">}</span><span style="color:#999999">)</span>
              <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">render</span><span style="color:#999999">(</span><span style="color:#669900">"Received request to list all users"</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
            <span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
        <span style="color:#999999">)</span>
      <span style="color:#999999">)</span>
    <span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span></code></span>

The only change necessary is at (1), where we provide the Ratpack application definition with an explicit Registry implementation. Now we can get started implementing the API layer.

As you follow through on the upcoming changes, it is again important to understand that Ratpack differs a great deal from traditional servlet-based web applications. As noted earlier, Ratpack’s HTTP layer is built on a non-blocking network interface, which supports its nature as a highly performant web framework. A servlet-based web application will spawn a new thread for each incoming request, which is resource inefficient, but allows each request processing flow to act in isolation. In this paradigm, a web application is able to do things like make calls to a database and wait for the corresponding results without worrying about (relatively) impacting its ability to service subsequent clients. In a non-blocking web application, the networking layer does not block while the client or server is not sending data, so a high number of concurrent requests are able to be made across a small pool of "request taking" threads. It means, however, that if the application code were to block on one of these request taking threads, throughput would suffer dramatically. To that extent, it is important that blocking operations, like calls to a database, do not take place within the request thread.

Luckily, Ratpack makes it easy to work with blocking operations within your application by exposing a blocking interface on the request’s context. This will schedule blocking operations to a different thread pool, and allow those calls to complete synchronously, while still servicing new incoming requests at a high volume. Once the blocking call is complete, the processing flow will return to the request taking thread, and a response can be written back to the client. As we build out the API layer, we need to ensure that any calls that make use of the UserRepository are routed through the blocking fixture, as shown in API layer’s implementation in Listing 9.

Listing 9

<span style="color:#000000"><code class="language-java"><span style="color:#0077aa">package</span> springpack<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> com<span style="color:#999999">.</span>fasterxml<span style="color:#999999">.</span>jackson<span style="color:#999999">.</span>core<span style="color:#999999">.</span>JsonProcessingException<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> com<span style="color:#999999">.</span>fasterxml<span style="color:#999999">.</span>jackson<span style="color:#999999">.</span>core<span style="color:#999999">.</span>type<span style="color:#999999">.</span>TypeReference<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> com<span style="color:#999999">.</span>fasterxml<span style="color:#999999">.</span>jackson<span style="color:#999999">.</span>databind<span style="color:#999999">.</span>ObjectMapper<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>exec<span style="color:#999999">.</span>Promise<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>handling<span style="color:#999999">.</span>Context<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>server<span style="color:#999999">.</span>RatpackServer<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> ratpack<span style="color:#999999">.</span>spring<span style="color:#999999">.</span>Spring<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> springpack<span style="color:#999999">.</span>model<span style="color:#999999">.</span>User<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> springpack<span style="color:#999999">.</span>model<span style="color:#999999">.</span>UserRepository<span style="color:#999999">;</span>

<span style="color:#0077aa">import</span> java<span style="color:#999999">.</span>util<span style="color:#999999">.</span>HashMap<span style="color:#999999">;</span>
<span style="color:#0077aa">import</span> java<span style="color:#999999">.</span>util<span style="color:#999999">.</span>Map<span style="color:#999999">;</span>

<span style="color:#0077aa">public</span> <span style="color:#0077aa">class</span> Main <span style="color:#999999">{</span>
  <span style="color:#0077aa">private</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">final</span> Map<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">></span> NOT_FOUND <span style="color:#a67f59">=</span> <span style="color:#0077aa">new</span> HashMap<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">></span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span><span style="color:#999999">{</span>
    <span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#669900">"status"</span><span style="color:#999999">,</span> <span style="color:#669900">"404"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#669900">"message"</span><span style="color:#999999">,</span> <span style="color:#669900">"NOT FOUND"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span><span style="color:#999999">}</span><span style="color:#999999">;</span>
  <span style="color:#0077aa">private</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">final</span> Map<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">></span> NO_EMAIL <span style="color:#a67f59">=</span> <span style="color:#0077aa">new</span> HashMap<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">></span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span><span style="color:#999999">{</span>
    <span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#669900">"status"</span><span style="color:#999999">,</span> <span style="color:#669900">"400"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#669900">"message"</span><span style="color:#999999">,</span> <span style="color:#669900">"NO EMAIL ADDRESS SUPPLIED"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span><span style="color:#999999">}</span><span style="color:#999999">;</span>

  <span style="color:#0077aa">public</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">main</span><span style="color:#999999">(</span>String<span style="color:#999999">[</span><span style="color:#999999">]</span> args<span style="color:#999999">)</span> <span style="color:#0077aa">throws</span> Exception <span style="color:#999999">{</span>
    RatpackServer<span style="color:#999999">.</span><span style="color:#dd4a68">start</span><span style="color:#999999">(</span>spec <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> spec
      <span style="color:#999999">.</span><span style="color:#dd4a68">registry</span><span style="color:#999999">(</span>Spring<span style="color:#999999">.</span><span style="color:#dd4a68">spring</span><span style="color:#999999">(</span>SpringBootConfig<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">)</span>
      <span style="color:#999999">.</span><span style="color:#dd4a68">handlers</span><span style="color:#999999">(</span>chain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> chain
        <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">"api/users"</span><span style="color:#999999">,</span> pchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> pchain
          <span style="color:#999999">.</span><span style="color:#dd4a68">prefix</span><span style="color:#999999">(</span><span style="color:#669900">":username"</span><span style="color:#999999">,</span> uchain <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> uchain
            <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
              <span style="color:slategray">// extract the "username" path variable</span>
              String username <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getPathTokens</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#669900">"username"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:slategray">// pull the UserRepository out of the registry</span>
              UserRepository userRepository <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>UserRepository<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:slategray">// pull the Jackson ObjectMapper out of the registry</span>
              ObjectMapper mapper <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>ObjectMapper<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:slategray">// construct a "promise" for the requested user object. This will</span>
              <span style="color:slategray">// be subscribed to within the respective handlers, according to what</span>
              <span style="color:slategray">// they must do. The promise uses the "blocking" fixture to ensure</span>
              <span style="color:slategray">// the DB call doesn't take place on a "request taking" thread.</span>
              Promise<span style="color:#a67f59"><</span>User<span style="color:#a67f59">></span> userPromise <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">blocking</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> userRepository<span style="color:#999999">.</span><span style="color:#dd4a68">findByUsername</span><span style="color:#999999">(</span>username<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              ctx<span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> method
                <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span>
                  <span style="color:slategray">// the .then() block will "subscribe" to the result, allowing</span>
                  <span style="color:slategray">// us to send the user domain object back to the client</span>
                  userPromise<span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span>user <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">sendUser</span><span style="color:#999999">(</span>ctx<span style="color:#999999">,</span> user<span style="color:#999999">)</span><span style="color:#999999">)</span>
                <span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">put</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                  <span style="color:slategray">// Read the JSON from the request</span>
                  String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  <span style="color:slategray">// Parse out the JSON body into a Map</span>
                  Map<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">></span> body <span style="color:#a67f59">=</span> mapper<span style="color:#999999">.</span><span style="color:#dd4a68">readValue</span><span style="color:#999999">(</span>json<span style="color:#999999">,</span> <span style="color:#0077aa">new</span> TypeReference<span style="color:#a67f59"><</span>Map<span style="color:#a67f59"><</span>String<span style="color:#999999">,</span> String<span style="color:#a67f59">>></span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
                  <span style="color:#999999">}</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  <span style="color:slategray">// Check to make sure the request body contained an "email" address</span>
                  <span style="color:#0077aa">if</span> <span style="color:#999999">(</span>body<span style="color:#999999">.</span><span style="color:#dd4a68">containsKey</span><span style="color:#999999">(</span><span style="color:#669900">"email"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
                    userPromise
                      <span style="color:slategray">// map the new email address on to the user entity</span>
                      <span style="color:#999999">.</span><span style="color:#dd4a68">map</span><span style="color:#999999">(</span>user <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                        user<span style="color:#999999">.</span><span style="color:#dd4a68">setEmail</span><span style="color:#999999">(</span>body<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#669900">"email"</span><span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                        <span style="color:#0077aa">return</span> user<span style="color:#999999">;</span>
                      <span style="color:#999999">}</span><span style="color:#999999">)</span>
                      <span style="color:slategray">// and use the blocking thread pool to save the updated details</span>
                      <span style="color:#999999">.</span><span style="color:#dd4a68">blockingMap</span><span style="color:#999999">(</span>userRepository<span style="color:#a67f59">:</span><span style="color:#a67f59">:</span>save<span style="color:#999999">)</span>
                      <span style="color:slategray">// finally, send the updated user entity back to the client</span>
                      <span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span>u1 <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">sendUser</span><span style="color:#999999">(</span>ctx<span style="color:#999999">,</span> u1<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  <span style="color:#999999">}</span> <span style="color:#0077aa">else</span> <span style="color:#999999">{</span>
                    <span style="color:slategray">// bad request; we didn't get an email address</span>
                    ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">status</span><span style="color:#999999">(</span><span style="color:#990055">400</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                    ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span>mapper<span style="color:#999999">.</span><span style="color:#dd4a68">writeValueAsBytes</span><span style="color:#999999">(</span>NO_EMAIL<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  <span style="color:#999999">}</span>
                <span style="color:#999999">}</span><span style="color:#999999">)</span>
                <span style="color:#999999">.</span><span style="color:#dd4a68">delete</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span>
                  userPromise
                    <span style="color:slategray">// make the DB delete call in a blocking thread</span>
                    <span style="color:#999999">.</span><span style="color:#dd4a68">blockingMap</span><span style="color:#999999">(</span>user <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                      userRepository<span style="color:#999999">.</span><span style="color:#dd4a68">delete</span><span style="color:#999999">(</span>user<span style="color:#999999">)</span><span style="color:#999999">;</span>
                      <span style="color:#0077aa">return</span> null<span style="color:#999999">;</span>
                    <span style="color:#999999">}</span><span style="color:#999999">)</span>
                    <span style="color:slategray">// then send a 204 back to the client</span>
                    <span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span>user <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                      ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">status</span><span style="color:#999999">(</span><span style="color:#990055">204</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                      ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                    <span style="color:#999999">}</span><span style="color:#999999">)</span>
                <span style="color:#999999">)</span>
              <span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:#999999">}</span><span style="color:#999999">)</span>
          <span style="color:#999999">)</span>
          <span style="color:#999999">.</span><span style="color:#dd4a68">all</span><span style="color:#999999">(</span>ctx <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
            <span style="color:slategray">// pull the UserRepository out of the registry</span>
            UserRepository userRepository <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>UserRepository<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            <span style="color:slategray">// pull the Jackson ObjectMapper out of the registry</span>
            ObjectMapper mapper <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>ObjectMapper<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
            ctx<span style="color:#999999">.</span><span style="color:#dd4a68">byMethod</span><span style="color:#999999">(</span>method <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> method
              <span style="color:#999999">.</span><span style="color:#dd4a68">post</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                <span style="color:slategray">// read the JSON request body...</span>
                String json <span style="color:#a67f59">=</span> ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getBody</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">getText</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                <span style="color:slategray">// ... and convert it into a user entity</span>
                User user <span style="color:#a67f59">=</span> mapper<span style="color:#999999">.</span><span style="color:#dd4a68">readValue</span><span style="color:#999999">(</span>json<span style="color:#999999">,</span> User<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                <span style="color:slategray">// save the user entity on a blocking thread and</span>
                <span style="color:slategray">// render the user entity back to the client</span>
                ctx<span style="color:#999999">.</span><span style="color:#dd4a68">blocking</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> userRepository<span style="color:#999999">.</span><span style="color:#dd4a68">save</span><span style="color:#999999">(</span>user<span style="color:#999999">)</span><span style="color:#999999">)</span>
                  <span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span>u1 <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#dd4a68">sendUser</span><span style="color:#999999">(</span>ctx<span style="color:#999999">,</span> u1<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
              <span style="color:#999999">}</span><span style="color:#999999">)</span>
              <span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#a67f59">-</span><span style="color:#a67f59">></span>
                <span style="color:slategray">// make the DB call, on a blocking thread, to list all users</span>
                ctx<span style="color:#999999">.</span><span style="color:#dd4a68">blocking</span><span style="color:#999999">(</span>userRepository<span style="color:#a67f59">:</span><span style="color:#a67f59">:</span>findAll<span style="color:#999999">)</span>
                  <span style="color:slategray">// and render the user list back to the client</span>
                  <span style="color:#999999">.</span><span style="color:#dd4a68">then</span><span style="color:#999999">(</span>users <span style="color:#a67f59">-</span><span style="color:#a67f59">></span> <span style="color:#999999">{</span>
                    ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">contentType</span><span style="color:#999999">(</span><span style="color:#669900">"application/json"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                    ctx<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span>mapper<span style="color:#999999">.</span><span style="color:#dd4a68">writeValueAsBytes</span><span style="color:#999999">(</span>users<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
                  <span style="color:#999999">}</span><span style="color:#999999">)</span>
              <span style="color:#999999">)</span>
            <span style="color:#999999">)</span><span style="color:#999999">;</span>
          <span style="color:#999999">}</span><span style="color:#999999">)</span>
        <span style="color:#999999">)</span>
      <span style="color:#999999">)</span>
    <span style="color:#999999">)</span><span style="color:#999999">;</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">private</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">notFound</span><span style="color:#999999">(</span>Context context<span style="color:#999999">)</span> <span style="color:#999999">{</span>
    ObjectMapper mapper <span style="color:#a67f59">=</span> context<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>ObjectMapper<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">status</span><span style="color:#999999">(</span><span style="color:#990055">404</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#0077aa">try</span> <span style="color:#999999">{</span>
      context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span>mapper<span style="color:#999999">.</span><span style="color:#dd4a68">writeValueAsBytes</span><span style="color:#999999">(</span>NOT_FOUND<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span> <span style="color:#0077aa">catch</span> <span style="color:#999999">(</span>JsonProcessingException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
      context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span>
  <span style="color:#999999">}</span>

  <span style="color:#0077aa">private</span> <span style="color:#0077aa">static</span> <span style="color:#0077aa">void</span> <span style="color:#dd4a68">sendUser</span><span style="color:#999999">(</span>Context context<span style="color:#999999">,</span> User user<span style="color:#999999">)</span> <span style="color:#999999">{</span>
    <span style="color:#0077aa">if</span> <span style="color:#999999">(</span>user <span style="color:#a67f59">==</span> null<span style="color:#999999">)</span> <span style="color:#999999">{</span>
      <span style="color:#dd4a68">notFound</span><span style="color:#999999">(</span>context<span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span>

    ObjectMapper mapper <span style="color:#a67f59">=</span> context<span style="color:#999999">.</span><span style="color:#dd4a68">get</span><span style="color:#999999">(</span>ObjectMapper<span style="color:#999999">.</span><span style="color:#0077aa">class</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">contentType</span><span style="color:#999999">(</span><span style="color:#669900">"application/json"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#0077aa">try</span> <span style="color:#999999">{</span>
      context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span>mapper<span style="color:#999999">.</span><span style="color:#dd4a68">writeValueAsBytes</span><span style="color:#999999">(</span>user<span style="color:#999999">)</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span> <span style="color:#0077aa">catch</span> <span style="color:#999999">(</span>JsonProcessingException e<span style="color:#999999">)</span> <span style="color:#999999">{</span>
      context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">status</span><span style="color:#999999">(</span><span style="color:#990055">500</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
      context<span style="color:#999999">.</span><span style="color:#dd4a68">getResponse</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span><span style="color:#669900">"Error serializing user to JSON"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    <span style="color:#999999">}</span>
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>

The most noteworthy element in the API layer’s implementation is the use of the blocking mechanism, which can be extracted from the Context object that comes through with each request. When calling ctx.blocking(), a Promise object is returned, which must be subscribed to in order to have the code execute. This allows us to stage a promise, as shown in the prefix(":username") chain, for reuse within different handlers, keeping the code clean.

Now that the API is implemented, we can again run a series of curl tests to ensure that the microservice is working as intended:

Following this sequence of commands, we can indeed see that our API layer is functioning properly, and we have a fully formed, data-driven Ratpack and Spring Boot microservice that is using Spring Data JPA!

The final step of the whole process is to prepare it for deployment. The easiest way to accomplish this is to execute the gradle installDist command. This will package your application and all of its runtime dependencies in tarball (.tar file) and zip (.zip file) archives. It will additionally create cross-platform start scripts for you to be able to start your microservice on any system that has Java 8 installed. After the installDist task completes, you can find these archives in the build/distributions directory of your project.

Through this post you have learned how to create a microservice application that builds the performance and ease of use of Ratpack, while leveraging the vast ecosystem offered by Spring Boot. You can use this example as a lift-off point on your journey to building cloud native and data driven microservices on the JVM.

Happy Ratpacking! Happy Spring Booting!

About the Author

Daniel Woods is a Technology Enthusiast specialising in enterprise Java, Groovy, and Grails development. He has over a decade of experience in JVM software development, and shares his experience by contributing to open source projects like the Grails and Ratpack web frameworks. Dan has been a speaker at the Gr8conf and SpringOne 2GX conferences, where he presents his expertise in enterprise application architecture on the JVM.

猜你喜欢

转载自blog.csdn.net/welchlam/article/details/81271179