I need to send a email in async way while saving the data into DB.
My approach was like this.
//I have tried with service layer annotating.But not worked.
#EnableAsync
class MyService{
public String saveMethod(List listOfData){
mail.sendEmailQuote(listOfData);
mail.sendEmailWorkflowTaskAssignment(listOfData);
myDao.saveData(listOfData);
}
}
I need to perform following methods in #Async way. Where should I put #EnableAsync annotation. This is not a Schedule related thing. This is happen when user click save button. Application is used flex spring blazeDS. There is no controller written by my self.
I have used #Async annotation in my code for following 2 methods. Those are in class call Mail.
#Async
sendEmailQuote(listOfData){}
#Async
sendEmailWorkflowTaskAssignment(listOfData){}
Could you help me to find where should I put #EnableAsync ?
I refer this sample
EnableAsync is used for configuration and enable Spring's asynchronous method execution capability, it should not be put on your Service or Component class, it should be put on your Configuration class like:
#Configuration
#EnableAsync
public class AppConfig {
}
Or with more configuration of your AsyncExecutor like:
#Configuration
#EnableAsync
public class AppConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
}
Please refer to it's java doc for more details.
And for the tutorial you followed, EnableAsync is put above Application class, which extends AsyncConfigurerSupport with AsyncExecutor configuration:
#SpringBootApplication
#EnableAsync
public class Application extends AsyncConfigurerSupport {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
}
Just make sure that #Async methods aren't called by the same class. Self invocation for proxy won't work.
Related
I am playing with a simple Spring Boot application and RabbitMQ.
However I cannot figure out how to run a method periodically.
Here is my Application class
#SpringBootApplication
public class SampleApp {
#Autowired
Sender sender;
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
sender.sendMessage();
}
}
And the sendMessage method is defined as below
#Scheduled(fixedRate = 3000L)
public void sendMessage() {
log.info("Sending message...");
rabbitTemplate.convertAndSend("my-exchange", "my-routing-key", "TEST MESSAGE");
}
However this method is called only once, I can see only a single line in the console.
What I missed in my code?
Thanks.
Looks like you are missing #EnableScheduling:
#EnableScheduling
#SpringBootApplication
public class SampleApp {
...
}
Quoting the documentation:
Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on #Configuration classes as follows:
#Configuration
#EnableScheduling
public class AppConfig {
// various #Bean definitions
}
This enables detection of #Scheduled annotations on any Spring-managed bean in the container.
I am usually using the Spring ThreadPoolTaskScheduler. You define it, as Bean for example then you wrap your method into a Runnable and you call it at intervals defined by a CronTrigger. The result can be retrieved using a ScheduledFuture
Check https://www.baeldung.com/spring-task-scheduler for a complete beginner tutorial.
Here's an alternative way if you don't want to rely on Spring's scheduler.
It's using rxjava2, here's an example:
#Component
public class MessagePublisher {
Sender sender;
Disposable d;
#Autowired
public Config(Sender sender) {
this.sender = sender;
this.d = doSomethingAfterStartup();
}
public Disposable doSomethingAfterStartup() {
return Observable.interval(3000, TimeUnit.MILLISECONDS).subscribe(tick -> {
sender.sendMessage();
});
}
}
I have several methods with the annotation #Scheduled. For each annotation or a group of them I want a different scheduler to be used. For example:
Group A has 3 methods with #Scheduled annotation which need to use Scheduler X.
Group B has 5 methods with #Scheduled annotation which need to use Scheduler Y.
From what I have read in Does spring #Scheduled annotated methods runs on different threads?, if the scheduler is not specified then only one of those methods will run at a time.
I know how this connection can be done using xml-based annotation. But is there a way that this can be done using Java-based annotation only?
It can be done using java config. But not using an annotation attributes.
You could have a look at the Spring API doc for some extended example.
For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
}
#Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
}
#Scheduled group is not yet supported. See this open issue.
If you want use more than one scheduler you have to create and configure them programmatically. For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
[...]
#Bean(destroyMethod="shutdown", name = "taskSchedulerA")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
#Bean(destroyMethod="shutdown", name = "taskSchedulerB")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
}
#Service
public class MyService {
#Autowired #Qualifier("taskSchedulerA")
private Executor taskSchedulerA;
#Autowired #Qualifier("taskSchedulerB")
private Executor taskSchedulerB;
#PostConstruct
public void schedule(){
Executors.newScheduledThreadPool(42).schedule(new Runnable() {
#Override
public void run() {
functionOfGroupA();
}
} , ..);
}
}
When I try to use #Async on a Spring Data Repository method I get the following error:
{"cause":null,"message":"PersistentEntity must not be null!"}
As if it tries to serialize CompletableFuture<>.
The repository code:
#Query("SELECT p FROM Produto p where descricao LIKE CONCAT(UPPER(:like),'%')")
#Async
CompletableFuture<List<Produto>> findByLikeAsync(#Param("like") String like);
The configuration:
#SpringBootApplication
#EntityScan(basePackages = {"..."})
#EnableJpaRepositories(basePackages = {"..."})
#EnableAsync
#Configuration
public class Application implements AsyncConfigurer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
I have tried to use Future<> instead of CompletableFuture<>, but it throws the same error.
Any Ideas?
Thanks.
Assuming you're using CrudRepository, methods in the repository can only return the class that they're registered for (or collections of them). In this case, if you have defined your repository to be:
public interface ProdutoRepository extends CrudRepository<Produto, Long>
You have to return List<Produto>. You can create a #Service that calls this and have a public method there annotated with #Async.
I'm trying to use Async annotation in Spring but I'm getting
java.lang.IllegalStateException: ThreadPoolTaskScheduler not initialized
error, when I try to run the method marked as Async. The following is the configuration for Async:
#EnableScheduling
#EnableAsync
#Configuration
public class SchedulingConfiguration implements AsyncConfigurer{
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
return scheduler;
}
}
and the following is the declaration of async method.
#Async
#Transactional(value = "baseTransactionManager", isolation = Isolation.READ_COMMITTED)
public void foo(Bar bar) {// some code here}
What am I missing in here?
Thanks in advance.
You have to explicitly call scheduler.initialize() after setting all properties but before returning the scheduler.
See full working test case here.
Spring framework has an interface AsyncConfigurer (and AsyncConfigurerSupport) for configuring everything necessary for having a thread pool executor and being able to run methods asynchronously (with #Async).
But usually, it's not a good practice to share the same thread pool among different functionalities, so usually I qualified them with a name, and I specify that name in Async annotation for it to use one specific thread pool.
Thing is, I would like to configure them through this convenient interface AsyncConfigurer and not loosing qualification, but I'm not able to.
Trying this:
#Configuration
#EnableAsync
public class PusherAsyncConfigurerSupport extends AsyncConfigurerSupport {
#Autowired
ExecutorConfig config;
#Override
#Qualifier(value = "executorOne")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
...
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
}
That thread pool is still not recognized when put in an Async annotation.
So, what is it needed for configuring several thread pools this way? Or isn't it the way for that at all?
mark with #Bean annotation
#Bean
#Qualifier(value = "executorOne")
public Executor getAsyncExecutor() {
//..
return executor;
}