How to use org.quartz.Scheduler Object in Service class - spring

I have created a Spring Boot application wherein main class I am creating a scheduler object.
prop.put("quartz.scheduler.instanceName", "ServerScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
prop.put("org.quartz.scheduler.instanceId", "CLUSTERED");
prop.put("org.quartz.scheduler.jobFactory.class", "org.quartz.simpl.SimpleJobFactory");
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
prop.put("org.quartz.jobStore.dataSource", "quartzDataSource");
prop.put("org.quartz.jobStore.tablePrefix", "H1585.QRTZ_");
prop.put("org.quartz.jobStore.isClustered", "false");
prop.put("org.quartz.scheduler.misfirePolicy", "doNothing");
prop.put("org.quartz.dataSource.quartzDataSource.driver", "com.ibm.db2.jcc.DB2Driver");
prop.put("org.quartz.dataSource.quartzDataSource.URL", url);
prop.put("org.quartz.dataSource.quartzDataSource.user", user);
prop.put("org.quartz.dataSource.quartzDataSource.password", passwrd);
prop.put("org.quartz.dataSource.quartzDataSource.maxConnections", "2");
SpringApplication.run(SchedulerApplication.class, args);
try {
SchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = stdSchedulerFactory.getScheduler();
scheduler.start();
I want to use the same scheduler object in my service class to trigger the job. The one which I am getting using below code is not working showing different instance id.
scheduler = StdSchedulerFactory.getDefaultScheduler();
How can I solve this?

you can create a singleton Scheduler, and autowired in your service class
#SpringBootApplication
public class SchedulerApplication {
public static void main(final String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public Scheduler scheduler() {
//create props as you above code
SchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = stdSchedulerFactory.getScheduler();
scheduler.start();
return scheduler;
}
}
then you can use in your service class
#Service
public class YourServiceClass {
#Autowired
private Scheduler scheduler;
}

Related

Implement TaskExecutor in spring boot where Scheduling is already defined

I have already define Scheduler in my spring boot project. Then I have to implement task scheduler which run thread pool. After I have added my scheduler task won't triggered. Even test case didn't run.
Scheduler Config Class
#Configuration
#EnableScheduling
public class SchedulerConfig {
}
Thread pool config class
#Configuration
#EnableAsync
public class AsyncConfig {
private ThreadPoolTaskExecutor executor;
#Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(500);
executor.initialize();
return executor;
}
}
#Async("threadPoolTaskExecutor")
public void smsProcessor(List<CampaignSchedulerData> campaignSchedulerDataList, int tenantId) throws InterruptedException {
// file reader from AWS and send message accordingly
}
Scheduler task
#Scheduled(cron = "${cron-expression.file-write}")
public void fileWriteScheduler() {
LocalDateTime startDateTime = LocalDateTime.now();
log.info("Run file Write Scheduler. Start time is :: {} ",
DateTimeUtil.DATE_TIME_FORMATTER.print(startDateTime));
//add config scheduler time
schedulerService.processFileWriteScheduler(startDateTime.minusMillis(file_write_SchedulerWaitingTime));
LocalDateTime endDateTime = LocalDateTime.now();
log.info("Run file Write Scheduler. ", "End time is :: {} ",
DateTimeUtil.DATE_TIME_FORMATTER.print(endDateTime), ". Time taken :: {} ", Period.fieldDifference(startDateTime, endDateTime));
}
I have tried to implement task scheduler then ApplicationSpringBoot class won't run.
#SpringBootApplication
#EnableFeignClients
//Enable Annotations
public class BusinessTemplateApplication {
}
I figured this out.
When I add the task scheduler to the SchedulerConfig class it will resolved
#Configuration
#EnableScheduling
public class SchedulerConfig {
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(500);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
}

How to schedule a cron job in spring boot without using #Scheduled() annotation

In spring boot, can I schedule a spring job by not using #Scheduled annotation to a method?
I am working with spring job in the spring boot. I want to schedule a job by using cron expression, but without using #Scheduled(cron = " ") annotation to the method.
I know that I can schedule a job inside this method as below.
#Scheduled (cron = "0 10 10 10 * ?")
public void execute() {
/ * some job code * /
}
But I want it to be dynamic so that I can take a cron expression as input from the user and schedule it.
I came up with a working example since I found your question interesting and have been interested in this problem before. It's based entirely on the source code so I have no idea if it comes close to following best practice. Nonetheless, you may be able to tune it to your needs. FYI, you don't necessarily need to create a new ScheduledTaskRegistrar object - I figured that since your objective is a dynamic scheduler, you wouldn't be interested in defining your tasks purely in the overwritten method.
#SpringBootApplication
public class TaskScheduler implements SchedulingConfigurer, CommandLineRunner {
public static void main(String[] args){SpringApplication.run(TaskScheduler.class, args);}
List<CronTask> cronTasks;
#Override
public void run(String... args) throws Exception {
CronTask task = this.createCronTask(new Runnable() {
#Override
public void run() {
System.out.println(LocalDateTime.now());
}
}, "1/10 * * * * *");
ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar();
taskRegistrar.addCronTask(task);
configureTasks(taskRegistrar);
Thread.sleep(51);
taskRegistrar.destroy();
taskRegistrar = null;
ScheduledTaskRegistrar taskRegistrar2 = new ScheduledTaskRegistrar();
taskRegistrar2.addCronTask(task);
configureTasks(taskRegistrar2);
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// "Calls scheduleTasks() at bean construction time" - docs
taskRegistrar.afterPropertiesSet();
}
public CronTask createCronTask(Runnable action, String expression) {
return new CronTask(action, new CronTrigger(expression));
}
}
I have experience using cron jobs in Azure and other places. Programming in Java, I have typically used #Scheduled with fixed times just for the sake of simplicity. Hope this is useful to you though.
Here is my working example, If somebody wants to use TaskScheduler without using #Scheduled Annotation
#Configuration Class
#Configuration
public class SchedulerConfig implements SchedulingConfigurer {
final Logger LOGGER = LogManager.getLogger(SchedulerConfig.class);
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
LOGGER.debug("Creating Async Task Scheduler");
scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
}
// This is mandatory otherwise it will to be able to find bean of
// taskScheduler. Without this it was giving runtime error says, can not find
// taskScheduler bean.
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(20); // Better to read it from property file.
scheduler.setThreadNamePrefix("ThreadScheduler-");
scheduler.initialize();
return scheduler;
}
}
Scheduler Class which is called from Application class.
#Component
public class MyTaskScheduler {
private TaskScheduler taskScheduler;
// Here we are auto-wiring taskScheduler, that's why need to create
// taskScheduler bean in configuration class
#Autowired
public void setScheduler(TaskScheduler scheduler) {
this.taskScheduler = scheduler;
}
public void schedule() {
taskScheduler.scheduleWithFixedDelay(new Runnable(){
#Override
public void run() {
System.out.println("I am running after every 1 second");
}
}, 1000);
}
}
If in any chance #Configuration annotation is not working so put #EnableConfigurationProperties at main running class.
Make sure that you put #EnableScheduling at Application class, so main runnable class will look like
#SpringBootApplication
#EnableScheduling
#EnableConfigurationProperties
public class MainApplication implements CommandLineRunner {
#Autowired
MyTaskScheduler myTaskScheduler;
public static void main(String[] args) {
final Logger logger = LogManager.getLogger(MainApplication.class);
SpringApplication.run(MainApplication.class, args);
logger.info("Application started");
}
#Override
public void run(String... args) throws Exception {
myTaskScheduler.schedule();
}
}
This answer is similar to the two previous ones, but is more compact because it leverages the scheduling registrar already provided in the application context:
#Configuration
#EnableScheduling
public class Schedule implements SchedulingConfigurer {
private final transient WorkflowTriggerService workflowTriggerService;
public Schedule(final WorkflowTriggerService workflowTriggerService) {
this.workflowTriggerService = workflowTriggerService;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
for (final WorkflowTrigger trigger : workflowTriggerService.getWorkflowTriggersWithSchedules()) {
taskRegistrar.addCronTask(new WorkflowTask(trigger), trigger.getSchedule());
}
}
}
Each WorkflowTrigger returned by the service has its own cron schedule, and therefore allows dynamic registration of scheduled tasks that are not known at compilation time.

Spring Autowired Shared Queue NullPointerException

I'm using Spring for the first time and am trying to implement a shared queue wherein a Kafka listener puts messages on the shared queue, and a ThreadManager that will eventually do something multithreaded with the items it takes off the shared queue. Here is my current implementation:
The Listener:
#Component
public class Listener {
#Autowired
private QueueConfig queueConfig;
private ExecutorService executorService;
private List<Future> futuresThread1 = new ArrayList<>();
public Listener() {
Properties appProps = new AppProperties().get();
this.executorService = Executors.newFixedThreadPool(Integer.parseInt(appProps.getProperty("listenerThreads")));
}
//TODO: how can I pass an approp into this annotation?
#KafkaListener(id = "id0", topics = "bose.cdp.ingest.marge.boseaccount.normalized")
public void listener(ConsumerRecord<?, ?> record) throws InterruptedException, ExecutionException
{
futuresThread1.add(executorService.submit(new Runnable() {
#Override public void run() {
try{
queueConfig.blockingQueue().put(record);
// System.out.println(queueConfig.blockingQueue().take());
} catch (Exception e){
System.out.print(e.toString());
}
}
}));
}
}
The Queue:
#Configuration
public class QueueConfig {
private Properties appProps = new AppProperties().get();
#Bean
public BlockingQueue<ConsumerRecord> blockingQueue() {
return new ArrayBlockingQueue<>(
Integer.parseInt(appProps.getProperty("blockingQueueSize"))
);
}
}
The ThreadManager:
#Component
public class ThreadManager {
#Autowired
private QueueConfig queueConfig;
private int threads;
public ThreadManager() {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
queueConfig.blockingQueue().take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Lastly, the main thread where everything is started from:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
ThreadManager threadManager = new ThreadManager();
try{
threadManager.run();
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
The problem
I can tell when running this in the debugger that the Listener is adding things to the queue. When the ThreadManager takes off the shared queue, it tells me the queue is null and I get an NPE. It seems like autowiring isn't working to connect the queue the listener is using to the ThreadManager. Any help appreciated.
This is the problem:
ThreadManager threadManager = new ThreadManager();
Since you are creating the instance manually, you cannot use the DI provided by Spring.
One simple solution is implement a CommandLineRunner, that will be executed after the complete SourceAccountListenerApp initialization:
#SpringBootApplication
public class SourceAccountListenerApp {
public static void main(String[] args) {
SpringApplication.run(SourceAccountListenerApp.class, args);
}
// Create the CommandLineRunner Bean and inject ThreadManager
#Bean
CommandLineRunner runner(ThreadManager manager){
return args -> {
manager.run();
};
}
}
You use SpringĀ“s programatic, so called 'JavaConfig', way of setting up Spring beans (classes annotated with #Configuration with methods annotated with #Bean). Usually at application startup Spring will call those #Bean methods under the hood and register them in it's application context (if scope is singleton - the default - this will happen only once!). No need to call those #Bean methods anywhere in your code directly... you must not, otherwise you will get a separate, fresh instance that possibly is not fully configured!
Instead, you need to inject the BlockingQueue<ConsumerRecord> that you 'configured' in your QueueConfig.blockingQueue() method into your ThreadManager. Since the queue seems to be a mandatory dependency for the ThreadManager to work, I'd let Spring inject it via constructor:
#Component
public class ThreadManager {
private int threads;
// add instance var for queue...
private BlockingQueue<ConsumerRecord> blockingQueue;
// you could add #Autowired annotation to BlockingQueue param,
// but I believe it's not mandatory...
public ThreadManager(BlockingQueue<ConsumerRecord> blockingQueue) {
Properties appProps = new AppProperties().get();
this.threads = Integer.parseInt(appProps.getProperty("threadManagerThreads"));
this.blockingQueue = blockingQueue;
}
public void run() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threads);
try {
while (true){
this.blockingQueue.take();
}
} catch (Exception e){
System.out.print(e.toString());
executorService.shutdownNow();
executorService.awaitTermination(1, TimeUnit.SECONDS);
}
}
}
Just to clarify one more thing: by default the method name of a #Bean method is used by Spring to assign this bean a unique ID (method name == bean id). So your method is called blockingQueue, means your BlockingQueue<ConsumerRecord> instance will also be registered with id blockingQueue in application context. The new constructor parameter is also named blockingQueue and it's type matches BlockingQueue<ConsumerRecord>. Simplified, that's one way Spring looks up and injects/wires dependencies.

Spring boot quartz 2.2.1 compilation error

This is the code i have used for saving new cron job to the postgresql database.But it is showing some compile time exceptions.I have originally migrated quartz 1.8.6 to 2.2.1 with spring boot web app.see the attachment.
private static final String JOBNAME = "sampleJobRunner";
private static final String JOB_GROUPNAME = "sampleGroup";
public void saveScheduledTime(String name) throws SchedulerException, ParseException {
Scheduler scheduler;
CronTrigger trigger;
String cronExp = null;
scheduler = this.quartzScheduler.getObject();
trigger = (CronTrigger) scheduler.getTrigger(name, JOB_GROUPNAME);
if (scheduler != null) {
cronExp = "0 0 12 ? 1 MON#1 *";
if (trigger != null) {
rescheduleJob(scheduler, trigger.getName(), cronExp);
} else {
createNewCronTrigger(scheduler, cronExp, name);
}
}
}
private void rescheduleJob(Scheduler scheduler, String triggerName, String cronExp)
throws SchedulerException, ParseException {
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerName, JOB_GROUPNAME);
trigger.setCronExpression(cronExp);
scheduler.rescheduleJob(triggerName, JOB_GROUPNAME, trigger);
}
private void createNewCronTrigger(Scheduler scheduler, String cronExp, String sentType)
throws SchedulerException, ParseException {
CronTrigger trigger = new CronTrigger(sentType, JOB_GROUPNAME, JOBNAME, JOB_GROUPNAME, cronExp);
scheduler.scheduleJob(trigger);
scheduler.start();
}
Spring boot quartz configuration
#Configuration
#ConditionalOnProperty(name = "quartz.enabled")
public class SchedulerConfig {
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory,
#Qualifier("sampleJobTrigger") Trigger sampleJobTrigger) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
// this allows to update triggers in DB when updating settings in config
// file:
factory.setOverwriteExistingJobs(true);
factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
// factory.setTriggers(sampleJobTrigger);
return factory;
}
#Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
#Bean
public JobDetailFactoryBean sampleJobDetail() {
return createJobDetail(SampleJob.class);
}
#Bean(name = "sampleJobTrigger")
public SimpleTriggerFactoryBean sampleJobTrigger(#Qualifier("sampleJobDetail") JobDetail jobDetail,
#Value("${samplejob.frequency}") long frequency) {
return createTrigger(jobDetail, frequency);
}
private static JobDetailFactoryBean createJobDetail(Class jobClass) {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
// job has to be durable to be stored in DB:
factoryBean.setDurability(true);
return factoryBean;
}
private static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(pollFrequencyMs);
factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
// in case of misfire, ignore all missed triggers and continue :
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
return factoryBean;
}
}
To fix the complilation errors in your code:
use scheduler.getTrigger(new TriggerKey(name, JOB_GROUPNAME));
use scheduler.rescheduleJob(new TriggerKey(name, JOB_GROUPNAME), trigger);
cast CronTrigger to CronTriggerImpl which has the setCronExpression() method
See my sample project for Spring Boot + Quartz on github: https://github.com/davidkiss/spring-boot-quartz-demo. I updated the SchedulerConfig class with a new createCronTrigger helper method that allows to create cron triggers in Spring Boot:
private static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
return factoryBean;
}
If you use Spring Boot in your project, I believe using the SchedulerConfig class is a cleaner way of configuring quartz jobs in Spring Boot then calling the saveScheduledTime() method.
In order to create any cron triggers, I'd add additional methods in the SchedulerConfig class to create the appropriate trigger beans similar to sampleJobTrigger.
This is purely a result of the migration from Quartz 1.8.6 to 2.2.1. Quartz did some code changes with 2.x version. You need to make changes in your application to respect that. Have a look at this: http://www.quartz-scheduler.org/documentation/quartz-2.x/migration-guide.html

Spring : #Scheduled task triggered through the #Controller and Websocket

I have an #Scheduled task which send data to a client every sec throught a websocket.
My need is to start running my scheduled task only when the client ask for it.
Instead of, my task starts when my server starts. it's not the behavior i want.
currently, I have a bean of my scheduled task which is declared in my SchedulingConfigurer :
#Configuration
#EnableScheduling
public class SchedulingConfigurer implements org.springframework.scheduling.annotation.SchedulingConfigurer {
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Bean
public ScheduledTask scheduledTask() {
return new ScheduledTask();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
}
}
Here is my spring controller code :
#MessageMapping("/hello")
public void greeting() throws Exception {
//How do I start my scheduled task here ?
}
Maybe isn't possible to do that with #Scheduled annotation and i have to use the TaskScheduler interface ?
remove #Scheduled declaration from ScheduledTask class
implements Runnable interface instead of
#Component
//#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ScheduledTask implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ScheduledTask.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
public void doWork() {
printMessage();
// TODO real work
}
private void printMessage() {
log.info("time to work: {}", dateFormat.format(new Date()));
}
#Override
public void run() {
doWork();
}
}
schedule Your task in controller area like this
#Controller
public class ScheduledTaskController {
#Autowired
private TaskScheduler taskScheduler;
#Autowired
private ScheduledTask scheduledTask;
#RequestMapping(value = "/task/run", method = RequestMethod.GET)
public String runTask() {
// start to run task every 5 sec.
taskScheduler.schedule(scheduledTask, new CronTrigger("0/5 * * * * ?"));
// ok, redirect
return "redirect:/task";
}
}
#Schedule is the declarative way, so not the point you're trying to achieve here.
You could create a Bean using one of the TaskScheduler implementations, such as ThreadPoolTaskScheduler and inject that bean in your application.
It has all the necessary methods to dynamically schedule tasks.

Resources