| jobClass | Job의 클래스명을 설정한다. | Y | N/A |
| jobDataAsMap |
이 속성값으로 설정된 데이터들은 Job 클래스 내에서 사용가능한 데이터로 Map 형태로 정의한다.
Job과 연관된 Trigger에 설정된 jobDataAsMap 속성값으로 설정한 Map 데이터와
JobDetail에 설정된 jobDataAsMap 속성값으로 설정한 Map 데이터는 함께 merge되어 사용될 수 있다. 이 데이터들은 기본적으로 처음 한번 설정된 뒤 변경되지 않으나, Stateful한 Job 객체를 실행시켜서 데이터 값을 변경시키게 되는 경우에는 변경이 가능하다. |
N | N/A |
| name | Job의 이름을 설정한다. | N | FactoryBean의 Bean name |
| targetObj | POJO로 작성된 Job 클래스명을 정의한다. | Y | N/A |
| targetMethod | targetObj에서 설정한 Object에서 실행하고자 하는 method 명을 입력한다. | Y | N/A |
| concurrent | 다수의 Job을 동시에 실행시키려면 true로 설정하고 순차적으로 실행시키려면 false로 설정한다. | N | true |
| jobDetail | 이 Trigger와 관련된 JobDetail의 클래스를 작성하는데, Spring Bean으로 작성된 경우 ref attribute를 이용하여 Bean의 id를 작성하도록 한다. | Y | N/A |
| repeatInterval | Trigger가 처음 수행 이후 반복 수행 시킬 Trigger의 interval 시간을 설정하는 값으로 Trigger가 반복되는 interval 시간을 milliseconds 단위로 설정한다. repeatInterval은 0이상이어야 한다. | N | 0 |
| repeatCount | Trigger가 처음 수행 이후 반복 수행 시킬 Trigger의 수행 횟수를 설정한다. | N | 0 |
| triggerListenerNames | 특정 trigger에만(즉, Non-global) TriggerListener를 설정하여 Trigger가 fire->execute->complete 혹은 misfire되는 시점에 특정 일을 수행하고자 할때 TriggerListener를 설정하는데 여기에는 triggerListenerName을 등록한다. 이 triggerListenerName은 TriggerListener 인터페이스를 구현한 클래스의 getName() 메소드에서 리턴한 이름을 작성하도록 한다. | N | N/A |
| jobDataAsMap |
이 속성값으로 설정된 데이터들은 Trigger에 설정된
JobDetail을 통해 Job에서 해당 데이터를 이용할 수 있다.
Trigger에 설정된 jobDataAsMap 속성값으로 설정한 Map 데이터와
JobDetail에 설정된 jobDataAsMap 속성값으로 설정한 Map 데이터는 함께 merge되어 사용될 수 있다. 이외에도 이 Trigger에 설정된 triggerListener 내부에서도 이 데이터를 이용할 수 있다. 이 데이터들은 기본적으로 처음 한번 설정된 뒤 변경되지 않으나, Stateful한 Job 객체를 실행시켜서 데이터 값을 변경시키게 되는 경우에는 변경이 가능하다. |
N | N/A |
| startTime | Trigger 수행 시작 시간을 설정한다. | N | N/A |
| startDelay | 처음 Job이 시작되기 전 지연 시간을 설정한다. 입력한 milliseconds 단위의 시간에 현재 시간을 더하면 시작 시간이 된다. startDelay 속성은 startTime 속성이 지정되지 않은 경우에 적용된다. 그러나 Spring Container를 구동시키면, startTime은 언제나 Container가 구동된 시간이므로 이러한 경우에는 절대적인 시간이 아닌 상대적인 시간을 설정하도록 한다. | N | 0 |
| endTime | Trigger 반복을 종료할 시간을 입력한다. | N | N/A |
| misfireInstructionName |
Trigger는 Scheduler에 문제가 발생하였거나 Job을 수행시키는 Quartz의 Thread pool에서 현재 사용 가능한
Thread가 존재하지 않을 때 실행되지 못하는 경우가 있다. 이러한 경우 Job Store 방식으로 DB를 사용하고 있다면, 다음 Scheduler가 정상 동작 시
실행되지 못한 Job들에 대해서 misfire 시킨다. (하위 Advanced Quartz >> Job Stores 내용 참고)
기본적으로 'smart policy' instruction이 사용되고, 그외 다음과 같은 misfire instruction명을 설정할 수 있다. 자세한 내용은 Quartz Tutorial >> Misfire Instructions 를 참조하도록 한다.
|
N | MISFIRE_ INSTRUCTION_ SMART_ POLICY |
| jobDetail | 이 Trigger와 관련된 JobDetail의 클래스를 작성하는데, Spring Bean으로 작성된 경우 ref attribute를 이용하여 Bean의 id를 작성하도록 한다. | Y | N/A |
| cronExpression |
Trigger가 반복되는 interval 시간을 milliseconds 단위로 설정한다.
cron expression은 기본적으로 다음과 같이 7개 필드로 구성되어 있다.
|
N | 0 |
| startTime | Trigger 수행 시작 시간을 설정한다. | N | N/A |
| endTime | Trigger 반복을 종료할 시간을 입력한다. | N | N/A |
| triggers | Scheduler에 사용되는 Trigger들을 등록시킨다. 여러 개의 Trigger를 등록시켜야 하므로 <list> 태그를 이용하여 설정한다. | Y | N/A |
| triggerListeners | 특정 Trigger에 한하여 적용할 TriggerListener를 등록한다. | N | N/A |
| globalTriggerListeners | 해당 Scheduler에 등록된 모든 Trigger들에게 적용할 TriggerListener를 등록한다. 이를 GlobalTriggerListener라고 한다. | N | N/A |
| dataSource | 이 속성 값을 설정하게 되면, Job과 Trigger에 관련된 정보가 자동으로 DB를 통해 관리된다. 이때 저장할 DB Table이 준비되어 있어야 하는데, Table은 Quartz를 다운로드 받은 후 docs/dbTables 폴더 내의 table-creation SQL script 중 사용하고자 하는 DB(ex. Oracle의 경우, tables_oracle.sql)에 맞는 script를 수행하여 생성시키도록 한다. | N | N/A |
<!-- JobDetailBean -->
<bean id="jobDetail01"
class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"
value="integration.anyframe.services.scheduling.SimpleQuartzJobBean" />
<property name="jobDataAsMap">
<map>
<entry>
<key>
<value>count</value>
</key>
<value>6</value>
</entry>
</map>
</property>
</bean>
public class SimpleQuartzJobBean extends QuartzJobBean {
public void setCount(int count) {
this.count=count;
}
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
this.count= this.count + 1;
context.getJobDetail().getJobDataMap().put("count",this.count);
System.out.println("count="+this.count);
...중략
<!-- JobDetailBean -->
<bean id="jobDetail02"
class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"
value="integration.anyframe.services.scheduling.StatefulQuartzJobBean" />
<property name="jobDataAsMap">
<map>
<entry>
<key>
<value>count</value>
</key>
<value>6</value>
</entry>
</map>
</property>
</bean>
import org.quartz.StatefulJob;
public class StatefulQuartzJobBean implements StatefulJob {
public void setCount(int count) {
this.count=count;
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
try {
BeanWrapper bw = PropertyAccessorFactory
.forBeanPropertyAccess(this);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValues(context.getScheduler().getContext());
pvs.addPropertyValues(context.getMergedJobDataMap());
bw.setPropertyValues(pvs, true);
} catch (SchedulerException ex) {
throw new JobExecutionException(ex);
}
executeInternal(context);
}
public void executeInternal(JobExecutionContext context)
throws JobExecutionException {
this.count = count + 1;
context.getJobDetail().getJobDataMap().putAsString("count", count);
System.out.println(this.getClass().getName() + "::" + this.count);
result = count;
}...중략
<bean id="jobDetail03"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="simplePOJOJobBean"/>
<property name="targetMethod" value="doSomething"/>
</bean>
<!-- MethodInvokingBean -->
<bean id="simplePOJOJobBean"
class="integration.anyframe.services.scheduling.SimplePOJOJobBean"/>
public class SimplePOJOJobBean {
public void doSomething(){
count = count + 1 ;
System.out.println(this.getClass().getName()+"::"+this.count);
}
...중략
}
<!-- SimpleTriggerBean --> <bean id="simpleTrigger01" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="jobDetail01" /> <property name="repeatInterval" value="1000"/> <property name="repeatCount" value="3"/> <property name="triggerListenerNames" value="triggerListener"/> </bean>
<!-- SimpleTriggerBean --> <bean id="simpleTrigger02" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="jobDetail02" /> <property name="repeatInterval" value="1000"/> <property name="repeatCount" value="3"/> <property name="triggerListenerNames" value="triggerListener"/> </bean>
<!-- CronTriggerBean -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail03" />
<property name="cronExpression" value="2 * * * * ?" />
</bean>
<!-- TriggerListener -->
<bean id="triggerListener"
class="integration.anyframe.services.scheduling.QuartzTriggerListener"/>
<bean id="globalTriggerListener"
class="integration.anyframe.services.scheduling.QuartzTriggerListener">
<property name="listenerType" value="Global"/>
</bean>
import org.quartz.TriggerListener;
public class QuartzTriggerListener implements TriggerListener {
String listenerType ="Non global";
public void setListenerType(String listenerType) {
this.listenerType=listenerType;
}
public void triggerFired(Trigger trigger, JobExecutionContext ctx) {
System.out.println(trigger.getJobName()+": triggerFired("+listenerType+")");
}
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext ctx) {
System.out.println(trigger.getJobName()+": vetoJobExecution("+listenerType+")");
return false;
}
public void triggerComplete(Trigger trigger, JobExecutionContext ctx, int arg) {
System.out.println(trigger.getJobName()+": triggerComplete("+listenerType+")");
}
public void triggerMisfired(Trigger trigger) {
System.out.println(trigger.getJobName()+": triggerMisfired("+listenerType+")");
}
public String getName() {
return "triggerListener";
}
<!-- SchedulerFactoryBean --> <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref local="simpleTrigger01" /> <ref local="simpleTrigger02" /> <ref local="cronTrigger" /> </list> </property> <property name="triggerListeners" ref="triggerListener"/> <property name="globalTriggerListeners" ref="globalTriggerListener"/> <!-- DataSource 설정 시 Job과 Trigger에 관련된 정보가 자동으로 DB를 통해 관리됨. --> <!--<property name="dataSource" ref="dataSource"/> --> </bean>
/**
* Scheduling 서비스를 통해 수행된 Job Object의 수행 결과가 올바른지 알아보는 테스트이다.
*/
/**
* SimpleTrigger에 등록된 JobDetailBean의 Stateless QuatzJob을 수행하고 결과 값을 확인한다.
*/
public void testSimpleTrigger01() throws Exception {
Thread.sleep(1000*5);
int result=SimpleQuartzJobBean.getResult();
System.out.println("main >> result of Stateless QuatzJobBean bean :"+ result);
if ( result!=7){
throw new Exception(" Scheduling Service failed : result is " + result );
}
}
/**
* SimpleTrigger에 등록된 JobDetailBean의 Stateful QuatzJob을 수행하고 결과 값을 확인한다.
*/
public void testSimpleTrigger02() throws Exception {
Thread.sleep(1000*5);
int result=StatefulQuartzJobBean.getResult();
System.out.println("main >> result of Stateful QuatzJobBean bean :"+ result);
if ( result!=10){
throw new Exception(" Scheduling Service failed : result is " + result );
}
}
/**
* CronTrigger에 등록된 POJO Job을 수행하고 결과 값을 확인한다.
*/
public void testCronTrigger() throws Exception {
Thread.sleep(1000*50);
SimplePOJOJobBean simplePOJOJobBean =
( SimplePOJOJobBean )context.getBean("simplePOJOJobBean");
int result= simplePOJOJobBean.getCount();
System.out.println("main >> result of simplePOJOJobBean :"+ result);
if ( result <= 0 ){
throw new Exception(" Scheduling Service failed ");
}
}