I'm setting up a scheduled tasks scheme in spring, using the task namespace.
I want to schedule most tasks to fire according to a cron expression, and some to fire just once, a fixed delay after startup, and then never again (i.e. what setting repeatCount to 0 on a SimpleTriggerBean would achieve).
Is it possible to achieve this within the task namespace, or do I need to revert to defining beans for my triggers?
If you don't need an initial delay, you can make it run 'just once' on startup as follows:
<task:scheduled-tasks>
<!-- Long.MAX_VALUE ms = 3E8 years; will run on startup
and not run again for 3E8 years -->
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }" />
</task:scheduled-tasks>
(Of course, if you think your code is going to run for longer than 3E8 years, you may need a different approach...)
If you need an initial delay, you can configure it as follows (I'm testing with Spring 3.1.1) - this doesn't require any additional dependencies and you don't have to write your own trigger, but you do have to configure the PeriodicTrigger provided by Spring:
<bean id="onstart" class="org.springframework.scheduling.support.PeriodicTrigger" >
<!-- Long.MAX_VALUE ms = 3E8 years; will run 5s after startup and
not run again for 3E8 years -->
<constructor-arg name="period" value="#{ T(java.lang.Long).MAX_VALUE }" />
<property name="initialDelay" value="5000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff" trigger="onstart" />
</task:scheduled-tasks>
Spring 3.2 appears to support the "initial-delay" attribute directly, but I haven't tested this; I'd guess this works:
<task:scheduled-tasks>
<task:scheduled ref="myThing" method="doStuff"
fixed-rate="#{ T(java.lang.Long).MAX_VALUE }"
initial-delay="5000"/>
</task:scheduled-tasks>
My working example:
<bean id="whateverTriggerAtStartupTime" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="whateverJob"/>
<property name="repeatCount" value="0"/>
<property name="repeatInterval" value="10"/>
</bean>
If you have a look at the Task namespace XSD, you'll see that there are only three different configuration types: fixed-delay, fixed-rate and cron.
And if you look at the source of ScheduledTasksBeanDefinitionParser, you'll see that no more than one of these values are evaluated. Here is the relevant part:
String cronAttribute = taskElement.getAttribute("cron");
if (StringUtils.hasText(cronAttribute)) {
cronTaskMap.put(runnableBeanRef, cronAttribute);
}
else {
String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");
if (StringUtils.hasText(fixedDelayAttribute)) {
fixedDelayTaskMap.put(runnableBeanRef, fixedDelayAttribute);
}
else {
String fixedRateAttribute = taskElement.getAttribute("fixed-rate");
if (!StringUtils.hasText(fixedRateAttribute)) {
parserContext.getReaderContext().error(
"One of 'cron', 'fixed-delay', or 'fixed-rate' is required",
taskElement);
// Continue with the possible next task element
continue;
}
fixedRateTaskMap.put(runnableBeanRef, fixedRateAttribute);
}
}
So there is no way to combine these attributes. In short: the namespace won't get you there.
This works and is way easier than the other answers.
// Will fire the trigger 1 + repeatCount number of times, start delay is in milliseconds
simple name: 'mySimpleTrigger', startDelay: 5000, repeatCount: 0
Related
We have Spring scheduled job with cron expression which is configured in database. I need to change schedule time without restart the application server. But i could not achieve this and i have tried many ways using SO solutions in other link, but none worked for me. Below is my code snippet.
Scheduler time will get from database during dispatcher servlet initializing and assign in the property variable test.scheduler
Scheduler.java
#Scheduled(cron = "${test.scheduler}")
public void testScheduler() {
System.out.println("Dynamic Scheduler Run Test"+new java.util.Date().getTime());
}
dispatcher-servlet.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<bean class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties">
<constructor-arg>
<bean class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
<constructor-arg value="TABLE_NAME" />
<constructor-arg value="TABLE_KEY" />
<constructor-arg value="TABLE_VALUE" />
</bean>
</constructor-arg>
</bean>
</property>
</bean>
From the table (TABLE_NAME), there is column's key is test.scheduler & value is 0 0/5 * * * ?
As far as I am aware, SpEL won't let you change the value of your scheduler once it has been initialised.
If you are using Spring Boot, you will be able to use Actuator to trigger a refreshed event:-
http://localhost:8080/actuator/refresh
In which case, you can make the bean implement RefreshedScope and update your scheduler by triggering the RefreshScopeRefreshedEvent:
#EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
// Read the database, update the scheduler.
}
An example implementation of this can be seen here.
This might not be the perfect solution to your problems but I am posting it for visibility and to help others.
An alternative solution may involve using a Trigger to determine the next execution time. An example StackOverflow answer can be seen here.
I'm working on a JMS intensive application that sends/receives hundreds of thousands of messages. I found that performance wasn't all that great and narrowed down the issue to 1 line like below, root cause from what I can tell is it doesn't play well with IBM MQ.
JMSTemplate.receive(queueName);
After wrapping this code in a simple timer, I found that receive was taking anywhere from 20-50 milliseconds and due to the sheer amount of throughput I'm dealing with that will surely add up over time. After a bit of googling I stumbled upon springs "CachingConnectionFactory", which I implemented with blind luck like below (wasn't sure if this would have worked with IBM MQ Connection factory that I was already using). Note that some code is omitted for legibility...
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
...
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="cacheFactory" />
</property>
...
</bean>
<!--This seems to be the magic piece-->
<bean id="cacheFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="ibmMQConnectionFactory" />
<property name="sessionCacheSize" value="100" />
</bean>
<bean id="ibmMQConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
...
</bean>
To my surprise, this cut down my JMSTemplate.receive() calls from anywhere between 20-50+ milliseconds to about 1-2 millis per message. I'm not able to find any solid information about how exactly this works behind the scenes and how "sessionCacheSize" will affect performance. My first test I used a value of 50 and the second time 100, with the second option proving much faster. So my question is, what is an ideal "sessionCacheSize" for an application with a massive amount of throughput, and what are any drawbacks to consider with this approach?
I look forward to what you guys have to say on this one...
My knowledge on Spring is limited. But by reading you description, I believe Spring is doing the following every time for receiving a message:
1) Creating a connection to IBM MQ Queue Manager
2) Opening specified queue
3) Getting message from queue
4) Closing the queue
5) Closing the connection.
Because of all the above operations, the time taken to receive single message is more. But when session is cached, Spring is re-using a cached connection. Hence better message receive throughput.
I am new to spring batch processing. I am using remote chunking where there is a master , multiple slaves and ActiveMQ for messaging.
Master has a job and a job launcher and the job launcher has a task-executor which is having following configuration
<task:executor id="batchJobExecutor" pool-size="2"queue-capacity="100" />.
Chunk configuration is
<bean id="chunkWriter"
class="org.springframework.batch.integration.chunk.ChunkMessageChannelItemWriter" scope="step">
<property name="messagingOperations" ref="messagingGateway" />
<property name="replyChannel" ref="replies" />
<property name="throttleLimit" value="50" />
<property name="maxWaitTimeouts" value="60000" />
</bean>
<bean id="chunkHandler"
class="org.springframework.batch.integration.chunk.RemoteChunkHandlerFactoryBean">
<property name="chunkWriter" ref="chunkWriter" />
<property name="step" ref="someJobId" />
</bean>
<integration:service-activator
input-channel="requests" output-channel="replies" ref="chunkHandler" />
So we are allowed to run two jobs at a time and the remaining jobs will be in the queue.
When two jobs are submitted Master is creating the chunk and submitting to the queue and slave is processing.
But the acknowledgment from the slave to master is giving error
java.lang.IllegalStateException: Message contained wrong job instance id [9331] should have been [9332].
at org.springframework.util.Assert.state(Assert.java:385) ~[Assert.class:4.1.6.RELEASE]
at org.springframework.batch.integration.chunk.ChunkMessageChannelItemWriter.getNextResult
Please help me with this.
The ChunkMessageChannelItemWriter is only designed for one concurrent step - you need to put it in step scope so each job gets its own instance - see this test case
EDIT
Actually, no; that won't work - since the bean instances are using the same reply channel, they could get each other's replies. I opened a JIRA Issue.
This is a very old post, but I think the issue you see here might be related to the throttle limit being larger than the maxWaitTimouts value 4.
What we have seen is that the implementation will not read more than maxWaitTimeouts entries from the reply queue after the job finished. I think this is a bug.
See also the question I asked on stackoverflow here : Remote batch job does not read all responses in afterStep method
I made a bug report for this as well: https://jira.spring.io/browse/BATCH-2651 and am creating a PR to fix the issue.
I have a java web service in centos 7 on tomcat 7, and i use this service to update a database. I need to call its method regulary, example:
createCustomer will called in every 12 hour,
createOrder will called in every 3 minute etc.
How can i write this triger and where this code have run (in same server or something else)
You can give quartz scheduler a shot. Here you can find more infomation regarding the subject :
http://quartz-scheduler.org/generated/2.2.1/html/qs-all/#page/Quartz_Scheduler_Documentation_Set/_qs_all.1.009.html#
In a nutshell you can define an xml file, stating which task would work on which condition ( or time frame ). In the example below :
<!-- Order Recorder Job -->
<bean id="orderRecorderJob" class="com.cemgunduz.btcenter.job.OrderRecorderJob"/>
<bean id="orderTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="orderRecorderJob"/>
<property name="targetMethod" value="execute"/>
</bean>
<bean id="orderRecorderJobTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="orderTask" />
<property name="cronExpression" value="0 0/5 * * * ?" />
</bean>
A task is defined named orderTask, which is the execute method in orderRecorderJob.
This task is associated with a cron expression, thereby the trigger is defined. So to sum it all up the example above would trigger execute method of orderRecorderJob class every five minutes as stated on its cron expression ( 0 0/5 * * * ?). More on cron expression syntax :
http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger
I have a CronExpression set for the job to be executed at every 30 minutes. But I need to skip a particular job if the earlier job is not complete.
For eg. I have 100 Employee whose Names to be updated in the database and I terms it as "Job1" which starts at 10AM. Now the case is like "Job1" is in process and by the time I have another job- "Job2" aligned where I need to update another 50 Employee's names. My problem is this,I need to skip "Job2" and further jobs till my currently running Job is completed.
<bean name="employeeNameUpdateJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="name" value="Employee Update Job"/>
<property name="group" value="Employee Update Group Job"/>
<property name="jobClass"
value="com.emp.scheduler.EmployeeUpdateScheduler" />
<property name="volatility" value="false" />
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="name" value="Employee Update Trigger"/>
<property name="group" value="Employee Update Group Trigger"/>
<property name="volatility" value="false" />
<property name="jobDetail" ref="employeeNameUpdateJob"/>
<property name="cronExpression" value="0 0/30 * * * ?"/>
</bean>
One way is to implement TriggerListener interface, which provides a vetoJobExecution(Trigger trigger, JobExecutionContext context) method to veto the execution of a next job. Returning true from this method will stop the execution of job.
Interface documentation: http://quartz-scheduler.org/api/2.0.0/org/quartz/TriggerListener.html#vetoJobExecution(org.quartz.Trigger, org.quartz.JobExecutionContext)
Sample:
//SampleTriggerListener.java
public class SampleTriggerListener implements TriggerListener {
#Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext ctx) {
if(!previousJobCompleted)
return true;
return false;
}
}
//Main.java
//init jobs, trigger & scheduler
this.scheduler.addTriggerListener(new SampleTriggerListener());
this.scheduler.start();
If they are the same job class : #DisallowConcurrentExecution
Else it sounds like you need to use a single threadpool executor. Inject the same executor to both classes ( or alternatively implement an orchestrator class to manage this ) and add the work units to the queue this way.
If you will never need to run multiple jobs in parallel you could set the worker thread pool that Quartz uses to 1. Then it will only ever run one job at a time. In your quartz.properties file, set:
org.quartz.threadPool.threadCount: 1