Cancel rest request if response takes too long - java

I would like to cancel a REST request, if the response takes longer than 3 seconds, but I haven't managed to figure out how to do it.
So let's say I have a thread A:
#Override
public void run() {
RestTemplate restTemplate = new RestTemplate();
IsAliveMessage isAliveMessage = new IsAliveMessage(nodeInfo.getHostname(), nodeInfo.getPort());
IsAliveResponse isAliveResponse = restTemplate.postForObject(
"http://" + nodeInfo.getHostname() + ":" + nodeInfo.getPort() + "/node/isAlive",
isAliveMessage,
IsAliveResponse.class);
}
that makes a request and expects an answer from B:
#RequestMapping( value="/isAlive", method= RequestMethod.POST)
public IsAliveResponse isAlive() throws ConnectException {
try {
Thread.sleep(100000);
IsAliveResponse response = new IsAliveResponse("here here!" ,true);
return response;
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
B "sleeps" and doesn't answer, but A keeps waiting for that answer to come. How can I make A give up the waiting after a certain time span?
Thanks in advance

You can configure your RestTemplate to wait three seconds for response like this:
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
private ClientHttpRequestFactory getClientHttpRequestFactory() {
int timeout = 3000;
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(timeout);
clientHttpRequestFactory.setReadTimeout(timeout);
return clientHttpRequestFactory;
}

Related

How to set a timeout with a default soap response in soap producer?

I know that timeout is a property of client , but we need to send a response in 2 minutes from spring soap endpoint.
How to timeout in spring soap and send a default response within a specified time from soap producer app?
Container : Tomcat
#Endpoint
public class SOAPEndpoint {
private static final String NAMESPACE_URI = "http://spring.io/guides/gs-producing-web-service";
private Repository repository;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getData")
#ResponsePayload
public Response getCountry(#RequestPayload SampleRequest request) {
Response response = new Response();
response.setCountry(repository.retrieveData(request.getParam())); // this lines takes 5 minutes to respond
return response;
}
}
I wasn't able to find a configuration-based solution, but here are possible library-based solutions:
Some DBs lets you to set a query timeout, so if it's available for you, it seems like a good approach. If you will specify the DB you use, I will dig into it.
You can use the TimeLimiter of resilience4j:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
#ResponsePayload
public GetCountryResponse getCountry(#RequestPayload GetCountryRequest request) {
GetCountryResponse response = new GetCountryResponse();
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1));
try {
Country country = timeLimiter.executeFutureSupplier(() ->
CompletableFuture.supplyAsync(() -> countryRepository.findCountry(request.getName())));
response.setCountry(country);
return response;
} catch (TimeoutException e) {
e.printStackTrace(); // handle timeout.
} catch (Exception e) {
e.printStackTrace(); // handle general error.
}
return null; // You may want to replace this.
}
The Producer code above is originated from - https://spring.io/guides/gs/producing-web-service/
and was tested against the Consumer - https://spring.io/guides/gs/consuming-web-service/

SpringBoot how to Send response to other URL

I have the following code:
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
public ResponseEntity<?> api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
String response="{SOME_JSON}";
URI callbackURL = new URI("http://otherAPIEnv/api2");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(callbackURL);
return new ResponseEntity<String>(response,httpHeaders, HttpStatus.OK);
}
I tried the above code, but when I hit the api1 through my curl I get the response on the same machine, but I want the response to be redirected to api2 at otherAPIEnv machine.
Could someone please suggest how to achieve this kind of request and response?
When you send a request to a URL it should respond to the same otherwise client will be in waiting for it until it times out.
So, the approach should be different in this scenario.
First, in your main rest API you have to send a response code to release the client.
Then, in the API method you have to call another method asynchronously which calls api2 and performs the desired operation.
Here is a simple example.
#Autowired
API2Caller api2Caller;
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
#ResponseStatus(HttpStatus.ACCEPTED)
public void api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
api2Caller.callApi2(requestBody);
}
and the APICaller should look like following
#Component
public class API2Caller {
#Async
public SomeResultPojo callApi2() {
// use RestTemplate to call the api2
return restTemplate.postForObject("http://otherAPIEnv/api2", request, SomeResultPojo.class);
}
}
But you can choose your most comfortable way to perform asynchronous operation.
Look like a job for redirect.
String redirectMe() {
return "redirect:http://otherAPIEnv/api2"
}
As for the curl. You have POST mapping of the method so be sure to try it with curl -X POST... or change it to GET.
This the more modular and more generic way to do such kind of things:
public #ResponseBody ClientResponse updateDocStatus(MyRequest myRequest) {
ClientResponse clientResponse = new ClientResponse(CTConstants.FAILURE);
try {
HttpHeaders headers = prepareHeaders();
ClientRequest request = prepareRequestData(myRequest);
logger.info("cpa request is " + new Gson().toJson(request));
HttpEntity<ClientRequest> entity = new HttpEntity<ClientRequest>(request, headers);
String uri = cpaBaseUrl + updateDocUrl ;
ClientResponse serviceResponse = Utilities.sendHTTPRequest(uri, entity);
clientResponse = serviceResponse;
if (serviceResponse != null) {
if (CTConstants.SUCCESS.equalsIgnoreCase(serviceResponse.getStatus())) {
clientResponse.setStatus(CTConstants.SUCCESS);
clientResponse.setMessage(" update success.");
}
}
} catch (Exception e) {
logger.error("exception occurred ", e);
clientResponse.setStatus(CTConstants.ERROR);
clientResponse.setMessage(e.getMessage());
}
return clientResponse;
}
public static ClientResponse sendHTTPRequest(String uri, HttpEntity<ClientRequest> entity) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(CTConstants.SERVICE_TIMEOUT);
rf.setConnectTimeout(CTConstants.SERVICE_TIMEOUT);
ParameterizedTypeReference<ClientResponse> ptr = new ParameterizedTypeReference<ClientResponse>() {
};
ResponseEntity<ClientResponse> postForObject = restTemplate.exchange(uri, HttpMethod.POST, entity, ptr);
return postForObject.getBody();
}
You need to use redirect and modify the return type of your method
public String api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException {
return "redirect:http://otherAPIEnv/api2";
}

Spring RestTemplate isn't mapping a 401 to a HttpClientErrorException

Running Spring 4.2.6.RELEASE, we are experiencing unexpected behavior with fairly typical RestTemplate usage; a 401 error is not being translated to a HttpClientErrorException.
Specifically, when we receive an expected 401 from the server, we receive a ResourceAccessException wrapping an IOException with the message Server returned HTTP response code: 401 for URL: ..., which is raised deep within the bowels of sun.net.www.protocol.http.HttpURLConnection.
Our template is configured more or less like so:
public RestTemplate restTemplate() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
// connections per route is not a meaningful limit for us, so set very high
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(10000);
poolingHttpClientConnectionManager.setMaxTotal(100);
CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
Our usage of the client looks like:
protected <T, U> ResponseEntity<T> request(UserClientAccount account, U body, String url, HttpMethod httpMethod,
ParameterizedTypeReference<T> responseType, boolean retry) {
try {
HttpEntity<U> request = createHttpEntity(body, account.getToken(this));
return getRestTemplate().exchange(url, httpMethod, request, responseType, new HashMap<String, String>());
}
catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
if (retry) {
log.warn("Unauthorized response for {}. Refreshing token to retry...", url);
refreshToken(account);
// tries again
return request(account, null, url, httpMethod, responseType, false);
}
else {
log.error("Unauthorized error calling {}. All attempts to retry exhausted ", url);
throw e;
}
}
throw new ProgramException("Error while performing " + httpMethod + " request to " + url + ". " +
"Response body: " + e.getResponseBodyAsString(), e);
}
}
Our catch of HttpClientErrorException is never hit; instead, we receive a ResourceAccessException with a cause of the above mentioned IOException.
What are we doing wrong?

How to use RestTemplate efficiently with RequestFactory?

I am working on a project in which I need to make a HTTP URL call to my server which is running Restful Service which returns back the response as a JSON String. I am using RestTemplate here along with HttpComponentsClientHttpRequestFactory to execute an url.
I have setup a http request timeout (READ and CONNECTION time out) on my RestTemplate by using HttpComponentsClientHttpRequestFactory.
Below is my Interface:
public interface Client {
// for synchronous
public String getSyncData(String key, long timeout);
// for asynchronous
public String getAsyncData(String key, long timeout);
}
Below is my implementation of Client interface -
public class DataClient implements Client {
private final RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public String getSyncData(String key, long timeout) {
String response = null;
try {
Task task = new Task(key, restTemplate, timeout);
// direct call, implementing sync call as async + waiting is bad idea.
// It is meaningless and consumes one thread from the thread pool per a call.
response = task.call();
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return response;
}
// for asynchronous call
#Override
public Future<String> getAsyncData(String key, long timeout) {
Future<String> future = null;
try {
Task task = new Task(key, restTemplate, timeout);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
And below is my simple Task class
class Task implements Callable<String> {
private RestTemplate restTemplate;
private String key;
private long timeout; // in milliseconds
public Task(String key, RestTemplate restTemplate, long timeout) {
this.key = key;
this.restTemplate = restTemplate;
this.timeout = timeout;
}
public String call() throws Exception {
String url = "some_url_created_by_using_key";
// does this looks right the way I am setting request factory?
// or is there any other effficient way to do this?
restTemplate.setRequestFactory(clientHttpRequestFactory());
String response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
return response;
}
private static ClientHttpRequestFactory clientHttpRequestFactory() {
// is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime?
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeout); // setting timeout as read timeout
factory.setConnectTimeout(timeout); // setting timeout as connect timeout
return factory;
}
}
Now my question is - Does the way I am using RestTemplate along with setRequestFactory in the call method of Task class everytime is efficient? Since RestTemplate is very heavy to be created so not sure whether I got it right.
And is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime? Will it be expensive?
What is the right and efficient way to use RestTemplate if we need to setup Read and Connection timeout on it.
This library will be used like this -
String response = DataClientFactory.getInstance().getSyncData(keyData, 100);
From what I can tell, you're reusing the same RestTemplate object repeatedly, but each Task is performing this line: restTemplate.setRequestFactory(clientHttpRequestFactory());. This seems like it can have race conditions, e.g. one Task can set the RequestFactory that another Task will then accidentally use.
Otherwise, it seems like you're using RestTemplate correctly.
How often do your timeouts change? If you mostly use one or two timeouts, you can create one or two RestTemplates using the RequestFactory constructor with the pre-loaded timeout. If you're a stickler for efficiency, create a HashMap<Integer, RestTemplate> that caches a RestTemplate with a particular timeout each time a new timeout is requested.
Otherwise, looking at the code for RestTemplate's constructor, and for HttpComponentsClientHttpRequestFactory's constructor, they don't look exceptionally heavy, so calling them repeatedly probably won't be much of a bottleneck.

SimpleClientHttpRequestFactory vs HttpComponentsClientHttpRequestFactory for Http Request timeout with RestTemplate?

I am working on a project in which I need to make a HTTP URL call to my server which is running Restful Service which returns back the response as a JSON String.
Below is my main code which is using the future and callables:
public class TimeoutThreadExample {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
public String getData() {
Future<String> future = executor.submit(new Task(restTemplate));
String response = null;
try {
response = future.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return response;
}
}
Below is my Task class which implements the Callable interface and uses the RestTemplate:
class Task implements Callable<String> {
private RestTemplate restTemplate;
public Task(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String call() throws Exception {
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
return response;
}
}
Problem Statement:
As you can see above, I am using default way of executing the URL using RestTemplate which doesn't use any Http Request timeout so that means internally it is using -1 as the read and connection timeout.
Now what I am looking to do is, I want to set up Http Request timeout using RestTemplate in my above code efficiently. And I am not sure which class I need to use for that, I can see HttpComponentsClientHttpRequestFactory and SimpleClientHttpRequestFactory so not sure which one I need to use?
Any simple example basis on my above code will help me understand better on how to set the Http Request timeout using RestTemplate.
And also does my Http Request timeout value should be less than future timeout value?
HttpComponentsClientHttpRequestFactory vs SimpleClientHttpRequestFactory. Which one to use?
Does my Http Request timeout value should be less than future timeout value?
By default RestTemplate uses SimpleClientHttpRequestFactory which depends on default configuration of HttpURLConnection.
You can configure them by using below attributes:
-Dsun.net.client.defaultConnectTimeout=TimeoutInMiliSec
-Dsun.net.client.defaultReadTimeout=TimeoutInMiliSec
If you want to use HttpComponentsClientHttpRequestFactory - it has a connection pooling configuration which SimpleClientHttpRequestFactory does not have.
A sample code for using HttpComponentsClientHttpRequestFactory:
public class TimeoutThreadExample {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private static final RestTemplate restTemplate = createRestTemplate();
private static RestTemplate createRestTemplate(){
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setReadTimeout(READ_TIME_OUT);
requestFactory.setConnectTimeout(CONNECTION_TIME_OUT);
return new RestTemplate(requestFactory);
}
public String getData() {
Future<String> future = executor.submit(new Task(restTemplate));
String response = null;
try {
response = future.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return response;
}
}
SimpleClientHttpRequestFactory uses the standard JDK's HTTP library, and hence does not support methods like HttpMethod.PATCH. So it's better to use HttpComponentsClientHttpRequestFactory now than change it later when you have to.

Categories

Resources