i'm using Spring-Version: 2.0.7, do i need to download quartz libraries and their dependencies to use it ? cause at first i though it was needed but its giving me an java.lang.IncompatibleClassChangeError.
So i figured that maybe it was integrated in the spring.jar since according to the 2.5 spring the bean is invoked on the application context trhough the spring library.
How ever when i removed quarta.jar, i can´t acces the JobExecutionContext class. Here is my bean declaration:
<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.bamboo.common.util.CheckAndProcessFilesJob" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5" />
</map>
</property>
</bean>
The java code
package com.bamboo.common.util;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* Created by IntelliJ IDEA.
* User: ernestobuttolazio
* Date: 19-may-2011
* Time: 16:44:54
* To change this template use File | Settings | File Templates.
*/
public class CheckAndProcessFilesJob extends QuartzJobBean {
private int timeout;
private int contador;
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailBean (5)
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
// do the actual work
contador += timeout;
}
}
You need to use quartz 2.1.6. JobDetailFactoryBean should be used in place of JobDetailBean.The configuration will look like,
<bean name="sampleJobDetail"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass"
value="com.altisource.scheduler.jobs.SampleJob" />
<property name="jobDataAsMap">
<map>
</map>
</property>
</bean>
<bean id="sampleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="sampleJobDetail" />
<property name="startDelay" value="30000" />
<property name="repeatInterval" value="30000" />
<property name="group" value="YOURCHOICE" />
</bean>
If your are using quartz with clustering, the scheduler factory org.springframework.scheduling.quartz.SchedulerFactoryBean will take only property triggers and restrict providing jobDetails. Make sure you configure scheduler factory org.springframework.scheduling.quartz.SchedulerFactoryBean with only triggers.
You need to use Quartz 1.8.x or earlier with Spring's current wrappers. Quartz 2.0 does not work with them.
You can use Quartz 2.0 along side Spring no problem - just not with Spring's wrappers.
Quartz 2.x versions are not compatible with spring. Some classes of the earlier versions have been converted to interfaces in quartz 2.x. But still it can be used with spring, without the help of it's integration support.
Check out this link http://shyarmal.blogspot.com/2011/07/quartz-20-schedule-cron-with-spring.html.
Related
I have a spring integration flow triggered by the cron expression like follows:
<int-ftp:inbound-channel-adapter id="my-input-endpoint" ...>
<int:poller trigger="my-trigger"/>
</int-ftp:inbound-channel-adapter>
<bean id="my-trigger"
class="org.springframework.scheduling.support.CronTrigger">
<constructor-arg value="0 * * * * *" />
</bean>
It works fine. But now I have to extend the implementation to make it cluster ready (job execution on only one cluster node at the same point of time).
My wish would be to use the Quartz framework in the cluster mode (persisting the job status in the database) to trigger this integration flow. Quartz provides a beautful solution out of the box. The only problem is how to integrate the Quartz with the existing inbout-channer-adaptor? The "trigger" attribute of the "poller" accepts only the subclasses of the org.springframework.scheduling.Trigger. I could not find any bridge between "poller trigger" and the Quartz framework.
many thanks in advance!
Here's one way...
Set the auto-startup attribute on the inbound-adapter to false.
Create a custom trigger that only fires once, immediately...
public static class FireOnceTrigger implements Trigger {
boolean done;
public Date nextExecutionTime(TriggerContext triggerContext) {
if (done) {
return null;
}
done = true;
return new Date();
}
public void reset() {
done = false;
}
}
In your quartz job, get a reference to the trigger and the SourcePollingChannelAdapter.
When the quartz trigger fires, have the quartz job
adapter.stop()
trigger.reset()
adapter.start()
the solution from Gary works. This my spring context:
<int-ftp:inbound-channel-adapter id="my-endpoint"
auto-startup="false">
<int:poller trigger="my-endpoint-trigger"/>
</int-ftp:inbound-channel-adapter>
<bean id="my-endpoint-trigger" class="com.my.FireOnceTrigger"/>
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="my-job-trigger" />
</list>
</property>
<property name="schedulerContextAsMap">
<map>
<entry key="inputEndpoint"><ref bean="my-input-endpoint" /></entry>
<entry key="inputEndpointTrigger"><ref bean="my-endpoint-trigger" /></entry>
</map>
</property>
</bean>
<bean id="my-job-trigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="cronExpression" value="0 * * * * ?" />
<property name="jobDetail" ref="my-job" />
</bean>
<bean name="my-job" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.my.MyActivatorJob " />
</bean>
and MyActivatorJob class:
public class MyActivatorJob extends QuartzJobBean implements {
private AbstractEndpoint inputEndpoint;
private FireOnceTrigger inputEndpointTrigger;
public void setInputEndpoint(final AbstractEndpoint pInputEndpoint) {
this.inputEndpoint = pInputEndpoint;
}
public void setInputEndpointTrigger(final FireOnceTrigger pInputEndpointTrigger) {
this.inputEndpointTrigger = pInputEndpointTrigger;
}
#Override
protected void executeInternal(final JobExecutionContext pParamJobExecutionContext)
throws JobExecutionException {
inputEndpoint.stop();
inputEndpointTrigger.reset();
inputEndpoint.start();
}
}
As a next step this spring context would have to be refactored to replace the usage of schedulerContextAsMap with something more flexible and be able to define more jobs activating and deactivating many different endpoints.
Thanks Gary so far!
tried to integrate the quartz and spring as you proposed but faced two other problems:
1.) IncompatibleClassChangeError exception when using Quartz 2.x and Spring 3.x. It is a known problem but I did not find any solution for that.
2.) Injection of other spring bean into the Quarz job instance. I found some solutions but no one works for me. I've tried the one with using
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory">
<bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory" />
</property>
<property name="triggers">
...
</property>
<property name="schedulerContextAsMap">
<map>
<entry key="inputEndpoint" value-ref="my-endpoint" />
</map>
</property>
</bean>
to inject other beans into the job but after adding this property into the SchedulerFactoryBean the jobs is not being executed (and I dont see any exception). Removing the property "schedulerContextAsMap" out makes the job running again.
I haven't tried it but see that the Quartz 2 and Spring compatibility issues seem to have been fixed in Spring 3.1.1. See https://jira.springsource.org/browse/SPR-8889
Im writting a Spring Application, which has to run a task in a new thread that should be started every couple of seconds. My XML looks like this:
<bean id="checkEmail" class="com.turbineam.dataloader.commons.QuartzSchedulerBean"></bean>
<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="delay" value="1000" />
<property name="period" value="1000" />
<property name="timerTask" ref="checkEmail" />
</bean>
And for Java code I have:
package com.turbineam.dataloader.commons;
import java.util.TimerTask;
public class QuartzSchedulerBean extends TimerTask {
#Override
public void run() {
System.out.println("printMe!");
}
}
But it doesn't affect my whole program (which makes something else)
how can I make this right?
Kind Regards,
Rafał
You're missing a TaskExecutor implementation.
See: http://static.springsource.org/spring/docs/2.5.x/reference/scheduling.html#scheduling-task-executor
We disable the quartz scheduler locally by commenting out the scheduler factory bean in the jobs.xml file.
Is there a setting for doing something similar in the quartz.properties file?
If you use Spring Framework you can make subclass from org.springframework.scheduling.quartz.SchedulerFactoryBean and override afterPropertiesSet() method.
public class MySchedulerFactoryBean extends org.springframework.scheduling.quartz.SchedulerFactoryBean {
#Autowired
private #Value("${enable.quartz.tasks}") boolean enableQuartzTasks;
#Override
public void afterPropertiesSet() throws Exception {
if (enableQuartzTasks) {
super.afterPropertiesSet();
}
}
}
Then change declaration of factory in xml file and set "enable.quartz.tasks" property in properties file. That's all.
Of course, instead using #Autowired you can write and use setter method and add
<property name="enableQuartzTasks" value="${enable.quartz.tasks}"/>
to MySchedulerFactoryBean declaration in xml.
No. But the properties file doesn't start the scheduler.
The scheduler doesn't start until/unless some code invokes scheduler.start().
It seems that there is a property autoStartup in org.springframework.scheduling.quartz.SchedulerFactoryBean. So you can configure it in XML config like this:
<bean id="quartzFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="autoStartup" value="${cron.enabled}"/>
<property name="triggers">
<list>
<ref bean="someTriggerName"/>
</list>
</property>
</bean>
Thanks to https://chrisrng.svbtle.com/configure-spring-to-turn-quartz-scheduler-onoff
You can disable Quartz Scheduler if you use Spring Framework 3.1 for creating and starting it.
On my Spring configuration file I use the new profiles feature of Spring 3.1 in this way:
<beans profile="production,test">
<bean name="bookingIndexerJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.xxx.indexer.scheduler.job.BookingIndexerJob" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="10" />
</map>
</property>
</bean>
<bean id="indexerSchedulerTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="bookingIndexerJob" />
<property name="startDelay" value="1000" />
<property name="repeatInterval" value="5000" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="indexerSchedulerTrigger" />
</list>
</property>
<property name="dataSource" ref="ds_quartz-scheduler"></property>
<property name="configLocation" value="classpath:quartz.properties" />
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
</bean>
</beans>
Only when I want to start the Scheduler (for example on the production environment), I set the spring.profiles.active system property, with the list of active profiles:
-Dspring.profiles.active="production"
More info here:
http://blog.springsource.com/2011/02/11/spring-framework-3-1-m1-released/
http://java.dzone.com/articles/spring-profiles-or-not
I personally like the answer from Demis Gallisto. If you can work with profiles, this would be my recommendation.
Nowadays people most likely prefer to work with Annotations, so as an addition to his answer.
#Configuration
#Profile({ "test", "prod" })
public class SchedulerConfig {
#Bean
// ... some beans to setup your scheduler
}
This will trigger the scheduler only when the profile test OR prod is active. So if you set an different profile, e.g. -Dspring.profiles.active=dev nothing will happen.
If for some reasons you cannot use the profile approach, e.g. overlap of profiles ...
The solution from miso.belica seems also to work.
Define a property. e.g. in application.properties: dailyRecalculationJob.cron.enabled=false and use it in your SchedulerConfig.
#Configuration
public class SchedulerConfig {
#Value("${dailyRecalculationJob.cron.enabled}")
private boolean dailyRecalculationJobCronEnabled;
#Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, Trigger trigger) throws
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(dailyRecalculationJobCronEnabled);
// ...
return factory;
}
// ... the rest of your beans to setup your scheduler
}
I had similar issue: disable scheduler in test scope.
Here is part of my applicationContext.xml
<task:annotation-driven scheduler="myScheduler" />
<task:scheduler id="myScheduler" pool-size="10" />
And I've disabled scheduler using 'primary' attribute and Mockito. Here is my applicationContext-test.xml
<bean id="myScheduler" class="org.mockito.Mockito" factory-method="mock" primary="true">
<constructor-arg value="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"/>
</bean>
Hope this help!
The simplest way I've found in a spring boot context for tests is to simply:
#MockBean
Scheduler scheduler;
This scala code works:
#Bean
def schedulerFactoryBean(): SchedulerFactoryBean = {
new SchedulerFactoryBean {
override def afterPropertiesSet(): Unit = {}
}
}
I am trying to inject the aspects in a service. For this service I am creating a proxied object using classic way.
I have written a bean- baseProxy of type (ProxyFactoryBean) which contains a list of all the required advices.
<bean id="baseProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>methodInvocationAdvice</value>
</list>
</property>
</bean>
I am creating a proxy for the service like this :
<bean id="singproxy" parent="baseProxy">
<property name="target" ref="singtarget" />
<property name="targetClass" value="com.spring.learning.SingingService"></property>
</bean>
Which doesn't work but when I revert these two properties and write like this :
<bean id="singproxy" parent="baseProxy">
<property name="targetClass" value="com.spring.learning.SingingService"></property>
<property name="target" ref="singtarget" />
</bean>
To my surprise it works fine. In spring does it matter on the order for bean ? Or its a special case with ProxyFactoryBean?
I tried with Spring 3.0 I am not sure same behavior exists with previous versions.
Concerning target and targetClass, It's one or the other, but not both. Here's the relevant source (from org.springframework.aop.framework.AdvisedSupport), a parent class of ProxyFactoryBean:
public void setTarget(Object target) {
setTargetSource(new SingletonTargetSource(target));
}
public void setTargetSource(TargetSource targetSource) {
this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
}
public void setTargetClass(Class targetClass) {
this.targetSource = EmptyTargetSource.forClass(targetClass);
}
As you can see, both setTarget() and setTargetClass() write to the same field, so the last assignment wins.
Can the following Spring DI xml be improved? Below the xml is the programmatic approach of configuring the target bean.
<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="jacksonSerializationConfig" />
<property name="targetMethod" value="setSerializationInclusion" />
<property name="arguments">
<list>
<value type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_NULL</value>
</list>
</property>
</bean>
ObjectMapper mapper = new
ObjectMapper();
mapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
XML is a really bad way of doing this. Yes, you can do this, but it's much easier to write a FactoryBean which configures your ObjectMapper:
public class MyObjectMapperFactoryBean extends AbstractFactoryBean<ObjectMapper> {
public Class<ObjectMapper> getObjectType() {
return ObjectMapper.class;
}
public ObjectMapper createInstance() {
// create and return ObjectMapper
}
}
and then in your XML:
<bean id="jacksonObjectMapper" class="x.y.MyObjectMapperFactoryBean" />
Still not totally ideal, but a little cleaner:
<bean id="objectMapperBuilder1" class="org.codehaus.jackson.map.ObjectMapper"/>
<bean id="objectMapperBuilder2" factory-bean="objectMapperBuilder1" factory-method="setSerializationInclusion">
<constructor-arg value="NON_NULL"/>
</bean>
<bean id="jsonWriter" factory-bean="objectMapperBuilder2" factory-method="writerWithDefaultPrettyPrinter" />
<!-- etc, etc -->
One downside is that you'll have unnecessary bean instances in memory. (I'm using this method, and I'll live with it until Spring decides to handle these). There are many threads here and on the Spring forums asking for support with fluent setters like the builder pattern used by Jackson, but until then you have to choose the lesser evil for you.
I agree with #skaffman's general approach of using a FactoryBean in place of the unavoidably convoluted Spring XML bean configuration to configure a Jackson ObjectMapper. Spring 3.2+ now provides such a FactoryBean out of the box. See JacksonObjectMapperFactoryBean / Jackson2ObjectMapperFactoryBean. Here's an example of the Spring XML to configure the ObjectMapper via the Spring FactoryBean -
<bean id="jacksonObjectMapper" factory-bean="&jacksonObjectMapperFactoryBean" factory-method="getObject"/>
<bean id="jacksonObjectMapperFactoryBean" class="org.springframework.http.converter.json.JacksonObjectMapperFactoryBean">
<property name="featuresToDisable">
<array>
<util:constant static-field="org.codehaus.jackson.map.SerializationConfig$Feature.WRITE_NULL_PROPERTIES"/>
</array>
</property>
</bean>
(Note the need to use & in the 'factory-bean' attribute to instruct Spring to use the factory method on the FactoryBean itself, rather than the bean it creates).