Whether it is possible to write AOP for Spring RestTemplate class and any external jar classes using spring AOP or Aspectj - spring

Is it possible to write AOP for Spring RestTemplate class using spring AOP or Aspectj. EX:
#Around("execution(* org.springframework.web.client.RestTemplate.getFor*(..))")  
Thanks

I had the same issue and couldn't make it work with AOP.
However, in this case, there is a workaround. Since RestTemplate extends InterceptingHttpAccessor, you can intercept all requests coming through theRestTemplate object.
Sample configuration that logs all HTTP requests :
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// (...)
// setup code for the RestTemplate object
restTemplate.getInterceptors().add((request, body, execution) -> {
logger.info("HTTP {} request to {}", request.getMethod(), request.getURI());
return execution.execute(request, body);
});
return restTemplate;
}
While this is not equivalent to using an aspect, you can get similar functionality with interceptors and pretty minimal configuration.

Related

how to do client side load balancing in spring integration

we can add client-side load balancing in spring boot applications by,
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
This will take care of the microservice resolution as well. ie. identifying service by the URL like "http://service_name/api/v1/endpoint/".
Is there any similar mechanism for name resolution in Spring integration?
See this ctor for Spring Integration HTTP Outbound Channel Adapter:
/**
* Create a handler that will send requests to the provided URI using a provided RestTemplate
* #param uri The URI.
* #param restTemplate The rest template.
*/
public HttpRequestExecutingMessageHandler(String uri, RestTemplate restTemplate) {
So, when you configure a #ServiceActivator (handle() in Java DSL), you just inject your load-balanced RestTemplate and everything should work as expected.

Spring Boot Reactive WebClient "serverWebExchange must be null" when Spring Security OAuth is in use

I would like to use a WebClient from a Spring Boot WebFlux app that is set up with Spring Security OAuth 2 Client Credentials.
But, I get: java.lang.IllegalArgumentException: serverWebExchange must be null
The code is here: https://github.com/mparaz/spring-apigee-client
When I disable Spring Security by removing it from the pom.xml, it works properly.
When I keep using Spring Security, but instead of returning the webClient() chain result to the controller, and just prints it out, it also works.
It looks like the Reactive client and server don't work together when Spring Security is used. How could I get them running together?
For me the problem was following
https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#oauth2Client-authorized-manager-provider
The DefaultOAuth2AuthorizedClientManager is designed to be used within the context of a HttpServletRequest. When operating outside of a HttpServletRequest context, use AuthorizedClientServiceOAuth2AuthorizedClientManager instead.
Comment on the following link worked for me
https://www.gitmemory.com/issue/spring-projects/spring-security/8444/621567261
Another link
https://github.com/spring-projects/spring-security/issues/8230
Comment
DefaultReactiveOAuth2AuthorizedClientManager is intended to be used within a request context.
Given that you're seeing serverWebExchange cannot be null, you must be operating outside of a request context, which in case you should use AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager instead.
NOTE: Change the ServerOAuth2AuthorizedClientRepository parameter to ReactiveOAuth2AuthorizedClientService.
Actual code
#Bean
fun serverOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations: List<ClientRegistration>)
: ServerOAuth2AuthorizedClientExchangeFilterFunction {
val clientRegistrationRepository = InMemoryReactiveClientRegistrationRepository(clientRegistrations)
val authorizedClientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository)
val oAuth2AuthorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientService
)
val filterFunction = ServerOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager)
filterFunction.setDefaultClientRegistrationId(clientId)
return filterFunction
}
It seems that if you use an UnAuthenticatedServerOAuth2AuthorizedClientRepository it will propagate the webExchange from the source request going into to your #RestController into the WebClient you are using to invoke the other service causing the java.lang.IllegalArgumentException: serverWebExchange must be null
To fix this, use the autowired implementation of the ServerOAuth2AuthorizedClientRepository (this happens to be AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository)
#Bean
#LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder(ReactiveClientRegistrationRepository clientRegistrations,
ObjectMapper objectMapper,
ServerOAuth2AuthorizedClientRepository clientRepository) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2ClientFilter = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations,
clientRepository);
oauth2ClientFilter.setDefaultClientRegistrationId("apigee");
WebClient.Builder builder = WebClient.builder();
builder.defaultHeader("Content-Type", MediaType.APPLICATION_JSON.toString());
builder.defaultHeader("Accept", MediaType.APPLICATION_JSON.toString());
builder.filter(oauth2ClientFilter);
return builder;
}

Spring cloud multiple RestTemplate

I am using spring cloud: Spring Boot Application with Eureka + Ribbon default configuration.
I am using 2 RestTemplate configurations, both are #LoadBalanced currently and both of them have the same UriTemplateHandler.
I declared both the #SpringBootApplication and also the #RibbonClient(name="${service.name}") annotations.
My problem is:
When I am trying to access the first configured RestTemplate, the RestTemplate resolvs (by eureka and load balancing by ribbon) to a server , not as I requested as configured in the UriTemplateHandler.
For example: in the UriTemplateHandler I configured "A-Service" and in real time the restTemplate sends the httpRequest to "B-Service"
This behavior happens often, not just for a specific request, but it looks like it only happens when I'm accessing the first configured RestTemplate.
Is it a problem to use 2 RestTemplate with the same uri?
I have no idea why it happens, please advise.
When creating these rest templates as beans, name them uniquely, like e.g.
#LoadBalanced
#Bean("integrationRestTemplate")
public RestTemplate restTemplate() throws Exception {
// build and return your rest template
return ....
}
Then, the other one might be without any specific name e.g.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Now, if you have these two distinctive rest templates, you can inject the former one e.g. like that:
#Service
public class MyService {
private final RestTemplate restTemplate;
public ApplicantService(#Qualifier("integrationRestTemplate") RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
// service methods
...
}
Basically, the point is you can choose whatever rest template you want, by specifying a #Qualifier.

Spring Boot RestTemplate Basic Authentication using RestTemplateBuilder

In Spring Boot I'm trying to create a RestTemplate which will use basic authentication using
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
builder.basicAuthorization("username", "password");
RestTemplate template = builder.build();
return template;
}
I then inject the RestTemplate in my service class as
#Autowired
private RestTemplate restTemplate;
However, my requests fail with a 401 unauthorized exception:
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
Using another REST Client (Postman) the requests to the same URL succeeds so I assume the basic authentication is not working correctly. From the debug output it looks as if the authentication header is not being set. What will make this work?
The problem is that you are using the RestTemplateBuilder in a wrong way. The RestTemplateBuilder is immutable. So when doing builder.basicAuthorization("username", "password") you actually get a new instance, with a BasicAuthorizationInterceptor added and configured, of the RestTemplateBuilder. (this applies to all configuration methods of the RestTemplateBuilder they all create a fresh copied instance).
However your code is discarding that specifically configured instance and you are basically using the non secured default RestTemplateBuilder.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
builder.basicAuthorization("username", "password");
RestTemplate template = builder.build();
return template;
}
This code should be replaced with something like this.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.basicAuthorization("username", "password").build();
}
Which will use the specifically configured instance.
One solution is to create the RestTemplate as follows:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate template = builder.build();
template.setMessageConverters(
Arrays.asList(
new FormHttpMessageConverter(),
new StringHttpMessageConverter()
)
);
template.getInterceptors().add(new BasicAuthorizationInterceptor("username", "password"));
return template;
}

How to inject RestTemplate

I am not using xml configurations to define beans. Instead using component scanning and autowire to define and inject dependencies.
RestTemplate is part of springframework. How can I inject this class ?
You do it like any other #Bean in a #Configuration class, and inject with #Autowire - However you question suggest that you should read a little more of the Spring documentation.
#Bean
public RestTemplate restTemplate() {
RestTemplate template = new RestTemplate();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(6);
template.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom().setConnectionManager(connectionManager).build()));
return template;
}
You almost always want to use it together with Apache HttpClient so you get connection pooling. If you need to use it with self-signed https certificates you need a bit more code (let me know if this is the case)

Resources