정상혁정상혁

Spring 3.0부터는 Quartz가 없이도 실행 스케쥴 설정이 가능합니다. 설정과 테스트 방법을 정리했습니다.

@Schedule 를 써서 크론 표현식 설정

실행을 하고 싶은 메소드를 @Component, @Schedule annotaion을 통해 지정을 합니다.

@Component
public class BaseballScheduledJobLauncher extends JobLaunchSupport {

.....

    @Scheduled(cron="45 * * * * MON-FRI")
    public void baseballJob() {
        JobParameters params = createTimeParameter();
        run("baseballJob", params);
    }


    @Scheduled(cron="45 10 5 * * MON-FRI")
    public void baseballExportJob() {
        JobParameters params = createTimeParameter();
        run("baseballExportJob", params);
    }

}

Java 파일에 설정된 Annotation을 인식하기 위해서 Application context의 xml파일에 component-scan과 annotation으로 schedule을 설정하겠다는 선언을 추가합니다. 그리고 schedule을 실행할 thread pool의 크기도 지정합니다.

    <context:component-scan base-package="edu.batch.baseball.schedule"/>
    <task:scheduler id="myScheduler" pool-size="10"/>
    <task:annotation-driven scheduler="myScheduler"/>

'task’와 'component’의 namespace가 xml에 추가되어 있어야 합니다.

테스트 코드

테스트 코드를 단순하게 만들기 위해서 크론표현식을 추출하고 검사하는 코드는 SpringCronExpressionTestUtils클래스로 분리했습니다.

package edu.batch.baseball.schedule;

import static edu.batch.support.launch.SpringcronExpressionTestUtils.*;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import edu.batch.baseball.schedule.BaseballScheduledJobLauncher;

public class BaseballScheduledJobLauncherTest {

    private static final String DATE_PATTERN = "yyyy/MM/dd hh:mm:ss";

    @Test
    public void testBaseballJobSchedule() {
        String initialTime = "2010/09/01 09:00:00";
        List<String> expectedTimeList = Arrays.asList(
                "2010/09/01 09:00:45",
                "2010/09/01 09:01:45",
                "2010/09/01 09:02:45");
        String cronExpression = getcronExpressionOfMethod(BaseballScheduledJobLauncher.class, "baseballJob");
        assertSchedule(cronExpression, initialTime, expectedTimeList,DATE_PATTERN);
    }
}
SpringCronExpressionTestUtils
package edu.batch.support.launch;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;

public class SpringCronExpressionTestUtils {

    public static String getCronExpressionOfMethod(Class<?> targetClass,
            String methodName)  {
        Method scheduledMethod;
        try {
            scheduledMethod = targetClass.getDeclaredMethod(methodName,
                    new Class[] {});
        } catch (SecurityException e) {
            throw new IllegalArgumentException("cannot access the method : " + methodName, e);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(e);
        }
        Scheduled scheduleInfo = scheduledMethod.getAnnotation(Scheduled.class);
        String cronExpression = scheduleInfo.cron();
        return cronExpression;
    }

    public static void assertSchedule(String cronExpression, String initialTime,
            List<String> expectedTimeList, String datePattern) {
        CronTrigger trigger = new CronTrigger(cronExpression);
        Date startTime;
        try {
            startTime = DateUtils.parseDate(initialTime,
                    new String[] { datePattern });
        } catch (ParseException e) {
            throw new IllegalArgumentException("wrong date format", e);
        }
        SimpleTriggerContext context = new SimpleTriggerContext();
        context.update(startTime, startTime, startTime);

        for (String exptectedTime : expectedTimeList) {
            Date nextExecutionTime = trigger.nextExecutionTime(context);
            String actualTime = DateFormatUtils.format(nextExecutionTime,
                    datePattern);
            assertThat("executed on expected time", actualTime,
                    is(exptectedTime));
            context.update(nextExecutionTime, nextExecutionTime,
                    nextExecutionTime);
        }
    }

}