SpringBoot 2 Actuator Build RESTful Web Service

Keywords: Spring Java Maven curl

Opening Word

Spring Boot Actuator Is a subproject of Spring Boot.It adds several production-level services to your application, which don't require much effort from you.In this guide, you will build an application that adds these services.
 

Applications you will create

This guide will guide you in creating a "Hello, world" RESTful Web service using Spring Boot Actuator.We will build a service that accepts the following HTTP GET requests:

$ curl http://localhost:9000/hello-world

It responds to the following JSON data:

{"id":1,"content":"Hello, World!"}

Many functions have been added to the application to manage services in a production (or other) environment.The business functions of the services we build are the same as those in building RESTful Web services.Although it may be interesting to compare the results, we do not need that guide to complete it.
 

Tools you will need

How to complete this guide

Like most Starter's Guide to Spring Similarly, you can start from scratch and complete each step, or you can bypass the basic setup steps you are already familiar with.Either way, you end up with code that works.

  • To start over, move to Start with Spring Initializr
  • To skip the basics, do the following:
    • Download and unzip what the guide will use source code Or clone it with Git: git clone https://github.com/spring-guides/gs-actuator-service.git
    • Switch to the gs-actuator-service/initial directory;
    • Jump to the Guide's Create a presentation class.

When you're ready, you can check the code in the gs-actuator-service/complete directory.

 

Start with Spring Initializr

For all Spring apps, you should start with Spring Initializr Start.Initializr provides a quick way to extract the dependencies your application needs and complete many settings for you.This example requires a Spring Web and a Spring Boot Actuator dependency.The following figure shows the Initializr settings for this sample project:

The figure above shows Initializr, which chooses Maven as the build tool.You can also use Gradle.It also displays the values of com.example and actuator-service as Group and Artifact, respectively.These values will be used in the rest of this example.

The following list shows the pom.xml file that was created when Maven was selected:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>actuator-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>actuator-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

The following list shows the build.gradle file that was created when you selected Gradle:

plugins {
	id 'org.springframework.boot' version '2.2.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

 

Run empty service

Spring Initializr creates an empty app that we can use to get started.The following example (from src/main/java/com/example/actuatorservice/ActuatorServiceApplication in the initial directory) shows the classes created by Spring Initializr:

package com.example.actuatorservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ActuatorServiceApplication {

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

}

The @SpringBootApplication annotation provides a default load of values, such as embedded servlet containers, based on the contents of the class path and other content.It also enables the @EnableWebMvc annotation for Spring MVC, which activates the Web endpoint.

There are no endpoints defined in this application, but there are enough endpoints to start things and see some of the capabilities of the Actuator.The SpringApplication.run() command knows how to start a Web application.All we need to do is run the following command:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar

The above command executed by the official website is different from my local one and I need to do so to run:. /gradlew clean build && java-jar build/libs/actuator-service-0.0.1-SNAPSHOT.jar.

We haven't written any code yet, open another terminal, and try the following command (and its output display):

$ curl localhost:8080
{"timestamp":1384788106983,"error":"Not Found","status":404,"message":""}

The output of the above command indicates that the server is running, but we have not defined any business endpoints yet.We will see generic JSON responses from the Actuator/error endpoint instead of HTML error responses generated by the default container.We can see the endpoints provided out of the box in the console log of the server startup.We can try some of these endpoints, including / health endpoints.The following example shows how to do this:

$ curl localhost:8080/actuator/health
{"status":"UP"}

The status is UP, so the actuator service is running.

For more details, see Spring Boot's Actuator Project.
 

Create a presentation class

First, we need to consider how the API looks.

If we want to process a GET request from/hello-world, we can choose to use the name query parameter.In response to such a request, we would like to send back a greeting JSON similar to the following:

{
    "id": 1,
    "content": "Hello, World!"
}

The id field is the unique identifier of the greeting, and the content contains the text display of the greeting.

To model a greeting presentation, create a presentation class.The following list (from src/main/java/com/example/actuatorservice/Greeting.java) shows the Greeting class:

package com.example.actuatorservice;

public class Greeting {

  private final long id;
  private final String content;

  public Greeting(long id, String content) {
    this.id = id;
    this.content = content;
  }

  public long getId() {
    return id;
  }

  public String getContent() {
    return content;
  }

}

Now we need to create an endpoint controller for the presentation class.
 

Create Resource Controller

In Spring, the REST endpoint is the Spring MVC controller.The following Spring MVC controllers (from src/main/java/com/example/actuatorservice/HelloWorldController.java) process GET requests for/hello-world endpoints and return Greeting resources:

package com.example.actuatorservice;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloWorldController {

  private static final String template = "Hello, %s!";
  private final AtomicLong counter = new AtomicLong();

  @GetMapping("/hello-world")
  @ResponseBody
  public Greeting sayHello(@RequestParam(name="name", required=false, defaultValue="Stranger") String name) {
    return new Greeting(counter.incrementAndGet(), String.format(template, name));
  }

}

The main difference between a user-oriented controller and a REST endpoint controller is how responses are created.Endpoint controllers do not rely on views, such as JSP s, to render model data in HTML, but instead return data that will be written directly to the response body.

The @ResponseBody comment tells Spring MVC not to render the model in the view, but to write the returned object into the response body.It is achieved by using one of Spring's message converters.Since Jackson 2 is in the class path, MappingJackson 2HttpMessageConverter converts the Greeting object to JSON if the requested Acept header specifies that JSON should be returned.

How do I know the class path for Jackson 2?Run mvn dependency:tree or. /gradlew dependencies, and we'll get a detailed dependency tree containing Jackson 2.x.We can also see that it comes from /spring-boot-starter-json , it is by itself spring-boot-starter-web Imported.
 

Run application

We can run the application from a custom main class or directly from one of the configuration classes.For this simple example, you can use the SpringApplication help class.Note that this is the application class that Spring Initializr created for us and we don't even need to modify it to make it work for this simple application.The following list (from src/main/java/com/example/actuatorservice/HelloWorldApplication.java) shows the application classes:

package com.example.actuatorservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloWorldApplication {

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

}

In regular Spring MVC applications, we will add @EnableWebMvc to activate critical behavior, including configuring Dispatcher Servlet.But when Spring Boot detects spring-webmvc on our class path, it automatically turns on the annotation.This allows us to build the controller in the next steps.
The @SpringBootApplication annotation also introduces the @ComponentScan annotation, which tells Spring to scan those controllers (and any other annotated component classes) in the com.example.actuatorservice application package.

Build Executable JAR

We can combine Gradle or Maven to run the application from the command line.We can also build and run an executable JAR file that contains all the required dependencies, classes, and resources.Building an executable JAR makes it easy to publish, version, and deploy services as applications throughout the development life cycle, across environments, and so on.

If you use Gradle, you can run the application with the help of. /gradlew bootRun.Or you can build a JAR file with the help of. /gradlew build and run the JAR file as follows:

java -jar build/libs/gs-actuator-service-0.1.0.jar

The above command from the official website executes differently than my local counterpart and I need to do so to run it: java-jar build/libs/actuator-service-0.0.1-SNAPSHOT.jar.

If you use Maven, you can run it with the help of. /mvnw spring-boot:run.Or you can build a JAR file with. /mvnw clean package and run the JAR file as follows:

java -jar target/gs-actuator-service-0.1.0.jar

The above command executed by the official website is not the same as my local command, and I need it to run: java-jar target/actuator-service-0.0.1-SNAPSHOT.jar.

We can still Build a classic WAR file.

After the service runs (because we run spring-boot:run on a terminal), it can be tested by running the following commands on a separate terminal:

$ curl localhost:8080/hello-world
{"id":1,"content":"Hello, Stranger!"}

Switch to a different server port

Spring Boot Actuator runs on port 8080 by default.By adding the application.properties file, we can override this setting.The following listing (from src/main/resources/application.properties) shows the necessary changes:

server.port: 9000
management.server.port: 9001
management.server.address: 127.0.0.1

Run the server again by running the following command in the terminal:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar

The above command executed by the official website is different from my local one and I need to do so to run:. /gradlew clean build && java-jar build/libs/actuator-service-0.0.1-SNAPSHOT.jar.

The service is now started from port 9000.

We can test if it runs on port 9000 by running the following command in the terminal:

$ curl localhost:8080/hello-world
curl: (52) Empty reply from server
$ curl localhost:9000/hello-world
{"id":1,"content":"Hello, Stranger!"}
$ curl localhost:9001/actuator/health
{"status":"UP"}

Test Application

To check if our application is working properly, we should write unit tests and integration tests for our application.The test classes in src/test/java/com/example/actuatorservice/HelloWorldApplicationTests.java ensure that:

  • Our controller is responsive;
  • Our management endpoint is responsive.
    Note that the test starts the application on a random port.The following list shows the test classes:
/*
 * Copyright 2012-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	  https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.actuatorservice;

import java.util.Map;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;

import static org.assertj.core.api.BDDAssertions.then;

/**
 * Basic integration tests for service demo application.
 *
 * @author Dave Syer
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"management.port=0"})
public class HelloWorldApplicationTests {

	@LocalServerPort
	private int port;

	@Value("${local.management.port}")
	private int mgt;

	@Autowired
	private TestRestTemplate testRestTemplate;

	@Test
	public void shouldReturn200WhenSendingRequestToController() throws Exception {
		@SuppressWarnings("rawtypes")
		ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
				"http://localhost:" + this.port + "/hello-world", Map.class);

		then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
	}

	@Test
	public void shouldReturn200WhenSendingRequestToManagementEndpoint() throws Exception {
		@SuppressWarnings("rawtypes")
		ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
				"http://localhost:" + this.mgt + "/actuator/info", Map.class);

		then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
	}

}

 

Summary

Congratulations!We just developed a simple RESTful service using Spring and added some useful built-in services using Spring Boot Actuator.
 

See

The following guidelines may also be helpful:

Want to see the rest of the guide?Visit the Guide's own column: ' Spring Official Guide>

103 original articles published, 6 praised, 5,000 visits
Private letter follow

Posted by mdemetri2 on Thu, 30 Jan 2020 17:11:47 -0800