I'm toying with asynchronous calls to reduce some waiting time on client side when calling a page.
As a experiement i have a controller that calls a pojo with a method annotated with #Async. In that menthod i sleep for 10000 ms to simulation operation to test whether my theory works, and it seems not to. Code below and further information can be found after it:
Test Controller
#Conroller
public class TestController {
#RequestMapping("/test")
public String testAsyncCall() {
new AsyncTestClass().asyncOpOne();
return "secondpage";
}
}
Asynchronous Class containing the #Async annotated method
public class AsyncTestClass {
#Async
public void asyncOpOne() {
try {
Thread.sleep(10000);
System.out.println("done working");
} catch (InterruptedException e) {
//
}
}
}
Now from my understanding when the client makes the call to "/test" in their browser the controller should return call the asynchronous method and instantly return "secondpage" to be rendered.
What is happening is that the controller doesn't return the second page until the 10000 ms in the asynchronous call has finished, only then it returns the secondpage.
FYI
#EnableAsync is added to one of my config files (using Java Configuration).
What am i doing wrong here that is causing the controller to wait for the async to finish its sleep before continuing?
Spring uses AOP to apply #Async behavior to your beans (the same goes for #Transactional for instance).
Spring will only apply AOP to beans it knows, as you are constructing a new instance outside of the scope of Spring the #Async does nothing. Simply add it as a bean to your configuration and inject it into your controller.
#Bean
public AsyncTestClass asyncTestClass() {
return new AsyncTestClass():
}
Then in your calling class.
#Conroller
public class TestController {
#Autowired
private AsyncTestClass asyncTestClass;
#RequestMapping("/test")
public String testAsyncCall() {
asyncTestClass.asyncOpOne();
return "secondpage";
}
}
Related
I have a method that does something like this
class EmployeeService {
#Transactional
public void perform() {
// perform DB operations()
createEmployee()
// call some async service that relies on the persisted data
runEmployeeBGV();
}
}
runEmployeeBGV() crates a separate thread which does bgv operations.
I want data to be persisted as soon as the createEmployee() method ends. But due to limitations on #Transactional(and my knowldge), I am not sure on how to exclude runEmployeeBGV() out of the scope of transaction.
Please note that these two operations need to reside within perform() method which is being called by another service class where EmployeeService is injected.
Is there any way we can achieve this without calling these methods separately from the client code?
You can move those methods to another service and move #Transaction to createEmployee()
something like that
class EmployeeService {
private EmployeeRepo repo;
private EmployeeBGV bgv;
public void perform() {
repo.createEmployee()
bgv.runEmployeeBGV();
}
}
class EmployeeRepo {
#Transactional
public void perform() {
//code
}
}
I want to send a post request within a period. I created my method like this ;
#Scheduled(cron = "0 0 */6 * *")
#PostMapping
public List<TagsRes> getTags(Date date) {
return null;
}
#Scheduled(cron = "0 0 5 * * ?")
#PostMapping
public List<TagsRes> getAll() {
return null;
}
Should i use #Scheduled in my controller ? Is there any better way to do it?
Thanks!
Controllers are meant to receive web requests, not to post anything.
You can think about them as endpoints exposed by your application and called by external service from time to time.
Now, the Controller abstraction by itself should do any business logic. You may want to validate some parameters received in the request, maybe convert the request parameters to java object with some customization and then call the class (usually mentioned as Service in spring universe) that actually executes your business logic.
Now back to your question.
I suspect you should not "POST a request" but should invoke some piece of code "as if someone called the controller's method (endpoint)". But this time not the external "user" will cause the code execution but an internal scheduler.
If so you can slightly refactor your code to achieve the better clarity:
Create a service that will execute the code
Do not put any scheduling related stuff on controller
From controller call the service
Create a bean and put a "#Scheduled" method on it. The bean will have the service injected and will call it just like the controller does.
Don't forget to put #EnableScheduling annotation - otherwise the scheduled code won't run.
public class MyService {
public void doBusinessLogic(){ ... }
}
#RestController
public class MyController {
#Autowired
private MyService service;
public void myPostMethod(...) {
service.doBusinessLogic(...);
}
}
public class MyScheduledInvoker {
#Autowired
private MyService service;
#Scheduled(...cron expression or whatever...)
public void invokeScheduled() {
service.doBusinessLogic(...);
}
}
#SpringBootApplication
#EnableScheduling
public class MyApp {
public static void main(String [] args) { .... run the app ...}
}
To schedule a job in spring boot application to run periodically, spring boot provides #EnableScheduling and #Scheduled annotations. In my opinion, since spring boot provides the annotation and functionality for scheduler using it will make more sense
Add #EnableScheduling annotation to your spring boot application class.#EnableScheduling is a Spring Context module annotation. It internally imports the SchedulingConfiguration via the #Import(SchedulingConfiguration.class) instruction
#SpringBootApplication
#EnableScheduling
public class SpringBootWebApplication {
}
Now you can add #Scheduled annotations on methods that you want to schedule. The only condition is that methods should be without arguments.
ScheduledAnnotationBeanPostProcessor that will be created by the
imported SchedulingConfiguration scans all declared beans for the
presence of the #Scheduled annotations.
For every annotated method without arguments, the appropriate executor thread pool will be created. This thread pool will manage the scheduled invocation of the annotated method.
#Scheduled(initialDelay = 1000, fixedRate = 10000)
public void run() {
logger.info("Current time is :: " + Calendar.getInstance().getTime());
}
Source: https://howtodoinjava.com/spring-boot/enable-scheduling-scheduled-job-example/
I am encountering a NullPointerException in my Spring application when calling a method on an autowired object. The class in question looks like the following:
#Component
public class Listener {
#Autowired
TemplateService templateService;
#Async
#EventListener
private Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
#Async
#EventListener
public Future<String> listener2(Event2 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
}
When I publish an event that triggers listener1, a null value is printed, but when I publish an event that triggers listener2, the toString() method of TemplateService is called (as I would expect). I'm probably misunderstanding some aspect of how #Async affects #Autowired objects, but I haven't been able to determine what that would be. Am I misusing #Async? Am I misunderstanding how to use #Autowired objects in a multithreaded environment?
Change the visibility of your listener1 method to be at least protected (package visibility , protected or public). This is because Spring creates a proxy which is a subclass of your component. It overrides your #Async annotated methods in order to add new logic to execute your code in a separate thread. However because it uses inheritance it can only override methods which are visible to the subclass.
This explains why listener2 method which is public works.
Change your method to
#Async
#EventListener
public Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
Spring needs an interface to create a proxy class. It's this proxy class that gets called every time you call the method, and it's through this method that the whole asynchronous execution happens. Without an interface Spring can't autowire, scan or make methods execute asynchronously.
public interface Listener {
public Future<String> listener1(Event1 event);
public Future<String> listener2(Event2 event);
}
#Component
public class ListenerImpl {
#Autowired
private TemplateService templateService;
#Async
#Override
public Future<String> listener1(Event1 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
#Async
#Override
public Future<String> listener2(Event2 event) {
System.out.println(templateService);
return new AsyncResult<>(null);
}
}
It's also worth noting that Spring can't run private methods asynchronously.
I am trying to update a resource inside an asynchronous thread that repeats its task until cancelled. I am using Future to store the thread in a hashmap so it can be cancelled at a later time. The thread forks as expected, and the object instance within the Async method changes its values fine, but when I try to commit them, the changes are not reflected in the DB, and I cannot see any error messages on the console. I've tried many different combinations of #Async and #Transactional but still haven't gotten it. What am I doing wrong? Thanks in advance for any help!
RoomService.java
#Service
#Transactional
public class RoomService {
#Inject private FooService fooService;
...
public Future<String> startRoom(Room room) {
Future<String> future = fooService.startFoo(room.getId());
map.put(room.getId(), future);
return future;
}
FooService.java
#Service
#Transactional
public class FooService {
...
public void save(Foo foo) {
getSession().saveOrUpdate(foo);
}
#Async
public Future<String> startFoo(int id) {
Foo foo = getFooById(id);
try {
while (true) {
foo.setName("Peter");
save(foo);
Thread.sleep(10000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new AsyncResult<String>("done");
}
Having the #Async method be part of a #Transactional bean means Spring intercepts your method twice: once for #Transactional to wrap the method in a Transaction and once for #Async to execute the method in a separate Thread.
Because of the way proxying works, once you are inside a method of FooService, invoking
another method is done on the actual object, not on the proxy which has all the #Transactional behavior. As such, no new transaction is created/committed/rolled back. The actual Transaction boundary is the #Async method itself. But since your method executes an infinite loop, that commit will never happen.
I suggest you make your #Async method in a different class for which you create a bean and inject the FooService. This way, the #Transactional behavior is wrapping the actual call to
fooService.save(foo); // fooService is actually a proxy
and is therefore committed on each invocation.
I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.