The error I get is as follows:

SEVERE: StandardWrapper.Throwable
org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.scheduling.quartz.JobDetailBean] for bean with name ‘runMeJob’ defined in ServletContext resource [/WEB-INF/quartz-servlet.xml]: problem with class file or dependent class; nested exception is java.lang.IncompatibleClassChangeError: class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as super class

 

이런 오류 메시지가 나올때는

 

quartz 다운로드 페이지에서 Quartz 1.8.6 버전으로 lib 교체해주면 된다.



출처 - http://jhroom.tistory.com/129



===================================================================================


WAS를 이용해서 주기적으로 특정 프로그램 실행하는 방법이 spring에서는 2가지가 있다. (뭐 더 있을수도 있다 -_-)

하나는 spring batch이고 다른 하나는 quartz를 이용하는 방법이다.

뭐가좋은지는 나도 잘 모른다. 일단 quartz가 어떤식으로 동작하는지 먼저 공부했으므로 이것부터 글 적는다.

 

1. 샘플 프로젝트 생성

STS 에서 [File] - [New] - [Spring Template Project] 메뉴를 클릭한 후 Spring MVC Project를 이용해 샘플 프로젝트를 생성한다.

 

2. pom.xml 파일 수정

quartz 관련 모듈을 이용하기 위해서 pom.xml 파일에 관련 라이브러리들 넣는다.

spring-tx, quartz, commons-collections, javax.transaction 4개 넣어줘야한다.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<!-- Quartz framework and dependencies -->
<dependency>
    <groupId>opensymphony</groupId>
    <artifactId>quartz</artifactId>
    <version>1.6.3</version>
    <scope>compile</scope>
</dependency>
<!-- Quartz 1.6.0 depends on commons collections -->
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
    <scope>runtime</scope>
</dependency>
<!-- Quartz 1.6.0 requires JTA in non J2EE environments -->
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>runtime</scope>
</dependency>

 

3. 주기적으로 실행 될 Bean 생성

2개의 Bean을 만든다.

CronQuartz1.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz1 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 1 (5 second): current time = " + sdf.format(time));
    }
 
}

CronQuartz2.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz2 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 2 (1 minute): current time = " + sdf.format(time));
    }
}

 

4. quartz Bean과 위에서 생성한 Bean 연동

2개의 Bean이 각각 다른 시간차이로 실행되도록 설정해줄것이다.

CronQuartz1.java - 5초마다 실행

CronQuartz2.java - 1분마다 실행

 

/src/main/webapp/WEB-INF/spring/root-context.xml

<!-- 1. Cron 대상이 되는 클래스 정의 -->
<bean id="cronQuartz1" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz1"/>
</bean>
<bean id="cronQuartz2" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz2"/>
</bean>
 
<!-- 2. Cron 시간 설정 -->
<bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz1"/>
    <property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
<bean id="cronTrigger2" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz2"/>
    <property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
 
<!-- 3. Cron 실행 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger1"/>
            <ref bean="cronTrigger2"/>
        </list>
    </property>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
            <prop key="org.quartz.threadPool.threadCount">3</prop>
            <prop key="org.quartz.threadPool.threadPriority">4</prop>
            <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
            <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
        </props>
    </property>
</bean>

 

 

Cron Expression

총 7개의 필드 있고 마지막 필드(년도)는 생략 가능하다

 필드이름허용 값 
초(Seconds)0 ~ 59 
분(Minutes)0 ~ 59 
시간(Hours)0 ~ 23
달의 날짜(Day-of-month)1 ~ 31
달(Month) 1 ~ 12 or JAN ~ DEC
주의 날짜(Day-of-week)1 ~ 7 or SUN-SAT
년도(Year) (선택가능) 빈값, 1970 ~ 2099

Cron Expression의 특수문자
Expression설명 예시 
    * 모든 수를 나타냄  
     -값의 사이를 의미* 10-13 * * * *     10,11,12,13분에 동작함 
     ,특정값 지칭* 10,11,13 * * * *      10,11,13분에 동작함
     /값의 증가를 표현* 0/5 * * * *       0분부터 시작해서 5분마다 동작 
     ?특별한 값이 없음을 나타냄(day-of-month, day-of-week 필드만 사용) 
     L마지막 날을 나타냄(day-of-month, day-of-week 필드만 사용) 

6. 실행결과

실행결과를 보면 하나는 5초마다, 다른 하나는 1분마다 Bean이 실행되고 있음을 알수 있다.

01.png





===================================================================================


sping mvc template project로 구성 시

quartz-1.8.6과 sprint-tx를 적용하여 문제 없이 구현 하였다.


spring-tx를 pom.xml에 추가 하지 않을 경우 다음과 같이 에러 발생

심각: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/transaction/TransactionException


===================================================================================


What Is Quartz

   

원문http://www.onjava.com/lpt/a/6207

저자Chuck Cavaness

2005-09-28

   

Quartz

Quartz는 오픈 소스 작업 스케줄링 프레임워크이다. Quartz는 완전히 자바로 작성되어 있으며, J2SE와 J2EE 어플리케이션 모두에서 사용될 목적으로 설계되었다. Quartz는 매우 유연하며 단순한 구조를 제공한다. 간단한 작업은 물론 복잡한 작업 모두에 대한 스케줄링을 작성할 수 있다. Quartz는 또한 EJB, JavaMail 등을 위한 데이터베이스 지원, 클러스터링, 플러그 인, 미리 내장된 작업들을 포함하고 있으며, cron과 유사한 표현식도 지원한다.

   

매일 또는 매주 오후 11시 30분 또는 매달 말일에만 실행하는 작업을 수행하는 어플리케이션을 작성해 본 적이 있는가? 수작업 없이 자동으로 실행될 수 있는 작업이 실행되는 동안 만약 실행하는 동안 심각한 오류가 발생할 경우, 어플리케이션은 잘못되었다는 것을 스스로 알아내 알려주고 이를 다시 실행시키도록 시도해야 할 것인가? 여러분과 팀이 자바로 프로그램을 작성하고 있는가? 만약 이들 질문들에 대한 대답이 "그렇다"라면, Quartz Scheduler를 사용해 보아라.

   

Job Scheduling Made Easy

Quartz는 오픈 소스 작업 스케줄링 프레임워크로 완전히 자바로 작성되었다. 작업 스케줄링 이라는 용어에 대해 너무 겁먹지 말아라. Quartz 프레임워크는 수많은 기능들이 매우 단순한 형태로 무장되어 있으며, 놀라울 정도로 매우 사용하기 쉽게 되어 있다.

   

org.quartz.Job 인터페이스를 구현하는 자바 클래스를 작성하기만 하면 된다. Job 인터페이스는 다음과 같은 하나의 메소드만을 포함하고 있다.

   

public void execute(JobExecutionContext context) throws JobExecutionException;

   

여러분이 작성한 Job 클래스에서 execute() 메소드에 몇 가지 로직을 추가한다. Job 클래스와 스케줄을 설정하게 되면, Quartz는 그 나머지 작업을 처리해준다. Scheduler가 Job에 알려줄 시간이라고 판단하게 되면, Quartz 프레임워크는 Job 클래스의 execute() 메소드를 실행하여 작업을 수행하도록 만들어준다. Scheduler에 어떠한 것도 보고해줄 필요가 없으며, 어떤 특별한 메소드를 호출할 필요도 없다. 단순히 Job 내에 있는 작업들만 수행해주면 끝이다. 만약 여러분이 이후에 다시 Job이 호출되도록 설정했다면, Quartz 프레임워크는 적절한 때에 다시 이를 호출하는 부분을 담당해준다.

   

만약 여러분이 Apache Struts와 같은 유명한 오픈 소스 프레임워크를 써본 경험이 있다면, Quartz의 설계 내용과 컴포넌트에도 쉽게 익숙해질 것이다. 비록 이 두 오픈 소스 프로젝트가 전혀 서로 다른 문제점들을 해결해주고 있지만, 오픈 소스 소프트웨어를 항시 사용하는 사람들이라면 편안한 느낌을 받을 것이다. Quartz는 표준 독립형 J2SE 어플리케이션 내에서라든지, 웹 어플리케이션 내부, 심지어는 J2EE 어플리케이션 서버 내에서 사용될 수 있다.

   

The History behind Quartz

Quartz가 주목을 받기 시작한 것은 올해부터지만, 나온 지는 좀 되었다. Quartz는 James House에 의해 개발되었으며, 원래는2001년 봄에 SourceForge 프로젝트에 추가되었다. 수년이 지나면서 많은 기능들이 추가되어 배포되었지만, 두각을 나타내기 시작하면서 주목을 받기 시작한 것은 OpenSymphony 프로젝트의 일부로 되면서 새로운 사이트로 옮겨지게 된 때부터였다.

   

House는 자신을 도와주는 여러 파트 타임 개발자와 함께 여전히 개발 작업의 많은 부분에 참여하고 있다. Quartz 개발 팀은 올 해 1.5 배포판을 포함해 다양한 새로운 버전들을 배포할 수 있었으며, 이는 현재 candidate 릴리즈 단계에 있다.

   

Getting Your Hands on Quartz

Quartz 프로젝트는 OpenSymphony 사이트에 호스팅 되어 있다. 이 사이트에는 JavaDocs, 튜토리얼, CVS, 사용자와 개발자 포럼 및 다운로드 링크와 같은 여러 유용한 정보들이 제공되고 있다.

   

다운로드 링크에서 배포판을 다운로드 한 후, 적절한 위치에 압축을 푼다. 이 안에 보면 어플리케이션에서 사용할 수 있는 Quartz바이너리 파일이 포함되어 있다. Quartz 프레임워크에서 요구 되는 다른 의존적인 라이브러리는 거의 없다.

   

배포판에 보면, <Quartz 설치 디렉터리>/lib/core와 <Quartz 설치 디렉터리>/lib/optional 디렉터리에 있는 다른 의존 라이브러리들을 프로젝트에 추가하면 된다. 이들 대부분은 표준 Jakarta Commons 라이브러리들로, 여러분도 잘 알고 있는 Commons Logging, Commons BeanUtils 등이다.

   

The quartz.properties File

Quartz에는 quartz.properties라는 환경 설정 파일이 포함되어 있다. 이 파일을 사용하면 Quartz 프레임워크의 런타임 환경을 수정할 수 있다. 디폴트로, Quartz 바이너리 내에 포함되어 있는 파일이 사용된다. 이 파일을 복사하여 클래스들이 있는 디렉터리에 두면 클래스 로더가 이를 참조할 수 있다. 예제 1은 quartz.properties 파일에 대한 간단한 예이다.

   

예제 1. quartz.properties 파일을 통해 Quartz 런타임을 변경할 수 있다.

#===============================================================

# Configure Main Scheduler Properties

#===============================================================

   

org.quartz.scheduler.instanceName = QuartzScheduler

org.quartz.scheduler.instanceId = AUTO

   

#===============================================================

# Configure ThreadPool

#===============================================================

   

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 5

org.quartz.threadPool.threadPriority = 5

   

#===============================================================

# Configure JobStore

#===============================================================

   

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

   

일단 Quartz 바이너리와 의존 라이브러리들을 프로젝트에 추가했고, quartz.properties 파일을 클래스패스 디렉터리에 추가했다면, 이제 몇몇 Job들을 작성해보자. 그러나, 이 작업을 해보기 전에 Quartz 아키텍처에 관해 간단하게 살펴보도록 하자.

   

Inside the Quartz Architecture

크기로 보자면, Quartz는 다른 대부분의 오픈 소스 프레임워크들과 유사하다. Quartz는 약 300 개의 자바 클래스들과 인터페이스들을 포함하고 있으며, 이들은 약 12개의 패키지로 구성되어 있다. 이는 Apache Struts에 있는 11개의 패키지에 약 325개의 클래스들과 인터페이스들에 비견될 수 있다. 비록 이 크기가 프레임워크의 질을 결정하는데 사용되는 특징이 될 수는 없겠지만, 여기에서 말하고자 하는 점은 Quartz 내에 수많은 기능들이 포함되어 있다는 사실이다. 그리고, 그러한 기능들이 바로 프레임워크의 질을 결정하는데 사용되는 하나의 요소이다. 이는 오픈 소스에만 국한되는 내용은 아니다.

   

The Quartz Scheduler

Quartz 프레임워크의 핵심은 Scheduler 이다. Scheduler는 Quartz 어플리케이션을 위한 런타임 환경을 관리하는 책임을 지니고 있다. Scheduler 그 자체가 모든 작업들 수행하는 것은 아니다. Scheduler는 프레임워크 내부에 있는 매우 중요한 몇몇 컴포넌트들에 그 작업을 의존하고 있다. 확장성을 보장하기 위해, Quartz는 멀티스레드 아키텍처를 기반으로 하고 있다. Quartz 프레임워크가 시작될 때, 프레임워크는 스케줄링 된 Job들을 실행하기 위해 Scheduler가 사용하는 "worker 스레드들"을 초기화 한다. Quartz가 동시에 수많은 Job들을 실행 가능한 이유가 바로 여기에 있다. Quartz는 스레드 환경을 관리하기 위해 ThreadPool관리 컴포넌트들에 의존하고 있는데, 그 결합도는 느슨하다. 이 글을 통해 여러 번 언급할 것이지만, 이는 Quartz에 있는 모든 것들은 환경 설정이 가능하거나 사용자가 정의해 지정할 수 있음을 의미한다. 예를 들면, 여러분이 정의한 "ThreadPool" 관리 기능을 플러그 인 형태로 끼워 넣고 싶은 경우, 이러한 작업이 가능하다.

   

Jobs, Jobs, and More Jobs

Quartz의 용어를 사용하자면, Job은 작업을 수행하는 간단한 자바 클래스이다. 이 작업은 자바에서 코드로 작성 가능한 그 어떠한 것이라도 될 수 있다. 필수적으로 요구되는 사항은, org.quartz.Job 인터페이스를 구현하고, 심각한 오류 발생 시JobExecutionException을 발생시키는 것뿐이다. 여러분은 이미 앞에서 Job 인터페이스와 포함된 execute() 메소드를 보았다.

   

Job 인터페이스와 execute() 메소드를 구현했다면, Quartz는 Job 실행 시기가 되었을 때, Job을 호출한다. execute() 메소드 안에서 무엇을 수행하는가는 전적으로 개발자에게 달려 있다. 다음은 Job 내부에서 실행할 작업들에 대한 몇몇 예이다.

   

● JavaMail이나 Commons Net과 같은 다른 메일 프레임워크를 사용하여 전자메일 전송

● EJB에 대한 원격 인터페이스를 생성한 후, 이 인터페이스의 메소드 호출

● Hibernate 세션을 얻어 관계형 데이터베이스에 있는 데이터 질의와 갱신

● OSWorkflow를 사용하여 Job으로부터 워크플로우 호출

● FTP를 사용해 파일 옮기기

● Ant 빌드 스크립트를 호출해 스케줄링 되어 있는 빌드 작업 시작

   

수많은 여러 작업들이 가능하며, 이것이 바로 Quartz 프레임워크를 매우 강력하게 만들어주는 이유이다. Quartz는 매우 일반적이고 반복적인 스케줄을 작성해주는 메커니즘을 제공하기 때문에, 개발자는 단지 실행을 위해 호출될 자바 클래스들만 작성하면 된다.

   

Job Management and Storage

Job들의 스케줄이 지정되었다면, Scheduler는 이러한 Job들을 기억하고 이들을 실행시킬 시간을 지속적으로 추적해야 한다. 만약 여러분의 Job이 30분 늦게 시작되거나 30초 일찍 시작된다면 Quartz는 그렇게 유용하지 않을 것이다. 사실, 스케줄이 지정된Job들 상에 있는 execute() 메소드를 호출하는 시간은 매우 정확해야 한다. Job 저장과 관리는 Quartz에서 JobStore로 일컬어지는 개념을 통해 이루어진다.

   

Available JobStores

Quartz 프레임워크에서는 두 가지 기본적인 JobStore 타입을 제공한다. Scheculer 정보를 유지하는데 일반적인 메모리(RAM)을 사용하는 첫 번째 타입은 RAMJobStore라 불린다. 이러한 타입의 JobStore는 설정 및 실행이 매우 간단하다. 많은 어플리케이션들에 대해 이러한 JobStore만으로도 충분할 것이다. 그러나, Scheduler 정보가 JVM에 할당되어 있는 메모리에 저장되기 때문에, 어플리케이션이 멈추게 되면, 스케줄과 관련된 모든 정보가 사라진다. 만약 어플리케이션이 재 시작하는 경우에도 이러한 스케줄 정보를 유지할 필요가 있다면, 두 번째 유형의 JobStore을 사용해야 할 것이다.

   

두 번째 타입의 JobStore는 실제로 Quartz 프레임워크에서 두 가지의 서로 다른 형태로 구현되어 제공되고 있지만, 이 둘 모두 일반적으로 JDBC JobStore로 일컬어지고 있다. 이 두 가지 모두의 JDBC JobStore는 JDBC 드라이버를 사용하며, 스케줄 정보를 유지하고 있는 관계형 데이터베이스로부터 정보를 가져온다. 이 두 가지 타입은 데이터베이스 트랜잭션을 제어하는지의 여부나 BEA의 WebLogic이나 JBoss와 같은 어플리케이션 컨테이너에 제어를 넘기는지의 여부에 그 차이점이 존재한다 (이는J2EE에서의 BMT와 CMT 사이의 차이점과 유사하다).

   

두 가지 유형의 JDBC JobStore는 다음과 같다.

   

● JobStoreTX: 트랜잭션을 제어하고 싶은 경우나, 서버 환경 없이 어플리케이션을 운영하려 할 때 사용된다.

● JobStoreCMT: 어플리케이션 서버 환경 내에서 어플리케이션이 운영되며 컨테이너가 트랜잭션을 관리하도록 하고 싶은 경우 사용된다.

   

JDBC JobStore는 어플리케이션이 중지되고 다시 시작된 후에라도 스케줄링 정보를 유지하여 Scheduler가 실행되도록 만들어야 할 경우를 위해 설계되었다.

   

Job and Triggers

Quartz 설계자들은 Job과 스케줄을 분리하였다. Quartz에서 Trigger는 Job이 트리거링 되거나 발생되어야 할 때, Scheduler에게 알려주는데 사용된다. Quartz 프레임워크에서는 간단한 Trigger 타입들을 제공하고 있는데, SimpleTrigger와 CronTrigger가 가장 일반적으로 사용된다.

   

SimpleTrigger는 스케줄을 간단히 발생시키는데 사용될 목적으로 설계되었다. 일반적으로, 주어진 시간에 Job을 발생시켜 (m)초 간격을 두고 여러 번(n) 이를 실행할 필요가 있을 경우, SimpleTrigger가 적합한 선택이 된다. 반면, Job에 요구되는 스케줄링이 복잡할 경우, CronTrigger가 적합할 것이다.

   

CronTrigger는 달력과 유사한 스케줄에 기반하고 있다. 만약 여러분의 Job이 매주 토요일과 일요일을 제외한, 매일 오전 10시30분마다 실행되어야 하는 경우에, CronTrigger가 사용된다. 이 이름이 암시하고 있듯, CronTrigger는 Unix의 cron 표현식을 기반으로 하고 있다. 예를 들면, 다음의 Quartz cron 표현식은 월요일부터 금요일에 걸쳐 매일 오전 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * MON-FRI

   

그리고 다음 표현식은 2002, 2003, 2004, 2005년 동안 매월 마지막 금요일 오후 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * 6L 2002-2005

   

이러한 작업은 SimpleTrigger로  수행할 수 없다. 이 둘 모두 Job에 사용될 수 있다. 어떠한 것을 사용할 지에 관한 선택은 스케줄링 될 작업 성격에 달려 있다.

   

Scheduling a Job

이제 예제 Job을 살펴보면서 실제 사용에 관한 부분에 대해 토의해보자. 클라이언트가 자신의 FTP 사이트에 파일을 저장할 때마다 부서에 전자 메일로 통지할 필요가 있는 상황을 여러분이 관리하고 있다고 가정해보자. 우리의 Job은 원격 서버에 있는 파일들을 다운로드 하는 것이 된다. 그런 후, Job은 발견된 파일들의 다운로드 횟수를 포함하고 있는 전자 메일을 전송하게 된다. 이Job은 누군가가 하루 동안 이러한 작업을 수작업으로 전송할 필요가 없도록 편리하게 만들어준다. 우리는 이러한 Job이 일주일 내내 하루 24시간 동안 매 60초마다 검사하도록 설정할 수 있다. 이는 바로 Quartz 프레임워크를 완벽하게 사용하는 예이다.

   

첫 번째 단계는 FTP와 Email 로직을 수행하는 Job 클래스를 작성하는 것이다. 다음 예제는 Quartz Job 클래스를 나타낸 것으로, org.quartz.Job 인터페이스를 구현하고 있다.

   

예제 2. FTP 사이트에서 파일들을 다운로드 받고 Email을 전송하는 Quartz Job

   

public class ScanFTPSiteJob implements Job {

    private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);

   

    /*

     * 정확한 시간에 스케줄러 프레임워크에 의해 호출된다.

    */

    public void execute(JobExecutionContext context) throws JobExecutionException {

        JobDataMap jobDataMap = context.getJobDataMap();

   

        try {

            // FTP 사이트에서 파일들 검사

            File[] files = JobUtil.checkForFiles(jobDataMap);

            JobUtil.sendEmail(jobDataMap, files);

        } catch (Exception ex) {

            throw new JobExecutionException(ex.getMessage());

        }

    }

}

   

이 글에서는 일부러 ScanFTPSiteJob을 매우 간단하게 구성하였다. 또한 이 글에서는 이 예제를 위해 JobUtil이라는 유틸리티 클래스를 작성하였다. 이 클래스는 Quartz의 부분이 아니지만, 다양한 Job들에서 재사용 할 수 있는 여러분만의 유틸리티 성격의 라이브러리를 만드는 것이 좋다. 이 글에서는 Job 클래스와 Quartz Scheduler 내부에 모든 코드들을 쉽게 둘 수도 있었지만, Quartz를 사용하는 것 때문에 재사용을 고려하지 않을 수는 없었다.

   

JobUtil.checkForFiles()와 JobUtil.sendEmail()이 사용하는 파라미터들은 JobDataMap 객체를 사용하고 있는데, 이 객체는Quartz가 생성한 객체이다. 이 인스턴스는 Job이 실행될 때마다 생성되며, 이를 사용해 Job 클래스로 환경 설정 파라미터들을 넘겨준다.

   

JobUtil의 구현 부분은 여기에 나타내지 않았지만, 우리는 FTP와 Email 기능을 모두 구현하고 있는 Jakarta의 Commons Net을 통해 매우 쉽게 사용할 수 있었다.

   

Calling Your Jobs with the Scheduler

Job을 생성하는 것이 첫 번째 작업이지만, Scheduler에 의해 Job이 호출되도록 하기 위해서는 Scheduler에게 얼마나 자주, 언제 Job이 호출되어야 하는지 알려주어야 한다. 이 작업은 Trigger를 Job에 연관시킴으로써 이루어진다. 우리는 Scheduler가 계속 매 60초마다 Job을 호출하는데 관심을 두고 있기 때문에, SimpleTrigger를 사용할 것이다.

   

Job과 Trigger의 스케줄은 Quartz Scheduler 인터페이스를 통해 이루어진다. Scheduler의 인스턴스를 얻기 위해서는 팩토리로부터 인스턴스를 얻어와야 한다. 이를 위한 가장 쉬운 방법은 StdSchedulerFactory 클래스의 static 메소드인getDefaultScheduler()를 호출하는 것이다.

   

Quartz 프레임워크를 사용할 때, 반드시 start() 메소드를 호출하여 Scheduler를 시작시켜야 한다. 예제 3에 있는 코드는 대부분의 Quartz 어플리케이션의 일반적인 패턴을 따르고 있다. 대부분의 Quartz 어플리케이션에서는, 하나 또는 그 이상의 Job들을 생성하고 Trigger들을 생성하고 설정한 후, Scheduler에 Job과 Trigger들에 대한 스케줄을 정하고 Scheduler를 시작시킨다(주: Scheduler를 먼저 시작시켜도 된다. 이는 중요하지 않다).

   

예제 3. Quartz Job들은 Quartz Scheduler를 통해 스케줄이 지정되어야 한다.

   

public class MyQuartzServer {

    public static void main(String[] args) {

        MyQuartzServer server = new MyQuartzServer();

   

        try {

            server.startScheduler();

        } catch(SchedulerException ex) {

            ex.printStackTrack();

        }

    }

   

    protected void startScheduler() throws SchedulerException {

        // 팩토리를 사용해 Scheduler 인스턴스를 생성

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

   

        // JobDetail은 Job들에 대한 정의를 포함한다.

        JobDetail jobDetail = new JobDetail("ScanFTPJob", Scheduler.DEFAULT_GROUP,

                                          ScanFTPSiteJob.class);

        // execute() 내에서 사용될 job 파라미터들을 저장

        jobDetails.getJobDataMap().put("FTP_HOST", "\home\cavaness\inbound");

   

        // 여기에 Job 파라미터들에 필요한 기타 다른 내용들이 온다.

   

        // 매 60초마다 발생하는 Trigger 인스턴스 생성

        Trigger trigger = TriggerUtils.makeSecondlyTrigger(60);

   

        // Scheduler에 Job과 Trigger를 설정

        scheduler.scheduleJob(jobDetail, trigger);

   

        // Scheduler 실행 시작

        scheduler.start();

    }

}

   

Programmatic vs. Declarative Scheduling

예제 3에서, 우리는 프로그래밍을 통해 ScanFTPSiteJob 스케줄을 작성했다. 즉, Scheduler에 Job과 Trigger를 설정하기 위해 자바 코드를 사용하였다는 의미이다. Quartz 프레임워크에서는 XML 파일들에 Job 스케줄을 선언적으로 설정할 수 있는 기능을 지원하고 있다. 선언적인 접근 방법을 통해 어떠한 Job이 언제 실행되어야 하는지를 보다 빠르게 지정할 수 있다.

   

Quartz 프레임워크에는 Quartz 어플리케이션이 시작하자마자, Job과 Trigger 정보를 포함하고 있는 XML 파일을 읽어 들이는"플러그 인"을 포함하고 있다. XML 내에 있는 모든 Job들은 이들과 관련된 Trigger들과 함께 Scheduler에 추가된다. 물론 Job클래스들을 작성해야 하지만, 그러한 Job들을 갖는 Scheduler의 환경을 설정하는 것은 매우 동적으로 이루어진다. 예제 4는 예제 3의 코드에 있는 동일한 로직을 수행하는데, 선언적인 방법으로 구성되어 있다.

   

예제 4. Quartz Job들은 XML 파일을 사용하여 스케줄링 될 수 있다.

   

<?xml version="1.0" encoding="utf-8"?>

<quartz>

    <job>

        <job-detail>

            <name>ScanFTPSiteJob</name>

            <group>DEFAULT</group>

            <description>A job that scans an ftp site for files</description>

            <job-class>ScanFTPSiteJob</job-class>

   

            <job-data-map allows-transient-data="true">

                <entry>

                    <key>FTP_HOST</key>

                    <value>homecavanessinbound</value>

                </entry>

   

                <!-- 다른 필요한 Job 파라미터들을 여기에 둔다 -->

            </job-data-map>

        </job-detail>

   

        <trigger>

            <simple>

                <name>ScanFTPSiteJobTrigger</name>

                <group>DEFAULT</group>

                <job-name>ScanFTPSiteJob</job-name>

                <job-group>DEFAULT</job-group>

                <start-time>2005-09-11 6:10:00 PM</start-time>

                <!-- 계속 60초마다 반복 실행 -->

                <repeat-count>-1</repeat-count>

                <repeat-interval>60000</repeat-interval>

            </simple>

        </trigger>

    </job>

</quartz>

   

예제 4에 있는 XML 엘리먼트들을 예제 3에 있는 자바 코드와 비교해볼 수도 있다. 이들은 개념적으로 동일하다. 예제 4에 보여지는 것과 같은 선언적인 접근 방법의 장점은 유지보수가 매우 간단해진다는 것이다. XML 파일만 변경하고 Quartz 어플리케이션을 재 시작 하기만 하면 되기 때문이다. 소스 코드를 수정하고 재 컴파일 하여 배포할 필요가 없다.

   

Stateful and Stateless Jobs

이 글에서 살펴보았던 Quartz Job 예제는 모두 상태 정보를 가지고 있지 않다. 즉, 각각의 Job이 실행에 대해, Job이 실행되는 동안 JobDataMap에 가해진 어떠한 변경 사항들도 유지되지 않는다는 것을 의미한다. 만약 JobDataMap에 값을 추가, 변경 또는 삭제하는 기능이 필요하며, 다음 실행에 이러한 변경 사항들이 Job에 반영되도록 해야 한다면, Quartz Stateful Job이 필요하다.

   

만약 여러분이 EJB 개발을 경험해 보았다면, Stateful이라는 것이 부정적인 의미를 담고 있다는 점 때문에 여러분은 지금 움찔하고 있을 것이다. 이는 주로 "Stateful EJB"가 가지고 있는 확장성 이슈로부터 기인한다. Quartz Stateful Job은org.quartz.StatefulJob 인터페이스를 통해 구현된다. Stateless Job과 Stateful Job 간의 주요 차이점은, Stateful Job은 한 번에Job을 실행하는 인스턴스가 오직 하나를 가질 수 있다는 점이다. 그러므로, 예제 3의 경우, "ScanFTPJob" Job을 실행하는 인스턴스는 한 번에 하나만 가지게 된다. 대부분의 경우, 이는 커다란 문제점을 나타내지는 않는다. 그러나, 만약 자주 실행될 필요가 있는 Job을 가지고 있다거나, 작업 완료까지 오랜 시간을 필요로 하는 Job을 가지고 있을 경우, Stateful Quartz Job은 확장성에 문제를 가져다 줄 수 있다.

   

Other Features of the Quartz Framework

Quartz 프레임워크는 매우 다양한 기능들을 가지고 있다. 사실, 한 번에 이 모든 기능들을 나열하기에는 너무나 많다. 다음 목록은 이 글에서는 자세히 언급할 시간이 없는 Quartz 내의 여러 기능들 중 몇몇을 간단하게나마 설명한 것이다.

   

Listeners and Plugins

오늘날 어떠한 오픈 소스 프레임워크라도 사용하고 있는 개념이 바로 이 둘이다.

   

Quartz Listener는 주요 이벤트가 발생할 때, 프레임워크 내부로부터 콜백을 받는 자바 클래스이다. 예를 들면, Job이 스케줄 되어 있거나 스케줄 되어 있지 않을 때, 또는 Trigger가 끝났거나, 더 이상 발생시키지 않을 때, 이러한 모든 것들은 Listener로 통지되도록 설정될 수 있다. Quartz 프레임워크는 Scheduler, Job, Trigger들을 위한 Listener들을 포함하고 있다. 또한, Job Listener와 Trigger Listener들을 특정한 한 Job이나 Trigger에 적용되도록 만들거나 전체에 걸쳐 적용되도록 설정할 수도 있다.

   

일단 Listener가 호출되면, 이 정보를 사용해 Listener 클래스 내에서 수행하려는 어떠한 로직이라도 실행시킬 수 있다. 예를 들면, 만약 Job이 완료될 때마다 전자 메일을 보내고 싶은 경우, 이를 Job에 프로그래밍 해 넣을 수 있다. 또한, JobListener를 사용할 수도 있다. 이 JobListener는 결합도를 느슨하게 만들어 보다 나은 설계를 만들어주는데 도움을 줄 수도 있다.

   

Quartz Plugin은 Quartz 소스를 수정하지 않고도 Quartz 프레임워크에 기능을 추가시켜주는 새로운 기능이다. 이것은 Quartz프레임워크를 확장해야 하는데, 변경한 기능을 Quartz 개발 팀에게 보내주고 다음 버전에 반영되기까지 기다릴 시간이 없는 개발자들을 위한 기능이다. 여러분이 Struts 플러그 인에 익숙하다면, Quartz 플러그 인을 사용하는 방법을 더 쉽게 이해할 수 있을 것이다.

   

Clustering Quartz Applications

Quartz 어플리케이션은 여러분의 요구 사항에 따라 수직/수평적으로 모두 클러스터링 될 수 있다. 클리스터링을 통해 다른 클러스터링 타입과 마찬가지의 이점들을 제공받을 수 있다.

   

● 확장성

● 높은 가용성

● 로드 밸런싱

   

현재 Quartz는 관계형 데이터베이스와 JDBC JobStore중 하나의 도움을 통해 클러스터링을 지원한다. 향후 버전에서는, 이러한 제한이 사라져, 데이터베이스 없이도 RAMJobStore에서 사용 가능해질 것이다.

   

The Quartz Web Application

Quartz 프레임워크를 2-3주나 몇 달간 사용한 후 보통 Quartz 사용자들이 가장 많이 요구하는 것들 중 하나는 Quartz를 GUI에 통합하는 것에 대한 것이다. 자바 서블릿을 사용하여 Quartz를 초기화 하고 시작시킬 수 있는 기능이 현재 프레임워크에 제공되고 있다. 일단 여러분이 Scheduler 인스턴스에 접근하게 되면, 이 인스턴스는 웹 컨테이너의 ServletContext에 저장하고Scheduler 인터페이스를 통해 스케줄링 환경을 관리할 수 있다.

   

다행히도, 몇몇 Quartz 개발자들은 Quartz Scheduler 환경을 보다 잘 관리하는데 사용될 수 있는 독립형 Quartz Web Application에 대해 작업해 오고 있다. Struts와 Spring과 같은 다수의 유명한 오픈 소스 프로젝트를 기반으로 구축되어 이 GUI는 많은 기능들을 지원하는데, 이들은 간단한 인터페이스에 랩핑 되어 있다. 그림 1은 이 GUI를 잡아낸 것이다.

   

   

그림 1. Quartz 환경을 보다 쉽게 관리해주는데 도움을 주는 Quartz Web Application

   

What's down the Road?

이미 다음 주요 릴리즈에 대한 움직임도 진행되고 있을 정도로 Quartz 프로젝트는 활발하게 진행되고 있다. OpenSymphony의wiki에서 Quartz 2.0에서 고려되고 있는 기능들과 설계에 대한 정보를 얻을 수도 있다.

   

항상 그렇듯이, 날마다 Quartz 사용자들은 프레임워크에서 고려될 수 있는 갖가지 기능들에 대한 제안이나 설계에 관해 자유롭게 제안하고 있다.

   

Find Out More about Quartz

Quartz 프레임워크의 보다 많은 기능들을 사용해 나갈수록, User and Developer Forum은 Quartz 사용자들과의 질문/답변 및 커뮤니케이션을 위한 매우 유용한 자원이 될 것이다.

   

출처 : http://blog.empas.com/kkamdung/12297998

[출처] Quartz - java 스케쥴러|작성자 순짱

 

===================================================================================

 

가끔 서버에서 주기적으로 어떠한 작업을 하고자 할때 리눅스에서는 크론탭을 사용하여 주기적으로 어떠한 작업을 처리합니다.
이런 주기적 작업을 처리하기위해 Spring에서 지원해 주는 Quartz스케쥴러를 통해 크론탭과 같은 역할을 하는 스케쥴러를 작성할 있습니다.
이번에는 Spring Quartz 연동하여 스케줄러를 작성해 보겠습니다.

작업순서는 
스프링 기본 세팅 -> Quartz 세팅 순으로 작업하겠습니다.

1.
스프링 기본 설정
1) springframework.org 
이동하셔서 스프링 라이브러리를 다운 받습니다

위와 같은 페이지가 뜨면 해당사항을 입력하시고 Access Download 클릭하셔서 다운로드 페이지로 이동합니다. (귀찮으신 분들은 하단의 파란색으로 "download page" 선택하시면 입력하시지 않고도 다운로드 페이지로 이동하실수 있습니다.

많은 버전의 라이브러리  spring-framework-2.5.6.SEC02.zip  다운 받습니다. 다른 버전을 다운 받으셔도 상관없습니다만 버전에 따라 세팅 내용이 조금 달라지므로 같은 버전의 라이브러리로 진행하는 것이 나을 것같네요~^^.

2)
이렇게 라이브러리까지 다운로드 받고 나면 Eclipse 같은 IDE에서 Dynamic Web Project 선택하여 Project 한개 생성합니다.
(
저는 SpringQuartz 라는 이름으로 생성했습니다.)

3)
프로젝트가 생성되면 프로젝트 안에 /WEB-INF/lib 디렉토리에 스프링 라이브러리를 압축 곳에 있는 dist/spring.jar 파일을 추가합니다.
*
 : 프로젝트를 진행하다 보면 위와같이 라이브러리 버전이 없는 jar파일을 그냥 추가하는 경우가 있는데 나중에 라이브러리를 업데이트 해야 할일이 생기게 되면 위와같이 spring.jar 라고 되어있으면 지금 적용되어 있는 버전이 인지 알수가 없습니다. 그렇기 때문에 항상 라이브러리 추가하실때는 추가하시는 라이브러리의 버전 번호를 파일이름 뒤에 추가하는 습관 들이 시는게 좋습니다
     ex) spring-2.5.6.jar

4)
프로젝트 안에 생성된 web.xml Spring 사용하기 위한 세팅을 추가해 줍니다.
*
저는
Quartz 사용하기 위한 최소한의 Spring 세팅을 해놓았기 때문에 세팅 내용이 단순합니다. 만약 웹프로젝트와 함께 Quartz 사용하신다면 웹에 맞게 설정하시고 사용하셔야 함을 알려드립니다.^^

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/config/applicationContext*.xml</param-value>

</context-param>

</web-app>


5) 쿼츠 라이브러리를 다운로드 받고 라이브러리를 추가해 줍니다.
쿼츠 라이브러리 다운로드 하신다음 압축을 풀어 줍니다.
해당 라이브러리를 프로젝트의 lib 디렉토리에 복사하여 넣어줍니다.
- quartz-all-1.8.3.jar
-
압축푼 lib 디렉터리의 log4j-1.2.14.jar
-
압축푼 lib 디렉터리의 slf4j-api-1.5.10.jar
-
압축푼 lib 디렉터리의 slf4j-log4j12-1.5.10.jar
추가 줍니다.
마지막으로 apache 
commons-logging-1.1.1.jar 
다운로드 하셔서 위와 같이 프로젝트의 lib 추가해주시면 라이브러리 추가는 끝이 납니다.

6) Quartz
핵심적인 기능을 /WEB-INF/config/applicationConext.xml 작성합니다.
스케쥴러의 핵심 세팅은 3가지 정도 입니다.
하나. 실제 주기적으로 실행될 클래스 등록
. 스케줄러가 동작하는 interval time 설정
. 실제 동작하게  설정

이런 세가지가 있겠습니다.

스케줄러 동작방식에는 두가지가 존재 합니다.
-Simple : interval time
간단하게 동작하는 방식으로 몇초, 혹은 몇분, 몇시간 단위로 작동하고 싶을때 사용합니다
<Simple type setting>

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:p="http://www.springframework.org/schema/p"

  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-2.5.xsd

                           http://www.springframework.org/schema/context

                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!-- 하나.주기적으로 실행될 클래스 설정 -->

<!-- property name은 jobClass로 fix, value는 사용자가 작성한 class 파일 위치 -->

<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>

</bean>


<!-- 둘.스케줄러의 interval time 설정 -->

<!-- 쿼츠에는 아래와 같이 몇초에 한번씩 돌게 하는 Simple type 과 -->

<!-- 무슨 요일 몇시에 한번씩 돌게 하는 날짜로 지정하는 Cron type 이 있다. -->

<!-- 현재는 Simple type으로 세팅 -->

<!-- jobDetail은 위에서 설정한 실제 동작할 클래스 id를 적어준다 -->

<!-- startDelay는 서버 시작후 몇초 뒤에 시작할지 세팅(ms 단위)  -->

<!-- repeatInterval은 몇 초에 한번씩 실행될 건지 세팅(ms 단위: 현재 1초) -->

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">

<property name="jobDetail" ref="simpleQuartzJob"/>

<property name="startDelay" value="1000"/>

<property name="repeatInterval" value="1000"/>

</bean>

<!--셋. 실제 동작하게끔 설정 -->

<!--ref bean은 위에서 설정한 interval time 아이디를 넣어주면 됨  -->

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<list>

<ref bean="simpleTrigger"/>

</list>

</property>

<!-- Quartz 실행시 세팅 -->

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

    <prop key="org.quartz.threadPool.threadCount">5</prop>

    <prop key="org.quartz.threadPool.threadPriority">4</prop>

    <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

    <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>

</beans>



-Cron : linux Cron tab 같은 역할을 하는 타입니다. 몇월, 몇일 몇시에 동작하게 하고 싶으면 Cron type 사용하시면 됩니다<?xml version="1.0" encoding="UTF-8" ?>.
<Cron type setting>

<beans xmlns="http://www.springframework.org/schema/beans"

  xmlns:context="http://www.springframework.org/schema/context"

  xmlns:p="http://www.springframework.org/schema/p"

  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-2.5.xsd

                           http://www.springframework.org/schema/context

                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!--하나. 주기적으로 실행될 클래스 설정 -->

<bean id="cronQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.CronQuartzJob"/>

</bean>

<!--둘. 스케줄러의 interval time 설정-->

<!--cronExpression을 통해서 스캐줄러 주기를 설정한다. -->

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">

<property name="jobDetail" ref="cronQuartzJob"/>

<property name="cronExpression" value="0/1 * * * * ?"/>

</bean>

<!--셋. 실제 동작하게끔 설정 -->

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<list>

<ref bean="cronTrigger"/>

</list>

</property>

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

    <prop key="org.quartz.threadPool.threadCount">5</prop>

    <prop key="org.quartz.threadPool.threadPriority">4</prop>

    <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

    <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>

</beans>


Cron type 사용하려면 CronExpression 알아야 합니다.

*Cron Expression
cron expression
각각의 필드는 다음을 나타낸다.(왼쪽 -> 오른쪽 )

 필드 이름

 허용

 허용된 특수 문자

 Seconds

 0 ~ 59

 , - * /

 Minutes

 0 ~ 59

 , - * /

 Hours

 0 ~ 23

 , - * /

 Day-of-month

 1 ~ 31

 , - * ? / L W

 Month

 1 ~12 or JAN ~ DEC

  , - * /

 Day-Of-Week

 1 ~ 7 or SUN-SAT

 , - * ? / L #

 Year (optional)

 empty, 1970 ~ 2099

 , - * /


Cron Expression
특수문자
'*' :
모든 수를 나타냄. 분의 위치에 * 설정하면 " 마다" 라는 .
'?' : day-of-month
day-of-week 필드에서만 사용가능. 특별한 값이 없음을 나타낸다.
'-' : "10-12"
같이 기간을 설정한다. 시간 필드에 "10-12" 이라 입력하면 "10, 11, 12시에 동작하도록 설정" 이란 .
',' : "MON,WED,FRI"
같이 특정 시간을 설정할 사용한다. "MON,WED,FRI" 이면 " ',,' 에만 동작" 이란 .
'/' :
증가를 표현합니다. 예를 들어 단위에 "0/15" 세팅 되어 있다면 "0 부터 시작하여 15 이후에 동작" 이란 .
'L' : day-of-month
day-of-week 필드에만 사용하며 마지막날을 나타냅. 만약 day-of-month "L" 되어 있다면 이번 달의 마지막에 실행하겠다는 것을 나타냄.
'W' : day-of-month
필드에만 사용되며, 주어진 기간에 가장 가까운 평일(~) 나타낸다. 만약 "15W" 이고 이번 달의 15일이 토요일이라면 가장가까운 14 금요일날 실행된다. 15일이 일요일이라면 가장 가까운 평일인 16 월요일에 실행되게 된다. 만약 15일이 화요일이라면 화요일인 15일에 수행된다.
"LW" : L
W 결합하여 사용할 있으며 "LW" "이번달 마지막 평일" 나타냄
"#" : day-of-week
사용된다. "6#3" 이면 3(3)번째 금요일(6) 이란 뜻이된다.1 일요일 ~ 7 토요일 

 Expression

 Meaning

 "0 0 12 * * ?"

 매일 12시에 실행

 "0 15 10 ? * *"

 매일 10 15분에 실행

 "0 15 10 * * ?"

 매일 10 15분에 실행

 "0 15 10 * * ? *"

 매일 10 15분에 실행

 "0 15 10 * * ?  2010" 

 2010 동안 매일 10 15분에 실행

 "0 * 14 * * ?"

 매일 14시에서 시작해서 14:59 끝남

 "0 0/5 14 * * ?"

 매일 14시에 시작하여 5 간격으로 실행되며 14:55분에 끝남

 "0 0/5 14,18 * * ?"

 매일 14시에 시작하여 5 간격으로 실행되며 14:55분에 끝나고, 매일 18시에 시작하여 5분간격으로 실행되며 18:55분에 끝난다.

 "0 0-5 14 * * ?"

 매일 14시에 시작하여 14:05 분에 끝난다.


*
시간에 맞춰 돌아가는 스케줄러에서 다른 클래스를 사용하고 싶을 때는 다음과 같이 설정합니다.

<!-- 스프링 DI : 사용할 Service 객체를 생성 -->

<bean id="quartzJobService" class="net.test.quartz.service.impl.QuartzJobServiceImpl"/>


<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>

<!-- 사용하고자 하는 class의 bean id를 등록 -->

<property name="jobDataAsMap">

<map>

<entry key="quartzJobService">

<ref local="quartzJobService"/>

</entry>

</map>

</property>

</bean>



 *두가지 스케줄러를 동시에 실행 시킬때

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<!--트리거를 두개 생성후 아래와 같이 세팅 -->

<list>

<ref bean="simpleTrigger"/>

<ref bean="cronTrigger"/>

</list>

</property>

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

    <prop key="org.quartz.threadPool.threadCount">5</prop>

    <prop key="org.quartz.threadPool.threadPriority">4</prop>

    <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

    <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>



7) 실제 작동할 Class파일 생성

package net.test.quartz;


import net.test.quartz.service.QuartzJobService;


import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;


public class SimpleQuartzJob extends QuartzJobBean{

//실행될 클래스는 꼭 QuartzJobBean을 상속받아야 되며 

//executeInternal method를 override 하면 자동으로 이 메소드가 실행


//Spring의 DI를 사용하여 Service객체를 setting

//DI를 사용하지 않는다면 필요 없는 부분

private QuartzJobService quartzJobService;

public void setQuartzJobService(QuartzJobService quartzJobService) {

this.quartzJobService = quartzJobService;

}



@Override

protected void executeInternal(JobExecutionContext ex)throws JobExecutionException {

quartzJobService.printLog();

}

}


위와 같은 방식으로 Spring Quartz 사용하여 스케줄러를 사용할 있습니다.

함께 업로드 하는 파일은 제가 직접 작업한 프로젝트이구요. 함께 확인 보시면 쉽게 쿼츠를 사용하실수 있으실 겁니다.~^^

 SpringQuartz.war

 

출처 - http://javastore.tistory.com/96


===================================================================================


매번 cronTab 으로 배치작업을 실행시키며, 이게 아직 쓰는 놈인지, 언제 만들었는지,
수정하기 번거롭네 등 에로사항이 많았으.. 기억도 안나고.. 그때그때 만든 것들은 버전관리도 안되고,,
해서. 스프링 프로젝트이니, 스프링에서 동작하는 배치를 쓰는게 낫겠다는 생각이 들어 이참에, 배치를 몽땅 quartz
로 만들어서 정리를 해야겠다는 생각이 들었심.

스프링 프로젝트 환경이 세팅이 되어있다고 치고,

 

*필자의 경우는
Dynamic Web Project 로 qbatch 라는 프로젝트를 만들었고,


디렉토리 구조는
qbatch
  |_src
  |_WebContent
          |_index.jsp
          |_META-INF
          |_WEB-INF
              |_classes
              |_lib
              |_views
              |_action-servlet.xml
              |_web.xml
             
이렇게 되어있고,
src 폴더에는 비지니스를 구현한 java 파일들(cotroller/service/dao 등),
classes 에는 src 의 컴파일된 class 파일들 과 applicationContext.xml 및 각종 properties 파일들,
lib 폴더에는 spring.jar, activation.jar, log4j.jar, mysql-connector-java-xxx.jar 등이,
views 에는 각종 jsp 파일들이 위치해있다.

 

암튼 시작해보면,

 

1. 일단 quartz 라이브러리를 다운받는다.
http://terracotta.org/downloads/open-source/destination?name=quartz-1.8.5.tar.gz&bucket=tcdistributions&file=quartz-1.8.5.tar.gz
압축을 풀고 quartz-all-1.8.5.jar 파일을 찾아서
WEB-INF/lib 폴더 밑에 복사해 넣으면 준비끝.

 

2. 실제 비지니스를 실행할 java 파일을 만든다.           
나의 비지니스 로직은 System.out.println("음. 배치가 실행되었구만~!!!!"); 이다.
이 로직만 빼고, 나머지는 코드는 동일하게 쓰면되심.

src/com/batch/service/SystemOutTestService.java 를 아래와같이 만든다.

 

 

package com.batch.service;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class SystemOutTestService extends QuartzJobBean {
 
 
 @Override
 protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
  try {
   
   System.out.println("음. 배치가 실행되었구만~!!!!");
  } catch (Exception e) {
   e.printStackTrace();
  }
  
 }

}

 


3. 이제 저 SystemOutTestService.java 가 실행스케줄링 되도록 applicationContext.xml 을 만져주자.
1. 실행할 비지니스로직 클래스를 빈으로 등록.
2. 해당빈이 스케줄을 만들 트리거 설정
3. 실제 동작되도록 설정.

이렇게 3가지만 세팅하면 되심. 아래와같이.

<beans 어쩌구저쩌구.... >

<!-- 1. 실행할 비지니스로직 클래스를 빈으로 등록. -->
<bean id="SystemOutTest" class="org.springframework.scheduling.quartz.JobDetailBean">
 <property name="jobClass" value="com.batch.service.SystemOutTestService"/>
 </bean>

<!-- 2. 해당빈이 스케줄을 만들 트리거 설정 -->
 <bean id="SystemOutTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="SystemOutTest"/>
  <property name="repeatInterval" value="3000"/> <!--  every 1000(1초)  -->
  <property name="startDelay" value="2000" /><!--  at first execution 2000(2초) 후에 실행  -->
 </bean>
 
<!-- 3. 실제 동작되도록 설정. -->
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no">
  <property name="triggers">
   <list>
    <ref bean="SystemOutTestTrigger"/>
   </list>
  </property>
  <property name="quartzProperties">
   <props>
    <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    <prop key="org.quartz.threadPool.threadCount">5</prop>
    <prop key="org.quartz.threadPool.threadPriority">4</prop>
    <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
    <prop key="org.quartz.jobStore.misfireThreshold">600000</prop>
   </props>
  </property>
 </bean>
 
 
이제 톰캣 재시작 하면 3초마다 "음. 배치가 실행되었구만~!!!!" 이라고 찍힐거임.
비지니스 로직이 넘 초라하니, 클래스 추가하여 실제 필요한 동작을 하게끔 비지니스 로직에
시간을 들이심이 좋을듯 해서 초간단으로 적었심. 더 세심한 설정은 구글형이나 네이년에
물어보시면 찾으실 수 있으실거임.


출처 - http://jejoong76.blog.me/70125014485


===================================================================================


Spring에서 Quartz JOB 설정 예제

  • 한 시간에 한번 keyGeneratorJob을 실행 시키는 예제 입니다.

applicationContext.xml 설정

<bean id="keyPool" class="com.oracleclub.web.support.KeyPool" init-method="init" lazy-init="true">
  <property name="keySize" value="1000"/>		
</bean>

<!-- jobClass 설정 -->
<bean id="keyGeneratorJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="com.oracleclub.web.job.KeyGeneratorJob"/>
  <property name="jobDataAsMap">
	<map>
      <entry key="keyPool">
   	    <ref bean="keyPool"/>
  	  </entry>
 	</map>
  </property>
</bean>

<!-- jobTrigger 설정 -->
<bean id="keyGeneratorTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="keyGeneratorJob" />
  <!--  1000 == 1 second, 60minuts=3600000, 1day=86400000-->
  <property name="startDelay" value="3600000" />
  <property name="repeatInterval" value="3600000" />
</bean>

<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >		
  <property name="triggers">
    <list>
      <ref bean="keyGeneratorTrigger"/>
    </list>
  </property>
</bean>

JAVA 예제 소스

  • 위에서 JobClass로 지정한 com.oracleclub.web.job.KeyGeneratorJob 소스 입니다.
  • 아래 소스는 한 시간에 한번씩 암호화 키를 다시 생성하는 예제 입니다.
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * Key Generator Job
 * 
 * @author : oramaster
 * 
 */
public class KeyGeneratorJob extends QuartzJobBean {
  private KeyPool keyPool;
  private final static Log LOG = LogFactory.getLog(KeyGeneratorJob.class);


  @Override
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

    List<KeyGenerator> newList = new ArrayList<KeyGenerator>();
    KeyGenerator keyGenerator;

    try {
      //배치때 실행되는 비지니스 로직
      for (int i = 0; i < keyPool.getKeySize(); i++) {
        keyGenerator = new KeyGenerator();
        byte[] originKey = XORMask.getGeneratorKey();

        keyGenerator.setBlowFishKey(BlowFish.encrypt(originKey, BlowFish.getKey()));
        keyGenerator.setOriginKey(originKey);

        newList.add(keyGenerator);
      }

      keyPool.setKeyList(newList);

    } catch (Exception e) {
      LOG.error(e.getMessage());
      throw new JobExecutionException(e.getMessage());
    }
  }

  public void setKeyPool(KeyPool keyPool) {
    this.keyPool = keyPool;
  }
}

문서정보


===================================================================================


Chapter 19. Quartz 혹은 Timer 를 사용한 스케쥴링

19.1. 소개

Spring은 스케쥴링을 지원하는 통합 클래스들을 제공한다. 현재적으로, Spring은 1.3 이후버전 JDK의 일부분인 Timer와 Quartz 스케쥴러 (http://www.quartzscheduler.org)를 지원하고 있다. 이 두개의 스케쥴러들은 각각 Timer 혹은 Triggers에 대한 선택적 참조를 가지는 FactoryBean을 사용하여 세팅된다. 게다가 당신이 타겟 object의 메써드를 편리하게 호출할 수 있도록 도와주는 Quartz 스케쥴러와 Timer에 대한 편의 클래스를 제공한다.(이것은 일반적인 MethodInvokingFactoryBeans와 비슷하다.)

19.2. OpenSymphony Quartz 스케쥴러 사용하기

Quartz는 TriggersJobs 그리고 모든 종류의 jobs를 인식하고 있는 JobDetail를 사용한다. Quartz에 깔려 있는 기본적인 개념을 알고 싶다면, http://www.opensymphony.com/quartz를 찾아보길 바란다. 편리한 사용을 위해서, Spring은 Spring 기반 어플리케이션 내에서 Quartz의 사용을 손쉽게 만들어주는 두 개의 클래스들을 제공한다.

19.2.1. JobDetailBean 사용하기

JobDetail 객체는 job을 실행하기 위해 필요한 모든 정보를 가지고 있다. Spring은 소위 JobDetailBean이라고 불리는 클래스를 제공하는데, 이것은 JobDetail을 합리적인 디폴트값을 가진 실질적인 JavaBean 객체로 만들어준다. 다음의 예제를 보도록 하자.

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
    <value>example.ExampleJob</value>
  </property>
  <property name="jobDataAsMap">
    <map>
      <entry key="timeout"><value>5</value></entry>
    </map>
  </property>
</bean>
			

위의 job detail bean은 job(ExampleJob)을 실행하기 위한 모든 정보를 가지고 있다. 타임아웃은 job data map으로 기술되었다. job data map은 (실행시 넘겨지는) JobExecutionContext를 통해 이용할 수 있지만, JobDetailBean 역시 job data map으로부터 실질적인 job의 프라퍼티들을 매핑할 수 있다. 때문에 이러한 경우, 만약 ExampleJob이 timeout이라는 프라퍼티를 가지고 있다면, JobDetailBean은 그것을 자동으로 적용할 것이다.

package example;

public class ExampleJob extends QuartzJobBean {

  private int timeout;
  
  /**
   * Setter called after the ExampleJob is instantiated
   * with the value from the JobDetailBean (5)
   */ 
  public void setTimeout(int timeout) {
    this.timeout = timeout;
  }
  
  protected void executeInternal(JobExecutionContext ctx)
  throws JobExecutionException {
      // do the actual work
  }
}
			

당신은 job detail bean의 모든 부가적인 세팅들 역시 마찬가지로 이용할 수 있다.

주의: name과 group 프라퍼티를 사용함으로써, 당신은 job의 name과 group을 변경할 수 있다. default로 job의 이름은 job detail bean의 이름과 동일하다.(위의 예에서는 exampleJob이 된다.)

19.2.2. MethodInvokingJobDetailFactoryBean 사용하기

종종 당신은 특정한 객체의 메써드를 호출할 필요가 있을 것이다. 당신은 MethodInvokingJobDetailFactoryBean을 사용하여 다음과 같이 할 수 있다.

<bean id="methodInvokingJobDetail" 
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예는 (아래에 있는) exampleBusinessObject의 doIt을 호출하는 것을 의미한다.

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
			

MethodInvokingJobDetailFactoryBean을 사용할 때, 메써드를 호출할 한줄짜리 jobs를 생성할 필요가 없으며, 당신은 단지 실질적인 비지니스 객체를 생성해서 그것을 묶기만 하면된다.

default로는 Quartz Jobs는 비상태이며, 상호 작용하는 jobs의 가능성을 가진다. 만약 당신이 동일한 JobDetail에 대해 두 개의 triggers를 명시한다면, 첫번째 job이 끝나기 이전에 두번째가 시작할지도 모른다. 만약 JobDetail 객체가 상태 인터페이스를 구현한다면, 이런 일은 발생하지 않을 것이다. 두번째 job은 첫번째가 끝나기 전에는 시작하지 않을 것이다. MethodInvokingJobDetailFactoryBean를 사용한 jobs가 동시작용하지 않도록 만들기 위해서는, concurrent 플래그를 false로 세팅해주어야 한다.

<bean id="methodInvokingJobDetail"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
    <property name="concurrent"><value>false</value></property>
</bean>
			

주의: 기본적으로 jobs는 concurrent 옵션에 따라 실행될 것이다.

19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기

우리는 job details과 jobs를 생성했고, 당신이 특정 객체의 메써드를 호출할 수 있도록 하는 편의클래스 bean을 살펴보았다. 물론, 우리는 여전히 jobs를 그자체로 스케쥴할 필요가 있다. 이것은 triggers와 SchedulerFactoryBean을 사용하여 이루어진다. 여러가지 triggers는 Quartz 내에서 이용할 수 있다. Spring은 편의를 위해 2개의 상속받은 triggers를 기본적으로 제공한다.:CronTriggerBean과 SimpleTriggerBean이 그것이다.

Triggers는 스케쥴될 필요가 있다. Spring은 triggers를 세팅하기 위한 프라퍼티들을 드러내는 SchedulerFactoryBean을 제공하고 있다. SchedulerFactoryBean은 그 triggers와 함께 실질적인 jobs를 스케쥴한다.

다음 두가지 예를 보자.

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail">
    <!-- see the example of method invoking job above -->    
    <ref bean="methodInvokingJobDetail"/>
  </property>
  <property name="startDelay">
    <!-- 10 seconds -->
    <value>10000</value>
  </property>
  <property name="repeatInterval">
    <!-- repeat every 50 seconds -->
    <value>50000</value>
  </property>
</bean>

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
    <ref bean="exampleJob"/>
  </property>
  <property name="cronExpression">
    <!-- run every morning at 6 AM -->
    <value>0 0 6 * * ?</value>
  </property>
</bean>
			

OK, 이제 우리는 두 개의 triggers를 세팅했다. 하나는 10초 늦게 실행해서 매 50초마다 실행될 것이고, 다른 하나는 매일 아침 6시에 실행될 것이다. 모든 것을 완료하기 위해서, 우리는 SchedulerFactoryBean을 세팅해야 한다.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list>
      <ref local="cronTrigger"/>
      <ref local="simpleTrigger"/>
    </list>
  </property>
</bean>
			

당신이 세팅할 수 있는 더욱 많은 속성들이 SchedulerFactoryBean에 있다. 이를테면, job details에 의해 사용되는 calendars라던가, Quartz를 커스터마이징할 수 있게 하는 프라퍼티같은 것들이 말이다. 더 많은 정보를 위해서는 JavaDoc(http://www.springframework.org/docs/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)을 참조하도록 해라.

19.3. JDK Timer support 사용하기

Spring에서 스케쥴링 업무를 처리하는 또다른 방법은 JDK Timer 객체들을 사용하는 것이다. Timers 자체에 대한 더 많은 정보는http://java.sun.com/docs/books/tutorial/essential/threads/timer.html에서 찾아볼 수 있다. 위에서 살펴 본 기본개념들은 Timer support에도 마찬가지로 적용된다. 당신은 임의의 timers를 생성하고 메써드들을 호출하기 위해 timer를 사용한다. TimerFactoryBean을 사용하여 timers를 묶는다.

19.3.1. 임의의 timers 생성하기

당신은 TimerTask를 사용하여 임의의 timer tasks를 생성할 수 있다. 이것은 Quartz jobs와 유사하다

public class CheckEmailAddresses extends TimerTask {

  private List emailAddresses;
  
  public void setEmailAddresses(List emailAddresses) {
    this.emailAddresses = emailAddresses;
  }
  
  public void run() {
    // iterate over all email addresses and archive them
  }
}
			

이것을 묶는 것 역시 간단하다:

<bean id="checkEmail" class="examples.CheckEmailAddress">
  <property name="emailAddresses">
    <list>
      <value>test@springframework.org</value>
      <value>foo@bar.com</value>
      <value>john@doe.net</value>
    </list>
  </property>
</bean>

<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <!-- wait 10 seconds before starting repeated execution -->
  <property name="delay">
    <value>10000</value>
  </property>
  <!-- run every 50 seconds -->
  <property name="period">
    <value>50000</value>
  </property>
  <property name="timerTask">
    <ref local="checkEmail"/>
  </property>
</bean>
			

task를 단지 한번만 실행하고자 한다면, period 속성을 -1(혹은 다른 음수값으)로 바꿔주면 된다.

19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기

Quartz support와 비슷하게, Timer 역시 당신이 주기적으로 메써드를 호출할 수 있도록 하는 요소들을 기술한다.

<bean id="methodInvokingTask" 
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예제는 (아래와 같은) exampleBusinessObject에서 호출되는 doIt에서 끝날 것이다

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

ScheduledTimerTask가 언급된 위의 예제의 참조값을 methodInvokingTask로 변경하면 이 task가 실행될 것이다.

19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기

TimerFactoryBean은 실질적인 스케쥴링을 세팅한다는 같은 목적을 제공한다는 점에서 Quartz의 SchedulerFactoryBean과 비슷하다. TimerFactoryBean는 실질적인 Timer를 세팅하고 그것이 참조하고 있는 tasks를 스케쥴한다. 당신은 대몬 쓰레드를 사용할 것인지 말것인지를 기술할 수 있다.

<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <!-- see the example above -->
      <ref local="scheduledTask"/>
    </list>
  </property>
</bean>
			

끝!


출처 - http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/scheduling.html













 

 

 

 

 

 

 

 

 

Posted by linuxism
,