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.
Related
I have a class which I want to run jobs from asynchronously. To do this I have the following code:
#Resource
private SimpleJobLauncher jobLauncher;
#PostConstruct
public void initLauncher(){
jobLauncher.setTaskExecutor(taskExecutor());
}
However, there is a situation where I need to do this synchronously. So, what I have done is added the following variable:
#Resource
private SimpleJobLauncher synchronousJobLauncher;
Which I hoped wouldn't have the taskExecutor on it making it synchronous. I then pass the synchronousJobLauncher to the places where I want to do things synchronously. However, using the synchronousJobLauncher gives me the same error as when I use the asynchronous one which leads me to believe that you cannot autowire the same variable twice like I am trying to do. If I do not do the #PostConstruct part of the code, the synchronous part works like I expect but not the asynchronous part, even though they use, what I think are, different job launchers.
Any ideas? I have tried using the #Resource annotation instead of #Autowired.
I haven't used SimpleJobLaunchers, but normally, in Spring I always used the #Async annotation, which make asynchronous execution quite simple. All you have to do is add this annotation, #EnableAsync in any configuration file, just like the one below.
#Configuration
#EnableAsync
public class MvcConfig extends WebMvcConfigurerAdapter { ... }
Now, is all about adding the #Async to any method that will run asynchronously.
#Component
class AsyncTask {
#Async
public Future<String> run() throws InterruptedException {
return new AsyncResult<String>("return value");
}
}
And if you want to wait for the result, you can do the following.
public void callAsyncTask() {
try {
Future<String> future = asyncTask.run();
// Do some other things while the async task is running.
// future.get() will block the function and wait for the result.
String asyncResult = future.get();
} catch(InterruptedException e) {
e.printStackTrace();
// Do something else.
}
}
Hope this helps. I know is not directly what you were asking for, but maybe this solution can facilitate your problem.
You can create two job launchers (one synchronous and one asynchronous) then inject the one you want using a qualifier. By default, the SimpleJobLauncher uses a synchronous task executor, so you need to configure a task executor only for the asynchronous one. Here is an example:
#Bean
public SimpleJobLauncher synchronousJobLauncher() {
return new SimpleJobLauncher();
}
#Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
#Bean
public SimpleJobLauncher asynchronousJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(threadPoolTaskExecutor());
return jobLauncher;
}
Then, inject the job launcher you want with a #Qualifier("synchronousJobLauncher") or #Qualifier("asynchronousJobLauncher") according to your needs.
Hope this helps.
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();
}
} , ..);
}
}
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.
I'm using spring boot. I have a batch job which I've implemented with these classes :
My main class is :
#SpringBootApplication
#ComponentScan("com.batch")
#PropertySource("classpath:application.properties")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
My scheduler is :
#Component
#EnableScheduling
public class JobScheduler {
#Scheduled(fixedRate = 10000)
public void runJob() {
SpringApplication.run(MyBatchConfig.class);
}
}
and my batch configuration class is :
#Configuration
#EnableBatchProcessing
public class MyBatchConfig {
#Value("${database.driver}")
private String databaseDriver;
#Value("${database.url}")
private String databaseUrl;
#Value("${database.username}")
private String databaseUsername;
#Value("${database.password}")
private String databasePassword;
#Bean
public Job myJob(JobBuilderFactory jobs, Step s) {
Job job = jobs.get("myJob")
.incrementer(new RunIdIncrementer())
.flow(s)
.end()
.build();
return job;
}
#Bean
public Step myStep(StepBuilderFactory stepBuilderFactory, ItemReader<Account> reader,
ItemWriter<Person> writer, ItemProcessor<Account, Person> processor) {
TaskletStep step = stepBuilderFactory.get("myStep")
.<Account, Person>chunk(1)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
step.setAllowStartIfComplete(true);
return step;
} ...
now, my problem is :
the scheduler works and it repeats every ten seconds,
but the job executes only on application startup(reader, processor and writer just execute once in startup) and it seems that
SpringApplication.run(MyBatchConfig.class);
has no effect on re-running the job.
what should I do?
Thanks in advance
This is what I can think of,
1.You put this property in application.properties so your batch job doesn't start automatically by call of SpringApplication.run(...) from mainmethod.
spring.batch.job.enabled=false
This will simply initialize your Spring Batch configuration and not actually start job.
2.Put annotation #EnableScheduling on your Spring Boot Batch Job starting class i.e. on Application class in your code.
3.Remove #EnableScheduling annotation from JobScheduler class and call , jobLauncher.run(job, jobParameters) from runJob() instead of calling SpringApplication.run(MyBatchConfig.class).
JobLauncher & Job beans can be auto wired to your scheduler class since context is already initialized.
Hope it helps !!
You need to create JobLauncher bean and use that in scheduler for starting new job instances.
I'm developing a project in Spring without Spring Boot
and I'm not using xml configurations, instead I'm using annotations.
Then how to use #Schedule(cron="bla bla...")
where should I put #EnableScheduling.
Below is my piece of code which is not working:
#RestController
public class MyController {
#Scheduled(cron = "0 40 22 * * SUN")
public void routinessundayBAS1() throws EntityNotFoundException {
System.out.println("coming");
}
}
You need to put #EnableScheduling on any #Configuration class.
As Jakub said, you have to add #EnableScheduling on any #Configuration class. You could configure the scheduler to customize your configuration by implementing the SchedulingConfigurer interface. An example:
#Configuration
#EnableScheduling
#ComponentScan("PACKAGES WITH SCHEDULED ANNOTATIONS")
public class ConfigScheduler implements SchedulingConfigurer {
...
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize( threadpoolsize );
scheduler.setThreadGroupName( threadgroupname );
scheduler.setThreadNamePrefix( threadPrefix );
scheduler.setAwaitTerminationSeconds( timeout );
return scheduler;
}
#Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
TaskScheduler scheduler = this.taskScheduler();
registrar.setTaskScheduler( scheduler );
}
...
}