Consecutive same Rest API Call using Spring Boot - java

I want to call a Rest API using springboot till a field in the response (hasMoreEntries) is 'Y'. Currently, am simply using a while loop and checking the response for the flag and calling the API again. PFB pseudocode. Is there any other efficient way to do this OR what is the best way.
String hasMoreEntries="Y";
while(!hasMoreEntries.equals("N"))
{
response = \\PERFORM REST SERVICE CALL HERE
hasMoreEntries=respone.body().getHasMoreEntries();
}

You can use Spring Retry mechanism.
For example you can create RetryTemplate bean with custom exception ResponseValidateException that is thrown when the response is invalid:
#Bean
public RetryTemplate retryTemplate() {
return RetryTemplate.builder()
.retryOn(ResponseValidateException.class)
.infiniteRetry()
.fixedBackoff(2000L)
.build();
}
And your service:
#Service
public class YourService {
#Autowired
private RetryTemplate retryTemplate;
public void bar() {
final ResponseEntity<SomeObject> entity = retryTemplate.execute(retryContext -> {
final ResponseEntity<SomeObject> response = null;
if ("Y".equals(response.getBody().getHasMoreEntries())) {
return response;
} else {
throw new ResponseValidateException();
}
});
}
}
You can also look at custom RetryPolicy (for example CircuitBreakerRetryPolicy) classes to add them in your RetryTemplate bean for your cases.

Related

Apache Camel aggregation completion not working

I've configured a route to extract some data from exchanges and aggregate them; here is simple summary:
#Component
#RequiredArgsConstructor
public class FingerprintHistoryRouteBuilder extends RouteBuilder {
private final FingerprintHistoryService fingerprintHistoryService;
#Override
public void configure() throws Exception {
from("seda:httpFingerprint")
.aggregate( (AggregationStrategy) (oldExchange, newExchange) -> {
final FingerprintHistory newFingerprint = extract(newExchange);
if (oldExchange == null) {
List<FingerprintHistory> fingerprintHistories = new ArrayList<>();
fingerprintHistories.add(newFingerprint);
newExchange.getMessage().setBody(fingerprintHistories);
return newExchange;
}
final Message oldMessage = oldExchange.getMessage();
final List<FingerprintHistory> fingerprintHistories = (List<FingerprintHistory>) oldMessage.getBody(List.class);
fingerprintHistories.add(newFingerprint);
return oldExchange;
})
.constant(true)
.completionSize(aggregateCount)
.completionInterval(aggregateDuration.toMillis())
.to("direct:processFingerprint")
.end();
from("direct:processFingerprint")
.process(exchange -> {
List<FingerprintHistory> fingerprintHistories = exchange.getMessage().getBody(List.class);
fingerprintHistoryService.saveAll(fingerprintHistories);
});
strong text
}
}
The problem is aggregation completion never works for example this is a sample of my test:
#SpringBootTest
class FingerprintHistoryRouteBuilderTest {
#Autowired
ProducerTemplate producerTemplate;
#Autowired
FingerprintHistoryRouteBuilder fingerprintHistoryRouteBuilder;
#Autowired
CamelContext camelContext;
#MockBean
FingerprintHistoryService historyService;
#Test
void api_whenAggregate() {
UserSearchActivity activity = ActivityFactory.buildSampleSearchActivity("127.0.0.1", "salam", "finger");
Exchange exchange = buildExchange();
exchange.getMessage().setBody(activity);
ReflelctionTestUtils.setField(fingerprintHistoryRouteBuilder, "aggregateCount", 1);
ReflectionTestUtils.setFiled(fingerprintHistoryRouteBuilder, "aggregateDuration", Duration.ofNanos(1));
producerTemplate.send(FingerprintHistoryRouteBuilder.FINGERPRINT_HISTORY_ENDPOINT, exchange);
Mockito.verify(historyService).saveAll(Mockito.any());
}
Exchange buildExchange() {
DefaultExchange defaultExchange = new DefaultExchange(camelContext);
defaultExchange.setMessage(new DefaultMessage(camelContext));
return defaultExchange;
}
}
with the following result:
Wanted but not invoked: fingerprintHistoryService bean.saveAll(
);
I build this simplified example, and the test passes, so it looks like your usage of aggregate is probably correct.
Have you considered that your Mockito.verify() call is happening before the exchange finishes routing? You could test this by removing the verify call and adding a .log() statement to the FINGERPRINT_PROCESS_AGGREGATION route. If you see the log output during execution, you know the exchange is being routed as you expect. If this is the case, then your verify() call needs to be able to wait for the exchange to finish routing. I don't use mockito much, but it looks like you can do this:
Mockito.verify(historyService, timeout(10000)).saveAll(Mockito.any());

external URL with API endpoint in SpringBoot [duplicate]

I want to call another web-api from my backend on a specific request of user. For example, I want to call Google FCM send message api to send a message to a specific user on an event.
Does Retrofit have any method to achieve this? If not, how I can do that?
This website has some nice examples for using spring's RestTemplate.
Here is a code example of how it can work to get a simple object:
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees.xml";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
Modern Spring 5+ answer using WebClient instead of RestTemplate.
Configure WebClient for a specific web-service or resource as a bean (additional properties can be configured).
#Bean
public WebClient localApiClient() {
return WebClient.create("http://localhost:8080/api/v3");
}
Inject and use the bean from your service(s).
#Service
public class UserService {
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(3);
private final WebClient localApiClient;
#Autowired
public UserService(WebClient localApiClient) {
this.localApiClient = localApiClient;
}
public User getUser(long id) {
return localApiClient
.get()
.uri("/users/" + id)
.retrieve()
.bodyToMono(User.class)
.block(REQUEST_TIMEOUT);
}
}
Instead of String you are trying to get custom POJO object details as output by calling another API/URI, try the this solution. I hope it will be clear and helpful for how to use RestTemplate also,
In Spring Boot, first we need to create Bean for RestTemplate under the #Configuration annotated class. You can even write a separate class and annotate with #Configuration like below.
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Then, you have to define RestTemplate with #Autowired or #Injected under your service/Controller, whereever you are trying to use RestTemplate. Use the below code,
#Autowired
private RestTemplate restTemplate;
Now, will see the part of how to call another api from my application using above created RestTemplate. For this we can use multiple methods like execute(), getForEntity(), getForObject() and etc. Here I am placing the code with example of execute(). I have even tried other two, I faced problem of converting returned LinkedHashMap into expected POJO object. The below, execute() method solved my problem.
ResponseEntity<List<POJO>> responseEntity = restTemplate.exchange(
URL,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<POJO>>() {
});
List<POJO> pojoObjList = responseEntity.getBody();
Happy Coding :)
Create Bean for Rest Template to auto wiring the Rest Template object.
#SpringBootApplication
public class ChatAppApplication {
#Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ChatAppApplication.class, args);
}
}
Consume the GET/POST API by using RestTemplate - exchange() method. Below is for the post api which is defined in the controller.
#RequestMapping(value = "/postdata",method = RequestMethod.POST)
public String PostData(){
return "{\n" +
" \"value\":\"4\",\n" +
" \"name\":\"David\"\n" +
"}";
}
#RequestMapping(value = "/post")
public String getPostResponse(){
HttpHeaders headers=new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity=new HttpEntity<String>(headers);
return restTemplate.exchange("http://localhost:8080/postdata",HttpMethod.POST,entity,String.class).getBody();
}
Refer this tutorial[1]
[1] https://www.tutorialspoint.com/spring_boot/spring_boot_rest_template.htm
As has been mentioned in the various answers here, WebClient is now the recommended route.
You can start by configuring a WebClient builder:
#Bean
public WebClient.Builder getWebClientBuilder(){
return WebClient.builder();
}
Then inject the bean and you can consume an API as follows:
#Autowired
private WebClient.Builder webClientBuilder;
Product product = webClientBuilder.build()
.get()
.uri("http://localhost:8080/api/products")
.retrieve()
.bodyToMono(Product.class)
.block();
Does Retrofit have any method to achieve this? If not, how I can do that?
YES
Retrofit is type-safe REST client for Android and Java. Retrofit turns your HTTP API into a Java interface.
For more information refer the following link
https://howtodoinjava.com/retrofit2/retrofit2-beginner-tutorial
In this case need download whit my API, files hosted in other server.
In my case, don't need use a HTTP client to download the file in a external URL, I combined several answers and methods worked in previous code for files that were in my local server.
My code is:
#GetMapping(value = "/download/file/pdf/", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<Resource> downloadFilePdf() throws IOException {
String url = "http://www.orimi.com/pdf-test.pdf";
RestTemplate restTemplate = new RestTemplate();
byte[] byteContent = restTemplate.getForObject(url, String.class).getBytes(StandardCharsets.ISO_8859_1);
InputStream resourceInputStream = new ByteArrayInputStream(byteContent);
return ResponseEntity.ok()
.header("Content-disposition", "attachment; filename=" + "pdf-with-my-API_pdf-test.pdf")
.contentType(MediaType.parseMediaType("application/pdf;"))
.contentLength(byteContent.length)
.body(new InputStreamResource(resourceInputStream));
}
and it works with HTTP and HTTPS urls!
Since the question explicitly tags spring-boot, it worth noting that recent versions already ship a pre-configured instance of a builder for WebClient, thus you can directly inject it inside your service constructor without the needing to define a custom bean.
#Service
public class ClientService {
private final WebClient webClient;
public ClientService(WebClient.Builder webClientBuilder) {
webClient = webClientBuilder
.baseUrl("https://your.api.com")
}
//Add all the API call methods you need leveraging webClient instance
}
https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-webclient.html
Simplest way I have found is to:
Create an annotated interface (or have it generated from somehing like OpenAPI)
Give that interface to Spring RestTemplate Client
The Spring RestTemplate Client will parse the annotations on the interface and give you a type safe client, a proxy-instance. Any invocation on the methods will be seamlessly translated to rest-calls.
final MyApiInterface myClient = SpringRestTemplateClientBuilder
.create(MyApiInterface.class)
.setUrl(this.getMockUrl())
.setRestTemplate(restTemplate) // Optional
.setHeader("header-name", "the value") // Optional
.setHeaders(HttpHeaders) // Optional
.build();
And a rest call is made by inoking methods, like:
final ResponseEntity<MyDTO> response = myClient.getMyDto();

feign error handling by using feign decoder

I have created service 'A', which needs to be calls by the service 'B' by using feign client
And if service 'A' fails due to some validation, then service 'A' send back the error response which contains the below details,
(1)http status code
(2)error message
(3) custom error map which contains the custom errorcode and their error message
for example, <"Emp-1001", "invalid employee Id">
From Service 'B' we are using feigndecoder for handling feign exception, but it only provides the http status code not the custom error code
And, In my case, for different-different scenario, the http status code is same but custom error map value is different.
on the combination of both(http status code + custom error map), we have to handle the exception in service 'B'.
kindly provide some suggestions on this
You can enable circuit breaker and also configure your application to apply different fallback methods depending on the error returned, follow the next steps:
1.- Enable the circuit breaker itself
#SpringBootApplication
#EnableFeignClients("com.perritotutorials.feign.client")
#EnableCircuitBreaker
public class FeignDemoClientApplication {
2.- Create your fallback bean
#Slf4j
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PetAdoptionClientFallbackBean implements PetAdoptionClient {
#Setter
private Throwable cause;
#Override
public void savePet(#RequestBody Map<String, ?> pet) {
log.error("You are on fallback interface!!! - ERROR: {}", cause);
}
}
Some things you must keep in mind for fallback implementations:
Must be marked as #Component, they are unique across the application.
Fallback bean should have a Prototype scope because we want a new one to be created for each exception.
Use constructor injection for testing purposes.
3.- Your ErrorDecoder, to implement fallback startegies depending on the HTTP error returned:
public class MyErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new MyCustomBadRequestException();
}
if (response.status() >= 500) {
return new RetryableException();
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
4.- In your configuration class, add the Retryer and the ErrorDecoder into the Spring context:
#Bean
public MyErrorDecoder myErrorDecoder() {
return new MyErrorDecoder();
}
#Bean
public Retryer retryer() {
return new Retryer.Default();
}
You can also add customization to the Retryer:
class CustomRetryer implements Retryer {
private final int maxAttempts;
private final long backoff;
int attempt;
public CustomRetryer() {
this(2000, 5); //5 times, each 2 seconds
}
public CustomRetryer(long backoff, int maxAttempts) {
this.backoff = backoff;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
try {
Thread.sleep(backoff);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
#Override
public Retryer clone() {
return new CustomRetryer(backoff, maxAttempts);
}
}
If you want to get a functional example about how to implement Feign in your application, read this article.

writing unit test cases for an interface which has methods without arguments

I have an interface which has a method without arguments
public interface HealthStatus {
Integer healthCheck();
}�
The implementation is as below
#Override
public Integer healthCheck() {
Integer status = 0;
try {
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
if (proxyConfigProperties.getEnabled()) {
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
simpleClientHttpRequestFactory.setProxy(proxy);
}
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
ResponseEntity<String> healthResponse = restTemplate.exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}�
How can I unit test this method for positive scenarios and negative scenarios.
Edit:
I have refactored my class as below
#Service
#Qualifier("implementation")
public class HealthStatusImpl implements HealthStatus {
#Autowired
RestTemplateConfig restTemplateConfig;
#Autowired
private EVerifyGovernmentProperties eVerifyGovernmentProperties;
#Override
public Integer healthCheck() {
Integer status = 0;
try {
ResponseEntity<String> healthResponse = restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}
}
Here is the class which instantiates the RestTemplate
#Component
public class RestTemplateConfig {
#Autowired
ProxyConfigProperties proxyConfigProperties;
public RestTemplate getRestTemplate(){
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
if (proxyConfigProperties.getEnabled()) {
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
simpleClientHttpRequestFactory.setProxy(proxy);
}
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
return restTemplate;
}
}
Actually this is what you want to mock :
restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
but this will require deep mocking and that is also a bad smell : you should not need to define such a statement to invoke the rest template. This responsibility should be defined in a specific class.
So move it into a method of a specific bean for example RestTemplateService that will relieve you from passing as many as parameters and it will also balance better responsibility of this class by reducing its dependencies :
ResponseEntity<String> healthResponse = restTemplateService.getForHealthCheck();
Now just mock it with Mockito.
Concerning RestTemplateService, you could create your own class or rely on Feign (here Spring Feign makes more sense) that enables declarative rest client via interface.
It would give :
public class HealthStatusImpl implements HealthStatus {
private RestTemplateService restTemplateService;
// favor constructor injection
public HealthStatusImpl(RestTemplateService restTemplateService){
this.restTemplateService = restTemplateService;
}
#Override
public Integer healthCheck() {
Integer status = 0;
try {
ResponseEntity<String> healthResponse = restTemplateService.getForHealthCheck();
status = healthResponse.getStatusCode().is2xxSuccessful() ? 200 : 400;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}
}
Note that Status.is2xxSuccessful() is generally better as it returns true for any successful response (200, 201, etc..). And if it is not successful you want to return an error response code.
From the unit test side, you should mock this dependency and record a mock behavior according to your scenarios.
Note that in your case, you don't want to load a whole spring context but you want to perform a plain unit test, that is without container. So don't use #SpringBootTest but only JUnit and Mockito.
For example with JUnit 5 :
#ExtendWith(MockitoExtension.class)
public class HealthStatusImplTest{
private HealthStatusImpl healthStatusImpl;
#Mock
private RestTemplateService restTemplateServiceMock;
#BeforeEach
public void beforeEach(){
healthStatusImpl = new HealthStatusImpl(restTemplateService);
}
#Test
public void healthCheck_when_200_is_returned(){
Mockito.when(restTemplateServiceMock)
.getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.OK));
assertEquals(200, healthStatusImpl.healthCheck());
}
#Test
public void healthCheck_when_200_is_not_returned(){
Mockito.when(restTemplateServiceMock)
.getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.NOT_FOUND));
assertEquals(400, healthStatusImpl.healthCheck());
}
}
Of course the RestTemplateService should also be unitary test and unitary tests don't relieve from writing integration tests for higher level components.
To unit test a web client such as a RestTemplate based class, you need a framework which mocks the server, e.g.
the one from Spring: MockRestServiceServer:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-client
https://www.baeldung.com/spring-mock-rest-template
WireMock: http://wiremock.org/docs/getting-started/
I'm not sure that your check (healthResponse.getStatusCode() == HttpStatus.OK) is correct, because if a status is not 2xx RestTemplate throws HttpStatusCodeException.
That's why it's always better to have some mocks if you integrate with a third party side.
That's why I also recommend you to consider MockRestServiceServer in your tests. see #Puce answer for links.
Also, there is no necessity to create a new RestTemplate for each request.
That's why I also recommend you to consider constructor injection. see #davidxxx answer for refactoring approaches. And don't forget to put connection timeouts in your RestTemplate settings.

spring-mvc: how to test Rx responses with mockMvc?

my controller is:
import rx.Single;
...
#GetMapping
Single<List<MyType>> fetchFromDB() {
return Single
.fromCallable(() -> dao.fetch())
.subscribeOn(Schedulers.io());
}
and it works perfectly. but i can't tests it. i tried:
MvcResult asyncResult = mvc.perform(get("/")).andReturn()
String result = mvc
.perform(asyncDispatch(asyncResult))
.andReturn().getResponse().getContentAsString()
but it fails with:
java.lang.IllegalStateException: Async result for handler [rx.Single<java.util.List<MyType>> MyController.fetchFromDB()] was not set during the specified timeToWait=-1
at org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:145)
at org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:121)
at org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch(MockMvcRequestBuilders.java:246)
at MyControllerSpec.should fetch from db...
so: how to test rx.Single with spring mvc?
i found the answer. when you create your mockMvc object, add a handler for Single:
return MockMvcBuilders.standaloneSetup(controller)
.setCustomReturnValueHandlers(new SingleReturnValueHandler())
if you use MockMvcBuilders.webAppContextSetup(context) instead, you can add the handler in your WebMvcConfigurerAdapter:
public class Config extends WebMvcConfigurerAdapter {
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new SingleReturnValueHandler());
}
}

Categories

Resources