I have a scenario in my springboot application, where I submit tasks into a threadpool for async execution.Now some of the methods inside child execution is part of aspect point advice with #AfterReturn.
I observe that even if processing is done asnyc, my main thread keeps executing the point cut advice from child thread and my service does not return a value until, all child thread finished execution.
Any pointer how to make the advice run on the executing thread itself?
So in short, controller method does not return response until dao method execution and its corresponding point cut is executed.
#Controller
#RequestMapping(value = "/api")
public class SampleController {
#Autowired
SampleService service;
#RequestMapping(value = "/action", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public String action(#RequestBody String request){
service.action(request);
return "Success";
}
}
#Service
public class SampleService{
#Autowired
SampleDao dao;
#Async("threadPoolExecutor")
public void action(String request){
dao.action(request);
}
}
#Repository
public class SampleDao{
public void action(String request){
//do some db things
}
}
#Aspect
#Component
public class SampleAspect{
#AfterReturning(
pointcut = "execution( * com.sample.*.*.SampleDao.action(..))",
returning = "result")
public void audit(JoinPoint joinPoint, Object result) {
//dosome thing
}
}
The #Async on the service method does not mean that it will be submitted to the executor service and then return immediately, but that you can can have several incoming calls to the endpoint which will then be handled concurrently (this is the case per default anyway afaik, #Async is pretty much a marker only).
You can read this guide to see how this can be done properly.
The gist is that your service needs to create (and optionally return) some sort of Future (in the case of the post, CompletableFuture, as in
#Async
void serviceMethod(String request) {
CompletableFuture.submit(() -> dao.action(request));
}
It sounds like you do want to wait for the result to arrive though, so while this will work, I expect you'll run into problems later.
Related
I have to build a Spring Batch system. In my project I have to call, inside a batch scheduled, an Api method defined in Controller class of my project.
This is an example of controller
#RestController
public class MyController implements ExampleApi {
// other methods
#Override
public ResponseEntity<ExampleResponse> method(String object){
final ExampleResponse response = // execution
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
I call the method above in my CustomProcessor. This is an example of Batch processor
public class CustomProcessor implements ItemProcessor<List<String>, List<String>> {
#Autowired
private ExampleApi exampleApi;
#Override
public List<String> process(#NonNull List<String> objects) throws Exception {
objects.forEach(object -> exampleApi.method(object));
return objects;
}
}
When batch starts running I can call the api, but when I call inside another api in another class annotated like this:
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ObjectFactoryImpl implements ObjectFactory {
#Autowired
#Qualifier("anotherService")
private ObjectProvider<AnotherApi> anotherApiObjectProvider;
public Object find(Long objectId) {
Object object;
try {
object = anotherApiObjectProvider.getObject().getObjectById(objectId).getObject();
} catch (Exception e) {
throw new Exception....
}
}
I get this exception:
IllegalStateException: No thread-bound request found: Are you
referring to request attributes outside of an actual web request, or
processing a request outside of the originally receiving thread? If
you are actually operating within a web request and still receive this
message, your code is probably running outside of DispatcherServlet:
In this case, use RequestContextListener or RequestContextFilter to
expose the current request.
I'm quite new to Spring Batch.
Hi I am new to multithreading in java. Can someone please help me with this:
My service:
#Async
public List<String> doSomething(int a){
//Do something
return list;
}
SpringbootApplication:
#SpringBootApplication
#EnableAsync
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
Async config:
#Configuration
#EnableAsync
public class AsyncConfig {
#Bean(name ="taskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("userThread-");
executor.initialize();
return executor;
}
}
Controller:
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public List<String> getAll(){
return service.doSomething(1);
}
}
When I hit this get request from postman it is showing up blank in the response. I understand that my call is going asynchronously and the response is coming back even before the my method is called. Is there any way to see this response by changing some settings in either my postman or spring boot application
If you want to process the request asynchronously but also want the API client to receive the response after it finishes processing such that from the client 's point of view , the request processing still looks like synchronous , the key is to use the Servlet3 asynchronous processing feature.
You do not need to configure it to execute asynchronously in the service level using #Aysnc. Instead configure the controller method to return CompletableFuture. Under the cover , it will trigger Servlet3 's asynchronous request processing which will process the request in another thread besides the HTTP thread that receive the request.
So your codes should look something like:
public class Service {
//No need to add #Async
public List<String> doSomething(int a){
return list;
}
}
#RestController
public class Controller{
#Autowired
private Service service;
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return CompletableFuture.supplyAsync(()->service.doSomething(1));
}
}
For details about the Servlet3 asynchronous request processing supported in spring-mvc , you can refer to this blog series start from this .
You can return CompletableFuture. You will receive http response when CompleteableFuture will be completed.
Service:
#Async
public CompletableFuture<List<String>> doSomething() {
return CompletableFuture.completedFuture(Arrays.asList("1", "2", "3"));
}
Controller:
#GetMapping("test")
public CompletableFuture<List<String>> getAll(){
return service.getAll();
}
If you want to use async I would split your single request into so called "start task" and "get task result" requests. Your application returns "request id" for "start task" request. Then you use "request id" when performing "get task result". Such a scenario is a common way in the Batch Processing task. If you use Spring, you may be interesting investigating Spring Batch framework, which has Start/Stop/Restart job functionality among others.
I'm trying to make a small REST using Spring Boot.
I've never used Spring and used Java a long time ago (Java 7)!
In the last 2 years I have used only Python and C# (but like I said, I already used Java).
So, now, I'm trying to make a REST using async methods, and checked several examples, but still, I don't understand very well the "correct way" to do this.
Looking at the following documentation: http://carlmartensen.com/completablefuture-deferredresult-async, Java 8 has CompletableFuture that I can use with Spring, so, I made the following code:
Service:
#Service
public class UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Repository:
public interface UserRepository extends MongoRepository<User, String> {
#Async
findByEmail(String email);
}
RestController:
#RestController
public class TestController {
private UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
}
}
This code give me the expected output.
Then, looking at another documentation (sorry, I lost the link), I see that Spring accept the following code (which give me the expected output too):
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email);
}
}
Is there a difference between the two methods?
Then, looking at the following guide: https://spring.io/guides/gs/async-method/, there's a #EnableAsync annotation in SpringBootApplication class.
If I include the #EnableAsync annotation and create a asyncExecutor Bean like the code from last link, my application don't return nothing on /test endpoint (only a 200 OK response, but with blank body).
So, my rest is async without the #EnableAsync annotation?
And why when I use #EnableAsync, the response body is blank?
The response body is blank because the #Async annotation is used at findEmail method of UserRepository class, it means that there is no data returned to the following sentence User user = userRepository.findByEmail(email); because findByEmail method is running on other different thread and will return null instead of a List object.
The #Async annotation is enabled when you declare #EnableAsync that is the reason why it only happens when you use #EnableAsync because it activates the #Async of findEmail method to run it on other thread.
The method return userService.findByEmail(email); will return a CompletableFuture object that is created from UserService class.
The difference with the second method call is that thenApplyAsync method will create a totally new CompletableFuture from the previous one that comes from userService.findByEmail(email) and will only return the user object that comes from the first CompletableFuture.
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
If you want to get the expected results just remove the #Async annotation from findByEmail method, and finally add the #EnableAsync Annotation
If you need to clarify ideas of how to use Async methods, lets say that you have to call three methods and each one takes 2 seconds to finish, in a normal scenario you will call them method1, then method2 and finally method3 in that case you entire request will take 6 seconds. When you activate the Async approach then you can call three of them and just wait for 2 seconds instead of 6.
Add this long method to user service:
#Async
public CompletableFuture<Boolean> veryLongMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(true);
}
And call it three times from Controller, like this
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();
CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
return userService.findByEmail(email);
}
Finally measure the time that takes your response, if it takes more than 6 seconds then you are not running Async method, if it takes only 2 seconds then you succeed.
Also see the following documentation: #Async Annotation, Spring async methods, CompletableFuture class
Hope it help.
The Asynchronous child threads start executing very late (around 20 to 30 seconds delay).
I'm using ThreadPoolTaskExecutor() in my main SpringBoot application class. You can also try the same if you consider performance as a factor.
This question already has an answer here:
Accessing HttpSession outside of the originally receiving thread
(1 answer)
Closed 6 years ago.
I have a fully-annotation-driven Spring Boot 1.3.5 app which has this asynchronous service which needs to autowire another service bean (And in the future it will need to autowire a repository bean, but I'm not there yet) in order to perform some business logic:
#Service
public class AsyncService {
#Autowired
public HelpingService helpingService;
#Async
public Future<String> doFoo(String someArgument)
throws InterruptedException {
Thread.sleep(3000);
System.out.println("about to do Foo "+someArgument);
String result = "";
try {
result = helpingService.getSomeStuff(someArgument);
}
catch (Exception e) {
e.printStackTrace();
}
return new AsyncResult<String>(hello);
}
}
That method above is being called from a #Controller bean, which has other endpoints (Non-async) that work as expected also using this
#Controller
public class MyController extends BaseController {
#Autowired
HelpingService helpingService;
#Autowired
AsyncService asyncService;
#RequestMapping(method=RequestMethod.GET, value={"/rest/threads/getIp/{jobId}"}, produces={"application/json"})
public ResponseEntity<?> getLog(#PathVariable("jobId") String jobId) throws InterruptedException {
asyncService.doFoo(jobId);
return new ResponseEntity<>(HttpStatus.OK);
}
}
And here's helpingService's implementation (It's an interface), calling any method works perfectly fine when I'm not doing it from the #Async method above:
#Service
#Validated
public class HelpingServiceImpl implements HelpingService {
#Autowired
HttpSession httpSession;
#Value(value="${projName}")
private String projName;
public String getServerAddress(){
AuthRegion region = (AuthRegion) httpSession.getAttribute("region");
if (region != null)
return region.getServerAddress();
else
return null;
}
#Override
public String getSomeStuff(String jobId) {
String responseString = "";
String projName = this.projName;
String serverAddress = getServerAddress(); // Code stops here with an exception
// Some code here that works fine outside this thread
return responseString;
}
}
This is the exception being caught:
about to do Foo (267)
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:309)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:64)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:366)
at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:361)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307)
at com.sun.proxy.$Proxy96.getAttribute(Unknown Source)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getRundeckServerPort(ThreadServiceImpl.java:45)
at corp.fernandopcg.myapp.service.ThreadServiceImpl.getJobExecutionOutput(ThreadServiceImpl.java:65)
at corp.fernandopcg.myapp.service.AsyncService.doFoo(AsyncService.java:40)
at corp.fernandopcg.myapp.service.AsyncService$$FastClassBySpringCGLIB$$7e164220.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
I added (With some changes as I couldn't extend AsyncConfigurer at the same time as SpringBootServletInitializer, and I had to catch an exception not mentiones there) the taskExecutor part to my Application main class as follows, guided by this tutorial which does look similar to what I need, in my opinion
#SpringBootApplication
#EnableAsync
#EnableJpaRepositories(repositoryFactoryBeanClass = DataTablesRepositoryFactoryBean.class)
public class MyApplication extends SpringBootServletInitializer implements AsyncConfigurer{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("SomeRandomLookup-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
// TODO Auto-generated method stub
return null;
}
}
Can I tell my #Async service to be able to use other services of the application? Because if that's not possible, I don't really see the use of these threading mechanism.
This is a great illustration of why request-scope injection can be problematic. Your HelpingServiceImpl has a hidden dependency on the request-specific HttpSession, which looks like a field but is actually a proxy that is resolved by Spring on each call to always refer to the "current" request (using a thread-local variable).
The problem is that by making your call #Async, you're separating the HelpingServiceImpl invocation from the request that triggered it, and there's no longer the implicit connection of being on the same thread that would allow it to pull information from the globalish context.
The most straightforward fix is to make your dependencies explicit--instead of having your HelpingServiceImpl grab the region directly off of the HttpSession, pass the region to it as a method parameter.
I did Google a lot to find my problem but I couldn't and sorry If this question already on the stack overflow because I have not find it.
First let take a look into the code
#Controller
public class Controller1 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
}
public int calculation(int i){
//Some Calcucation
return i;
}
}
and second controller is
#Controller
public class Controller2 {
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView methodHandler(Parameters) {
//In this I want to call the calculation(1) method of controller1.
}
}
My question is that is there any way to call the method of calculation() of controler1 in to controller2. But remember I don't want to make method static in controller1.Is there anyway to call it without make it static?
Thanks
Yasir
You should create service bean for example in configuration file (or use # one of the annotaions) and inject it into controller. For example ()
#Configuration
public class MyConfig {
#Bean
public MyService myService(){
return new MyService();
}
}
#Controller
public class Controller1 {
#Autowire
private MyService myService;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
#Controller
public class Controller2 {
#Autowire
private MyBean myBean;
#RequestMapping(value = "URL", method = RequestMethod.GET)
public ModelAndView First(Parameters) {
myService.calculation();
}
}
Your controllers should not call each other. If there is a logic which needs to be used by both controllers, it is much better to put that into separate bean, which will be used by both controllers. Then you can simply inject that bean to whicheveer controller neccessary. Try not to put any business logic to controllers, try tu put it to specialized class instead which will be web independent if possible and will accept web agnostic business data as user email, account number etc. No http request or response. This way your class with actual logic is reusable and can be unit tested much more easily. Also, if there is state, it should be contained in your classes outside controllers. Controllers should be stateless and not contail any state at all.
When using MVC pattern and you are deciding where to put your logic, you should separate business logic into model and into controllers you should put only logic regarding user interaction, as explained in this stack overflow post.