How to add Spring Boot Actuator 2.X Support to Spring MVC / REST Application without Spring Boot

In the previous article, we have secured the REST API with Spring Security and JSON Web Token (JWT) without using Spring Boot. Now we are gonna add Spring Boot actuator 2.X support to a non Spring Boot application. So we are gonna use the same Spring REST application that we have created earlier and add actuator support to it.

Spring Boot Actuator 2.x defines its model, pluggable and extensible without relying on MVC for this. So we just need to add the spring-boot-starter-actuator dependency only.

Project Dependencies

pom.xml

We are gonna add the Spring Boot Starter Actuator dependency to the existing pom.xml file.

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>

Spring Configuration

WebConfig.java

We are gonna add @EnableAutoConfiguration and @PropertySource annotations to our existing configuration class. You can put these annotations to any of the Spring configuration classes annotated with @Configuration

@EnableAutoConfiguration is used to enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined.

I’ve excluded the DataSourceAutoConfiguration.class since we have already configured the database using the JpaConfig class and persistence.xml

@PropertySource annotation is used to add the property source (application.properties) to the Spring’s Environment

package com.javachinna.config;

import java.util.List;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@EnableWebMvc
@Configuration
@ComponentScan("com.javachinna")
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@PropertySource(value = { "classpath:application.properties" })
public class WebConfig implements WebMvcConfigurer {

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.add(new MappingJackson2HttpMessageConverter());
	}
}

Actuator Endpoints Security

Since we are using JWT authentication, actuator endpoints are also secured. So we need to pass the JWT token in the request. In case if you don’t want to secure the /actuator endpoints then you can configure the same in WebSecurityConfig.configure(HttpSecurity httpSecurity) method as shown below

http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
            requests.anyRequest().permitAll());

Or if you want to enable role-based authentication, then you need to define user roles and then you can configure the authentication as shown below

http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
                requests.anyRequest().hasRole("ENDPOINT_ADMIN"));

Application Properties

application.properties

By default, all endpoints except for shutdown are enabled. To configure the enablement of an endpoint, use its management.endpoint.<id>.enabled property. The following example enables the shutdown endpoint:

management.endpoint.shutdown.enabled=true

By default, only the info, health endpoints are exposed. To expose all the available endpoints, add the management.endpoints.web.exposure.include property as shown below

management.endpoints.web.exposure.include=*

Build and Deploy Application

Run mvn clean install command to clean and build the war file and deploy the generated war file in a server like tomcat.

Test Application

Request

GET /SpringRestJwt/actuator HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqYXZhY2hpbm5hIiwiZXhwIjoxNTgxNTAxMDUwLCJpYXQiOjE1ODE0ODMwNTB9.hqCZCMIHtuKng8PCQkNeuqQQCMiP59VQ3_9YGgjCgRfBPk_LmzIZQEBcPu1kTTobBS1w_6sSS9c4jHjE1frXaw
Cache-Control: no-cache
Postman-Token: 52bf6a85-d034-9907-dc51-a3d80ddd1065

Response

{
    "_links": {
        "self": {
            "href": "http://localhost:8080/SpringRestJwt/actuator",
            "templated": false
        },
        "beans": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/beans",
            "templated": false
        },
        "caches-cache": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/caches/{cache}",
            "templated": true
        },
        "caches": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/caches",
            "templated": false
        },
        "health": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/health",
            "templated": false
        },
        "health-path": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/health/{*path}",
            "templated": true
        },
        "info": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/info",
            "templated": false
        },
        "conditions": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/conditions",
            "templated": false
        },
        "shutdown": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/shutdown",
            "templated": false
        },
        "configprops": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/configprops",
            "templated": false
        },
        "env": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/env",
            "templated": false
        },
        "env-toMatch": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/env/{toMatch}",
            "templated": true
        },
        "heapdump": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/heapdump",
            "templated": false
        },
        "threaddump": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/threaddump",
            "templated": false
        },
        "metrics-requiredMetricName": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/metrics/{requiredMetricName}",
            "templated": true
        },
        "metrics": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/metrics",
            "templated": false
        },
        "scheduledtasks": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/scheduledtasks",
            "templated": false
        },
        "mappings": {
            "href": "http://localhost:8080/SpringRestJwt/actuator/mappings",
            "templated": false
        }
    }
}

Health Check

Request

GET /SpringRestJwt/actuator/health HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqYXZhY2hpbm5hIiwiZXhwIjoxNTgxNTAxMDUwLCJpYXQiOjE1ODE0ODMwNTB9.hqCZCMIHtuKng8PCQkNeuqQQCMiP59VQ3_9YGgjCgRfBPk_LmzIZQEBcPu1kTTobBS1w_6sSS9c4jHjE1frXaw
Cache-Control: no-cache
Postman-Token: ec3cfac7-c30b-24e7-5cca-e6aeaa607f1b

Response

{
“status”: “UP”
}

If we want to get the complete details of the health of the application, we can add the following property in application.properties.

management.endpoint.health.show-details=always

The resulting response will be as shown below. If your application has a database like MongoDB, Redis, or MySQL, for instance, the health endpoint will show the status of those too. However, it will not be shown in our case since we have added the DataSourceAutoConfiguration.class to the exclusion list of @EnableAutoConfiguration

{
    "status": "UP",
    "components": {
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 356515835904,
                "free": 328041304064,
                "threshold": 10485760
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}

Application Metrics

You can get the list of metrics available using /actuator/metrics endpoint.

Request

GET /SpringRestJwt/actuator/metrics HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqYXZhY2hpbm5hIiwiZXhwIjoxNTgxNTAxMDUwLCJpYXQiOjE1ODE0ODMwNTB9.hqCZCMIHtuKng8PCQkNeuqQQCMiP59VQ3_9YGgjCgRfBPk_LmzIZQEBcPu1kTTobBS1w_6sSS9c4jHjE1frXaw
Cache-Control: no-cache
Postman-Token: 634c1dab-866c-54dc-af07-e61df20288e2

Response

{
    "names": [
        "jvm.threads.states",
        "jvm.memory.used",
        "jvm.gc.memory.promoted",
        "jvm.memory.max",
        "jvm.gc.max.data.size",
        "jvm.memory.committed",
        "system.cpu.count",
        "logback.events",
        "jvm.buffer.memory.used",
        "jvm.threads.daemon",
        "system.cpu.usage",
        "jvm.gc.memory.allocated",
        "jvm.threads.live",
        "jvm.threads.peak",
        "process.uptime",
        "process.cpu.usage",
        "jvm.classes.loaded",
        "jvm.classes.unloaded",
        "jvm.gc.live.data.size",
        "jvm.gc.pause",
        "jvm.buffer.count",
        "jvm.buffer.total.capacity",
        "process.start.time"
    ]
}

JVM Memory Used Metrics Request

GET /SpringRestJwt/actuator/metrics/jvm.memory.used HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJqYXZhY2hpbm5hIiwiZXhwIjoxNTgxNTAxMDUwLCJpYXQiOjE1ODE0ODMwNTB9.hqCZCMIHtuKng8PCQkNeuqQQCMiP59VQ3_9YGgjCgRfBPk_LmzIZQEBcPu1kTTobBS1w_6sSS9c4jHjE1frXaw
Cache-Control: no-cache
Postman-Token: 24dbef68-7daa-f520-a3f5-2e6efff90ff5

JVM Memory Used Metrics Response

{
    "name": "jvm.memory.used",
    "description": "The amount of used memory",
    "baseUnit": "bytes",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 1216768800
        }
    ],
    "availableTags": [
        {
            "tag": "area",
            "values": [
                "heap",
                "nonheap"
            ]
        },
        {
            "tag": "id",
            "values": [
                "CodeHeap 'profiled nmethods'",
                "G1 Old Gen",
                "CodeHeap 'non-profiled nmethods'",
                "G1 Survivor Space",
                "Compressed Class Space",
                "Metaspace",
                "G1 Eden Space",
                "CodeHeap 'non-nmethods'"
            ]
        }
    ]
}

Source code

https://github.com/JavaChinna/spring-rest-jwt-sample

Conclusion

That’s all folks. In this article, we have added Spring Boot Actuator support to the Spring REST API without using Spring Boot. Thank you for reading !!!

This Post Has 10 Comments

  1. Randy

    Greetings,

    I’m not the best speller but I see the word “Continous” is spelled incorrectly on your website. In the past I’ve used a service like SpellAlerts.com or SiteChecker.com to help keep mistakes off of my websites.

    -Randy

    1. Chinna

      Thanks for pointing that out and the tip. I’ve corrected that.

  2. Rip Jal

    Hi There,

    I am trying to use spring actuator in my traditional spring mvc xml project.As @EnableAutoConfiguration will auto configure all the beans where as we have already defined beans in the xml.I dont think I can exclude all of them.Is there anyother way possible to enableautoconfiguration only for spring actuator beans or some other solution that you can suggest

    1. Chinna

      Hi There,

      As per the spring documentation below, if you have already defined the beans via XML, then auto-configuration will not try to configure the beans that are already registered. So you don’t need to exclude everything manually. If you still face issues with that, you can go for your own custom auto configuration. Please let me know if you have further questions.

      “Auto-configuration tries to be as intelligent as possible and will back away as you define more of your own configuration. Auto-configuration is always applied after user-defined beans have been registered.”

  3. Sachin

    Hi There,
    First of all this is a very nice article for those who want to enable or use the features of spring boot actuator in old spring applications which are not spring boot enabled. I also tried the approach and it works perfectly but I was exploring the readiness and liveliness probes and unfortunately they end up in as below where the probes does not seem to be up. I am not sure what is the reason behind but if I try creating spring boot application from scratch then I see the probes as UP.
    Is there anything different when it comes to readiness and liveliness probes

    {“status”:”DOWN”,”components”:{“diskSpace”:{“status”:”UP”,”details”:{“total”:498559520768,”free”:323541663744,”threshold”:10485760,”exists”:true}},”livenessState”:{“status”:”DOWN”},”ping”:{“status”:”UP”},”readinessState”:{“status”:”OUT_OF_SERVICE”}},”groups”:[“liveness”,”readiness”]}

    1. Chinna

      Hi Sachin, The liveness and rediness probes depends on the application lifecycle events published by the Spring application contexts. Your application code should also be able to contribute to this. Since you are using an older version of the spring framework, these events may not be published at all. You may want to learn more about these probes here to see if you can get them working in your application.

  4. Amir

    Hi, thank for your useful post.
    I have a question : my project is a old project and all of beans and metadata of spring are in xml format and I don’t have permission to use annotation format of metadata in this project , is there any way to use actuator info endpoint in this project?

    1. Chinna

      Hi Amir, I think you can use the spring boot actuator in your project as well. You just need to use the respective XML configuration instead of annotations that I have used in this post.

    1. Chinna

      Hi Andre, Sorry I didn’t get some time to look into your issue. Do you still need some help?

Leave a Reply