How to bring Quartz scheduler to standby - spring

I have quartz job implementation using spring. My scheduler works fine and jobs are getting executed perfectly.
My question is how to bring this scheduler to standby? So that no jobs gets triggered once I decide to bring the scheduler to standby mode.
Below is the job class
public class MyJobClass extends QuartzJobBean {
//my job logic
}
Snippet from applicationContext_Scheduler.xml
<bean name="myJobBean"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="MyJobClass" />
</bean>
<bean id="rsHourlyJobCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJobBean" />
<property name="cronExpression" value="00 0/1 * * * ?" />
</bean>

Instead of creating the scheduler bean in xml. I'd make it programatically as follows:
#Configuration
public class QuartzSchedulerConfiguration {
#Autowired
private ApplicationContext applicationContext;
#Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("Quartz Scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
#Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
public static ApplicationContext getContext() {
return context;
}
}
This will create your quartz scheduler bean that is application context aware allowing you to autowire spring objects into your quartz jobs.
Then create some Scheduling service like:
#Service
public class SchedulerService {
#Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
#PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
}
public void standBy() throws Exception {
if (scheduler != null && !scheduler.isInStandbyMode()) {
scheduler.standby();
}
}
}
Then add other methods that you need for scheduling.

Related

Initilizing a constructor and inject a Repository bean of Jpa in Quartz [duplicate]

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

How to autowired in quartz?

Previously I had set it up to autowired in a quartz job.
Note here.
But, the autowired of the job inner class will fail.
My job code example is here.
public class MyJob extends QuartzJobBean {
#Autowired
private Hello hello; //<--- this is suceess!
#Override
public void executeInternal(JobExecutionContext context) {
//...
Do do = new Do();
do.doSomething();
//...
}
}
Do.java
public class Do {
#Autowired
priavte Do2 do2; // <---- ***this is null !***
//...
}
Why is this happening?
How do I solve it and what concepts should I know more?
Quartz jobs are not ran in the same context as spring so autowired objects become null within the same class. You have to make Quartz Spring aware.
First add sprint-context-support
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
Then create a Application Context Holder that is Application Context Aware
#Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
public static ApplicationContext getContext() {
return context;
}
}
Then you can create your Quartz Scheduler Configuration Class
#Configuration
public class QuartzSchedulerConfiguration {
#Autowired
private ApplicationContext applicationContext;
/**
* Create the job factory bean
* #return Job factory bean
*/
#Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* Create the Scheduler Factory bean
* #return scheduler factory object
*/
#Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("My Scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
Now this will place your quartz scheduler in the same context as Spring, so you can now create a SchedulerService class.
#Service
public class SchedulerService {
#Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
/**
* Initialize the scheduler service
*/
#PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
}
}
now you can populate this class with methods to create your schedules using the scheduler object and when the task is triggered the class that extends Job will be context aware with spring and the autowired objects will no longer be null
to address the follow up question implement the ApplicationContextHolder component then autowire it into your SchedulerConfig class
#Autowire
ApplicationContextHolder holder
#Bean
// injecting SpringLiquibase to ensure liquibase is already initialized and created the quartz tables:
public JobFactory jobFactory(SpringLiquibase springLiquibase) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(holder);
return jobFactory;
}

Spring with Quartz, using SchedulerFactoryBean in custom service

I have job with a bean injected to it. I've acheaved using this solution.
In this solution the job trigger is setted during configuration of SchedulerFactoryBean bean in the config class.
But I want to schedule it in my custom SchedulerService service by colling scheduleTrackRetry method.
I try something like this(see below) but the job is not fired up at the appropriate time.
#Service
public class SchedulerService {
#Autowired
SchedulerFactoryBean quartzScheduler;
#Autowired
JobDetailFactoryBean jobDetailFactoryBean;
#Autowired
CronTriggerFactoryBean cronTriggerFactoryBean;
public void scheduleTrackRetry() {
cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
quartzScheduler.setTriggers(cronTriggerFactoryBean.getObject());
}
So, please tell me how could I acheave the desired behaviour?
Here is my job and conf classes:
#Component
public class TrackRetryJob implements Job {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackRetryJob.class);
#Autowired
private TimeformBatchService timeformBatchService;
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
LOGGER.info("Run retry for fetching greyhound tracks");
timeformBatchService.consumeTracks();
}
}
#Configuration
public class QuartzConfig {
#Autowired
private ApplicationContext applicationContext;
#Bean
public SchedulerFactoryBean quartzScheduler() {
SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
// custom job factory of spring with DI support for #Autowired!
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
quartzScheduler.setJobFactory(jobFactory);
quartzScheduler.setTriggers(getTrigger().getObject());
return quartzScheduler;
}
#Bean
public JobDetailFactoryBean retryTrackFetch() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
jobDetailFactory.setJobClass(TrackRetryJob.class);
jobDetailFactory.setGroup("group1");
return jobDetailFactory;
}
#Bean
public CronTriggerFactoryBean getTrigger() {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(retryTrackFetch().getObject());
cronTriggerFactoryBean.setCronExpression("0 53 * * * ?");
cronTriggerFactoryBean.setGroup("group1");
return cronTriggerFactoryBean;
}
}
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;
}
}
I found the solution here
I could not use SchedulerFactoryBean as a normal bean. When I try to inject it, spring inject Scheduler bean. Therefore my service should look like this:
#Service
public class SchedulerService {
#Autowired
Scheduler scheduler;
}

pass job parameters to custom writer Spring batch

I have a custom writer with a FlatFileItemWriter and i want to pass a job parameter( a output file) defined in the main class
How can i deal with this ?
Thank you very much
CustomWriter
public class PersonItemWriter implements ItemWriter<Person> {
private FlatFileItemWriter<String> flatFileItemWriter = new FlatFileItemWriter<String>();
private Resource resource;
#Override
public void write(List<? extends Person> personList) throws Exception {
flatFileItemWriter.setResource(new FileSystemResource(resource.getFile()));
PassThroughLineAggregator<String> aggregator = new PassThroughLineAggregator<String();
flatFileItemWriter.setLineAggregator(aggregator);
flatFileItemWriter.open(new ExecutionContext());
flatFileItemWriter.write(Arrays.asList(aggregator.aggregate("test")));
flatFileItemWriter.close();
}
public void setResource(Resource resource) {
this.resource = resource;
}
}
Launcher
JobLauncher jobLauncher = (JobLauncher) applicationContext.getBean("jobLauncher");
Job job = (Job) applicationContext.getBean("personJob");
/* Parameters sent to job */
JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
jobParametersBuilder.addString("outputFileName", "file:" + personFile); // pass this to the itemWriter
configuration job xml
<bean id="personWriter" class="com.dev.writer.PersonItemWriter" scope="step>
<property name="resource" value="#{jobParameters[outputFileName]}" />
</bean>
You have to declare the bean with either step scope or job scope so you can have late binding of a property based on the job parameter:
<bean id="personWriter" class="com.dev.writer.PersonItemWriter" scope="step">
<property name="resource" value="#{jobParameters[outputFileName]}" />
</bean>
These scopes are not available by default, you need to include them either by either using the batch namespace or defining the following bean:
<bean class="org.springframework.batch.core.scope.StepScope" />
Update:
Here's the complete writer:
public class PersonItemWriter implements ItemWriter<Person> {
FlatFileItemWriter<String> flatFileItemWriter = new FlatFileItemWriter<String>();
private Resource resource;
#Override
public void write(List<? extends Person> personList) throws Exception {
flatFileItemWriter.setResource(resource);// how the pass the job parameter file here
PassThroughLineAggregator<String> aggregator = new PassThroughLineAggregator<String();
flatFileItemWriter.setLineAggregator(aggregator);
aggregator.aggregate("test"); // do not save in output file
}
public FlatFileItemWriter<String> getFlatFileItemWriter() {
return flatFileItemWriter;
}
public void setFlatFileItemWriter(FlatFileItemWriter<String> flatFileItemWriter) {
this.flatFileItemWriter = flatFileItemWriter;
}
public void setResource(Resource resource) {
this.resource = resource;
}
}
You can define a HashMap and use this HashMap instead of jobParameter.
<bean id="paramBean" class="java.util.HashMap"/>
<bean id="personWriter" class="com.dev.writer.PersonItemWriter" scope="step">
<property name="resource" value="#{paramBean[outputFileName]}" />
</bean>
Write the setter method in ItemWriter and set the values in the HashMap.
private HashMap paramBean;
public void setParamBean(HashMap paramBean) {
this.paramBean= paramBean;
}
paramBean.set(<key>,<value>);

How to inject bean in a Tapestry service

I want to inject bean in a Tapestry service (not in a page).
For the moment, I use this :
public class EntityRealm extends AuthorizingRealm {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
It works, but I want use this :
public class EntityRealm extends AuthorizingRealm {
#Inject
private SecurityServices securityServices;
And my applicationContext is in the web.xml.
In this second case, the injection doesn't work. Why ?
AppModule.java :
public class AppModule
{
//#Resource(name = "realm")
#Inject
private static EntityRealm realm;
#Contribute(WebSecurityManager.class)
public static void addRealms(Configuration<EntityRealm> configuration) {
//EntityRealm realm = new EntityRealm();
configuration.add(realm);
}
public static void contributeFactoryDefaults( MappedConfiguration<String, Object> configuration)
{
configuration.override(SecuritySymbols.LOGIN_URL, "/login");
configuration.override(SecuritySymbols.UNAUTHORIZED_URL, "/login");
configuration.override(SecuritySymbols.SUCCESS_URL, "/index");
configuration.override(SymbolConstants.APPLICATION_VERSION, "2.0-SNAPSHOT");
}
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> configuration)
{
configuration.add(SymbolConstants.HMAC_PASSPHRASE, new BigInteger(130, new SecureRandom()).toString(32));
configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en,fr");
configuration.add( "tapestry.default-cookie-max-age", "31536000" );
}
public RequestFilter buildTimingFilter(final Logger log)
{
return new RequestFilter()
{
public boolean service(Request request, Response response, RequestHandler handler)
throws IOException
{
long startTime = System.currentTimeMillis();
try
{
return handler.service(request, response);
} finally
{
long elapsed = System.currentTimeMillis() - startTime;
log.info(String.format("Request time: %d ms", elapsed));
}
}
};
}
public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration,
#Local
RequestFilter filter)
{
configuration.add("Timing", filter);
}
}
And the EntityRealm.java :
public class EntityRealm extends AuthorizingRealm {
//***************************************
//************* Attributes *************
//***************************************
//ApplicationContext ctx = new ClassPathXmlApplicationContext("/application-context-security.xml");
//SecurityServices securityServices = (SecurityServices)ctx.getBean("securityServices");
//#Resource(name = "securityServices")
#Inject
private SecurityServices securityServices;
//***************************************
//************ Constructors *************
//***************************************
public EntityRealm() {
super(new MemoryConstrainedCacheManager());
setName("myapprealm");
setAuthenticationTokenClass(UsernamePasswordToken.class);
}
//***************************************
//********** Public Methods *************
//***************************************
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) throw new AuthorizationException("PrincipalCollection was null, which should not happen");
application-context.xml :
<bean id="realm" class="net.atos.m2m.telecom.ihm.services.EntityRealm">
<property name="securityServices" ref="securityServices"></property>
</bean>
<bean id="securityServices" class="net.atos.m2m.telecom.ihm.applicatif.services.security.impl.SecurityServicesImpl">
<property name="servicesTelSecu" ref="servicesTelSecu"></property>
<property name="converterSecDSPtoDTO" ref="converterSecDSPtoDTO"></property>
<property name="converterSecDTOtoDSP" ref="converterSecDTOtoDSP"></property>
</bean>
Can you help me ?
Thank you.
How i say in previous comment, if you create EntityRealm in this way .. new EntityRealm() the inject\autowire does not work.
You must define EntityRealm as bean .. XML or Annotation.
<bean id="entityRealm" class="package.EntityRealm"/>
<bean id="securityServices" class="package.SecurityServices"/>
You can use #Resource instead,
#Resource(name = "securityServices")
private SecurityServices securityServices;
And make sure that application-context-security.xml file is loaded by Spring.

Resources