I am integrating Quartz with Spring boot and postgres.
I have created all the required tables for quartz.
Issue : The application starts but the Job is not getting executed as per the cron.
I want that the jobs should be automatically scheduled on start-up and should run as per the cron expression.
But right now the application starts and no trigger is fired.
The QuartzJobFactory class
public class QuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
The ConfigQuartz class
#Configuration
public class ConfigQuartz {
#Value("${quartz.dataSource.myDS.URL}")
String orgQuartzDatasourceMydsUrl;
#Value("${spring.datasource.username}")
String orgQuartzDatasourceMydsUser;
#Value("${spring.datasource.password}")
String orgQuartzDatasourceMydsPassword;
#Value("${quartz.enabled}")
Boolean isQuartzEnabled;
private static List<Trigger> triggers = new ArrayList<>();
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
QuartzJobFactory sampleJobFactory = new QuartzJobFactory();
sampleJobFactory.setApplicationContext(applicationContext);
return sampleJobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory)
throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(isQuartzEnabled);
// factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
return factory;
}
public Properties setQuartzProperties() {
Properties properties = new Properties();
properties.setProperty("org.quartz.dataSource.myDS.URL",orgQuartzDatasourceMydsUrl);
properties.setProperty("org.quartz.dataSource.myDS.user",orgQuartzDatasourceMydsUser);
properties.setProperty("org.quartz.dataSource.myDS.password",orgQuartzDatasourceMydsPassword);
return properties;
}
#Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
propertiesFactoryBean.setProperties(setQuartzProperties());
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
public static SimpleTriggerFactoryBean createTrigger(JobDetail jobDetail, long pollFrequencyMs,
String triggerName) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(0L);
factoryBean.setRepeatInterval(pollFrequencyMs);
factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
factoryBean.setName(triggerName);
// in case of misfire, ignore all missed triggers and continue :
factoryBean.setMisfireInstruction(
SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
return factoryBean;
}
// Use this method for creating cron triggers instead of simple triggers:
public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression,
String triggerName) throws Exception {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
factoryBean.setName(triggerName);
try {
factoryBean.afterPropertiesSet();
} catch (ParseException e) {
throw new Exception(e.getMessage());
}
triggers.add(factoryBean.getObject());
return factoryBean;
}
public 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;
}
}
The QuartzJobFactory class
public class QuartzJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
The CronExpression class
public class CronExpression {
private static final Map<String, String> cronExpressionMap = ImmutableMap
.<String, String>builder()
.put(TestJob.CLASS_NAME, "* 0 0 ? * * *")
.build();
public static String get(String key) {
return cronExpressionMap.get(key);
}
}
The TestJob class
#Component
#DisallowConcurrentExecution
public class TestJob implements Job {
public static final String CLASS_NAME = "TestJob";
private final String JOB_BEAN_NAME = CLASS_NAME + AppConstants.QUARTZ_JOB_SUFFIX;
private final String TRIGGER_BEAN_NAME = CLASS_NAME + AppConstants.QUARTZ_TRIGGER_SUFFIX;
#Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
LocalDateTime start = LocalDateTime.now();
System.out.println("*************** execute method is running *******");
}
#Bean(name = JOB_BEAN_NAME)
public JobDetailFactoryBean sampleJob() {
return ConfigQuartz.createJobDetail(this.getClass());
}
#Bean(name = TRIGGER_BEAN_NAME)
public CronTriggerFactoryBean sampleJobTrigger(
#Qualifier(JOB_BEAN_NAME) JobDetailFactoryBean jobDetail) throws Exception {
return ConfigQuartz.createCronTrigger(jobDetail.getObject(), CronExpression.get(CLASS_NAME),
CLASS_NAME);
}
}
The main class
EnableConfigurationProperties
#EntityScan(basePackages = {"com.example"})
#ComponentScan({"com.example"})
#SpringBootApplication
#EnableScheduling
public class QuartzSchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(QuartzSchedulerApplication.class, args);
}
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/quartz
spring.datasource.username=postgres
spring.datasource.password=mysecretpassword
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.show-sql=true
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.datasource.driverClassName=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=none
spring.datasource.hikari.maximum-pool-size=5
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
quartz.dataSource.myDS.URL=jdbc:postgresql://localhost:5432/quartz
quartz.enabled=true
The quartz.properties
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = PropScheduler
org.quartz.scheduler.instanceId = 10
#org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource=myDS
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 5000
org.quartz.scheduler.name=test-schedulers
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.myDS.driver=org.postgresql.Driver
org.quartz.dataSource.myDS.maxConnections = 5
org.quartz.dataSource.myDS.validationQuery = select 1
#============================================================================
# Configure trigger history loging , enble if needed more information regaring triggers
#============================================================================
#org.quartz.plugin.triggerHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin
#org.quartz.plugin.triggerHistory.triggerFiredMessage=Trigger [{1}.{0}] fired job [{6}.{5}] scheduled at: {2, date, dd-MM-yyyy HH:mm:ss.SSS}, next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
#org.quartz.plugin.triggerHistory.triggerCompleteMessage=Trigger [{1}.{0}] completed firing job [{6}.{5}] with resulting trigger instruction code: {9}. Next scheduled at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
#org.quartz.plugin.triggerHistory.triggerMisfiredMessage=Trigger [{1}.{0}] misfired job [{6}.{5}]. Should have fired at: {3, date, dd-MM-yyyy HH:mm:ss.SSS}
Your TriggerListener class (SimpleTriggerFactoryBean) doesn't seem to be implementing TriggerListener interface of quartz.
You can create custom class which implements TriggerListener and see if that works.
Reference
Related
I've tried the following methods to use the scheduler, but I can't find an answer, so I'd like to ask you a question.
The scheduler service is running exactly on time.
However, only 'sessionService.getUserInfo()' runs.
We are not doing any work on youtubeSearchService.searchYoutube and youtubeSearchService.searchYoutube.
Why there's only work one service. I wonder what to do to use multiple services.
#Component
public class SchedulerService {
private static final Logger logger = LoggerFactory.getLogger(SchedulerService.class);
#Autowired
private YoutubeSearchService youtubeSearchService;
#Autowired
private WeatherService weatherService;
#Autowired
private SessionService sessionService;
public static int i = 0;
public static int j = 0;
#Scheduled(cron="0 40 0/1 * * *")
public void weatherSchedulerService() throws Exception {
++i;
weatherService.insertRTweather(sessionService.getUserInfo());
}
#Scheduled(cron = "0 0/1 * * * *")
public void youtubeSearchSchedulerService() throws Exception {
++j;
youtubeSearchService.searchYoutube(sessionService.getUserInfo(),j);
}
}
I used to have challenges with spring scheduler, especially with annotation based config and finally use following solution I hope it helps:
SchedulerConfig.java using quartz
#Configuration
public class SchedulerConfig
{
private static final Logger logger = Logger.getLogger(SchedulerConfig.class.getName());
#Autowired
private ApplicationContext applicationContext;
#Bean
public SpringBeanJobFactory springBeanJobFactory()
{
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
logger.info("Configuring Job factory");
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job)
{
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
logger.info("Setting the Scheduler up");
schedulerFactory.setJobFactory(springBeanJobFactory());
schedulerFactory.setJobDetails(job);
schedulerFactory.setTriggers(trigger);
return schedulerFactory;
}
#Bean
public JobDetailFactoryBean jobDetail()
{
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(SchedulerService.class);
jobDetailFactory.setName("Qrtz_Job_Detail");
jobDetailFactory.setDescription("Invoke Sample Job service...");
jobDetailFactory.setDurability(true);
return jobDetailFactory;
}
#Bean
public CronTriggerFactoryBean trigger(JobDetail job)
{
CronTriggerFactoryBean trigger = new CronTriggerFactoryBean();
//three am
trigger.setCronExpression("0 0 3 * * ?");
trigger.setJobDetail(job);
trigger.setName("Qrtz_Trigger");
return trigger;
}
}
quartz.properties : put it in classptah
# thread-pool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=1
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
# job-store
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
# others
org.quartz.jobStore.misfireThreshold = 60000
Your scheduler service define as follow :
#Component
public class SchedulerService implements Job {
#Autowired
private YoutubeSearchService youtubeSearchService;
#Autowired
private WeatherService weatherService;
#Autowired
private SessionService sessionService;
public void execute(JobExecutionContext context) throws JobExecutionException
{
weatherService.insertRTweather(sessionService.getUserInfo());
}
}
if you want two define more than one schedule, you can it to schedulerconfig by adding a jobdetail and trigger.
Don't forget to add quartz jar file
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
I have implemented Spring Quartz scheduler example using this link
I am having simple MyJobTwo.java component that has a method executeInternal() that is being called using CronTriggerFactoryBean.
This is my QuartzConfiguration.java
#Configuration
#ComponentScan("com.example")
public class QuartzConfiguration {
// we need to create a bean that will excuted by MethodInvokingJobDetailFactoryBean
// in this case we have myJobOne is the simple bean
#Bean
public MethodInvokingJobDetailFactoryBean methodInvokingJobDetailFactoryBean() {
MethodInvokingJobDetailFactoryBean obj = new MethodInvokingJobDetailFactoryBean();
obj.setTargetBeanName("myJobOne");
obj.setTargetMethod("myTask");
return obj;
}
// This trigger will schedule the job after 3 seconds and repeat after every 30 seconds for 3+1 times.
#Bean
public SimpleTriggerFactoryBean simpleTriggerFactoryBean(){
SimpleTriggerFactoryBean stFactory = new SimpleTriggerFactoryBean();
stFactory.setJobDetail(methodInvokingJobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
stFactory.setRepeatInterval(30000);
stFactory.setRepeatCount(1);
return stFactory;
}
// We use it to configure complex job such as job scheduling using cron-expression
#Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(MyJobTwo.class);
// Map<String,Object> map = new HashMap<String,Object>();
// map.put("myJobOne", myJobOne);
// map.put(MyJobTwo.myJodOne, 1);
//factory.setJobDataAsMap(map);
//factory.setGroup("mygroup");
//factory.setName("myjob");
return factory;
}
// CronTriggerFactoryBean configures JobDetailFactoryBean
// We also configure start delay, trigger name, and cron-expression to schedule the job
#Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(){
CronTriggerFactoryBean stFactory = new CronTriggerFactoryBean();
stFactory.setJobDetail(jobDetailFactoryBean().getObject());
stFactory.setStartDelay(3000);
//stFactory.setName("mytrigger");
//stFactory.setGroup("mygroup");
stFactory.setCronExpression("0 0/1 * 1/1 * ? *");
return stFactory;
}
// SchedulerFactoryBean use to register the triggers
// those registered triggers will be executed
#Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setTriggers(cronTriggerFactoryBean().getObject());
//scheduler.setTriggers(simpleTriggerFactoryBean().getObject());
return scheduler;
}
}
This is the bean that I am executing using CronTriggerFactoryBean.
MyJobTwo.java
#Component
public class MyJobTwo extends QuartzJobBean {
private SmtpMailSender smtpMailSender;
#Autowired
public MyJobTwo(MyJobOne myJobOne, SmtpMailSender smtpMailSender) {
super();
this.myJobOne = myJobOne;
this.smtpMailSender = smtpMailSender;
}
#Override
protected void executeInternal(JobExecutionContext ctx)
throws JobExecutionException {
System.out.println("this is the test");
myJobOne.myTask();
System.out.println("task is done");
}
}
Whenever I am trying to inject other beans and service I am getting these errors. Anyone having any idea what is causing these errors, what changes do I need to make?
org.quartz.SchedulerException: Job instantiation failed
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:45)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:127)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:375)
Caused by: java.lang.InstantiationException: com.example.job.MyJobTwo
at java.lang.Class.newInstance(Class.java:427)
at org.springframework.scheduling.quartz.AdaptableJobFactory.createJobInstance(AdaptableJobFactory.java:58)
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:41)
... 2 common frames omitted
Caused by: java.lang.NoSuchMethodException: com.example.job.MyJobTwo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 4 common frames omitted
The default job factory implementation AdaptableJobFactory doesn't have autowiring capability.
To use dependency injection do following:
1.Create job factory
package com.concretepage.config;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
public Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job); //the magic is done here
return job;
}
}
Implementation is found on http://codrspace.com/Khovansa/spring-quartz-with-a-database/
2.Update schedulerFactoryBean declaration in QuartzConfiguration:
#Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean scheduler = new SchedulerFactoryBean();
scheduler.setTriggers(simpleTriggerFactoryBean().getObject(), cronTriggerFactoryBean().getObject());
scheduler.setJobFactory(jobFactory());
return scheduler;
}
#Bean
public JobFactory jobFactory() {
return new AutowiringSpringBeanJobFactory();
}
Use setter-based injection instead of constructor injection
I use the following configuration class to integrate spring framework with Quartz,It works fine,but the job fires dynamically once the application has started because I use #Configuration annotation,I want to fire the job manually by controller and ui.
How to Fire Quartz manually By Spring controller?
Quartz configuration class
#Configuration
public class QuartzConfig {
#Autowired
private PlatformTransactionManager transactionManager;
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
quartzScheduler.setTransactionManager(transactionManager);
quartzScheduler.setOverwriteExistingJobs(true);
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
Trigger[] triggers = {
processMyJobTrigger().getObject()
};
quartzScheduler.setTriggers(triggers);
return quartzScheduler;
}
#Bean
public JobDetailFactoryBean processMyJob() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(HelloJob.class);
jobDetailFactory.setDurability(true);
return jobDetailFactory;
}
#Bean
public CronTriggerFactoryBean processMyJobTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(processMyJob().getObject());
cronTriggerFactoryBean.setCronExpression("0 0/1 * * * ?");
return cronTriggerFactoryBean;
}
}
Quartz job
#Service
#Transactional
public class HelloJob implements Job{
#Inject
TestrecordRepository testrecordRepository;
#Inject
ScoreRepository scoreRepository;
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Hello Quartz!");
List<Testrecord> records=testrecordRepository.findAll();
for(Testrecord t:records){
Testrecord testrecord = new Testrecord();
testrecord.setValue_integer(t.getValue_integer());
testrecord.setId(t.getId());
RuleExecutor ruleExecutor = new RuleExecutor();
Score score= ruleExecutor.processRules(testrecord);
scoreRepository.save(score);
}
}
}
From the controller you need to get access to SchedulerFactoryBean, then take scheduler and trigger job you like.
Scheduler scheduler = (Scheduler) getApplicationContext().getBean("schedulerFactoryBean");
scheduler.triggerJob(JobKey jobKey)
Ive being from sometime trying to setup a little program that uses Spring and Quartz together to schedule a task. I followed some other similar answers with no luck.
At the moment I think I have all configured correctly, I see no more exceptions but my job looks like its not kicking off.
In the log.out that Spring generates, I see the following messages at the end:
2015-06-04T15:46:57.928 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver]
Searching for key 'spring.liveBeansView.mbeanDomain' in
[systemProperties] 2015-06-04T15:46:57.929 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver]
Searching for key 'spring.liveBeansView.mbeanDomain' in
[systemEnvironment] 2015-06-04T15:46:57.929 DEBUG
[org.springframework.core.env.PropertySourcesPropertyResolver] Could
not find key 'spring.liveBeansView.mbeanDomain' in any property
source. Returning [null]
I will show you my codes...
This is the class from which I start the scheduler:
public class JobRunner {
public static void main(String[] args) throws SchedulerException {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(WhatsTheTimeConfiguration.class);
AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory = new AutowiringSpringBeanJobFactory();
autowiringSpringBeanJobFactory.setApplicationContext(applicationContext);
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
}
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
I want to mention that If I use the method schedulerFactoryBean.getScheduler().start(), it throws me a null pointer exception on the scheduler, so thats why im calling start() on the factory.
The class AutowiringSpringBeanJobFactory was copy pasted from another answer here in stackoverflow. I decided to do that since all other answers where I found something was only configuration done via xml and I don't want to use xml.
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
This is the class that represents the Job that I want to trigger:
#Component
public class WhatsTheTimeManager extends QuartzJobBean {
#Autowired
private WhatsTheTime usecase;
#Autowired
private LocationRetriever locationDataProvider;
public WhatsTheTimeManager() {
}
#Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
usecase.tellMeWhatsTheTimeIn(locationDataProvider.allLocations());
}
public void setUsecase(WhatsTheTime usecase) {
this.usecase = usecase;
}
public void setLocationDataProvider(LocationRetriever locationDataProvider) {
this.locationDataProvider = locationDataProvider;
}
}
My Spring configuration is doing component scanning, its very simple:
#Configuration
#ComponentScan(basePackages = "com.springpractice")
public class WhatsTheTimeConfiguration {
}
From this point everything I have are just some interfaces, components and a domain object, but I will paste them also, just in case I forgot something:
public interface LocationRetriever {
List<String> allLocations();
}
public interface TimeOutputRenderer {
TimeReport renderReport(String timeInLocation, String location);
}
public interface TimeRetriever {
String timeFor(String location);
}
#Component
public class LocationRetrieverDataProvider implements LocationRetriever{
public LocationRetrieverDataProvider() {
}
#Override
public List<String> allLocations() {
return asList("Europe/London", "Europe/Madrid", "Europe/Moscow", "Asia/Tokyo", "Australia/Melbourne", "America/New_York");
}
}
#Component
public class TimeOutputRendererDataProvider implements TimeOutputRenderer {
public TimeOutputRendererDataProvider() {
}
#Override
public TimeReport renderReport(String location, String time) {
System.out.println(location + " time is " + time);
return new TimeReport(location, time);
}
}
#Component
public class TimeRetrieverDataProvider implements TimeRetriever {
public TimeRetrieverDataProvider() {
}
#Override
public String timeFor(String location) {
SimpleDateFormat timeInLocation = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
timeInLocation.setTimeZone(TimeZone.getTimeZone(location));
return timeInLocation.format(new Date());
}
}
Just one last detail, that maybe is of interest.
The versions I am using in my libraries are the following:
quartz 2.2.1
spring 4.1.6.RELEASE
When I run the appliaction, I expect the times of those countries to be printed every second, but it doesn't happen.
If you want to clone the code and try for yourself and see, you can find it at this git repo(Feel free to fork if you want): https://github.com/SFRJ/cleanarchitecture
The main error in your code is that you're not letting Spring handle the scheduling for you.
While you can use Quartz in code as any other code, the idea of the integration with Spring is to tell Spring about the work you want to be done and let Spring do the hard work for you.
In order to allow Spring to run the Quartz scheduling, you need to declare the Job, the JobDetail and the Trigger as Beans.
Spring only handles Beans if they are created through the Spring life-cycle (i.e. using annotations or XML) but not if the objects are created in code with a new statement.
The following code needs to be removed from JobRunner.java:
SpringBeanJobFactory springBeanJobFactory = new SpringBeanJobFactory();
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobFactory(springBeanJobFactory);
schedulerFactoryBean.start();
...
private static SimpleTrigger trigger() {
return newTrigger()
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
That code will have to be re-written into WhatsTheTimeConfiguration.java, and here's how it looks now:
#Configuration
#ComponentScan(basePackages = "com.djordje.cleanarchitecture")
public class WhatsTheTimeConfiguration {
#Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setTriggers(trigger());
schedulerFactoryBean.setJobDetails(jobDetail());
schedulerFactoryBean.setJobFactory(springBeanJobFactory());
return schedulerFactoryBean;
}
#Bean
public SpringBeanJobFactory springBeanJobFactory() {
return new AutowiringSpringBeanJobFactory();
}
#Bean
public JobDetail jobDetail() {
JobDetailImpl jobDetail = new JobDetailImpl();
jobDetail.setKey(new JobKey("WhatsTheTime"));
jobDetail.setJobClass(WhatsTheTimeManager.class);
jobDetail.setDurability(true);
return jobDetail;
}
#Bean
public SimpleTrigger trigger() {
return newTrigger()
.forJob(jobDetail())
.withIdentity("whatsTheTimeJobTrigger", "jobsGroup1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
}
}
SchedulerFactoryBean is now a Bean and will be handled and initialized by Spring, and so are SimpleTrigger and AutowiringSpringBeanJobFactory.
I added the missing JobDetail class which was missing and added the necessary wiring to SimpleTrigger and SchedulerFactoryBean. They both need to know about JobDetail which is the only place that knows which class is the job class that needs to be triggered.
I have the QuartzConfig configuration class that have three beans ,How can I call this beans in spring mvc controller method.
This beans executed automatically once I start application,But I don't want that I want to execute this beans by controller
Quartz configuration class
#Configuration
public class QuartzConfig {
#Autowired
private PlatformTransactionManager transactionManager;
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
quartzScheduler.setTransactionManager(transactionManager);
quartzScheduler.setOverwriteExistingJobs(true);
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
Trigger[] triggers = {
processMyJobTrigger().getObject()
};
quartzScheduler.setTriggers(triggers);
return quartzScheduler;
}
#Bean
public JobDetailFactoryBean processMyJob() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(HelloJob.class);
jobDetailFactory.setDurability(true);
return jobDetailFactory;
}
#Bean
public CronTriggerFactoryBean processMyJobTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(processMyJob().getObject());
cronTriggerFactoryBean.setCronExpression("0 0/1 * * * ?");
return cronTriggerFactoryBean;
}
}
Quartz job
#Service
#Transactional
public class HelloJob implements Job{
#Inject
TestrecordRepository testrecordRepository;
#Inject
ScoreRepository scoreRepository;
public void execute(JobExecutionContext context)
throws JobExecutionException {
System.out.println("Hello Quartz!");
List<Testrecord> records=testrecordRepository.findAll();
for(Testrecord t:records){
Testrecord testrecord = new Testrecord();
testrecord.setValue_integer(t.getValue_integer());
testrecord.setId(t.getId());
RuleExecutor ruleExecutor = new RuleExecutor();
Score score= ruleExecutor.processRules(testrecord);
scoreRepository.save(score);
}
}
}