Initially I was using regular java Multithreading using the 'implements' method. However #Autowired does not work when a class is created with new in Spring, so I am trying to change it to using Spring'sAsync method. This is what I have so far. How would I go about adding the threads to the ThreadPoolExecutor?
The class that should create the threads
#Component
public class ScheduledCountyScraper {
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
// #Scheduled(cron = "0 0 */3 * * *")
#EventListener(ApplicationReadyEvent.class)
public void scrapeCountyLinks() {
System.out.println("Scrape county links ran!");
try {
List<String> stateLinks = stateScrapeQueueRepository.getStatesLinks(website);
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
//what to do here?
executor.shutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("---------------------");
}
}
}
The Async class
#Component
#EnableAsync
public class CountyScraper {
volatile private String stateLink;
#Autowired
StateScrapeQueueRepository stateScrapeQueueRepository;
#Autowired
CountyScrapeRepository countyScrapeRepository;
public CountyScraper() {
}
public CountyScraper(String stateLink) {
this.stateLink = stateLink;
}
#Async("countyScraper")
public void run() {
try {
// other code
stateScrapeQueueRepository.updateScrapeTimestamp(stateLink);
countyScrapeRepository.insertCountyLinks(countyLinks, website);
} catch (Exception e) {
e.printStackTrace();
}
}
}
By default Spring uses a SimpleAsyncTaskExecutor to execute async methods. This will by default spawn a new thread for every operation.
To define your own executor for use with async tasks, create a bean that implements the TaskExecutor interface or an Executor bean named "taskExecutor".
If you'd like to have your own custom executor just for this component, you can implement AsyncConfigurer and provide your own executor service:
#Override
public Executor getAsyncExecutor() {
return MY_EXECUTOR;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return MY_EXCEPTION_HANDLER;
}
Related
I have configured two different thread pools, one for #Scheduled and other for #Async. However, I notice that the thread-pool for #Async is not being used.
Here is the Scheduler configuration
#Configuration
#EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-sched-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
Here is the Configuration for Async
#Configuration
#EnableAsync
public class AppConfig {
#Bean(name = "asyncTaskExecutor")
public TaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setMaxPoolSize(15);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("my-async-pool-");
executor.initialize();
return executor;
}
}
Here is how I invoke them
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
asyncMethod();
}
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
Here are the logs
{"thread":"my-sched-pool-1","level":"DEBUG","description":"here is the message from schedule"}
{"thread":"my-sched-pool-1","level":"INFO","description":"here is the message from async"}
As you can notice, both logs are having same pool of that scheduler. but I expect to see the second one to come from async
If you call #Async methods from the same class they are declared you are effectively bypassing Spring's proxy mechanism and that is why your example is not working. Try calling the method from a separate class annotated with #Service or any of the other #Component types.
#Service
SomeScheduledClass {
private final SomeAsyncClass someAsyncClass;
public SomeScheduledClass(SomeAsyncClass someAsyncClass) {
this.someAsyncClass = someAsyncClass;
}
#Scheduled(fixedRateString = "2000" )
public void schedule() {
log.debug("here is the message from schedule");
someAsyncClass.asyncMethod();
}
}
#Service
SomeAsyncClass {
#Async("asyncTaskExecutor")
public void asyncMethod(){
log.info("here is the message from async");
}
}
I am doing a Spring Boot Project
This is the main class
#SpringBootApplication
#ComponentScan(basePackages="blabla.quartz")
#EnableScheduling
public class App
{
public static void main( String[] args )
{
ConfigurableApplicationContext context =SpringApplication.run(App.class, args);
}
}
This is the controller
#RestController
public class Controller {
#Autowired
private SampleTask m_sampletask;
#Autowired TaskScheduler taskScheduler;
ScheduledFuture scheduledFuture;
int jobid=0;
#RequestMapping(value = "start/{job}", method = RequestMethod.GET)
public void start(#PathVariable String job) throws Exception {
m_sampletask.addJob(job);
Trigger trigger = new Trigger(){
#Override
public Date nextExecutionTime(TriggerContext triggerContext) {
org.quartz.CronExpression cronExp=null;
CronSequenceGenerator generator = new CronSequenceGenerator("0 * * ? * *");
Date nextExecutionDate = generator.next(new Date());
System.out.println(nextExecutionDate);
return nextExecutionDate;
}
};
scheduledFuture = taskScheduler.schedule(m_sampletask, trigger);
}
}
This is the ScheduleConfigurer implementation
#Service
public class MyTask implements SchedulingConfigurer{
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("somegroup-");
scheduler.setPoolSize(10);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(20);
return scheduler;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
}
}
This is the class which I am calling from controller as scheduled job
#Component
public class SampleTask implements Runnable{
private List<String> jobs=new ArrayList<String>();
private String jobName;
public void addJob(String job){
jobName=job;
}
#Override
public void run() {
System.out.println("Currently running "+jobName);
}
}
How to stop the schedule job by a rest endpoint(Suppose "/stop/{jobname}").. When I have started the job using the "/start/{jobname}" rest endpoint?
You will probably need to use the quartz scheduler (if not already), and add a service with the required methods, then inject that service into your controller.
There's a decent example here: https://github.com/javabypatel/spring-boot-quartz-demo
If you want an in-memory job store (that isn't a database), checkout the RAMJobStore: http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigRAMJobStore.html
Stop Example
This is an excerpt from the demo project. Credit goes to Jayesh Patel: https://github.com/javabypatel
/**
* Stop a job
*/
#Override
public boolean stopJob(String jobName) {
System.out.println("JobServiceImpl.stopJob()");
try{
String jobKey = jobName;
String groupKey = "SampleGroup";
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jkey = new JobKey(jobKey, groupKey);
return scheduler.interrupt(jkey);
} catch (SchedulerException e) {
System.out.println("SchedulerException while stopping job. error message :"+e.getMessage());
e.printStackTrace();
}
return false;
}
1.How to inject a spring bean into thread
2.How to start a thread inside spring bean.
here is my code.
MyThread.java
#Component
public class MyThread implements Runnable {
#Autowired
ApplicationContext applicationContext;
#Autowired
SessionFactory sessionFactory;
public void run() {
while (true) {
System.out.println("Inside run()");
try {
System.out.println("SessionFactory : " + sessionFactory);
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(10000);
System.out.println(Arrays.asList(applicationContext.getBeanDefinitionNames()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
i am calling run method from below class like (Please suggest if i am following wrong appraoch for calling a thread inside spring bean )
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
Thread t = new Thread(new MyThread());
t.start();
}
}
}
spring is not performing dependency injection on MyThread class
There are a couple of things wrong with your setup.
You shouldn't be creating and managing threads yourself, Java has nice features for that use those.
You are creating new bean instances yourself and expect Spring to know about them and inject dependencies, that isn't going to work.
Spring provides an abstraction to execute tasks, the TaskExecutor. You should configure one and use that to execute your task not create a thread yourself.
Add this to your #Configuration class.
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
Your MyThread should be annotated with #Scope("prototype").
#Component
#Scope("prototype")
public class MyThread implements Runnable { ... }
Now you can inject these beans and an ApplicationContext into your MyServiceCreationListener
#Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private ApplicationContext ctx;
#Autowired
private TaskExecutor taskExecutor;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {
System.out.println("\nThread Started");
taskExecutor.execute(ctx.getBean(MyThread.class));
}
}
}
This will give you a pre-configured, fresh instance of MyThread and execute it on a Thread selected by the TaskExecutor at hand.
Your MyThread is created manually rather than via spring context new Thread(new MyThread()); so no chance for spring to inject a bean.
Instead you can add a trick with static access to spring context where you can get a necessary bean from the context (see here or here).
Alternatively you can use ThreadLocal or InheritableThreadLocal to store necessary objects to be used in the thread.
You are creating Thread t = new Thread(new MyThread());.Spring container will not inject the dependency and also not maintain the life cycle of bean.
Example :
#Component
#Scope("prototype")
public class PrintThread extends Thread{
#Override
public void run() {
System.out.println(getName() + " is running");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " is running");
}
}
to access the thread object from spring context.
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
private static final String USER_THREAD = "printThread";
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
public static UserService getUserService(){return ctx.getBean(USER_THREAD );}
}
I am working on an application that supports multi tenancy. The tenant's unqiue identifier is stored in a thread local and can be accessed via some service.
To allow parallel processing, I have created a Callable wrapper, sets the thread local variable:
class TenantAwareCallable<T> implements Callable<T> {
private final String tenantName;
private final Callable<T> delegate;
TenantAwareCallable(Callable<T> delegate, String tenantName) {
this.delegate = delegate;
this.tenantName = tenantName;
}
#Override
public T call() throws Exception {
// set threadlocal
TenantContext.setCurrentTenantName(tenantName);
try {
return delegate.call();
} catch (Exception e) {
// log and handle
} finally {
TenantContext.clear();
}
}
}
This can already be used in the application. But what I would like to have is some custom #Async annotation, like for example #TenantAwareAsync or #TenantPreservingAsync, that wraps the callable, created by Spring in this one and then executes it.
Is there some way to get started with this?
Thanks in advance!
I have a working solution for this, so I think sharing it here might help some people some day.
I solved it not by using the TenantAwareCallable but by customizing the Executor service. I chose to extend Spring's ThreadPoolTaskScheduler (since this is what we use in the project).
public class ContextAwareThreadPoolTaskScheduler extends ThreadPoolTaskScheduler {
#Override
protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
return new ContextAwareThreadPoolTaskExecutor(poolSize, threadFactory, rejectedExecutionHandler);
}
}
The actual setting of the context data is done in a customized ScheduledThreadPoolExecutor:
public class ContextAwareThreadPoolTaskExecutor extends ScheduledThreadPoolExecutor {
public ContextAwareThreadPoolTaskExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
super(poolSize, threadFactory, rejectedExecutionHandler);
}
#Override
protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) {
return new ContextAwareTask<V>(task);
}
#Override
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) {
return new ContextAwareTask<V>(task);
}
static private class ContextAwareTask<T> implements RunnableScheduledFuture<T> {
private final RunnableScheduledFuture<T> delegate;
private final TenantContextHolder multitenantContextHolder;
private final LoggingContextHolder loggingContextHolder;
private final SecurityContext securityContext;
ContextAwareTask(RunnableScheduledFuture<T> delegate) {
this.delegate = delegate;
multitenantContextHolder = TenantContextHolder.newInstance();
loggingContextHolder = LoggingContextHolder.newInstance();
securityContext = SecurityContextHolder.getContext();
}
#Override
public void run() {
multitenantContextHolder.apply();
loggingContextHolder.apply();
SecurityContextHolder.setContext(securityContext);
delegate.run();
SecurityContextHolder.clearContext();
loggingContextHolder.clear();
multitenantContextHolder.clear();
}
// all other methods are just delegates
}
}
The Holders are basically just objects to store context state and apply it in the new thread.
public class TenantContextHolder {
private String tenantName;
public static TenantContextHolder newInstance() {
return new TenantContextHolder();
}
private TenantContextHolder() {
this.tenantName = TenantContext.getCurrentTenantName();
}
public void apply() {
TenantContext.setCurrentTenantName(tenantName);
}
public void clear() {
TenantContext.clear();
}
}
The custom implementation of the Scheduler can then be configured in the Spring environment.
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
private ThreadPoolTaskScheduler taskScheduler;
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
if (taskScheduler == null) {
taskScheduler = new ContextAwareThreadPoolTaskScheduler();
}
return taskScheduler;
}
#Override
public Executor getAsyncExecutor() {
return taskScheduler();
}
}
Trying to enable async event handling combining the #Async and #EventListener annotations, but I still see that the listener is running in the publishing thread.
The example you can find here:
#SpringBootApplication
#EnableAsync
class AsyncEventListenerExample {
static final Logger logger = LoggerFactory.getLogger(AsyncEventListenerExample.class);
#Bean
TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor();
}
static class MedicalRecordUpdatedEvent {
private String id;
public MedicalRecordUpdatedEvent(String id) {
this.id = id;
}
#Override
public String toString() {
return "MedicalRecordUpdatedEvent{" +
"id='" + id + '\'' +
'}';
}
}
#Component
static class Receiver {
#EventListener
void handleSync(MedicalRecordUpdatedEvent event) {
logger.info("thread '{}' handling '{}' event", Thread.currentThread(), event);
}
#Async
#EventListener
void handleAsync(MedicalRecordUpdatedEvent event) {
logger.info("thread '{}' handling '{}' event", Thread.currentThread(), event);
}
}
#Component
static class Producer {
private final ApplicationEventPublisher publisher;
public Producer(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void create(String id) {
publisher.publishEvent(new MedicalRecordUpdatedEvent(id));
}
#Async
public void asynMethod() {
logger.info("running async method with thread '{}'", Thread.currentThread());
}
}
}
and my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = AsyncEventListenerExample.class)
public class AsyncEventListenerExampleTests {
#Autowired
Producer producer;
#Test
public void createEvent() throws InterruptedException {
producer.create("foo");
//producer.asynMethod();
// A chance to see the logging messages before the JVM exists.
Thread.sleep(2000);
}
}
However in logs I see that both #EventListeners run in the main thread.
2016-05-12 08:52:43.184 INFO 18671 --- [ main] c.z.e.async2.AsyncEventListenerExample : thread 'Thread[main,5,main]' handling 'MedicalRecordUpdatedEvent{id='foo'}' event
2016-05-12 08:52:43.186 INFO 18671 --- [ main] c.z.e.async2.AsyncEventListenerExample : thread 'Thread[main,5,main]' handling 'MedicalRecordUpdatedEvent{id='foo'}' event
The async infrastructure is initialised with #EnableAsync with an asynchronous TaskExecutor.
Not sure what I am doing wrong. Could you help?
Thanks.
Using Spring Boot 1.4.2.M2, so Spring 4.3.0.RC1
There was a regression in Spring Framework 4.3.0.RC1 that leads to that very issue you're having. If you use the SNAPSHOT, your project runs fine.
onTicketUpdatedEvent runs in also main Thread with Spring Framework 4.2.4 Release as follows.
But it runs in SimpleAsyncTaskExecutor if AsyncConfigurer is not implemented.
#EnableAsync(proxyTargetClass = true)
#Component
#Slf4j
public class ExampleEventListener implements AsyncConfigurer {
#Async
#EventListener
public void onTicketUpdatedEvent(TicketEvent ticketEvent) {
log.debug("received ticket updated event");
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
I resolved my issue by configuring task-executor bean as follows.
#Bean(name = "threadPoolTaskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
#Async("threadPoolTaskExecutor")
#EventListener
public void onTicketUpdatedEvent(TicketEvent ticketEvent) {
log.debug("received ticket updated event");
}