How to use actuator endpoints programmatically? - java

I need some metrics in to the spring boot application. So I used MetricsEndpoint like below. But I can't access to metrics. invoke() method does not appear.
spring boot version: 2.2.6.RELEASE
#Controller
public class DashboardControllerImpl implements IDashboardController {
#Autowired
private MetricsEndpoint metricsEndpoint;
#Override
public ResponseEntity listDiskUsage() {
metricsEndpoint.invoke(); // invoke does not appear
// code block
}
}

I found some thing like that, that solved my problem!
((MetricsEndpoint.Sample)metricsEndpoint.metric("process.uptime", null).getMeasurements().get(0)).getValue()

Related

Is there any way to create a #ConditionalOnMissingBean but only after spring is fully configured?

I'm building a Spring Boot Starter for a college project on Java Reflection and Bytecode alteration.
The Reflection/Bytecode is done now, but it will scan for Spring #Controllers/#RestControllers so it can detect certain annotations to run the process.
My question here is what's the best approach? Seems to me that an annotation processor doesn't quite work nicely, and my idea is to create a #Configuration class. Now I need to ensure that all #Controller beans have been booted before I actually process them and I also need to put the result of this processing in a bean that could already exist.
So for example:
#Configuration
public class TestConfig {
#Autowired //I want to autowire but it may not exist, if the user doesn't define I need to create it
private ExternalAnnotatedRequestsModel model;
#Autowired // needed for the framework to acess spring controllers
private ConfigurableApplicationContext ctx;
#Bean // this can also be overriden since the definitions can be done via yaml
public ExternalRequestsProvider() {
return new AnnotationExternalRequestsProvider(ctx);
}
}
Now I also want that when the ExternalRequestsProvider bean is started, it runs the process method and saves the result in the object in the "model" variable.
Using #EventListener for ApplicationReadyEvent to run your process after Spring is fully configured.
#Configuration
public class ExternalRequestsConfig {
#Autowired
private ExternalAnnotatedRequestsModel model;
#Autowired
private ExternalRequestsProvider provider;
#EventListener(ApplicationReadyEvent.class)
public void onApplicationReady(ApplicationReadyEvent event) {
// do your process
}
}

spring boot actuator health endpoint + dynamic resilience4j circuit breakers

I have a spring boot app, which uses resilience4j AOP-based #CircuitBreakers.
Now I would like to make the circuit breakers' information available in the /actuator/health endpoint, but I'm not seeing the details.circuitBtreakers objects described in the docs in the JSON output.
What am I doing wrong?
By comparison, getting dynamic cache information to appear in the /actuator/metrics endpoint required a small amount of custom wiring, but this is well documented. I wonder if there is a similar trick that I can apply for dynamically defined #CircuitBreakers to be registerd with the /actuator/health endpoint.
MyService.java:
#Service
public class MyService {
#Autowired
private CacheManager cacheManager;
#Autowired
private CacheMetricsRegistrar cacheMetricsRegistrar;
#PostConstruct
public void postConstruct() {
// On-the-fly defined (annotation-based) caches are not auto-registered with micrometer metrics.
final Cache cache = cacheManager.getCache("myCache");
cacheMetricsRegistrar.bindCacheToRegistry(cache);
}
#CircuitBreaker(name = "myCB", fallbackMethod = "fallbackCallAnApi")
public String callAnApi() throws RestClientException {
// ...
}
#Cacheable("myCache")
public String getSomethingCacheable() {
// ...
}
}
application.properties:
resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
management.endpoints.web.expose=health,metrics
management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.enabled=true
management.endpoint.metrics.enabled=true
management.metrics.enable.resilience4j.circuitbreaker.calls=true
management.health.circuitbreakers.enabled=true
Dynamically registering CircuitBreakers for the HealthIndicator endpoint doesn't work at the moment.
Unfortunately you have to configure them:
resilience4j.circuitbreaker:
configs:
default:
registerHealthIndicator: true
instances:
myCB:
baseConfig: default
You could say it's a bug.
https://github.com/resilience4j/resilience4j/blob/master/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java#L99-L102

Spring Boot + Spring Security authorization success audit

Has anyone managed to get Spring Boot w/ Spring Security to handle AuthorizedEvent's (i.e. for audit log)?
I have implemented the following application event listener:
#Component
public class AuthorizationSuccessAudit implements ApplicationListener<AuthorizedEvent> {
private static Logger auditLogger = LoggerFactory.getLogger("audit");
#Override
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}
}
and have a test MVC endpoint annotated with #PreAuthorize. I was expecting that the spring security grants would show up on the log. While this works for every other event I used (AuthenticationSuccessEvent, AuthenticationFailureEvent, AbstractAuthenticationFailureEvent) it does not for the AuthorizedEvent.
I tried browsing the Spring Boot source and it seems this event is not handled in AuthorizationAuditListener.java, is this possibly a bug or am I hacking at it the wrong way?
As per spring boot documentation, Use Spring Boot Actuator (audit framework for Spring Boot), and provide your own implementations of AbstractAuthorizationAuditListener.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
And something similar to this..
class TestAuthorizationAuditListener extends AbstractAuthorizationAuditListener {
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
}
#Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
}
}
It looks like spring boot can not realize that here you want to handle event.
Try to annotate method so that spring knows that here you want to handle event
#EventListener(value = {AuthorizedEvent.class})
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}
On successful authorization AuthorizedEvent should be triggered. make sure
FilterSecurityInterceptor should set setPublishAuthorizationSuccess true

Spring MVC Swagger issue

I am using Spring MVC + Swagger version 1.0.2 integration. I am facing issue of duplicates API being seen on the REST docs API. Not sure why?
But I debug the issue, I following link https://dzone.com/articles/documenting-your-spring-api and A 'simple' way to implement Swagger in a Spring MVC application, as per this link I added following code
#Configuration
#EnableSwagger
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
#Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
#Bean
// Don't forget the #Bean annotation
public SwaggerSpringMvcPlugin customImplementation() {
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig).apiInfo(
apiInfo()).includePatterns(".*");
}
private ApiInfo apiInfo() {
return new ApiInfo("API", "API",
null, "test#yahoo.com",
"License", "http://test.license");
}
}
But when I added, since then I see same API is getting loaded twice like below
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails) throws java.lang.Exception
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails)
throws java.lang.Exception
..................
How to prevent loading beans twice ?
I was able to solve this issue. The issue was nothing to do with your applicationContext.xml or mvc-rest.xml etc, your application code should not be using #EnableSpring at all, then it worked. Even this link https://github.com/springfox/springfox/issues/565#issuecomment-172956702 also suggests the same.

How to mock remote REST API in unit test with Spring?

Assume I have made a simple client in my application that uses a remote web service that is exposing a RESTful API at some URI /foo/bar/{baz}. Now I wish to unit test my client that makes calls to this web service.
Ideally, in my tests, I’d like to mock the responses I get from the web service, given a specific request like /foo/bar/123 or /foo/bar/42. My client assumes the API is actually running somewhere, so I need a local "web service" to start running on http://localhost:9090/foo/bar for my tests.
I want my unit tests to be self-contained, similar to testing Spring controllers with the Spring MVC Test framework.
Some pseudo-code for a simple client, fetching numbers from the remote API:
// Initialization logic involving setting up mocking of remote API at
// http://localhost:9090/foo/bar
#Autowired
NumberClient numberClient // calls the API at http://localhost:9090/foo/bar
#Test
public void getNumber42() {
onRequest(mockAPI.get("/foo/bar/42")).thenRespond("{ \"number\" : 42 }");
assertEquals(42, numberClient.getNumber(42));
}
// ..
What are my alternatives using Spring?
Best method is to use WireMock.
Add the following dependencies:
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.smack</groupId>
<artifactId>smack-core</artifactId>
<version>4.0.6</version>
</dependency>
Define and use the wiremock as shown below
#Rule
public WireMockRule wireMockRule = new WireMockRule(8089);
String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json").withBody(response)));
If you use Spring RestTemplate you can use MockRestServiceServer. An example can be found here REST Client Testing With MockRestServiceServer.
If you want to unit test your client, then you'd mock out the services that are making the REST API calls, i.e. with mockito - I assume you do have a service that is making those API calls for you, right?
If on the other hand you want to "mock out" the rest APIs in that there is some sort of server giving you responses, which would be more in line of integration testing, you could try one of the many framework out there like restito, rest-driver or betamax.
You can easily use Mockito to mock a REST API in Spring Boot.
Put a stubbed controller in your test tree:
#RestController
public class OtherApiHooks {
#PostMapping("/v1/something/{myUUID}")
public ResponseEntity<Void> handlePost(#PathVariable("myUUID") UUID myUUID ) {
assert (false); // this function is meant to be mocked, not called
return new ResponseEntity<Void>(HttpStatus.NOT_IMPLEMENTED);
}
}
Your client will need to call the API on localhost when running tests. This could be configured in src/test/resources/application.properties. If the test is using RANDOM_PORT, your client under test will need to find that value. This is a bit tricky, but the issue is addressed here: Spring Boot - How to get the running port
Configure your test class to use a WebEnvironment (a running server) and now your test can use Mockito in the standard way, returning ResponseEntity objects as needed:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestsWithMockedRestDependencies {
#MockBean private OtherApiHooks otherApiHooks;
#Test public void test1() {
Mockito.doReturn(new ResponseEntity<Void>(HttpStatus.ACCEPTED))
.when(otherApiHooks).handlePost(any());
clientFunctionUnderTest(UUID.randomUUID()); // calls REST API internally
Mockito.verify(otherApiHooks).handlePost(eq(id));
}
}
You can also use this for end-to-end testing of your entire microservice in an environment with the mock created above. One way to do this is to inject TestRestTemplate into your test class, and use that to call your REST API in place of clientFunctionUnderTest from the example.
#Autowired private TestRestTemplate restTemplate;
#LocalServerPort private int localPort; // you're gonna need this too
How this works
Because OtherApiHooks is a #RestController in the test tree, Spring Boot will automatically establish the specified REST service when running the SpringBootTest.WebEnvironment.
Mockito is used here to mock the controller class -- not the service as a whole. Therefore, there will be some server-side processing managed by Spring Boot before the mock is hit. This may include such things as deserializing (and validating) the path UUID shown in the example.
From what I can tell, this approach is robust for parallel test runs with IntelliJ and Maven.
What you are looking for is the support for Client-side REST Tests in the Spring MVC Test Framework.
Assuming your NumberClient uses Spring's RestTemplate, this aforementioned support is the way to go!
Hope this helps,
Sam
Here is a basic example on how to mock a Controller class with Mockito:
The Controller class:
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private UserService userService;
public Page<UserCollectionItemDto> getUsers(Pageable pageable) {
Page<UserProfile> page = userService.getAllUsers(pageable);
List<UserCollectionItemDto> items = mapper.asUserCollectionItems(page.getContent());
return new PageImpl<UserCollectionItemDto>(items, pageable, page.getTotalElements());
}
}
Configure the beans:
#Configuration
public class UserConfig {
#Bean
public UsersController usersController() {
return new UsersController();
}
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
}
The UserCollectionItemDto is a simple POJO and it represents what the API consumer sends to the server. The UserProfile is the main object used in the service layer (by the UserService class). This behaviour also implements the DTO pattern.
Finally, mockup the expected behaviour:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
#Import(UserConfig.class)
public class UsersControllerTest {
#Autowired
private UsersController usersController;
#Autowired
private UserService userService;
#Test
public void getAllUsers() {
initGetAllUsersRules();
PageRequest pageable = new PageRequest(0, 10);
Page<UserDto> page = usersController.getUsers(pageable);
assertTrue(page.getNumberOfElements() == 1);
}
private void initGetAllUsersRules() {
Page<UserProfile> page = initPage();
when(userService.getAllUsers(any(Pageable.class))).thenReturn(page);
}
private Page<UserProfile> initPage() {
PageRequest pageRequest = new PageRequest(0, 10);
PageImpl<UserProfile> page = new PageImpl<>(getUsersList(), pageRequest, 1);
return page;
}
private List<UserProfile> getUsersList() {
UserProfile userProfile = new UserProfile();
List<UserProfile> userProfiles = new ArrayList<>();
userProfiles.add(userProfile);
return userProfiles;
}
}
The idea is to use the pure Controller bean and mockup its members. In this example, we mocked the UserService.getUsers() object to contain a user and then validated whether the Controller would return the right number of users.
With the same logic you can test the Service and other levels of your application. This example uses the Controller-Service-Repository Pattern as well :)

Categories