Spring RestTemplate as a Spring Service - spring

I have developed a REST server with our app specific APIs. we also have deployed a different rest Job server into another location. Currently the way I am doing is .
#RestController
public class SparkJobController {
#Autowired
private IJobSchedulerService jobService;
...
And the Service Implementation is
#Service(value="jobService")
public class JobSchedulerServiceImpl implements IJobSchedulerService {
#Override
public Map triggerJob(String context) {
Map<String, ?> s = new HashMap<String,Object>();
RestTemplate restTemplate = new RestTemplate();
// restTemplate call to other REST API. and returns Map.
...
}
My question is , Is my approach correct ? Or Does Spring framework enables us to use some predefined APIs which can help to use RESTTemplate as a Service
[EDIT] : the deployed REST service is third party application.

I did some research and havent' seen yet a way to implement RestTemplate as a service.
I have seen RestTemplate defined in the bean config and auto wired in - https://www.informit.com/guides/content.aspx?g=java&seqNum=546
To summarize, most of the examples I have seen use Resttemplate, similar to how you have implemented in your code.

Related

Rest API send websocket message as well as response entity

I'm using spring boot and I was wondering if I can create a REST API that also sends a message to a websocket channel? so anyone subscribed to it can get it. Since it's a rest api, there would also be a response entity when it's done as well. If so, can i see an example of how that would work? i've been googling everywhere.
Good starting points to build a Spring Boot application with Websocket support are
WebSockets chapter in the official Spring documentation
WebSocket Security chapter in the Spring security documentation
Baeldung post Intro to Security and WebSockets
To determine connected users you can use SimpUserRegistry bean and to send messages to them you can use SimpMessagingTemplate, for example:
#RestController
public class ApiController {
private final SimpMessagingTemplate template;
private final SimpUserRegistry userRegistry;
public ApiController(SimpMessagingTemplate template, SimpUserRegistry userRegistry) {
this.template = template;
this.userRegistry = userRegistry;
}
#PostMapping("/api/users/{username}/send")
public ResponseEntity<?> sendMessage(#RequestBody Message message, #PathVariable String username) {
Set<SimpUser> users = userRegistry.getUsers();
if (users.stream().anyMatch(simpUser -> simpUser.getName().equals(username))) {
template.convertAndSendToUser(username, "/messages", message);
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
}
}
You can check my minimal example of working Websockets demo application.

Using MockRestServiceServer only in subset of tests

I want to test outgoing HTTP calls from my service using MockRestServiceServer. I got it working using following code:
#SpringBootTest
class CommentFetcherTest {
#Autowired
RestTemplate restTemplate;
#Autowired
CommentFetcher commentFetcher;
#Test
public void shouldCallExternalService(){
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(ExpectedCount.once(), requestTo("/test/endpoint")).andExpect(method(HttpMethod.GET));
//when
commentFetcher.fetchData();
mockServer.verify();
}
}
However the problem I run into is that RestTemplate is a bean shared between all tests from suite what makes impossible to further run integration tests which should be calling external services. This results in :
java.lang.AssertionError: No further requests expected
How is it possible to use MockRestServiceServer only for subset of tests?
I can't get rid of Dependency Injection through #Autowired and construct my service in tests such as
RestTemplate restTemplate = new RestTemplate();
CommentFetcher commentFetcher = new CommentFetcher(restTemplate);
As this would not read properties from application.properties necessary to proper functioning of my service.
Spring Boot provides a convenient way to test your client classes that make use of Spring's RestTemplate using #RestClientTest.
With this annotation, you get a Spring Test Context that only contains relevant beans and not your whole application context (which is what you currently get with #SpringBootTest).
The MockRestServiceServer is also part of this context and the RestTemplate is configured to target it.
A basic test setup can look like the following:
#RestClientTest(CommentFetcher.class)
class UserClientTest {
#Autowired
private CommentFetcher userClient;
#Autowired
private MockRestServiceServer mockRestServiceServer;
// ...
}
What's left is to stub the HTTP method call and then invoke the method on your class under test (commentFetcher.fetchData(); in your case).
Refer to this article if you want to understand more about how #RestClientTest can be used to test your RestTemplate usage.

Eureka server and UnknownHostException

I have installed an Eureka server and have registered a service called pricing-service.
The Eureka dashboard shows
UP pricing-service:4ac78ca47bdbebb5fec98345c6232af0
under status.
Now I have a completely separate Spring boot web service which calls (through a WebClient instance) the pricing-service as http://pricing-service but I get "reactor.core.Exceptions$ReactiveException: java.net.UnknownHostException: No such host is known (pricing-service)"
exception.
So the Controller can't find the pricing-service by hostname.Further, how is the controller aware of the Eureka server in order to get to pricing-service? Shouldn't there be a reference to it in the application.properties of the web service? I couldn't find anything around the web.
WebClient doesn't know anything about Eureka out of the box. You need to use #LoadBalancerClient and #LoadBalanced to wire it up through the load balancer. See the docs here:
https://spring.io/guides/gs/spring-cloud-loadbalancer/
Now I have a completely separate Spring boot web service which calls (through a WebClient instance) the pricing-service as http://pricing-service
This separate service (the WebClient Service) of yours must also register itself with Eureka Server.
By default, webclient is not aware of having to use load-balancer to make calls to other eureka instances.
Here is one of the ways to enable such a WebClient bean:
#Configuration
public class MyBeanConfig {
#Bean
WebClient webClient(LoadBalancerClient lbClient) {
return WebClient.builder()
.filter(new LoadBalancerExchangeFilterFunction(lbClient))
.build();
}
}
Then, you can use this webClient bean to make calls as:
#Component
public class YourClient {
#Autowired
WebClient webClient;
public Mono<ResponseDto> makeCall() {
return webClient
.get()
.uri("http://pricing-service/")
// <-- change your body and subscribe to result
}
Note: Initializing a Bean of WebClient can be explored further here.
I had the issue that WebClient was not working with #LoadBalanced for me when I created a bean that returned WebClient. You have to create a bean for WebClient.Builder instead of just WebClient, otherwise the #LoadBalanced annotation is not working properly.
#Configuration
public class WebClientConfig {
#Bean
#LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.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 1.3.3 create multiple resttemplate per env

I am on spring boot 1.3.3 version, I have a requirement where my spring boot application need to call endpoint(s) based on env passed,
which means if env passed as Dev i would need to call devendpoint,
if env passed as Dev1 then need to call dev1endpoint and so on.
So how can I do this ?
Do I need to create multiple restTemplate instances ?
Should I construct the resttemplate dynamically based on env passed ?
As part of constructing resttemplate i would also need to add appllicable interceptor based on env selected.
Plesae suggest.
You can have two beans of the same class. One can be labeled as the primary, and the use on the #Autowired can specify which one to use with the #Qualifier.
Example:
#Configuration
public class MyConfig {
#Bean
#Primary
public RestTemplate typicalConfig() {
// various configs on your rest template
return new RestTemplate();
}
#Bean
public RestTemplate lessTypical() {
// various alternate configurations
return new RestTemplate();
}
}
Now in your service class:
#Service
public class MyService {
#Autowired
RestTemplate typicalRestTemplate;
#Autowired
#Qualifier("lessTypical")
private RestTemplate alternateRestTemplate;
...
}
correct me if I didn't understand your question. I understand that you are going to have different environments but you are going to change this endpoints in runtime depends on some information or whatever, but I don't understand the part when you said you have to create multiple instances of restTemplate for that environments, from my experience on spring boot applications you don't have to do things like that, You just have to create your restTemplate configuration bean.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
And then injected that object on your services class and do whatever you want with them. I recommend you to read the follow article about restTempalte may this could help you http://www.baeldung.com/rest-template

Resources