I am having a wrapper that autowire ribbon aware RestTemplate and then I add a interceptor-
#Component
public class MyRestTemplate {
#Autowired
private RestTemplate restTemplate;
#PostConstruct
public void init(){
restTemplate.setInterceptors(Collections.singletonList(new
MyRestTemplateRequestInterceptor(applicationName)));
}
}
If I #AutoWire RestTemplate in some other component, will I get same instance of RestTemplate with this incterceptor? My observation is the autowired resttemplate in other component also adds this interceptor. Please help to validate the understanding.
Yyou will get the same instance of RestTemplate. When the bean MyRestTemplate is constructor (rather after it's construction), you are adding the MyRestTemplateRequestInterceptor to the RestTemplate interceptor.
So this interceptor will be applied to RestTemplate.
If you are looking to apply an interceptor for a specific feign client you can create your own configuration:
#Configuration
#IgnoreDuringScan
public class FeignClientConfiguration {
#Autowired
private Environment environment;
#Bean
public Encoder feignEncoder() {
return new GsonEncoder();
}
#Bean
public ErrorDecoder feignDecoder() {
return new GsonDecoder();
}
#Bean
public RequestInterceptor requestHeaderInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate requestTemplate) {
//impl
}
};
}
#Bean
public Feign.Builder feingBuilder() {
return Feign.builder().encoder(feignEncoder()).decoder(feignDecoder()).errorDecoder(new YourErrorDecoder())
.requestInterceptor(requestHeaderInterceptor);
}
}
Then just apply the configuration to your feign client:
#FeignClient(name = "myFeign", url = "url/...", configuration = {
FeignClientConfiguration.class
})
public interface YourFeignClient {
//....
}
Related
I have this test project which I would like to migrate to more recent version:
#Configuration
public class LoadbalancerConfig extends RibbonLoadBalancerClient {
public LoadbalancerConfig(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
Full code example: https://github.com/rcbandit111/Generic_SO_POC/blob/master/src/main/java/org/merchant/database/service/sql/LoadbalancerConfig.java
Do you know how I can migrate this code to latest load balancer version?
I think examining the RibbonAutoConfiguration class gives you a good hint of how you should configure things.
First remove #Configuration from LoadbalancerConfig, I also renamed LoadbalancerConfig to CustomLoadbalancer to prevent confusion.
public class CustomLoadbalancer extends RibbonLoadBalancerClient {
public CustomLoadbalancer(SpringClientFactory clientFactory) {
super(clientFactory);
}
}
add the following dependency to your gradle
com.netflix.ribbon:ribbon:2.7.18
then add a configuration class like:
#Configuration
#ConditionalOnClass({Ribbon.class})
#AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
#ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
havingValue = "true", matchIfMissing = true)
#AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
public class LoadBalancerClientConfig {
#Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
#Bean
public CustomLoadbalancer customLoadbalancer() {
return new CustomLoadbalancer(springClientFactory());
}
#Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
}
If you want to use Spring cloud load balancer instead of above configuration add spring-cloud-starter-loadbalancer dependency to your gradle.build and for configuration you only need this bean:
#LoadBalanced
#Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
}
This RestTemplate pretty works identical to standard RestTemplate class, except instead of using physical location of the service, you need to build the URL using Eureka service ID.
Here is an example of how could you possibly use it in your code
#Component
public class LoadBalancedTemplateClient {
#Autowired
RestTemplate restTemplate;
#Autowired
User user;
public ResponseEntity<Result> getResult() {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder
.fromHttpUrl("http://application-id/v1/") // application id registered in eureka
.queryParam("id", user.getUserId());
return restTemplate.getForEntity(uriComponentsBuilder.toUriString(),
Result.class);
}
}
Also if you wish to use reactive client the process is the same first define the bean:
#LoadBalanced
#Bean
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
and then inject and use it when you need:
#Autowired
private WebClient.Builder webClient;
public Mono<String> doSomething() {
return webClient
.build()
.get()
.uri("http://application-id/v1/")
.retrieve()
.bodyToMono(String.class);
}
Also you can check documentation for additional information: documentation
I'm trying to define a common bean to be used for all my application so to add inside a logger and other logic. My idea would be:
public class MyRestTemplate extends RestTemplate{
Then:
#Configuration
public class RestTemplateConfig {
#Bean
public MyRestTemplate myRestTemplate(RestTemplateBuilder builder){
return (MyRestTemplate) builder.build(); //throws classcast exception!
}
}
What am I doing wrong? Is there another way? I want to be sure that people will have to use my customized class.
If you want some customizations in your restTemplate you could define a class that implements RestTemplateCustomizer and add a custom interceptor to it.
public class CustomRestTemplateCustomizer implements RestTemplateCustomizer {
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(new CustomClientHttpRequestInterceptor());
}
}
Then you have to define that custom interceptor for all the requests going out of this restTemplate with
public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
// This is where you can do a lot of thing with this request like logging
return execution.execute(request, body);
}
}
And finally, just define a bean for the custom restTemplate you have written
#Bean
public CustomRestTemplateCustomizer customRestTemplateCustomizer() {
return new CustomRestTemplateCustomizer();
}
builder.build() returns a RestTemplate, not a MyRestTemplate.
If you change your code as shown below you would create a bean named myRestTemplate. Spring use the name of the method as bean name if you don't override it in the #Bean annotation.
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate myRestTemplate(RestTemplateBuilder builder){
return builder.build(); //throws classcast exception!
}
}
Please also see https://docs.spring.io/spring-boot/docs/1.5.x/reference/html/boot-features-restclient.html
I am writing Integration tests in spring-boot.
One of my beans is using UrlResource("http://localhost:8081/test") for its creation.
I want to create a mock server, which will serve the above url with a mock response.
But I want this mock server to be created before any bean is initialized, as the mock server should be available to serve requests before the bean is initialized.
I have tried using the MockRestServiceServer in the #TestConfiguration
Following is the pseudo code which is failing:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestApiGatewayApplicationTests {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void contextLoads() {
}
#TestConfiguration
class TestConfig {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
String mockKeyResponse = "{\"a\":\"abcd\"}";
mockServer.expect(requestTo("http://localhost:8081/test"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(mockKeyResponse, MediaType.TEXT_PLAIN));
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
when(builder.build()).thenReturn(restTemplate);
return builder;
}
}
}
Following is the sample code for creation of bean which is to be tested.
#Configuration
public class BeanConfig {
#Bean
public SampleBean sampleBean(){
Resource resource = new UrlResource("");
// Some operation using resource and create the sampleBean bean
return sampleBean;
}
}
Using the above approach I am getting
" java.net.ConnectException: Connection refused (Connection refused)"
error as it is not able to access the http://localhost:8081/test endpoint.
Use #InjectMocks.
Reference : documentation and Explaination with example.
I have solved this issue by creating a MockServiceServer in the testConfiguration.
Sample code is as follows.
#TestConfiguration
static class TestConfig {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
String mockKeyResponse = "{\"a\":\"abcd\"}";
mockServer.expect(requestTo("http://localhost:8081/test"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(mockKeyResponse, MediaType.TEXT_PLAIN));
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
when(builder.build()).thenReturn(restTemplate);
return builder;
}
}
Then in the class BeanConfig where I needed to use this I have autowired using constructor injection, so that RestTemplate will be created before bean of BeanConfig class is created.
Following is how I did it.
#Configuration
public class BeanConfig {
private RestTemplate restTemplate;
public BeanConfig(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Bean
public SampleBean sampleBean(){
Resource resource = new UrlResource("");
// Some operation using resource and create the sampleBean bean
return sampleBean;
}
}
I encountered a problem with my Ribbon application. Here is my code:
#SpringBootApplication
#EnableDiscoveryClient
#RestController
#RibbonClient(name= "bye", configuration=RibbonConfig.class )
public class RibbonAppApplication {
#Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(RibbonAppApplication.class, args);
}
#GetMapping
public String getService() {
return restTemplate.getForObject("http://bye",String.class);
}
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
And my RibbonConfig.class:
#Configuration
public class RibbonConfig {
#Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl(false,"/health");
}
#Bean
public IRule ribbonRule(IClientConfig config) {
return new AvailabilityFilteringRule();
}
}
However, I got the following error:
Parameter 0 of method ribbonPing in practice.zuul.zach.ribbonapp.RibbonConfig required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
Action:
Consider defining a bean of type 'com.netflix.client.config.IClientConfig' in your configuration.
Is there any ways to solve it?
problem solved when i add this line in RibbonAppApplication class
#SpringBootApplication(scanBasePackages={"com.netflix.client.config.IClientConfig"})
I've just created a FactoryBean implementation in order to request RestTemplate:
#Component
public class RestTemplateFactory
implements FactoryBean<RestTemplate>, InitializingBean {
//init resttemplate headers
}
So, now I'm able to inject a RestTemplate at whichever class:
#Service
public class DocumentServiceBackOffice {
private RestTemplate restTemplate;
public DocumentServiceBackOffice(RestTemplate restTemplate) {//...}
}
However, I'd like to create another FactoryBean<RestTemplate> in order to initialize other parameters.
How could I create in order to inject one or other according to a qualifier?
Any ideas?
EDIT
#Component
public class RestTemplateFactory
implements FactoryBean<RestTemplate>, InitializingBean {
private RestTemplate restTemplate;
private JWTService jwtService;
public RestTemplateFactory(JWTService jwtService) {
this.jwtService = jwtService;
}
public RestTemplate getObject() {
return this.restTemplate;
}
public Class<RestTemplate> getObjectType() {
return RestTemplate.class;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() {
this.restTemplate = new RestTemplate();
JWTHeaderRequestInterceptor jwtInterceptor = new JWTHeaderRequestInterceptor(this.jwtService);
this.restTemplate.setInterceptors(Arrays.asList(jwtInterceptor));
}
}
Instead of using a FactoryBean just use an #Bean annotated method which accepts a RestTemplateBuilder and use that to configure instances.
#Bean
#Primary
public RestTemplate fooRestTemplate(RestTemplateBuilder builder, JWTService jwtService) {
return builder.additionalInterceptors(Collections.singletonList(new JwtHeaderInterceptor(jwtService)).build();
}
#Bean
public RestTemplate barRestTemplate(RestTemplateBuilder builder {
return builder.build();
}
This will result in 2 available RestTemplate instances. The fooRestTemplate (marked as default due to #Primary) and barRestTemplate. To specify the specific one to use add an #Qualifier("barRestTemplate") to use the not default one.
public DocumentServiceBackOffice(#Qualifier("barRestTemplate") RestTemplate restTemplate) { ... }
Another way to do it would be defining a configuration with two RestTemplate beans with qualifiers.
#Configuration
public class Configuration {
#Bean
#Qualifier("firstRestTemplate")
public RestTemplate firstRestTemplate(){
// any building logic here
return new Resttemplate();
}
#Bean
#Qualifier("secondRestTemplate")
public RestTemplate secondRestTemplate(){
// any building logic here
return new Resttemplate();
}
}
Then, in your code, use the right #Qualifier when autowiring.
Setter injection example:
#Service
public class Service {
#Autowired
#Qualifier("firstRestTemplate")
private RestTemplate template;
// ...
}
Constructor injection example:
#Service
public class Service {
private RestTemplate template;
public Service(#Autowired #Qualifier("firstRestTemplate") RestTemplate template) {
this.template = template;
}
// ...
}