I have 2 spring-boot-starter-web services. Service A sends a request via Retrofit to service B. I have configured it to timeout after 10 seconds. Service A detects the timeout (SocketTimeoutException), but I have no way for service B to detect it. How can I verify that the socket is closed? I send a file via outputstream of httpServletResponse and it does not detect that it is closed. It looks like it sends the file to service A, when service A has already timed out.
try (FileInputStream in = new FileInputStream(file)){
OutputStream out = httpServletResponse.getOutputStream();
IOUtils.copy(in,out); // copy from in to out
out.close();
} catch (IOException e) {
// In AWS I get a "broken pipe" IOException. But, locally, I don't get any exception.
}
I don't know if there is a way to check if a Socket is closed until the server tries to read/write from it. I workaround is handle this IOException's like this:
#ExceptionHandler(IOException.class)
#ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) //(1)
public Object exceptionHandler(IOException e, HttpServletRequest request) {
if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(e), "Broken pipe")) { //(2)
return null; //(2) socket is closed, cannot return any response
} else {
return new HttpEntity<>(e.getMessage()); //(3)
}
}
This blog post Spring - How to handle "IOException: Broken pipe" can help.
Related
I am writing two services that do some calculations. One of them must only send a lot of requests to second service without waiting for a response:
...
for (String url: oneService.getUrlList()) {
try {
sendRequests(restTemplate, url);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
...
#Async
void sendRequests(RestTemplate restTemplate, String url) throws InterruptedException {
restTemplate.postForLocation("http://second-servive/calculation/" + url, String.class);
}
I have in controller of second service:
#RequestMapping(value = "calculation/{url}", method = RequestMethod.POST)
public void getShortCircuitCalculation(#PathVariable String url) {
secondService.getCalculation(url);
}
I created many instances of the second service with eureka server to make calculations faster. But with #Async annotation and instances of second service it takes a lot of time, just like with a single instance. The first service is still waiting for responses from the second service. I tried to send responses in separate threads, but got the same.
Are there any ways to not wait for the response from the second service and continue sending responses so instances make calculations?
I'm currently running a spring boot application.
I am putting this webpage live for multiple people to use. However, this is the first time launching it and I'm unsure if I've worked out all the bugs so I'm trying to find a way to alert myself when something happens.
I have created a custom error controller in Spring. I want this to display a custom error page that just simply tells the user that something is wrong and that we've been made aware of it. This part is working already.
Also with that, I want it to send me an email with the stack trace information. I would love the same page that the default error controller shows, to be sent to me via email. I'm using AWS SES.
Here's my code sample.
#GetMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
if(statusCode == HttpStatus.NOT_FOUND.value()) {
return "404";
}
}
sesSender.sendErrorEmail("strack trace error");
return "error";
}
I found the following question provided 5 years ago Spring Boot Custom Error Page Stack Trace
I'm hoping that since then, they've allowed this functionality.
If you are using Spring Boot you can use this bean and this method ExceptionUtils.getStackTrace(). You will need to import the commons.lang dependency. The method from ExceptionUtils will get you the stack trace you are looking for to send to your email. But this will only work for Serverside Errors. If you want emails sent with a stack trace for front end errors you will need to create your own Dispatcher Servlet and handle it in the DoService Filter
#Bean
HandlerExceptionResolver errorHandler() {
return (request, response, handler, ex) -> {
Logger logger = LoggerFactory.getLogger(DispatcherServlet.class);
ModelAndView model = new ModelAndView("error/error");
model.addObject("exceptionType", ex);
model.addObject("handlerMethod", handler);
logger.error(ExceptionUtils.getMessage(ex));
System.out.println("" +
"\n" + ExceptionUtils.getStackTrace(ex));
try {
Utility.sendStackTraceToDeveloper(ex, request, javaMailSender);
} catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return model;
};
}
I am using Spring Rest template along with apache's PoolingHttpClientConnectionManager for making API calls. The project in which I am working on requires setting custom timeout for each of the HTTP request I make via rest template. In order to achieve this, I am using CompletableFuture with a separate ExecutorService and calling get(Timeout) method.
try{
CompletableFuture<BidResponse> future = CompletableFuture.supplyAsync(() -> bidderService.getBid(), executorService);
bidResponse = future.get(bidderTimeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException | TimeoutException | ExecutionException e) {
bidResponse = getTimeoutBidResponse();
}
Unfortunately, the problem with this approach is that in cases of timeout, the underlying thread keeps on working until the rest template finishes its call. So I am kind of losing out a thread from the thread pool, as well as a connection from the HTTP connection pool.
Is there a way to close the HTTP connection as soon as we receive a Timeout exception, and return the HTTP connection back to the pool ?
p.s. I also tried using Spring Webclient with Mono.timeout. Turns out it actually closes the HTTP connection immediately, but does not return it back to the HTTP pool.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
{
return restTemplateBuilder
.setConnectTimeout(...)
.setReadTimeout(...)
.build();
}
I'm trying to write a custom response using HttpServletResponse.getOutputStream() and HttpServletResponse.setStatus(int).
But anything that is an status different from 200 doesn't consideres the response body that I wrote.
I have 2 web applications running on different ports, the application "A" must request data from application "B". For this I created a controller to tunnel all requests on application "A" to application "B".
Example:
#RequestMapping("/tunnel/**")
public void exchange(HttpServletRequest request, HttpServletResponse response) throws IOException {
// my service tunnel the request to another server
// and the response of the server must be replied
ResponseDescriptor tunnelResponse = tunnelService.request(request);
response.setStatus(tunnelResponse.getStatus());
// if the status was different them 200, the next line will not work
response.getOutputStream().write(tunnelResponse.getResponseBodyAsByteArray());
}
Note, I need to response from application A the exact response that come from application B.
You need to catch HttpStatusCodeException to get responses other than 200.
try {
ResponseDescriptor tunnelResponse = tunnelService.request(request);
response.setStatus(tunnelResponse.getStatus());
response.getOutputStream().write(tunnelResponse.getResponseBodyAsByteArray());
} catch (HttpStatusCodeException e) {
response.setStatus(e.getStatusCode().value());
response.getOutputStream().write(e.getResponseBodyAsByteArray());
}
Solved!
I created a #ExceptionHandler and a custom exception TunnelException extends RuntimeException.
So, on my exchange(...) method, I catch RestClientResponseException and throw my own exception encapsulating (in the exception) the Headers, HttpStatus and the ResponseBody byte array.
This is the exception handler:
#ExceptionHandler(TunnelException.class)
public ResponseEntity<?> handleTunnelException(TunnelException ex) throws IOException {
return ResponseEntity
.status(ex.getStatus())
.contentType(ex.getHeaders().getContentType())
.body(ex.getBody());
}
I've read the Jersey documentation, and it says Jersey automatically closes a connection after an entity is read (e.g. response.readEntity(SomeObject.class))
But when an exception is thrown, either a bad request or a socket timeout, does Jersey automatically close the connection, or should I have a finally clause that calls client.close()?
No. Neither does Jersey call client.close() in case of an exception nor does the JerseyClient implement AutoCloseable.
You can easily test this. A client throws a IllegalStateException if you invoke a method after closing:
Client client = ClientBuilder.newClient();
client.close();
client.target("http://stackoverflow.com").request().get(); // IllegalStateException
But you can invoke a method after catching an exception:
Client client = ClientBuilder.newClient();
try {
client.target("http://foo.bar").request().get(); // java.net.ConnectException: Operation timed out
} catch (Exception ex) {
client.target("http://stackoverflow.com").request().get(); // works
}
So closing is your job.
Update: JAX-RS 2.1 will use AutoClosables.