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.
Related
I started to create a cron job using Quartz in my Java web application to execute one of cleanup task. The actual job is firing as expected and no
problem with the Quatrz job execution. From the below code snippet When I try to use MyService to call the required method as part of job, Dependency is not working.
Object for the MyService class is not created. So I'm getting a null pointer exception. To avoid this I tried to use Google Guice (tried dogger as well) to manage
dependency injection but MyService object creation depends on other classes.
Let's say in this example CustomerService and this CustomerService depends on other classes and those classes depends on some other etc.. So I am going to end up creating object creation chain by using Guice or Dogger.
Code snippet:
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import org.quartz.Job;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
#Singleton
public class CustomJobFactory implements JobFactory {
#Inject
private Injector injector;
#Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
return (Job) injector.getInstance(bundle.getJobDetail().getJobClass());
}
}
public class QuartzJobHelper {
private final Scheduler scheduler;
#Inject
public QuartzSchedulerImpl(Scheduler scheduler, CustomJobFactory customJobFactory) {
this.scheduler = scheduler;
try {
this.scheduler.setJobFactory(customJobFactory);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void executeJob() throws Exception {
// The Guice injector used to create instances.
Injector injector = Guice.createInjector(new MyModule());
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("myjob", "mygroup")
.build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("mytrigger", "mygroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// Inform Quartz scheduler to use our factory.
scheduler.setJobFactory((injector.getInstance(CustomJobFactory.class));
scheduler.scheduleJob(jobDetail, trigger);
// Start the scheduler.
scheduler.start();
}
}
import org.quartz.spi.JobFactory;
import java.io.IOException;
public class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(JobFactory.class).to(MyJobFactory.class);
bind(MyService.class).to(MyServiceImpl.class);
bind(CustomerService.class).to(CustomerServiceImpl.class);
bind(MyDao.class).to(MyDaoImpl.class);
bind(CustomCache.class).to(CustomCacheImpl.class);
bind(ProxyDao.class).to(ProxyDaoImpl.class);
bind(RequestDao.class).to(DefaultRequestDao.class);
|
|
|
etc.. Likewise so many dependencies are there to inject but finding 100's of dependencies.
}
}
public class MyJob extends org.quartz.Job {
#Inject
MyService service;
public void execute(JobExecutionContext jobContext) throws JobExecutionException {
service.cleanupJunkData();
}
}
class MyService {
void cleanupJunkData();
}
#Named
class MyServiceImpl implments MyService {
private CustomerService cs;
private MyDao dao;
private CustomCache cache;
#Inject
public MyServiceImpl(CustomerService cs, MyDao dao, CustomCache cache) {
this.cs = cs;
this.dao=dao;
this.cache = cache;
}
#Override
public void cleanupJunkData() {
List<Customer> customerData = cs.getCustomerData(dao.getExpiredCustId());
cache.clear(customerData);
}
}
public Class MainClass {
public static void main(Stirng[] args) {
QuartzJobHelper.executeJob();
}
}
Could someone suggest how to deal with object creation in Quartz job?
You haven't provided any Guice specific code in your example, but you want to create a JobFactory, which will be responsible for injecting each job as they are fired:
public class GuiceJobFactory implements JobFactory {
#Inject
private Injector guiceInjector;
#Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
return (Job) guiceInjector.getInstance(bundle.getJobDetail().getJobClass());
}
}
You can create this however you choose, but you'll need to somehow get the Injector into it.
Edit
From your comment, I don't really understand. We have the following setup (with multiple jobs):
JobThatDoesSomething:
#Inject Service1 service1;
Service1:
#Inject Dao1 dao1;
#Inject Dao2 dao2;
#Inject ValidationFactory validationFactory;
ValidationFactory:
#Inject ValidationProvider provider;
This is at a high level the hierarchy of just a single job. We have about 100 that have the same level of injected dependencies. From what I can tell, this is exactly the same as you?
What are you saying? That with this factory your:
MyService is not injected?
MyService.CustomerService is not injected?
Etc?
Edit 2
You need to create your injector somehow. I've added your main code, with an example of how you would use your CustomModule and JobFactory:
public Class MainClass {
public static void main(Stirng[] args) {
// You need to create your injector here
Injector injector = Guice.createInjector(new CustomModule());
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler scheduler = schedFact.getScheduler();
scheduler.setJobFactory(new GuiceJobFactory(injector)); // Use it here
scheduler.start();
// define the job and tie it to our HelloJob class
JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
JobDataMap data = new JobDataMap();
data.put("latch", this);
JobDetail jobDetail = jobBuilder.usingJobData("example", "com.QuartzSchedulerExample")
.usingJobData(data)
.withIdentity("myJob", "group1")
.build();
// Fire at curent time + 1 min every day
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startAt(DateBuilder.todayAt(10, 20, 20))
.withSchedule(CronScheduleBuilder.cronSchedule("0 " + (min + 1) + " " + hour + " * * ? *"))
.build();
// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(jobDetail, trigger);
}
}
I am trying to use Spring AOP with Quartz job spring beans. The jobs are autowired into spring container using the following approach:
a. Create AutowiringSpringBeanJobFactory extending SpriongBeanJobFactory implementing ApplicationContextAware
b. override createJobInstance and use the AutowiringCapableBeanFactory to autowire the job beans.
See solution
Spring boot Application Configuration:
#Configuration
public class MyApplicationConfig {
#Bean
#Primary
public SchedulerFactoryBean schedulerFactoryBean() throws Exception {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(ApplicationContextProvider.getContext());
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setDataSource(datasource());
factoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
factoryBean.setFactory(jobFactory);
}
}
Autowiring of Jobs done as:
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext() {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
Using this setup, I have created an Aspect:
#Aspect
#Configuration
public class MyAspect {
#Around("execution(public void com.myapp.jobs.*.execute(..))")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
// do something with pjp
}
}
This is my sample job:
#Service
public class Myjob implements Job {
#Autowired
IAuditService auditService;
public void execute(JobExecutionContext jctx) throws JobExecutionException {
//do something
}
}
However, when the above job executes, spring aop does not invoke MyAspect.process().
I am already autowring my job beans and I am able to autowire other beans into my jobs but only the aspect does not get invoked. Whats missing here?
First of all you can use #Component instead of #Configuration as annotation of your aspect, thus it will look like this:
#Aspect
#Component
public class MyAspect {
#Around("execution(public void com.myapp.jobs.*.execute(..))")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
// do something with pjp
}
}
Secondly make sure that the pointcut expression is correct, whether you are referring to the right package/classe/method.
Lastly, if you are not using spring boot you should add #EnableAspectJAutoProxy to your configuration class otherwise no need for that with spring boot as it's auto configured !
To enable AOP for my jobs, i had to enhance "AutowiringSpringBeanJobFactory " to manually weave aspects in for the jobs as follows:
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
private Object jobAspect;
public void setJobAspect(Object aspect) {
this.jobAspect = aspect;
}
#Override
public void setApplicationContext() {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
AspectJProxyFactory pFactory = new AspectJProxyFactory(job);
pFactory.addAspect(this.jobAspect);
return pFactory.getProxy();
}
}
However, there are additional issues with this if your job uses annotations on its method(s). That's for another time though.
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
In an application, since I converted it from a classical Spring webapp (deployed in a system Tomcat) to a Spring Boot (V1.2.1) application I face the problem that the Quartz-based scheduled jobs are not working anymore.
I schedule these Quartz jobs like this:
// My own Schedule object which holds data about what to schedule when
Schedule schedule = scheduleService.get(id of the schedule);
String scheduleId = schedule.getId();
JobKey jobKey = new JobKey(scheduleId);
TriggerKey triggerKey = new TriggerKey(scheduleId);
JobDataMap jobData = new JobDataMap();
jobData.put("scheduleId", scheduleId);
JobBuilder jobBuilder = JobBuilder.newJob(ScheduledActionRunner.class)
.withIdentity(jobKey)
.withDescription(schedule.getName())
.usingJobData(jobData);
JobDetail job = jobBuilder.build();
TriggerBuilder triggerBuilder = TriggerBuilder.newTrigger()
.forJob(jobKey)
.withIdentity(triggerKey)
.withDescription(schedule.getName());
triggerBuilder = triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(schedule.toCronExpression()));
Trigger trigger = triggerBuilder.build();
org.quartz.Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.scheduleJob(job, trigger);
ScheduledActionRunner:
#Component
public class ScheduledActionRunner extends QuartzJobBean {
#Autowired
private ScheduleService scheduleService;
public ScheduledActionRunner() {
}
#Override
public void executeInternal(final JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
final JobDataMap jobDataMap = context.getMergedJobDataMap();
final String scheduleId = jobDataMap.getString("scheduleId");
final Schedule schedule = scheduleService.get(scheduleId);
// here it goes BANG since scheduleService is null
}
}
ScheduleService is a classical Spring service which fetches data from Hibernate.
As I said above, this worked fine until I moved to Spring Boot.
When I implemented this code with the classical Spring application, SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); did the trick to take care of autowiring the service.
What is needed to make this work again in the Spring Boot environment ?
Edit:
At the end I chose to move away from using Quartz in favour of Spring's ThreadPoolTaskScheduler.The code was much simplified and it works as expected.
The SpringBeanAutowiringSupport uses the web application context, which is not available in your case. If you need a spring managed beans in the quartz you should use the quartz support provided by spring. This will give you full access to all the managed beans.
For more info see the quartz section at spring docs at http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html. Also see following example of usage quartz with spring managed beans. Example is based on your code.
So you can change the first code snippet (where the quartz initialization is done) with follwoing spring alternatives.
Create job detail factory
#Component
public class ScheduledActionRunnerJobDetailFactory extends JobDetailFactoryBean {
#Autowired
private ScheduleService scheduleService;
#Override
public void afterPropertiesSet() {
setJobClass(ScheduledActionRunner.class);
Map<String, Object> data = new HashMap<String, Object>();
data.put("scheduleService", scheduleService);
setJobDataAsMap(data);
super.afterPropertiesSet();
}
}
Create the trigger factory
#Component
public class ActionCronTriggerFactoryBean extends CronTriggerFactoryBean {
#Autowired
private ScheduledActionRunnerJobDetailFactory jobDetailFactory;
#Value("${cron.pattern}")
private String pattern;
#Override
public void afterPropertiesSet() throws ParseException {
setCronExpression(pattern);
setJobDetail(jobDetailFactory.getObject());
super.afterPropertiesSet();
}
}
And finally create the SchedulerFactory
#Component
public class ActionSchedulerFactoryBean extends SchedulerFactoryBean {
#Autowired
private ScheduledActionRunnerJobDetailFactory jobDetailFactory;
#Autowired
private ActionCronTriggerFactoryBean triggerFactory;
#Override
public void afterPropertiesSet() throws Exception {
setJobDetails(jobDetailFactory.getObject());
setTriggers(triggerFactory.getObject());
super.afterPropertiesSet();
}
}
My answer not fully matches to you question, but Spring expose you another ability - to start cron-expression based scheduler on any service.
Using Spring.Boot you can configure your application to use scheduler by simple placing
#EnableScheduling
public class Application{
....
After that just place following annotation on public(!) method of #Service
#Service
public class MyService{
...
#Scheduled(cron = "0 * * * * MON-FRI")
public void myScheduledMethod(){
....
}
I am trying to code a batch job using Spring Batch Java configuration and Quartz as the scheduler.
Relevant code snippet for java configuration :-
#Configuration
#EnableBatchProcessing
#EnableScheduling
public class BatchProcessingConfig {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private EntityManagerFactory entityManagerFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
#Qualifier("theJob")
public Job job() {
return jobBuilderFactory.get("theJobName")
.start(jobStep()).build();
}
#Bean
public Step jobStep() {
StepBuilder stepBuilder = stepBuilderFactory.get("stepName");
SimpleStepBuilder<Person, Person> chunk = stepBuilder
.<Person, Person> chunk(10);
return chunk.reader(personeReader())
.processor(jobProcessor()).build();
}
#Bean
public JobProcessor<Person, Person> invoiceRejobProcessorminderProcessor() {
return new JobProcessor();
}
The job has been wired the ItemReader and ItemProcessor.
I am able to successfully Junit Test the Job/Step using JobLauncherTestUtils class.
Problem :-
How can I configure Quartz scheduler to run the above configured Job.
All the samples & examples I could explore have created their own Job class by extending QuartzJobBean but I want my above configured Job to be scheduled for running.
How do I unit test the scheduler functionality.
This is how I did it, when I had such a requirement
public class Schedule {
public Schedule()throws Exception{
SchedulerFactory sf=new StdSchedulerFactory();
Scheduler sched=sf.getScheduler();
sched.start();
JobDetail jd=new JobDetailImpl("myjob",sched.DEFAULT_GROUP,QuartzJob.class);
SimpleTrigger st=new SimpleTriggerImpl("mytrigger",sched.DEFAULT_GROUP,new Date(),
null,SimpleTrigger.REPEAT_INDEFINITELY,60L*1000L);
sched.scheduleJob(jd, st);
}
public static void main(String args[]){
try{
new Schedule();
}catch(Exception e){}
}
}
==================================
public class QuartzJob implements Job{
#Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
String[] springConfig =
{
"resources\\spring-batch.xml" ,"SPRING_BATCH_JOB_NAME"
};
CommandLineJobRunner.main( springConfig );
System.out.println("Done");
}
}