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 !!!
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
Thanks for pointing that out and the tip. I’ve corrected that.
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
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.”
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”]}
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.
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?
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.
This article is awesome!!
I was able to make some progress but I’m not able to hit “actuator/health” yet, I’m getting the following error:
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
could you please help me?
Here is the source code:
https://drive.google.com/file/d/1F0mmRpkhtROGS7Ug6P_fATL_TDJtkOh9/view?usp=sharing
Thank you.
Hi Andre, Sorry I didn’t get some time to look into your issue. Do you still need some help?