Quartz 中集群如何工作
一個 Quartz 集群中的每個節(jié)點(diǎn)是一個獨(dú)立的 Quartz 應(yīng)用,它又管理著其他的節(jié)點(diǎn)。
意思是你必須對每個節(jié)點(diǎn)分別啟動或停止。不像許多應(yīng)用服務(wù)器的集群,獨(dú)立的 Quartz 節(jié)點(diǎn)并不與另一其的節(jié)點(diǎn)或是管理節(jié)點(diǎn)通信咒劲。
Quartz 應(yīng)用是通過數(shù)據(jù)庫表來感知到另一應(yīng)用的。每個節(jié)點(diǎn)直接與數(shù)據(jù)庫通信诫隅,若離開數(shù)據(jù)庫將對其他節(jié)點(diǎn)一無所知腐魂。
創(chuàng)建Quartz數(shù)據(jù)庫表
因?yàn)镼uartz 集群依賴于數(shù)據(jù)庫,所以必須首先創(chuàng)建Quartz數(shù)據(jù)庫表逐纬。
Quartz 包括了所有被支持的數(shù)據(jù)庫平臺的 SQL 腳本蛔屹。在 quartz目錄/docs/dbTables 目錄下找到那些 SQL 腳本。
這里使用tables_mysql_innodb.sql豁生。
配置文件
- 配置jdbc.properties文件
#MySQL配置database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306
database.databaseName=quartz
database.param=useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true
database.username=root
database.password=0123456789
#c3p0配置
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=10
c3p0.acquireIncrement=5
c3p0.maxIdleTime=0
c3p0.idleConnectionTestPeriod=1800
c3p0.preferredTestQuery=select 1 from dual
c3p0.testConnectionOnCheckout=false
c3p0.testConnectionOnCheckin=true
c3p0.maxStatementsPerConnection=0
c3p0.numHelperThreads=32
- 配置spring的spring-dataSource.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${database.driver}" />
<property name="jdbcUrl" value="${database.url}/${database.databaseName}?${database.param}" />
<property name="user" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
<property name="minPoolSize" value="${c3p0.minPoolSize}" />
<property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
<property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
<property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}" />
<property name="preferredTestQuery" value="${c3p0.preferredTestQuery}" />
<property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}" />
<property name="testConnectionOnCheckin" value="${c3p0.testConnectionOnCheckin}" />
<property name="maxStatementsPerConnection" value="${c3p0.maxStatementsPerConnection}" />
<property name="numHelperThreads" value="${c3p0.numHelperThreads}" />
</bean>
</beans>
- 配置quartz.properties文件
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#可為任何值兔毒,用在 JDBC JobStore 中來唯一標(biāo)識實(shí)例,但是所有集群節(jié)點(diǎn)中必須相同甸箱。
org.quartz.scheduler.instanceId = AUTO
#AUTO即可育叁,基于主機(jī)名和時間戳來產(chǎn)生實(shí)例 ID
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#屬性為 JobStoreTX,將任務(wù)持久化到數(shù)據(jù)中芍殖。
#因?yàn)榧褐泄?jié)點(diǎn)依賴于數(shù)據(jù)庫來傳播 Scheduler 實(shí)例的狀態(tài)豪嗽,你只能在使用 JDBC JobStore 時應(yīng)用Quartz 集群。
#這意味著你必須使用 JobStoreTX 或是 JobStoreCMT 作為 Job 存儲;你不能在集群中使用 RAMJobStore昵骤。
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = qrtz_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered = true
#你就告訴了Scheduler 實(shí)例要它參與到一個集群當(dāng)中。這一屬性會貫穿于調(diào)度框架的始終肯适,用于修改集群環(huán)境中操作的默認(rèn)行為变秦。
org.quartz.jobStore.clusterCheckinInterval = 20000
#屬性定義了Scheduler 實(shí)例檢入到數(shù)據(jù)庫中的頻率(單位:毫秒)。默認(rèn)值是 15000 (即15 秒)框舔。
- 配置quartz的spring-quartz.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="simpleService" class="com.hao.service.SimpleService" />
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:config/quartz.properties"/>
<property name="triggers">
<list>
<ref bean="trigger1"/>
<ref bean="trigger2"/>
</list>
</property>
</bean>
<bean id="jobDetail1" class="com.hao.MethodInvokingJobDetailFactoryBean">
<property name="concurrent" value="true" />
<property name="targetObject" ref="simpleService"/>
<property name="targetMethod" value="testMethod1"/>
<property name="shouldRecover" value="true"/>
</bean>
<bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail1"/>
<!-- 每分鐘執(zhí)行1次 -->
<property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
<bean id="jobDetail2" class="com.hao.MethodInvokingJobDetailFactoryBean">
<property name="concurrent" value="true" />
<property name="targetObject" ref="simpleService"/>
<property name="targetMethod" value="testMethod2"/>
<property name="shouldRecover" value="true"/>
</bean>
<bean id="trigger2" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail2"/>
<property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
</beans>
創(chuàng)建Job測試服務(wù)類
package com.hao.service;
import java.io.Serializable;
public class SimpleService implements Serializable {
private static final long serialVersionUID = 1L;
public void testMethod1() {
//這里執(zhí)行定時調(diào)度業(yè)務(wù)
System.out.println("testMethod1.......1");
}
public void testMethod2() {
System.err.println("testMethod2.......2");
}
}
因?yàn)镴ob需要持久化到數(shù)據(jù)庫中蹦玫,SimpleService必須實(shí)現(xiàn)Serializable接口。
運(yùn)行Quartz集群
package com.hao;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
new ClassPathXmlApplicationContext(new String[]{"classpath:config/spring/spring-dataSource.xml","classpath:config/spring/spring-quartz.xml"});
}
}
MethodInvokingJobDetailFactoryBean.java
package com.hao;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.StatefulJob;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.MethodInvoker;
/**
* This is a cluster safe Quartz/Spring FactoryBean implementation, which produces a JobDetail implementation that can invoke any no-arg method on any Class.
* <p>
* Use this Class instead of the MethodInvokingJobDetailBeanFactory Class provided by Spring when deploying to a web environment like Tomcat.
* <p>
* <b>Implementation</b><br>
* Instead of associating a MethodInvoker with a JobDetail or a Trigger object, like Spring''s MethodInvokingJobDetailFactoryBean does, I made the [Stateful]MethodInvokingJob, which is not persisted in the database, create the MethodInvoker when the [Stateful]MethodInvokingJob is created and executed.
* <p>
* A method can be invoked one of several ways:
* <ul>
* <li>The name of the Class to invoke (targetClass) and the static method to invoke (targetMethod) can be specified.
* <li>The Object to invoke (targetObject) and the static or instance method to invoke (targetMethod) can be specified (the targetObject must be Serializable when concurrent=="false").
* <li>The Class and static Method to invoke can be specified in one property (staticMethod). example: staticMethod = "example.ExampleClass.someStaticMethod"
* <br><b>Note:</b> An Object[] of method arguments can be specified (arguments), but the Objects must be Serializable if concurrent=="false".
* </ul>
* <p>
* I wrote MethodInvokingJobDetailFactoryBean, because Spring''s MethodInvokingJobDetailFactoryBean does not produce Serializable
* JobDetail objects, and as a result cannot be deployed into a clustered environment like Tomcat (as is documented within the Class).
* <p>
* <b>Example</b>
* <code>
* <ul>
* <bean id="<i>exampleTrigger</i>" class="org.springframework.scheduling.quartz.CronTriggerBean">
* <ul>
<i><!-- Execute example.ExampleImpl.fooBar() at 2am every day --></i><br>
<property name="<a value="0 0 2 * * ?" /><br>
<property name="jobDetail">
<ul>
<bean class="frameworkx.springframework.scheduling.quartz.<b>MethodInvokingJobDetailFactoryBean</b>">
<ul>
<property name="concurrent" value="<i>false</i>"/><br>
<property name="targetClass" value="<i>example.ExampleImpl</i>" /><br>
<property name="targetMethod" value="<i>fooBar</i>" />
</ul>
</bean>
</ul>
</property>
</ul>
</bean>
<p>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<ul>
<property name="triggers">
<ul>
<list>
<ul>
<ref bean="<i>exampleTrigger</i>" />
</ul>
</list>
</ul>
</property>
</ul>
</bean>
</ul>
* </code>
* In this example we created a MethodInvokingJobDetailFactoryBean, which will produce a JobDetail Object with the jobClass property set to StatefulMethodInvokingJob.class (concurrent=="false"; Set to MethodInvokingJob.class when concurrent=="true"), which will in turn invoke the static <code>fooBar</code>() method of the "<code>example.ExampleImpl</code>" Class. The Scheduler is the heart of the whole operation; without it, nothing will happen.
* <p>
* For more information on <code>cronExpression</code> syntax visit <a >http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html</a>
*
* @author Stephen M. Wick
*
* @see #afterPropertiesSet()
*/
public class MethodInvokingJobDetailFactoryBean implements FactoryBean, BeanNameAware, InitializingBean {
private Log logger = LogFactory.getLog(getClass());
/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method of this Class will be assigned to the Group specified by this property. Default: Scheduler.DEFAULT_GROUP
* @see #afterPropertiesSet()
* @see Scheduler#DEFAULT_GROUP
*/
private String group = Scheduler.DEFAULT_GROUP;
/**
* Indicates whether or not the Bean Method should be invoked by more than one Scheduler at the specified time (like when deployed to a cluster, and/or when there are multiple Spring ApplicationContexts in a single JVM<i> - Tomcat 5.5 creates 2 or more instances of the DispatcherServlet (a pool), which in turn creates a separate Spring ApplicationContext for each instance of the servlet</i>)
* <p>
* Used by <code>afterPropertiesSet</code> to set the JobDetail.jobClass to MethodInvokingJob.class or StatefulMethodInvokingJob.class when true or false, respectively. Default: true
* @see #afterPropertiesSet()
*/
private boolean concurrent = true;
/** Used to set the JobDetail.durable property. Default: false
* <p>Durability - if a job is non-durable, it is automatically deleted from the scheduler once there are no longer any active triggers associated with it.
* @see <a >http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
* @see #afterPropertiesSet()
*/
private boolean durable = false;
/**
* Used by <code>afterPropertiesSet</code> to set the JobDetail.volatile property. Default: false
* <p>Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.
* <p>I set the default to false to be the same as the default for a Quartz Trigger. An exception is thrown
* when the Trigger is non-volatile and the Job is volatile. If you want volatility, then you must set this property, and the Trigger''s volatility property, to true.
* @see <a >http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
* @see #afterPropertiesSet()
*/
private boolean volatility = false;
/**
* Used by <code>afterPropertiesSet</code> to set the JobDetail.requestsRecovery property. Default: false<BR>
* <p>RequestsRecovery - if a job "requests recovery", and it is executing during the time of a ''hard shutdown'' of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true.
* @see <a >http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
* @see #afterPropertiesSet()
*/
private boolean shouldRecover = false;
/**
* A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.
*
* @see #afterPropertiesSet()
**/
private String[] jobListenerNames;
/** The name assigned to this bean in the Spring ApplicationContext.
* Used by <code>afterPropertiesSet</code> to set the JobDetail.name property.
* @see afterPropertiesSet()
* @see JobDetail#setName(String)
**/
private String beanName;
/**
* The JobDetail produced by the <code>afterPropertiesSet</code> method, and returned by the <code>getObject</code> method of the Spring FactoryBean interface.
* @see #afterPropertiesSet()
* @see #getObject()
* @see FactoryBean
**/
private JobDetail jobDetail;
/**
* The name of the Class to invoke.
**/
private String targetClass;
/**
* The Object to invoke.
* <p>
* {@link #targetClass} or targetObject must be set, but not both.
* <p>
* This object must be Serializable when {@link #concurrent} is set to false.
*/
private Object targetObject;
/**
* The instance method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
* <p>
* targetMethod or {@link #staticMethod} should be set, but not both.
**/
private String targetMethod;
/**
* The static method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
* <p>
* {@link #targetMethod} or staticMethod should be set, but not both.
*/
private String staticMethod;
/**
* Method arguments provided to the {@link #targetMethod} or {@link #staticMethod} specified.
* <p>
* All arguments must be Serializable when {@link #concurrent} is set to false.
* <p>
* I strongly urge you not to provide arguments until Quartz 1.6.1 has been released if you are using a JDBCJobStore with
* Microsoft SQL Server. There is a bug in version 1.6.0 that prevents Quartz from Serializing the Objects in the JobDataMap
* to the database. The workaround is to set the property "org.opensymphony.quaryz.useProperties = true" in your quartz.properties file,
* which tells Quartz not to serialize Objects in the JobDataMap, but to instead expect all String compliant values.
*/
private Object[] arguments;
/**
* Get the targetClass property.
* @see #targetClass
* @return targetClass
*/
public String getTargetClass() {
return targetClass;
}
/**
* Set the targetClass property.
* @see #targetClass
*/
public void setTargetClass(String targetClass) {
this.targetClass = targetClass;
}
/**
* Get the targetMethod property.
* @see #targetMethod
* @return targetMethod
*/
public String getTargetMethod() {
return targetMethod;
}
/**
* Set the targetMethod property.
* @see #targetMethod
*/
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
/**
* @return jobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean
* @see #jobDetail
* @see #afterPropertiesSet()
* @see FactoryBean#getObject()
*/
public Object getObject() throws Exception {
return jobDetail;
}
/**
* @return JobDetail.class
* @see FactoryBean#getObjectType()
*/
public Class getObjectType() {
return JobDetail.class;
}
/**
* @return true
* @see FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/**
* Set the beanName property.
* @see #beanName
* @see BeanNameAware#setBeanName(String)
*/
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/**
* Invoked by the Spring container after all properties have been set.
* <p>
* Sets the <code>jobDetail</code> property to a new instance of JobDetail
* <ul>
* <li>jobDetail.name is set to <code>beanName</code><br>
* <li>jobDetail.group is set to <code>group</code><br>
* <li>jobDetail.jobClass is set to MethodInvokingJob.class or StatefulMethodInvokingJob.class depending on whether the <code>concurrent</code> property is set to true or false, respectively.<br>
* <li>jobDetail.durability is set to <code>durable</code>
* <li>jobDetail.volatility is set to <code>volatility</code>
* <li>jobDetail.requestsRecovery is set to <code>shouldRecover</code>
* <li>jobDetail.jobDataMap["targetClass"] is set to <code>targetClass</code>
* <li>jobDetail.jobDataMap["targetMethod"] is set to <code>targetMethod</code>
* <li>Each JobListener name in <code>jobListenerNames</code> is added to the <code>jobDetail</code> object.
* </ul>
* <p>
* Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.
* <ul>
* <li>DEBUG: start
* <li>DEBUG: Creating JobDetail <code>{beanName}</code>
* <li>DEBUG: Registering JobListener names with JobDetail object <code>{beanName}</code>
* <li>INFO: Created JobDetail: <code>{jobDetail}</code>; targetClass: <code>{targetClass}</code>; targetMethod: <code>{targetMethod}</code>;
* <li>DEBUG: end
* </ul>
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
* @see JobDetail
* @see #jobDetail
* @see #beanName
* @see #group
* @see MethodInvokingJob
* @see StatefulMethodInvokingJob
* @see #durable
* @see #volatility
* @see #shouldRecover
* @see #targetClass
* @see #targetMethod
* @see #jobListenerNames
*/
public void afterPropertiesSet() throws Exception {
try {
logger.debug("start");
logger.debug("Creating JobDetail " + beanName);
jobDetail = new JobDetail();
jobDetail.setName(beanName);
jobDetail.setGroup(group);
jobDetail.setJobClass(concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
jobDetail.setDurability(durable);
jobDetail.setVolatility(volatility);
jobDetail.setRequestsRecovery(shouldRecover);
if (targetClass != null)
jobDetail.getJobDataMap().put("targetClass", targetClass);
if (targetObject != null)
jobDetail.getJobDataMap().put("targetObject", targetObject);
if (targetMethod != null)
jobDetail.getJobDataMap().put("targetMethod", targetMethod);
if (staticMethod != null)
jobDetail.getJobDataMap().put("staticMethod", staticMethod);
if (arguments != null)
jobDetail.getJobDataMap().put("arguments", arguments);
logger.debug("Registering JobListener names with JobDetail object " + beanName);
if (this.jobListenerNames != null) {
for (int i = 0; i < this.jobListenerNames.length; i++) {
this.jobDetail.addJobListener(this.jobListenerNames[i]);
}
}
logger.info("Created JobDetail: " + jobDetail + "; targetClass: " + targetClass + "; targetObject: " + targetObject + "; targetMethod: "
+ targetMethod + "; staticMethod: " + staticMethod + "; arguments: " + arguments + ";");
} finally {
logger.debug("end");
}
}
/**
* Setter for the concurrent property.
*
* @param concurrent
* @see #concurrent
*/
public void setConcurrent(boolean concurrent) {
this.concurrent = concurrent;
}
/**
* setter for the durable property.
*
* @param durable
*
* @see #durable
*/
public void setDurable(boolean durable) {
this.durable = durable;
}
/**
* setter for the group property.
*
* @param group
*
* @see #group
*/
public void setGroup(String group) {
this.group = group;
}
/**
* setter for the {@link #jobListenerNames} property.
*
* @param jobListenerNames
* @see #jobListenerNames
*/
public void setJobListenerNames(String[] jobListenerNames) {
this.jobListenerNames = jobListenerNames;
}
/**
* setter for the {@link #shouldRecover} property.
*
* @param shouldRecover
* @see #shouldRecover
*/
public void setShouldRecover(boolean shouldRecover) {
this.shouldRecover = shouldRecover;
}
/**
* setter for the {@link #volatility} property.
*
* @param volatility
* @see #volatility
*/
public void setVolatility(boolean volatility) {
this.volatility = volatility;
}
/**
* This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring
* ApplicationContext.
* <p>
* The only entries this Job expects in the JobDataMap are "targetClass" and "targetMethod".<br>
* - It uses the value of the <code>targetClass</code> entry to get the desired bean from the Spring ApplicationContext.<br>
* - It uses the value of the <code>targetMethod</code> entry to determine which method of the Bean (identified by targetClass) to invoke.
* <p>
* It uses the static ApplicationContext in the MethodInvokingJobDetailFactoryBean,
* which is ApplicationContextAware, to get the Bean with which to invoke the method.
* <p>
* All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.
*
* @see MethodInvokingJobDetailFactoryBean#applicationContext
* @see #execute(JobExecutionContext)
*
* @author Stephen M. Wick
*/
public static class MethodInvokingJob implements Job {
protected Log logger = LogFactory.getLog(getClass());
/**
* When invoked by a Quartz scheduler, <code>execute</code> invokes a method on a Class or Object in the JobExecutionContext provided.
* <p>
* <b>Implementation</b><br>
* The Class is identified by the "targetClass" entry in the JobDataMap of the JobExecutionContext provided. If targetClass is specified, then targetMethod must be a static method.<br>
* The Object is identified by the ''targetObject" entry in the JobDataMap of the JobExecutionContext provided. If targetObject is provided, then targetClass will be overwritten. This Object must be Serializable when <code>concurrent</code> is set to false.<br>
* The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.<br>
* The "staticMethod" entry in the JobDataMap of the JobExecutionContext can be used to specify a Class and Method in one entry (ie: "example.ExampleClass.someStaticMethod")<br>
* The method arguments (an array of Objects) are identified by the "arguments" entry in the JobDataMap of the JobExecutionContext. All arguments must be Serializable when <code>concurrent</code> is set to false.
* <p>
* Logging is provided at the DEBUG and INFO levels; 8 lines at the DEBUG level, and 1 line at the INFO level.
* @see Job#execute(JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
logger.debug("start");
String targetClass = context.getMergedJobDataMap().getString("targetClass");
logger.debug("targetClass is " + targetClass);
Class targetClassClass = null;
if (targetClass != null) {
targetClassClass = Class.forName(targetClass); // Could throw ClassNotFoundException
}
Object targetObject = context.getMergedJobDataMap().get("targetObject");
logger.debug("targetObject is " + targetObject);
String targetMethod = context.getMergedJobDataMap().getString("targetMethod");
logger.debug("targetMethod is " + targetMethod);
String staticMethod = context.getMergedJobDataMap().getString("staticMethod");
logger.debug("staticMethod is " + staticMethod);
Object[] arguments = (Object[]) context.getMergedJobDataMap().get("arguments");
logger.debug("arguments are " + arguments);
logger.debug("creating MethodInvoker");
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetClass(targetClassClass);
methodInvoker.setTargetObject(targetObject);
methodInvoker.setTargetMethod(targetMethod);
methodInvoker.setStaticMethod(staticMethod);
methodInvoker.setArguments(arguments);
methodInvoker.prepare();
logger.info("Invoking: " + methodInvoker.getPreparedMethod().toGenericString());
methodInvoker.invoke();
} catch (Exception e) {
throw new JobExecutionException(e);
} finally {
logger.debug("end");
}
}
}
public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob {
// No additional functionality; just needs to implement StatefulJob.
}
public Object[] getArguments() {
return arguments;\n }\n\n public void setArguments(Object[] arguments) {
this.arguments = arguments;
}
public String getStaticMethod() {
return staticMethod;
}
public void setStaticMethod(String staticMethod) {
this.staticMethod = staticMethod;
}
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
}