Spring Boot Actuator – Creating Custom Endpoints

Introduction

Here we will see example on creating custom endpoints in Spring Boot Actuator. There are a number of endpoints provided by Spring Boot however we may want to create a custom endpoint using Spring Boot Actuator.

We have seen few examples on Spring Boot actuators:

Prerequisites

First go through the example Spring Boot Actuator – Production Ready Features

Custom Endpoints

If you add a @Bean annotated with @Endpoint, any methods annotated with @ReadOperation, @WriteOperation, or @DeleteOperation are automatically exposed over JMX and, in a web application, over HTTP as well. Endpoints can be exposed over HTTP using Jersey, Spring MVC, or Spring WebFlux.

You can also write technology-specific endpoints by using @JmxEndpoint (exposed only over JMX and not over HTTP) or @WebEndpoint (exposed only over HTTP and not over JMX).

You can write technology-specific extensions by using @EndpointWebExtension and @EndpointJmxExtension. These annotations let you provide technology-specific operations to augment an existing endpoint.

Finally, if you need access to web-framework-specific functionality, you can implement Servlet or Spring @Controller and @RestController endpoints at the cost of them not being available over JMX or when using a different web framework.

Here we will see examples on @Endpoint, @ControllerEndpoint and @RestControllerEndpoint.

VO class

Create below VO (Value Object) class, which is a POJO class holds data or value and does nothing special.

package com.roytuts.springboot.actuator.vo;

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;
	}

}

@Endpoint

Create below class, which is annotated with @Endpoint and it means that we are creating custom endpoint using Spring Boot Actuator.

The class must have @Component annotation in order to let container pick up the class.

You must provide a unique id to the endpoint in order to identify it.

We have shown here @ReadOperation, which is equivalent to HTTP GET request. You can also create other operations, such as, @WriteOperation (POST), @DeleteOperation (DELETE).

We have taken an input as a query parameter called name in sayHello() method. If you do not provide any name to the parameter then it will automatically pick the name as variable name. Here it is “String name”, so the parameter name is “name”.

package com.roytuts.springboot.actuator.endpoint;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;

import com.roytuts.springboot.actuator.vo.Greeting;

@Component
@Endpoint(id = "hello-endpoint")
public class GreetingEndpoint {

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

	@ReadOperation
	public Greeting sayHello(@RequestParam(defaultValue = "Stranger") String name) {
		return new Greeting(counter.incrementAndGet(), String.format(template, name));
	}
}

@ControllerEndpoint and @RestControllerEndpoint

@ControllerEndpoint and @RestControllerEndpoint can be used to implement an endpoint that is only exposed by Spring MVC or Spring WebFlux. Methods are mapped using the standard annotations for Spring MVC and Spring WebFlux such as @RequestMapping and @GetMapping, with the endpoint’s ID being used as a prefix for the path. Controller endpoints provide deeper integration with Spring’s web frameworks but at the expense of portability. The @Endpoint and @WebEndpoint annotations should be preferred whenever possible.

@ControllerEndpoint

You must provide a unique id to the endpoint in order to identify it.

We have used @ResponseBody because we want response to be displayed directly on the body of the response rather than using a view file, such as, jsp.

package com.roytuts.springboot.actuator.endpoint;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.roytuts.springboot.actuator.vo.Greeting;

@Component
@ControllerEndpoint(id = "controller-endpoint")
public class GreetingController {

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

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

}

@RestControllerEndpoint

You must provide a unique id to the endpoint in order to identify it.

package com.roytuts.springboot.actuator.endpoint;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.roytuts.springboot.actuator.vo.Greeting;

@Component
@RestControllerEndpoint(id = "rest-controller-endpoint")
public class GreetingRestController {

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

	@GetMapping("/hello")
	public ResponseEntity<Greeting> greet(@RequestParam(defaultValue = "Stranger") String name) {
		return new ResponseEntity<Greeting>(new Greeting(counter.incrementAndGet(), String.format(template, name)),
				HttpStatus.OK);
	}

}

Exposing Endpoints

Now either you can expose only the endpoints you have created above or you can expose all endpoints including the above endpoints.

If you want to expose all endpoints then add below line to application.properties file:

management.endpoints.web.exposure.include=*

If you want to expose only the above endpoints, which you have created then add below line to the application.properties file:

management.endpoints.web.exposure.include=hello-endpoint,controller-endpoint,rest-controller-endpoint

Main Class

The main class helps you to start and deploy the Spring Boot application into embedded Tomcat server.

package com.roytuts.springboot.actuator.main;

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

@SpringBootApplication(scanBasePackages = "com.roytuts.springboot.actuator")
public class ActuatorApplication {

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

}

Testing the Application

Run the above main class to deploy the application.

Now hit the URLs either from REST client or in the browser directly.

You will get same output for the below URLs:

http://localhost:8080/actuator/hello-endpoint?name=Soumitra
http://localhost:8080/actuator/controller-endpoint/hello?name=Soumitra
http://localhost:8080/actuator/rest-controller-endpoint/hello?name=Soumitra
spring boot actuator - creating custom endpoints

You will get same output for the below URLs:

http://localhost:8080/actuator/controller-endpoint/hello
http://localhost:8080/actuator/rest-controller-endpoint/hello
spring boot actuator - creating custom endpoints

Source Code

download source code

Thanks for reading.

1 thought on “Spring Boot Actuator – Creating Custom Endpoints

Leave a Reply

Your email address will not be published. Required fields are marked *