Cannot inject LoadBalanced annotated OAuth2RestTemplate - spring

I am using Spring Cloud Angel.SR4. My Configuration class for creating an OAuth2RestTemplate bean is as follows:
#Configuration
public class OAuthClientConfiguration {
#Autowired
private MyClientCredentialsResourceDetails resource;
public OAuthClientConfiguration() {
}
#Bean
#Qualifier("MyOAuthRestTemplate")
public OAuth2RestTemplate restTemplate() {
return new OAuth2RestTemplate(this.resource);
}
}
This configuration is totally fine since I am using this RestTemplate in a Feign RequestInterceptor for injecting access tokens to the feign requests. The problem is that when I annotate an autowired OAuth2RestTemplate with #LoadBalanced the dependency injection engine raises a NoSuchBeanDefinitionException exception. For example, the following would raise an exception:
#LoadBalanced
#Autowired
#Qualifier("MyOAuthRestTemplate")
private OAuth2RestTemplate restTemplate;
and when I remove the #LoadBalanced, everything works fine. What is wrong with #LoadBalanced? Do I need any additional configurations (I already have #EnableEurekaClient)?

I found a workaround. The problem was that I had misunderstood the #LoadBalanced annotation. This is just a qualifier for the auto-created load-balanced RestTemplate bean, and it would not create a proxy around the annotated RestTemplate for injecting the load balancing capability.
After seeing this https://github.com/spring-cloud/spring-cloud-commons/blob/v1.0.3.RELEASE/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java, I revised my OAuth2RestTemplate bean definition as follows, and it solved the problem.
#Bean
#Qualifier("MyOAuthRestTemplate")
public OAuth2RestTemplate restTemplate(RestTemplateCustomizer customizer) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(this.resource);
customizer.customize(restTemplate);
return restTemplate;
}

I used #LoadBalanced with restTemplate in spring cloud with ribbon behind the scenes.
adding #LoadBalanced in the bean definition it works
like this:
in my class i have
#Autowired
#LoadBalanced
#Qualifier("bookRepositoryServiceRestTemplate") private RestTemplate bookRepositoryServiceRestTemplate;
and in my configuration class i have:
#Configuration
public class ServiceConfig {
#Bean
#LoadBalanced
public RestTemplate bookRepositoryServiceRestTemplate(SpringClientFactory clientFactory, LoadBalancerClient loadBalancer){
RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory = new RibbonClientHttpRequestFactory(clientFactory,loadBalancer);
return new RestTemplate(ribbonClientHttpRequestFactory);
}
....
}
this works for me
I hope that this can help

Related

Mockito: How to mock WebClient configuring Beans

I'm using Mockio, Wiremock and WebClient and I want to test my service layer.
My goal is to use an instance of the webclient and do a real request to wiremock.
Therefore I have to use a standard configuration and not my oauth config from the production mode.
In the service class, I execute reuqets to another api. So the class under test ist annotated with #Service.
Here is the class:
#Service
public class UserServiceImpl implements UserService{
private final Logger log = Logger.getLogger(this.getClass().getName());
private final WebClient webClient;
private final ApplicationConstants applicationConstants;
public UserServiceImpl (WebClient webClient, ApplicationConstants applicationConstants) {
this.applicationConstants = applicationConstants;
this.webClient = webClient;
}
#Override
public User getUserById(#NotNull(message = "userId must not be null.") #NotBlank(message = "userId must not be blank.") String userId) {
return webClient.get()...
}
I configured my WebClient to use Oauth via two Bean Methods in a class annotated with #Configuration.
#Configuration
public class WebClientConfig {
#Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
...
}
/*
Manages the auth process and token refresh process
*/
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
...
}
}
Because I want to use the webClient without oauth to call wiremock, I want to replace the Beans to return a simple Webclient.builder().build();
So I did:
#ExtendWith({SpringExtension.class, WireMockExtension.class, MockitoExtension.class})
public class TestClass {
#Mock
WebClientConfig webClientConfig;
#MockBean
WebClient webClient;
#InjectMocks
UserServiceImpl userService;
In general as I understand Mockito, i would use my class under test ( the userServiceImpl) with #InjectMocks, so a real instance is used and the dependencies are injected. Therefor I have to provide a Mock for the Webclient. As I don't want to Mock the webclient and just want to configure it different, I do not have to use #Mock. Instead it should be somethig like #MockBean as this annotation creates a bean and replaces existing ones in the context. So I have to mock the Webclientconfig class with #Mock and define something like
when(webclientConfig).webclient(any(OAuth2AuthorizedClientManager.class)).thenReturn(Webclient.builder.build);
But this does not work as I always get a nullpointer exception on the call.
So the basic questions are:
Is my understanding of Mockito right?
How do I have to Manage the Webclient config?
Looks like a case of configuring MockBeans before the rest of the application starts up, which is answered here:
Configure #MockBean component before application start
As of this writing, the answer above mentions using #Primary or #MockBean(answer = Answers.CALLS_REAL_METHODS) as workarounds.

Multiple rest templates in spring boot

I want to configure multiple rest template clients to access different API's. Both are having different authorization headers. I already configured one, Same way configured other rest template too, but that throws error bean 'restTemplate' defined in class path resource .class could not be registered..
#Configuration
public class RestTemplateConfig {
#Autowired
private HeaderRequestInterceptor headerRequestInterceptor;
//constructor
public RestClientConfig() {}
#Bean
public RestTemplate restTemplate( RestTemplateBuilder builder ) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(headerRequestInterceptor));
return restTemplate;
}
}
HeaderRequestInterceptor has base64 encoded authorization, so could not post that code here.
Another RestTemplate:
#Configuration
public class AnotherRestClientConfig {
#Autowired
private AnotherHeaderRequestInterceptor anotherHeaderRequestInterceptor;
#Bean
public RestTemplate restTemplate( RestTemplateBuilder builder ) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(anotherHeaderRequestInterceptor));
return restTemplate;
}
}
Could someone let me know how to configure multiple rest templates in an application.
you could use #Qualifier as mentioned by #VirtualTroll. Or create a specific client bean per api and hold the restemplate instance there.
#Component
public class ApiClient1 {
private final RestTemplate customRestTemplate;
public ApiClient1() {
this.customRestTemplate = ...
}
public void useApi() {
}
}

Bean and Autowiring

So I have a bean definition for a class and an autowiring of the same class. Can you tell me if these are separate. I think they are, but it does not make sense with the rest of the code (I am following a tutorial).
#Bean
public OAuth2RestTemplate googleOpenIdTemplate(final OAuth2ClientContext clientContext) {
final OAuth2RestTemplate template = new OAuth2RestTemplate(googleOpenId(), clientContext);
return template;
}
#Autowired
private OAuth2RestTemplate restTemplate;
The two snippets of code would created two different instances of the same class with DIFFERENT configurations, correct?
If I had
OAuth2RestTemplate instance = (OAuth2RestTemplate) context.getBean(googleOpenIdTemplate)
I would be retrieving a bean with the same configurations (singleton vs prototype) whereas in the first case, I would just get a bean of OAuth2RestTemplate without the same configurations.

Spring boot test: MockedBean conflicts with others

Currently, I've two RestTemplate beans:
#Bean
#Primary
public RestTemplate jwtRestTemplate(
RestTemplateBuilder builder,
JWTService jwtService) {
return builder
.additionalInterceptors(
Collections.singletonList(
new JWTHeaderRequestInterceptor(jwtService)
)
)
.build();
}
#Bean
public RestTemplate rawRestTemplate(RestTemplateBuilder builder) {
return builder.build();
}
The first one is primary and the other one is requested by #Qualifier("rawRestTemplate").
However, I'm mocking a ResTemplate into my tests:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class AuditoryTest {
#MockBean()
private RestTemplate frontOfficeRestTemplate;
#Autowired
private DocumentServiceBackOffice documentService;
DocumentServiceBackOffice constructor is:
public DocumentServiceBackOffice(RestTemplate restTemplate);
I'm getting an exception:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in net.gencat.transversal.espaidoc.backoffice.service.DocumentServiceBackOffice required a single bean, but 2 were found:
- rawRestTemplate: defined by method 'rawRestTemplate' in class path resource [net/gencat/transversal/espaidoc/backoffice/config/BackOfficeConfiguration.class]
- jwtRestTemplate: defined by method 'createMock' in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Message is pretty clear, but I don't quite figure out how to solve that.
Any ideas?
I've solved that using that additional Configuration class:
#TestConfiguration
public static class RestTemplateTestConfiguration {
#Bean("jwtRestTemplate")
#Primary
public static RestTemplate someService() {
return Mockito.mock(RestTemplate.class);
}
}

How to Create or configure Rest Template using #Bean in Spring Boot

I want to define RestTemplate as an application bean using #Bean annotation in my configuration class in a spring boot application.
I am calling 4 rest services in different places in my application flow. Currently I am creating RestTemplate every time every request. Is there a way I can define that as application bean using #Bean and inject that using #Autowired?
Main reason for this question is I can able to define RestTemplate using #Bean but when I inject it with #Autowired I am loosing all defined interceptors (Interceptors are not getting called.)
Configuration Class
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new RestServiceLoggingInterceptor());
restClient.setInterceptors(interceptors);
return restClient;
}
Service Class
public class MyServiceClass {
#Autowired
private RestTemplate appRestClient;
public String callRestService() {
// create uri, method response objects
String restResp = appRestClient.getForObject(uri, method, response);
// do something with the restResp
// return String
}
}
It seems my Interceptors are not getting called at all with this configuration. But RestTemplate is able to make a call to the REST service and get a response.
Answer for Spring boot 2.*.* version.
I am using Spring boot 2.1.2.RELEASE and I also added RestTemplate in my project in a class where mail method exists.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.setConnectTimeout(Duration.ofMillis(300000))
.setReadTimeout(Duration.ofMillis(300000)).build();
}
and Used in my service or other classes like this
#Autowired
RestTemplate res;
and in methods
HttpEntity<String> entity = new HttpEntity<>(str, headers);
return res.exchange(url, HttpMethod.POST, entity, Object.class);
Judging form the name of the interceptor, I'm guessing you're doing some logging in it? You could of missed logging level configuration. I created a small application to check weather your configuration works, using 1.3.6.RELEASE version.
In this class I define the RestTemplate bean and the interceptor with logging.
package com.example;
// imports...
#SpringBootApplication
public class TestApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(TestApplication.class);
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
// Add one interceptor like in your example, except using anonymous class.
restClient.setInterceptors(Collections.singletonList((request, body, execution) -> {
LOGGER.debug("Intercepting...");
return execution.execute(request, body);
}));
return restClient;
}
}
For logging to work, I also have to set the correct debug level in application.properties.
logging.level.com.example=DEBUG
Then I create a service where I inject this RestTemplate.
#Service
public class SomeService {
private final RestTemplate appRestClient;
#Autowired
public SomeService(#Qualifier("appRestClient") RestTemplate appRestClient) {
this.appRestClient = appRestClient;
}
public String callRestService() {
return appRestClient.getForObject("http://localhost:8080", String.class);
}
}
And also an endpoint to test this out.
#RestController
public class SomeController {
private final SomeService service;
#Autowired
public SomeController(SomeService service) {
this.service = service;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String testEndpoint() {
return "hello!";
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return service.callRestService();
}
}
By performing a GET request to http://localhost:8080/test I should expect to get the String hello! getting printed (the service makes a call to http://localhost:8080 which returns hello! and sends this back to me). The interceptor with logger also prints out Intercepting... in the console.
Edd's solution won't work if you're using Spring Boot 1.4.0 or later. You will have to use RestTemplateBuilder to get this working. Here is the example
#Bean(name="simpleRestTemplate")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
RestTemplate template = restTemplateBuilder.requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.interceptors(logRestRequestInterceptor) //This is your custom interceptor bean
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
return template;
}
Now you can autowire the bean into your service class
#Autowired
#Qualifier("simpleRestTemplate")
private RestTemplate simpleRestTemplate;
Hope this helps

Resources