Call Rest API by Spring RestTemplate within a #Async method - spring

As I know, Spring RestTemplate is synchronous and blocks the thread until the web client receives the response, and Spring WebClient is asynchronous and non-blocking.
But what if we call an API using RestTemplate within an #Async annotated method?
Does it block the new thread created by #Async?
Finally, what's your suggestion for the async call of Rest APIs (without WebClient because I'm using Spring version older than 5).
Thanks

what if we call an API using RestTemplate within an #Async annotated method?
The method will run asynchronously on the executor you specify in the #Async annotation parameter. e.g. #Async("threadPool") where "threadPool" is the name of the Executor bean.
Does it block the new thread created by #Async?
Yes, it will block the thread on which Spring runs your method. However, the thread won't necessarily be created by Spring, it can be taken from the thread pool you define in the #Async annotation.
What's your suggestion for the async call of Rest APIs (without WebClient because I'm using Spring version older than 5)?
You can use CompletableFuture API or #Async if you just need the "async" effect. But if you also need the "non-blocking" property you need to use some non-blocking HTTP client, for instance, okhttp.
The non-blocking async HTTP call with okhttp will look as follows:
public CompletableFuture<Response> call() {
Request request = new Request.Builder()
.url(URL)
.build();
Call call = client.newCall(request);
CompletableFuture<Response> result = new CompletableFuture<>();
call.enqueue(new Callback() {
public void onResponse(Call call, Response response) throws IOException {
result.complete(response);
}
public void onFailure(Call call, IOException e) {
result.completeExceptionally(e);
}
});
return result;
}

Related

MissingServletRequestParameterException intermittently being thrown even though request parameter is provided

I've got a Spring Boot 2.7.3 app with the following controller defined:
#RestController
#EnableAutoConfiguration
public class TrainController {
#CrossOrigin(origins = "http://localhost:3000")
#RequestMapping(value = "/trains/history", method = RequestMethod.GET)
public List<TrainStatus> getTrainStatusesForTimestamp(
#RequestParam long timestamp
) {
// do stuff
}
}
Invoking this API endpoint typically works just fine, certainly when I'm running the app locally, but in production under heavier load, e.g. repeated calls to this API endpoint in parallel with lots of calls to other API endpoints defined by my app across multiple controllers, I start to see messages like these in my logs:
2022-09-06 20:48:37.939 DEBUG 19282 --- [https-openssl-nio-443-exec-10] o.s.w.f.CommonsRequestLoggingFilter : Before request [GET /trains/history?timestamp=1662511707]
2022-09-06 20:48:37.945 WARN 19282 --- [https-openssl-nio-443-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'timestamp' for method parameter type long is not present]
2022-09-06 20:48:37.945 DEBUG 19282 --- [https-openssl-nio-443-exec-10] o.s.w.f.CommonsRequestLoggingFilter : After request [GET /trains/history?timestamp=1662511707]
(The CommonsRequestLoggingFilter DEBUG log lines are coming from a bean I've defined in accordance with this doc; I was curious if the required timestamp parameter was actually being defined or not, which is why I added it.)
Furthermore, when these errant MissingServletRequestParameterException exceptions are thrown, the response is a 400 Bad Request. I've confirmed from the client side of things that timestamp is indeed being included as a request parameter, and the Spring Boot app logs seem to confirm this, yet I'm intermittently seeing these exceptions under heavy load.
What's going on? Am I hitting some kind of connection or thread limit defined by Tomcat or something? As far as I can tell the app has plenty of additional headroom with regards to memory.
Thanks in advance for any help anyone can provide!
For reference, here are some apparently similar issues I've found:
Is there any situation QueryString is present but HttpServletRequest.getParameterMap() is empty?
After reading this blog post, I believe I've just figured out what's going on: I've got another filter PublicApiFilter operating on a separate set of API endpoints that is asynchronously invoking a function where I pass the request object, i.e. the instance of HttpServletRequest, into it and invoke various methods offered by it. These asynchronous operations on these requests appear to be affecting subsequent requests, even ones to other API endpoints not covered by PublicApiFilter. I was able to simply make the invocation of this function synchronous instead of asynchronous by removing the #Async annotation I was using and now the issue appears to have been resolved!
Here are some snippets of my code in case it's useful to someone else someday:
#EnableScheduling
#SpringBootApplication // same as #Configuration #EnableAutoConfiguration #ComponentScan
#EnableAsync
public class Application implements WebMvcConfigurer, AsyncConfigurer {
// ...
#Override // AsyncConfigurer
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(1);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
#Override // AsyncConfigurer
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
#Component
public class PublicApiFilter extends GenericFilterBean {
private final PublicApiService publicApiService;
#Autowired
public PublicApiFilter(PublicApiService publicApiService) {
this.publicApiService = publicApiService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// ...
chain.doFilter(request, response);
this.publicApiService.logRequest(httpRequest);
}
}
#Service
public class PublicApiService {
// ...
#Async // <- simply removing this annotation appears to have done the trick!
public void logRequest(HttpServletRequest request) {
// invoke request.getRequestURI(), request.getHeader(...), request.getRemoteAddr, and request.getParameterMap() for logging purposes
}
}
Do not pass HttpServletRequest into any async method!
Must reads for solving above problem:
Never pass a request to an asynchronous thread! There are pits!
How to correctly use request in asynchronous threads in springboot
Occasional MissingServletRequestParameterException, who moved my parameters?

Why AsyncRestTemplate in a spring doesn't has Interceptors like RestTemplate?

I have one situation where I need to intercept the request and I need to set authorization header into that request.
So I got the solution that I can use interceptors to set that header but when I check AsyncRestTemplate then It doesn't have that property like RestTemplate.
Is there any specific reason to not include that property?
The AsyncRestTemplate extends the InterceptingAsyncHttpAccessor abstract class, which exposes the method setInterceptors. So of course you can set Interceptors, just like you would do with the non async RestTemplate. Note that your interceptor needs to implement the AsyncClientHttpRequestInterceptor instead:
public class AsyncFooBarInterceptor implements AsyncClientHttpRequestInterceptor {
#Override
public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException {
return null; // do your thing instead
}
}
Then use it:
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
asyncRestTemplate.setInterceptors(Collections.singletonList(new AsyncFooBarInterceptor()));

Spring Cloud sleuth breaking DelegatingSecurityContextExecutor

I've a spring boot app version 1.3.3.RELEASE and I've added Spring Cloud Sleuth get those tracing goodies. After adding spring-cloud-sleuth I see some of my async API's are breaking as passed in org.springframework.security.concurrent.DelegatingSecurityContextExecutor. is null. Short story, I'm using org.springframework.security.concurrent.DelegatingSecurityContextExecutor from spring security to pass security context around different executors. For that I'm just wrapping those executors into org.springframework.security.concurrent.DelegatingSecurityContextExecutor instances during application start up. What I'm not sure is why Spring Sleuth is becoming a problem here. The way we're using async processing is, we have created a custom annotation and then created a point cut's around that to inject async behavior.
Example:
public class Async implements AsyncConfigurer {
public Executor getCustomAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//all configuration for thread pool
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
public Executor getProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//all configuration for thread pool
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
}
public class MyControllerClass {
#Autowired
private Executor myProcessingExecutor;
#MyCustomAsyncAnnotation
public ResponseObject getResponse() {
//As soon as below code get's executed I get NPE as
//`org.springframework.security.concurrent.DelegatingSecurityContextExecutor.executor` is null. What I'm not sure is why it's null??
// As you see above in my Async class I've registered executor in DelegatingSecurityContextAsyncTaskExecutor, then why it's null here?
//If I remove spring-cloud-sleuth code works fine once again.
CompletableFuture.supplyAsync(() -> ..., myProcessingExecutor);
}
}
class MyCustomAspect {
private Executor myCustomAsyncExecutor;
//create point cut here for those method which have
// #MyCustomAsyncAnnotionation and process it async via above executor.
}
I'm using spring boot 1.3.3.RELEASE, rest easy, spring-security 4.0.3.RELEASE, spring-cloud-sleuth 1.0.12.RELEASE

How to expose both a SOAP web-service and RESTful API at the same time in Spring Boot?

In Spring Boot 1.4.3 I exposed a SOAP web service endpoint which works successfully on port 8080.
For the purpose of running a health check I also need to expose a RESTful API. I tried both using Actuator and a rest controller:
#RestController
public class RESTapis {
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, value = "/health")
public String healthCheck() {
return "ACK";
}
}
but in both cases I get the same response: HTTP 405 (method not allowed).
The REST api returns HTTP 200 if I disable the web-service.
How can I have both the SOAP web-service and REST working at the same time?
Here is the web-service configuration:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/*");
}
}
So going off the messageDispatcherServlet method it looks like you are binding your servlet to all the incoming request due to the wildcard registration:
return new ServletRegistrationBean(servlet, "/*");
Hence the MessageDispatcher is intercepting all of your incoming requests and trying to find the /health and throwing http 405.
Fix:
return new ServletRegistrationBean(servlet, "/soap-api/*");
Explanation:
By binding the Message Dispatcher to a specific URI namespace we can ensure that all the request fired on the /soap-api/* namespace ONLY will be intercepted by the MessageDispatcher. And all the other requests will be intercepted by the DispatcherServlet making it possible to run a Rest interface in parallel to your Soap WS.
Suggestion:
Not knowing the reasons / specifics of the app, but going off the name of the method healthcheck(), you can look at using spring boot actuator to generate health checks, metrics for you app. You can also override it for customisations.
Reference for actuator: https://spring.io/guides/gs/actuator-service/

Design: Background thread with a Spring service

I have Spring service which after performing some tasks,initiates a background Async tasks. The task itself I have defined as a component.
Now, If I have to use some method belonging to my the service which initiated the thread. I can Autowire the service in the thread(Component) and it will work. Problem is...The design. It brings in kind of circular dependency?
How can we tackle such issue?
ServiceA-> Starts a ThreadA component->ThreadA Needs to call a method in the ServiceA.
What about using an #Async annotated method as described here
A non tested example:
#Service
public class MyService {
private final RestTemplate restTemplate;
public String mySyncMethod(){
return "Hello World";
}
#Async
public Future<String> myAsyncMethod() throws InterruptedException {
return new AsyncResult<>(mySyncMethod());
}
}

Resources