最初の完了後に、Quartz (バージョン 1.6) が実行時間の長いジョブの再スケジュールに失敗するという問題がありました。正常に再スケジュールされるジョブは他にもありますが、通常、反復時間よりも長く実行されることはありません。(ジョブのステータスはDBを参照してきました) ジョブが開始するとBLOCKEDの状態に移行し、prev_fire_timeが設定され、next_fire_timeが1分後に設定されます。
例: next_fire_time = 9.50、prev_fire_time = 9.49
その後、ジョブは 9.55 まで終了しません。(注: ジョブは、トランザクションのバックログのバッチ更新を担当します。10 秒かかる場合もあれば、10 分かかる場合もありますが、バックログが大きくなる可能性があるため、間隔を 10 分に設定することは避けたいと考えています。
ジョブが終了すると、ステータスは WAITING に戻りますが、next_fire_time は過去のままです。不思議なことに、10秒ごとに反復し、1分間スリープするジョブを使用して、ローカル環境でテストをセットアップしようとしましたが、スケジューラーはそれを正常に処理したようです。
カスタムの Job Details Factory を使用しているため、それが影響するかどうかはわかりません。
これを修正する方法についていくつかの提案を期待しています。これは製品のレガシー バージョンであるため、大幅な変更が必要な場合、ライブラリのアップグレードが難しい場合があります。
詳細は次のとおりです。
<bean id="baseSchedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="autoStartup" value="true"/>
<property name="dataSource" ref="baseDataSource"/>
<property name="waitForJobsToCompleteOnShutdown" value="true"/>
<property name="overwriteExistingJobs" value="true"/>
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<property name="triggers">
<list>
<ref bean="updateRecentlyModifedchedules" />
</list>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.threadCount">${org.quartz.threadPool.threadCount}</prop>
</props>
</property>
</bean>
<bean id="updateRecentlyModifedchedules"
class="org.springframework.scheduling.quartz.SimpleTriggerBean"
depends-on="springApplicationContext">
<property name="jobDetail" ref="updateRecentlyModifedchedulesJob" />
<!-- delay for 280 seconds -->
<property name="startDelay" value="280000" />
<!-- repeat every 70 seconds -->
<property name="repeatInterval" value="70000" />
</bean>
<bean id="updateRecentlyModifedchedulesJob" class="com.app.application.backoffice.quartz.MethodInvokingJobDetal">
<property name="targetBeanName" value="scheduleChangeManager" />
<property name="targetMethod" value="assetSchedules" />
<property name="concurrent" value="false" />
</bean>
そして、カスタム ジョブ ファクトリ:
public class MethodInvokingJobDetal implements FactoryBean, BeanNameAware, BeanClassLoaderAware,
BeanFactoryAware, InitializingBean
{
private String name;
private String group = Scheduler.DEFAULT_GROUP;
private boolean concurrent = true;
private String targetBeanName;
private String[] jobListenerNames;
private String beanName;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private BeanFactory beanFactory;
private JobDetail jobDetail;
private String targetMethod;
public void setTargetMethod(String targetMethod)
{
this.targetMethod = targetMethod;
}
public void setName(String name)
{
this.name = name;
}
public void setGroup(String group)
{
this.group = group;
}
public void setConcurrent(boolean concurrent)
{
this.concurrent = concurrent;
}
public void setTargetBeanName(String targetBeanName)
{
this.targetBeanName = targetBeanName;
}
public void setJobListenerNames(String[] names)
{
this.jobListenerNames = names;
}
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
public void setBeanClassLoader(ClassLoader classLoader)
{
this.beanClassLoader = classLoader;
}
public void setBeanFactory(BeanFactory beanFactory)
{
this.beanFactory = beanFactory;
}
protected Class<?> resolveClassName(String className) throws ClassNotFoundException
{
return ClassUtils.forName(className, this.beanClassLoader);
}
protected void prepare()
{
}
public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException
{
prepare();
// Use specific name if given, else fall back to bean name.
String name = (this.name != null ? this.name : this.beanName);
// Consider the concurrent flag to choose between stateful and stateless job.
Class<?> jobClass = (this.concurrent ? (Class<?>) MyMethodInvokingJob.class
: StatefulMyMethodInvokingJob.class);
// Build JobDetail instance.
this.jobDetail = new JobDetail(name, this.group, jobClass);
this.jobDetail.getJobDataMap().put("targetBeanName", targetBeanName);
this.jobDetail.getJobDataMap().put("targetMethod", targetMethod);
this.jobDetail.setVolatility(false);
this.jobDetail.setDurability(true);
// Register job listener names.
if (this.jobListenerNames != null)
{
for (int i = 0; i < this.jobListenerNames.length; i++)
{
this.jobDetail.addJobListener(this.jobListenerNames[i]);
}
}
postProcessJobDetail(this.jobDetail);
}
/**
* Callback for post-processing the JobDetail to be exposed by this FactoryBean.
* <p>The default implementation is empty. Can be overridden in subclasses.
* @param jobDetail the JobDetail prepared by this FactoryBean
*/
protected void postProcessJobDetail(JobDetail jobDetail)
{
}
/**
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
*/
public Class<?> getTargetClass()
{
Class<?> targetClass = null; //super.getTargetClass();
if (targetClass == null && this.targetBeanName != null)
{
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
targetClass = this.beanFactory.getType(this.targetBeanName);
}
return targetClass;
}
/**
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
*/
public Object getTargetObject()
{
Object targetObject = null; // super.getTargetObject();
if (targetObject == null && this.targetBeanName != null)
{
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
targetObject = this.beanFactory.getBean(this.targetBeanName);
}
return targetObject;
}
public Object getObject()
{
return this.jobDetail;
}
public Class<?> getObjectType()
{
return JobDetail.class;
}
public boolean isSingleton()
{
return true;
}
public static class StatefulMyMethodInvokingJob extends MyMethodInvokingJob
implements StatefulJob
{
// No implementation, just an addition of the tag interface StatefulJob
// in order to allow stateful method invoking jobs.
}
private static class MyMethodInvokingJob extends QuartzJobBean
{
private String targetBeanName;
private String targetMethod;
public void setTargetBeanName(String targetBeanName)
{
this.targetBeanName = targetBeanName;
}
public void setTargetMethod(String targetMethod)
{
this.targetMethod = targetMethod;
}
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException
{
Object targetObject = SpringApplicationContext.getBean(targetBeanName);
try
{
Method method = targetObject.getClass().getMethod(targetMethod);
method.invoke(targetObject);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}