How Do I Access The Spring Boot Startup Actuator During A Test - spring-boot

I would like to record the startup information about my application during a Spring Boot test. I have the startup actuator configured and working in Spring Boot 'bootrun' mode. However, when I try to access that actuator during a test using a TestRestTemplate, I get a 404 error.
I have written an example program that demonstrates the problem. The issue isn't with acutators overall as I have the metrics and health actuators working in the same test. Just the startup actuator.
The example code is on GitHub

I have a solution for this so I thought I would post it. For complete details, see the original repo in GitHub and check the solution branch.
One possible way to enable ApplicationStartup data collection during a Spring Boot Test is to create a ContextCustomizer. This allows you to get into the testing context early enough to record all of the data that you are looking for. The ContextCustomizer should have a single static BufferingApplicationStartup that it registers as a singleton bean into the test context's bean factory. It also needs to set the bean factory's ApplicationStartup because that will be passed to the SpringApplication just before it is run.
Here is the snippet of the customizer that holds the key:
#Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Object possibleSingleton = beanFactory.getSingleton(BEAN_NAME);
// The only way it wouldn't be an instance of a BufferingApplicationStartup is if it is null or we haven't
// run yet (and it is the DefaultApplicationStartup). In either case, jam our BufferingApplicationStartup
// in here.
if(!(possibleSingleton instanceof BufferingApplicationStartup)) {
beanFactory.registerSingleton(BEAN_NAME, APPLICATION_STARTUP);
beanFactory.setApplicationStartup(APPLICATION_STARTUP);
}
}
When you do this, make sure you implement a good equals and hashCode for your customizer or else you will break the test context caching and you will refresh your test context with every test class. Since the only relevant part of the customizer is the static BufferingApplicationStartup, I chose to return its hashcode.
Finally, don't forget to add your ContextCustomizerFactory to the src/test/resources/META-INF/spring.factories or else the rest of the Spring Boot testing support won't see your customizer.
Once this is all setup, you can access the Startup Actuator endpoint just like you would any other actuator.

Related

Proper way to get Spring Boot health status from inside

I have an external requirement that I provide an endpoint to tell the load balancer to send traffic to my app. Much like the Kubernetes "readiness" probe, but it has to be a certain format and path, so I can just give them the actuator health endpoint.
In the past I've used the HealthEndpoint and called health(), but that doesn't work for reactive apps. Is there a more flexible way to see if the app is "UP"? At this level I don't care if it's reactive or servlet, I just want to know what Spring Boot says about the app.
I haven't found anything like this, most articles talk about calling /actuator/health, but that isn't what I need.
Edit:
Just a bit more detail, I have to return a certain string "NS_ENABLE" if it's good. There are certain conditions where I return "NS_DISABLE", so I can't just not return anything, which would normally make sense.
Also, I really like how Spring Boot does the checking for me. I'd rather not re-implement all those checks.
Edit 2: My final solution
The answers below got me very far along even though it wasn't my final solution, so I wanted to give a hint to my final understanding.
It turns out that the HealthEndpoint works for reactive apps just as well as servlet apps, you just have to wrap them in Mono.
How do we define health of any web servers?
We look at how our dependent services are, we check the status of Redis, MySQL, MongoDB, ElasticSearch, and other databases, this's what actuator does internally.
Actuator checks the status of different databases and based on that it returns Up/Down.
You can implement your own methods that would check the health of dependent services.
Redis is healthy or not can be checked using ping command
MySQL can be verified using SELECT 1 command or run some query that should always success like SHOW TABLES
Similarly, you can implement a health check for other services. If you find all required services are up then you can declare up otherwise down.
What about shutdown triggers? Whenever your server receives a shutdown signal than no matter what's the state of your dependent services, you should always say down, so that upstream won't send a call to this instance.
Edit
The health of the entire spring app can be checked programmatically by autowiring one or more beans from the Actuator module.
#Controller
public class MyHealthController{
#Autowired private HealthEndpoint healthEndpoint;
#GetMapping("health")
public Health health() {
Health health = healthEndpoint.health();
return healthEndpoint.health();
}
}
There're other beans related to health check, we can auto wire required beans. Some of the beans provide the health of the respective component, we can combine the health of each component using HealthAggregator to get the final Health. All registered health indicator components can be accessed via HealthIndicatorRegistry.
#Controller
public class MyHealthController{
#Autowired private HealthAggregator healthAggregator;
#Autowired private HealthIndicatorRegistry healthIndicatorRegistry;
#GetMapping("health")
public Health health() {
Map<String, Health> health = new HashMap<>();
for (Entry<String, HealthIndicator> entry : healthIndicatorRegistry.getAll().entrySet()) {
health.put(entry.getKey(), entry.getValue().health());
}
return healthAggregator.aggregate(health);
}
}
NOTE: Reactive component has its own health indicator. Useful classes are ReactiveHealthIndicatorRegistry, ReactiveHealthIndicator etc
Simple solution is to write your own health endpoint instead of depending on Spring.
Spring Boot provides you production-ready endpoints but if it doesn't satisfy your purpose, write your end-point. It will just return "UP" in response. If the service is down, it will not return anything.
Here's the spring boot documentation on writing reactive health endpoints. Folow the guide and should be enough for your usecase.
They also document on how to write liveliness and Readiness of your application.
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#reactive-health-indicators

Spring Boot: Retrieve config via rest call upon application startup

I d like to make a REST call once on application startup to retrieve some configuration parameters.
For example, we need to retrieve an entity called FleetConfiguration from another server. I d like to do a GET once and save the keep the data in memory for the rest of the runtime.
What s the best way of doing this in Spring? using Bean, Config annotations ..?
I found this for example : https://stackoverflow.com/a/44923402/494659
I might as well use POJOs handle the lifecycle of it myself but I am sure there s a way to do it in Spring without re-inventing the wheel.
Thanks in advance.
The following method will run once the application starts, call the remote server and return a FleetConfiguration object which will be available throughout your app. The FleetConfiguration object will be a singleton and won't change.
#Bean
#EventListener(ApplicationReadyEvent.class)
public FleetConfiguration getFleetConfiguration(){
RestTemplate rest = new RestTemplate();
String url = "http://remoteserver/fleetConfiguration";
return rest.getForObject(url, FleetConfiguration.class);
}
The method should be declared in a #Configuration class or #Service class.
Ideally the call should test for the response code from the remote server and act accordingly.
Better approach is to use Spring Cloud Config to externalize every application's configuration here and it can be updated at runtime for any config change so no downtime either around same.

Implement multi-tenanted application with Keycloak and springboot

When we use 'KeycloakSpringBootConfigResolver' for reading the keycloak configuration from Spring Boot properties file instead of keycloak.json.
Now there are guidelines to implement a multi-tenant application using keycloak by overriding 'KeycloakConfigResolver' as specified in http://www.keycloak.org/docs/2.3/securing_apps_guide/topics/oidc/java/multi-tenancy.html.
The steps defined here can only be used with keycloak.json.
How can we adapt this to a Spring Boot application such that keycloak properties are read from the Spring Boot properties file and multi-tenancy is achieved.
You can access the keycloak config you secified in your application.yaml (or application.properties) if you inject org.keycloak.representations.adapters.config.AdapterConfig into your component.
#Component
public class MyKeycloakConfigResolver implements KeycloakConfigResolver {
private final AdapterConfig keycloakConfig;
public MyKeycloakConfigResolver(org.keycloak.representations.adapters.config.AdapterConfig keycloakConfig) {
this.keycloakConfig = keycloakConfig;
}
#Override
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
// make a defensive copy before changing the config
AdapterConfig currentConfig = new AdapterConfig();
BeanUtils.copyProperties(keycloakConfig, currentConfig);
// changes stuff here for example compute the realm
return KeycloakDeploymentBuilder.build(currentConfig);
}
}
After several trials, the only feasible option for spring boot is to have
Multiple instances of the spring boot application running with different spring 'profiles'.
Each application instance can have its own keycloak properties (as it is under different profiles) including the realm.
The challenge is to have an upgrade path for all instances for version upgrades/bug fixes, but I guess there are multiple strategies already implemented (not part of this discussion)
there is a ticket regarding this problem: https://issues.jboss.org/browse/KEYCLOAK-4139?_sscc=t
Comments for that ticket also talk about possible workarounds intervening in servlet setup of the service used (Tomcat/Undertow/Jetty), which you could try.
Note that the documentation you linked in your first comment is super outdated!

Reload property value when external property file changes ,spring boot

I am using spring boot, and I have two external properties files, so that I can easily change its value.
But I hope spring app will reload the changed value when it is updated, just like reading from files. Since property file is easy enough to meet my need, I hope I don' nessarily need a db or file.
I use two different ways to load property value, code sample will like:
#RestController
public class Prop1Controller{
#Value("${prop1}")
private String prop1;
#RequestMapping(value="/prop1",method = RequestMethod.GET)
public String getProp() {
return prop1;
}
}
#RestController
public class Prop2Controller{
#Autowired
private Environment env;
#RequestMapping(value="/prop2/{sysId}",method = RequestMethod.GET)
public String prop2(#PathVariable String sysId) {
return env.getProperty("prop2."+sysId);
}
}
I will boot my application with
-Dspring.config.location=conf/my.properties
I'm afraid you will need to restart Spring context.
I think the only way to achieve your need is to enable spring-cloud. There is a refresh endpoint /refresh which refreshes the context and beans.
I'm not quite sure if you need a spring-cloud-config-server (its a microservice and very easy to build) where your config is stored(Git or svn). Or if its also useable just by the application.properties file in the application.
Here you can find the doc to the refresh scope and spring cloud.
You should be able to use Spring Cloud for that
Add this as a dependency
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.2.RELEASE'
And then use #RefreshScope annotation
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
Also relevant if you have Spring Actuator
For a Spring Boot Actuator application there are some additional management endpoints:
POST to
/env to update the Environment and rebind #ConfigurationProperties and log levels
/refresh for re-loading the boot strap context and refreshing the #RefreshScope beans
Spring Cloud Doc
(1) Spring Cloud's RestartEndPoint
You may use the RestartEndPoint: Programatically restart Spring Boot application / Refresh Spring Context
RestartEndPoint is an Actuator EndPoint, bundled with spring-cloud-context.
However, RestartEndPoint will not monitor for file changes, you'll have to handle that yourself.
(2) devtools
I don't know if this is for a production application or not. You may hack devtools a little to do what you want.
Take a look at this other answer I wrote for another question: Force enable spring-boot DevTools when running Jar
Devtools monitors for file changes:
Applications that use spring-boot-devtools will automatically restart
whenever files on the classpath change.
Technically, devtools is built to only work within an IDE. With the hack, it also works when launched from a jar. However, I may not do that for a real production application, you decide if it fits your needs.
I know this is a old thread, but it will help someone in future.
You can use a scheduler to periodically refresh properties.
//MyApplication.java
#EnableScheduling
//application.properties
management.endpoint.refresh.enabled = true
//ContextRefreshConfig.java
#Autowired
private RefreshEndpoint refreshEndpoint;
#Scheduled(fixedDelay = 60000, initialDelay = 10000)
public Collection<String> refreshContext() {
final Collection<String> properties = refreshEndpoint.refresh();
LOGGER.log(Level.INFO, "Refreshed Properties {0}", properties);
return properties;
}
//add spring-cloud-starter to the pom file.
Attribues annotated with #Value is refreshed if the bean is annotated with #RefreshScope.
Configurations annotated with #ConfigurationProperties is refreshed without #RefreshScope.
Hope this will help.
You can follow the ContextRefresher.refresh() code implements.
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}

How to test REST in spring app with spring security

I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...

Resources