Scheduler 프로그래밍

본 장에서는 JEUS Scheduler 프로그래밍에 필요한 기본 지식과 사용 방법에 대해서 설명한다.

1. 개요

JEUS 5 이후 버전에서는 이전(4.x)에 사용되던 방식과 비교하여 다음과 같이 몇 가지 사항이 변경 및 추가되었다. 하지만 JEUS 9 Scheduler는 기존 버전과 호환성을 유지하고 있기 때문에 기존에 작성된 프로그램도 별도의 수정 없이 운영이 가능하다.

  • SchedulerListener 인터페이스 추가

    작업을 정의하기 위한 인터페이스로 ScheduleListener 인터페이스가 추가되었다.

    ScheduleListener 인터페이스는 최상위 작업 인터페이스로 모든 작업은 이 인터페이스를 구현(implements)해야 한다. 기존에 존재하던 Schedule 작업 클래스도 ScheduleListener에서 구현하고 있다. 따라서 Schedule 작업 클래스를 상속해서 작업을 정의하지 않아도 ScheduleListener를 구현해서 작업을 정의할 수 있다.

  • SchedulerFactory 클래스 추가

    기존에 사용되던 클라이언트와 서버용 SchedulerManager는 더 이상 사용되지 않으며(deprecated), 새롭게 SchedulerFactory 클래스가 추가되었다. SchedulerFactory는 Scheduler 객체를 얻는 데 사용되며 Scheduler 객체를 통해 클라이언트 환경, 서버 환경, 리모트 클라이언트 환경의 구별 없이 작업을 등록할 수 있다.

  • 다양한 메소드 추가

    Scheduler 인터페이스에는 작업을 등록하는 다양한 메소드들이 추가되었다. 이제 작업을 등록할 때 시작시간, 주기, 종료시간, 최대 수행 횟수를 지정할 수 있다.

  • Thread Pool을 이용한 멀티 스레드 방식으로 변경

    기존의 JEUS Scheduler는 기본적으로 싱글 스레드 방식으로 작업을 수행했지만 새로운 JEUS Scheduler는 Thread Pool을 이용하여 각 작업을 별도의 스레드로 동작시킨다. 따라서 어떤 한 작업이 수행 중에 블록되더라도 다른 작업 수행에 영향이 없다.

  • Job-list 기능 추가

    프로그래밍 방식으로 작업을 등록하지 않고 JEUS 서버 설정에 작업을 등록할 수 있는 Job-list 기능이 추가되었다.

JEUS Scheduler 프로그래밍을 이해하기 위해 먼저 JEUS Scheduler를 구성하고 있는 클래스들을 살펴보고, Scheduling 작업을 정의하는 방법, 작업을 등록하는 방법, 등록된 작업을 제어하는 방법에 대해 설명한다. 이후에 JEUS Scheduler를 어떤 경우에 사용할 수 있는지를 알아본다. 본 장에서 설명하는 모든 예제 파일들은 JEUS_HOME/samples/scheduler 디렉터리에서 찾아볼 수 있다.

2. JEUS Scheduler 클래스

기본적으로 JEUS Scheduler는 J2SE Timer와 개념적인 부분뿐만 아니라 유사한 인터페이스를 가지고 있다. 작업을 나타내는 java.util.TimerTask 클래스는 JEUS Scheduler의 jeus.schedule.ScheduleListener 인터페이스와 동일하고, 작업을 등록하는 java.util.Timer 클래스는 JEUS Scheduler의 jeus.schedule.Scheduler 인터페이스와 동일하다. 따라서 이러한 유사성을 고려하여 JEUS Scheduler를 사용한다면 좀 더 쉽게 익숙해질 수 있을 것이다.

다음은 각 Scheduler API 클래스에 대한 설명이다.

figure jeus scheduler api
Scheduler API 클래스
API 클래스 설명

package jeus.schedule

JEUS Scheduler의 모든 클래스와 인터페이스는 jeus.schedule 패키지와 그 하위 패키지에 소속되어 있다.

interface ScheduleListener

정해진 시간에 수행되어야 할 작업은 ScheduleListener 인터페이스를 구현(implements)하여 클래스로 정의된다. ScheduleListener는 하나의 Callback 메소드인 onTime()을 가지고 있어 해당 시간이 되었을 때 이 메소드가 호출된다.

abstract class Schedule

ScheduleListener를 구현한 추상 클래스로 JEUS 5 이전에 사용하던 작업 클래스이다. 이 클래스는 onTime() 이외에도 nextTime() Callback 메소드가 있어서 작업을 등록할 때 호출될 시간을 예약하는 것이 아니라 작업을 등록한 후에 동적으로 다음 호출될 시간을 결정할 수 있다.

abstract class RemoteSchedule

특수한 Schedule 클래스로 initialize() Callback 메소드를 가지고 있어 객체를 생성할 때 초기화 파라미터 값을 받을 수 있다.

class SchedulerFactory

실제(concrete) Scheduler 객체를 얻어오기 위해 사용된다.

interface Scheduler

JEUS Scheduler에 작업을 등록하기 위한 핵심 인터페이스로 다양한 registerSchedule() 메소드를 정의하고 있다.

interface ScheduleController

Scheduler에 작업을 등록하면 ScheduleController 인터페이스를 구현한 객체를 리턴받는다. 이 핸들 객체는 작업에 대한 정보를 얻거나 작업을 취소할 때 사용된다.

exception JeusSchedulerException

JEUS Scheduler에 작업 등록하거나 취소할 때에 내부적으로 문제가 발생하는 경우 JeusSchedulerException이 발생할 수 있다.

각 인터페이스나 클래스에 대한 자세한 설명은 JEUS Scheduler Javadoc API를 참고한다.

3. 작업 정의

작업을 수행하기 위해서는 작업 클래스를 정의하고 작업의 수행시간 및 주기에 따라 알맞은 방법으로 작업을 정의해야 한다.

3.1. ScheduleListener 인터페이스 구현

정해진 시간에 수행되어야 할 작업은 ScheduleListener 인터페이스를 구현(implements)하여 클래스로 정의된다.

ScheduleListener는 하나의 Callback 메소드인 onTime()을 가지고 있어 해당 시간이 되었을 때 이 메소드가 호출된다. 따라서 작업 클래스를 정의하기 위해서는 onTime() 메소드를 구현하고 메소드 내에서 작업을 수행하도록 프로그램을 작성한다.

Task object example
public class SimpleTask implements ScheduleListener {
    private String name;
    private int count;

    // no-arg constructor is required if classname is used for task registration
    public SimpleTask() {
    }

    public SimpleTask(String name) {
        this.name = name;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }
...
}

3.2. Schedule 클래스 상속

ScheduleListener를 바로 구현하지 않고 Schedule 클래스나 RemoteSchedule 클래스를 상속하여 작업 클래스를 정의할 수 있다. Schedule 클래스나 RemoteSchedule 클래스는 JEUS 5 이전 Scheduler에서 사용되던 작업 클래스이다. JEUS 5와 그 이후부터는 일반적인 작업을 정의할 때 ScheduleListener를 구현하도록 하고 있지만 하위 호환성을 위해 기존 작업 클래스를 그대로 제공하고 있다.

Schedule 추상 클래스는 onTime() 이외에도 nextTime()이라는 Callback 메소드가 있어서 작업을 등록할 때 호출될 시간을 예약하지 않고 작업 클래스 내에서 다음 호출될 시간을 결정하도록 한다. 따라서 고정적인 주기를 갖는 작업보다는 가변적인 주기를 갖는 작업의 경우에 좀 더 효율적으로 사용된다.

JEUS Scheduler는 Schedule 작업 객체의 처음 수행시간을 결정하기 위해 작업 객체를 등록한 후에 먼저 nextTime()을 호출하여 처음 수행시간을 정한다. 그런 다음 해당 시간이 되면 onTime()을 호출하여 작업을 수행하고 onTime()이 종료되면 다시 nextTime()을 호출하여 다음 수행시간을 정하게 된다. nextTime()은 다음에 작업이 수행될 절대시간을 Milli-Second(ms) 값으로 넘겨주어야 한다. 이때 0을 리턴하면 작업이 더 이상 수행되지 않게 된다.

Schedule 작업 객체는 onTime()이 수행된 후에 nextTime()을 호출하기 때문에 onTime()에서 작업을 수행한 시간만큼 nextTime() 호출이 지체된다. 따라서 정확한 간격으로 작업을 호출하도록 프로그래밍하기가 쉽지 않다. 그렇기 때문에 되도록 작업 내에서 nextTime()을 통해 반복 주기를 구현하기 보다는 작업을 등록할 때 반복 주기를 설정하는 것이 좋다.

Schedule object example
public class SimpleSchedule extends Schedule {
    private String name;
    private int count;
    private long period = 2000; // 2 seconds

    // no-arg constructor is required if classname is used for task registration
    public SimpleSchedule() {
    }

    public SimpleSchedule(String name) {
        this.name = name;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }

    public long nextTime(long currentTime) {
        return currentTime + period;
    }
...
}

3.3. RemoteSchedule 클래스 상속

RemoteSchedule 클래스는 원격으로 작업을 등록할 때 초기화 변수를 지정할 수 있는 Schedule 객체이다. 주로 원격에서 클래스 이름을 통해 작업 객체를 등록할 때 사용한다. 이 클래스는 initialize() Callback 메소드를 가지고 있는데 작업 등록 후에 초기화 파라미터로 이 메소드가 한 번 호출된다. 따라서 원격에서 클래스 이름으로 작업을 등록할 때 초기화 값을 설정할 경우 사용할 수 있다.

initialize() Callback은 Scheduler.registerSchedule(classname, hashtable, daemon_flag) 메소드를 이용하여 RemoteSchedule 작업 객체를 등록하는 경우에만 호출된다.

RemoteSchedule object example
public class SimpleRemoteSchedule extends RemoteSchedule {
    private String name;
    private int count;
    private long period;

    // no-arg constructor is required if classname is used for task registration
    public SimpleRemoteSchedule() {
    }

    // this is called by scheduler after creation
    public void initialize(Hashtable parameters) {
        name = (String) parameters.get("name");
        Long interval = (Long) parameters.get("interval");
        if (interval != null)
            period = interval.longValue();
        else
            period = 2000;
    }

    public void onTime() {
        count++;
        echo("##### " + name + " is waked on " + new Date());
    }

    public long nextTime(long currentTime) {
        return currentTime + period;
    }
...
}

작업을 Job-list를 사용하여 등록하거나 classname을 사용하는 API를 통해 등록하는 경우에는 컨터이너가 해당 클래스를 초기화하기 때문에 작업 클래스에는 no-arg(default) 생성자(constructor)가 반드시 필요하다.

4. Scheduler 객체 얻기

본 절에서는 Scheduler 객체를 얻는 방법에 대해서 알아본다. JEUS Scheduler는 로컬 환경과 원격 환경에서 모두 구동된다.

4.1. 로컬 환경에서 Scheduler 객체 얻기

로컬 환경에서 구동된다는 것은 프로그램이 구동되고 있는 로컬 JVM 내에 Scheduler 인스턴스가 생성되며, 등록된 모든 작업이 같은 JVM 내에서 구동된다는 것을 의미한다.

JEUS Scheduler는 JVM 내에서 현재 하나의 인스턴스만 생성되며 이 인스턴스를 Default Scheduler라고 한다. 따라서 현재는 JVM 내에 모든 클라이언트들은 Default Scheduler를 공유하게 된다. 로컬 환경의 JEUS Scheduler는 일반 J2SE 애플리케이션이나 Jakarta EE 애플리케이션 클라이언트, Jakarta EE 컴포넌트 등에서(Servlet, JSP, EJB 등) 사용된다. 로컬 환경의 JEUS Scheduler를 사용하기 위해서 SchedulerFactory를 이용한다.

다음과 같이 간단하게 Default Scheduler 인스턴스를 얻을 수 있다.

// Get the default scheduler
Scheduler scheduler = SchedulerFactory.getDefaultScheduler();

4.2. 원격 환경에서 Scheduler 객체 얻기

원격 환경에서 구동된다는 것은 프로그램이 구동되고 있는 JVM이 아닌 원격의 다른 JVM에서 Scheduler 인스턴스가 생성되고, 등록된 모든 작업이 원격 JVM 내에서 구동된다는 것을 의미한다.

원격 Scheduler는 RMI 객체 형태로 나타나기 때문에 클라이언트는 RMI Call을 통해 JEUS Scheduler를 사용하게 된다. JEUS 환경에서는 JEUS 서버에 원격 Scheduler Service가 기동된다. 원격 환경의 JEUS Scheduler는 원격 클라이언트가 JEUS 서버에 작업을 등록할 때 사용된다.

JEUS 서버에 있는 원격 JEUS Scheduler를 사용하기 위해서 JNDI Lookup을 이용한다.

다음과 같이 JEUS Server Scheduler Instance(Stub)를 얻을 수 있다.

// Get the remote scheduler
InitialContext ic = new InitialContext();
Scheduler scheduler = (Scheduler)ic.lookup(Scheduler.SERVER_SCHEDULER_NAME);

JEUS 5 이전에 사용되던 jeus.schedule.server.SchedulerManager와 jeus.schedule.client.SchedulerManager는 더 이상 사용되지 않는다(deprecated). 대신 SchedulerFactory를 통해 Scheduler 객체를 얻어서 사용하기를 권장한다. 하지만 하위 호환성을 유지하기 위해 위 클래스들은 그대로 제공된다.

5. 작업 등록

본 절에서는 Scheduler Interface를 이용하여 작업을 등록하는 방법에 대해서 알아본다. 로컬 환경이나 원격 환경에서 Scheduler Instance를 얻어왔다면 작업을 등록하는 방법은 동일하다. 단, 원격 JEUS Scheduler는 작업 객체가 원격으로 전송(serialization)되어 원격으로 운용된다.

5.1. 한 번 수행되는 작업 등록

단지 특정 시간에 한 번만 수행되어야 할 작업의 경우에는 하나의 수행시간만 설정하여 작업을 등록할 수 있다. 이때 수행시간은 java.util.Date 객체로 절대시간을 설정하거나, Milli-Second(ms) 값으로 현재 시간을 기준으로 얼마의 시간이 지난 후에 수행되어야 하는지 설정할 수 있다.

다음의 메소드를 이용하여 작업을 등록한다.

registerSchedule(ScheduleListener task, Date time, boolean isDaemon)
registerSchedule(ScheduleListener task, long delay, boolean isDaemon)

다음은 메소드 사용에 대한 예이다. 메소드의 파라미터에 대한 설명은 반복되는 작업 등록을 참고한다.

SimpleTask task1 = new SimpleTask("task1");
Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
ScheduleController handle1 = scheduler.registerSchedule(task1, firstTime1, false);

5.2. 반복되는 작업 등록

반복되는 작업의 경우 첫 수행시간, 주기, 종료시간, 최대 수행 횟수 등을 주어 작업을 등록할 수 있다. 반복되는 작업의 특성에 따라 반복 주기를 Fixed-delay 방식이나 Fixed-rate 방식으로 결정해야 한다.

  • Fixed-delay 방식

    작업이 수행되는 간격이 일정하게 유지된다. 작업의 다음 수행시간은 이전 수행시간과 주기에 의해서 결정된다. 만약 작업의 수행이 지체(작업 수행시간이 오래 걸리거나 Garbage Collection과 같은 외부 이유에 의해서 지체되는 경우)되어 다음 작업이 수행되어야 할 시기가 지난 경우에 다음 작업은 바로 수행되며, 그 이후에 수행되는 작업들은 그만큼 지체된다. 따라서 장기적으로는 작업의 수행시간이 조금씩 뒤쳐질 수 있다.

  • Fixed-rate 방식

    작업이 수행되는 비율이 일정하게 유지된다. 작업의 다음 수행시간은 첫 수행시간과 주기에 의해서 결정된다. 작업의 수행이 지체되더라도 다음 작업은 바로 뒤따라 수행되며, 시간당 수행되는 비율을 유지한다. 장기적으로 작업의 수행시간이 초기에 지정한 주기에 따라 계속 유지된다.

JEUS Scheduler는 Fixed-rate 방식으로 작업을 등록하면 비교적 정확한 호출시간을 보장해주기 위해 작업 수행이 지체되더라도 시간이 되면 다른 스레드에 의해 작업을 호출한다. 따라서 작업 수행이 지체되는 경우에 같은 작업이 동시에(concurrently) 수행된다. 따라서 이러한 경우에는 작업 객체가 Thread-safe한지 고려해야 한다.

다음의 메소드를 이용하여 작업을 등록한다.

registerSchedule(ScheduleListener task, Date firstTime, long period, Date endTime,
                 long maxcount, boolean isDaemon)

registerSchedule(ScheduleListener task, long delay, long period, Date endTime,
                 long maxcount, boolean isDaemon)

registerScheduleAtFixedRate(ScheduleListener task, Date firstTime, long period,
                 Date endTime, long maxcount, boolean isDaemon)

registerScheduleAtFixedRate(ScheduleListener task, long delay, long period,
                 Date endTime, long maxcount, boolean isDaemon)

작업을 등록할 때 사용하는 파라미터들은 다음과 같다.

파라미터 설명

Date firstTime

시작시간으로 처음 수행될 시간을 지정한다.

long delay

시작시간이다. 현재 이후에 처음 수행될 시간을 지정한다. (단위: ms)

long period

반복 수행 주기를 지정한다. (단위: ms)

Date endTime

종료시간이다. 이 시간 이후에는 작업이 더 이상 수행되지 않고 null인 경우에는 종료시간의 제약이 없다.

long maxcount

최대 수행 횟수이다. Scheduler.UNLIMITED인 경우에는 제한이 없다.

boolean isDaemon

원격으로 Schedule을 등록하는 경우에만 의미가 있으며 true 값으로 설정하면 클라이언트와의 연결이 종료되었을 때 작업이 종료된다.

현재 RMI Runtime의 DGC(Distrubuted Garbage Collection) 정책에 의해 클라이언트가 연결이 종료되었음을 판단하기 때문에 실제로 클라이언트의 연결이 종료되고 15분 정도가 지나야 종료되었음을 탐지하게 되어 Scheduling이 취소된다.

boolean isThreaded

더 이상 사용되지 않는다(deprecated).

다음은 작업을 등록하는 예제이다.

반복되는 작업 등록
SimpleTask task2 = new SimpleTask("task2");
ScheduleController handle2 = scheduler.registerSchedule(task2, 2000, 2000, null,
                         Scheduler.UNLIMITED, false);

SimpleTask task3 = new SimpleTask("task3");
Date firstTime3 = new Date(System.currentTimeMillis() + 2000);
Date endTime3 = new Date(System.currentTimeMillis() + 10 * 1000);
ScheduleController handle3 = scheduler.registerScheduleAtFixedRate(task3,
                             firstTime3, 2000, endTime3, 10, false);

5.3. Schedule 작업 객체 등록

Schedule이나 RemoteSchedule 작업 객체를 등록하는 것은 하위 호환성을 유지하기 위해 제공된다.

작업의 처음 수행시간과 이후에 반복되는 수행시간은 Schedule 작업 객체의 nextTime() 메소드를 이용하기 때문에 등록할 때는 별도의 파라미터를 줄 필요가 없다. 이 경우 다음 메소드를 이용하여 작업을 등록한다.

registerSchedule(Schedule task, boolean isDaemon)
registerSchedule(String classname, Hashtable params, boolean isDaemon)

다음은 작업을 등록하는 예제이다.

Schedule 작업 객체 등록
Hashtable params = new Hashtable();
params.put("name", "task3");
params.put("interval", new Long(3000));
ScheduleController handle3 = scheduler.registerSchedule(
    "samples.scheduler.SimpleRemoteSchedule", params, true);

SimpleSchedule task4 = new SimpleSchedule("task4");
ScheduleController handle4 = scheduler.registerSchedule(task4, true);

Scheduler는 Managed Server(MS)의 기동 단계에서 제공하는 서비스이므로 설정에 오류가 없도록 주의해야 한다. 만약 설정이 잘못되어 Scheduler를 생성할 수 없다면, MS는 에러 메시지를 표시하고 기동 자체에 실패하게 된다.

6. 작업 제어

JEUS Scheduler에 작업을 등록하면 핸들(handle)인 ScheduleController 객체를 리턴한다. 이 객체는 등록된 작업 하나당 만들어지는데 등록된 작업을 제어하기 위해 사용한다. 이 핸들을 이용하여 작업에 대한 정보를 얻어오거나 작업을 취소할 수 있다.

다음은 ScheduleController.cancel() 메소드를 호출하여 작업을 취소하는 예제이다.

ScheduleController.cancel 메소드 사용
SimpleTask task2 = new SimpleTask("task2");
ScheduleController handle2 = scheduler.registerSchedule(task2, 2000, 2000, null,
                              Scheduler.UNLIMITED, false);
Thread.sleep(10 * 1000);
handle2.cancel();

7. Scheduler 사용

작업 정의, Scheduler 객체 얻기, 작업 등록, 작업 제어까지 완료하였다면 JEUS Scheduler를 사용할 준비가 모두 되었다. 본 절에서는 환경에 따른 Schduler의 사용 방법에 대해 설명한다.

7.1. Standalone 환경에서 사용

일반 J2SE 애플리케이션이나 Jakarta EE 애플리케이션 클라이언트에서 JEUS Scheduler를 사용할 수 있다. 이 경우에는 JEUS 서버와 별개로 JEUS Scheduler를 J2SE Timer와 같이 라이브러리처럼 사용할 수 있다. Scheduler 객체를 얻기 위해서는 SchedulerFactory 클래스를 이용한다. 또한 로컬 환경에서 작업을 등록할 때 daemon flag는 사용되지 않으므로 어떤 값을 넣어도 무방하다.

다음은 Standalone 클라이언트 예제이다.

Standalone 클라이언트에서 Scheduler 사용
public class StandAloneClient {
    public static void main(String args[]) {
        try {
            // Get the default scheduler
            Scheduler scheduler = SchedulerFactory.getDefaultScheduler();

            // Register SimpleTask which runs just one time
            echo("Register task1 which runs just one time...");
            SimpleTask task1 = new SimpleTask("task1");
            Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
            ScheduleController handle1 = scheduler.registerSchedule(task1,
                            firstTime1, false);

            Thread.sleep(5 * 1000);
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-delay
            echo("Register task2 which is repeated " + "until it is canceled...");
            SimpleTask task2 = new SimpleTask("task2");
            ScheduleController handle2 = scheduler.registerSchedule(task2, 2000,
                          2000, null, Scheduler.UNLIMITED, false);

            Thread.sleep(10 * 1000);
            handle2.cancel();
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-rate
            echo("Register task3 which is repeated " + "for 10 seconds...");
            SimpleTask task3 = new SimpleTask("task3");
            Date firstTime3 = new Date(System.currentTimeMillis() + 2000);
            Date endTime3 = new Date(System.currentTimeMillis() + 10 * 1000);
            ScheduleController handle3 = scheduler.registerScheduleAtFixedRate(
                    task3, firstTime3, 2000, endTime3, 10, false);

            Thread.sleep(12 * 1000);
            echo("");

            // Register SimpleSchedule which is repeated
            // every 2 seconds
            echo("Register task4 which is repeated " + "every 2 seconds...");
            // SimpleSchedule class extends jeus.schedule.Schedule
            // and has 2-seconds delayed scheduling logic in "nextTime" method.
            SimpleSchedule task4 = new SimpleSchedule("task4");
            ScheduleController handle4 = scheduler.registerSchedule(task4, false);

            Thread.sleep(10 * 1000);
            echo("");

            // Cancel all tasks
            echo("Cancel all tasks registerd on the scheduler...");
            scheduler.cancel();
            Thread.sleep(5 * 1000);

            System.out.println("Program terminated.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void echo(String s) {
        System.out.println(s);
    }

}

Jakarta EE 애플리케이션 클라이언트에서 JEUS Scheduler를 사용하는 경우에는 DD(jeus-client-dd.xml)에 JEUS Scheduler의 Thread Pool에 관련된 설정을 할 수 있다.

  1. Scheduler의 Thread Pool과 관련된 설정은 JEUS Reference 안내서의 Thread Management 관련 명령어를 참고한다.

  2. Scheduler를 사용한 코드를 컴파일하거나 기동시키기 위해서는 JEUS 관련 클래스(jeus.jar 등)가 클래스 패스에 지정되어 있어야 한다.

7.2. JEUS 서버에서 사용

JEUS 서버에 Scheduler Service가 기동되어 있다면 원격 클라이언트가 이것을 사용할 수 있다. 그 전에 먼저 JEUS 서버에 Scheduler Service가 기동되도록 설정되어 있어야 한다.

JEUS 서버 Scheduler Service는 RMI Scheduler 객체를 JNDI에 등록한다. 따라서 클라이언트에서는 JNDI Lookup을 통해서 원격 Scheduler 객체(실제로는 Stub 객체)를 얻을 수 있다. 이 객체의 JNDI 이름은 “jeus_service/Scheduler”로 Scheduler.SERVER_SCHEDULER_NAME 상수를 사용한다.

일단 Scheduler 객체를 얻으면 작업을 등록하는 방법은 동일하다. 단, 등록된 작업 객체는 전송(Serialization)되어 원격 Scheduler에서 실제로 운용된다. 즉, 원격 JEUS 서버에서 수행된다. 이 경우 등록할 때 daemon flag는 의미가 있으며, daemon flag를 true로 등록하면 원격 클라이언트가 종료되었을 때 원격 작업도 종료된다.

JEUS 서버에 Scheduler Service가 기동되도록 설정하는 방법은 JEUS Reference 안내서의 Thread Management 관련 명령어를 참고한다.

다음은 리모트 클라이언트 예제이다.

리모트 클라이언트에서 Scheduler 사용
public class RemoteClient {
    public static void main(String args[]) {
        try {
            // Get the remote scheduler
            InitialContext ic = new InitialContext();
            Scheduler scheduler = (Scheduler)ic.lookup(
                Scheduler.SERVER_SCHEDULER_NAME);

            // Register SimpleTask which runs just one time
            echo("Register task1 which runs just one time...");
            SimpleTask task1 = new SimpleTask("task1");
            Date firstTime1 = new Date(System.currentTimeMillis() + 2000);
            ScheduleController handle1 = scheduler.registerSchedule(task1,
                             firstTime1, true);

            Thread.sleep(5 * 1000);
            echo("");

            // Register SimpleTask which is repeated
            // with fixed-delay
            echo("Register task2 which is repeated " + "until it is canceled...");
            SimpleTask task2 = new SimpleTask("task2");
            ScheduleController handle2 = scheduler.registerSchedule(task2,
                    2000, 2000, null, Scheduler.UNLIMITED, true);

            Thread.sleep(10 * 1000);
            handle2.cancel();
            echo("");

            // Register SimpleRemoteSchedule which is repeated
            // every 3 seconds
            echo("Register task3 which is repeated " + "every 3 seconds...");
            Hashtable params = new Hashtable();
            params.put("name", "task3");
            params.put("interval", new Long(3000));
            // SimpleRemoteSchedule class extends
            // jeus.schedule.RemoteSchedule
            // and has 3-seconds delayed scheduling logic in "nextTime" method.
            ScheduleController handle3 = scheduler.registerSchedule(
                    "samples.scheduler.SimpleRemoteSchedule", params, true);

            Thread.sleep(10 * 1000);
            echo("");

            // Cancel all tasks
            echo("Cancel all tasks registerd on the scheduler...");
            scheduler.cancel();
            Thread.sleep(5 * 1000);

            System.out.println("Program terminated.");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private static void echo(String s) {
        System.out.println(s);
    }
}

위 예제를 실행하려면 해당 작업 클래스 파일을 JAR 파일로 묶어서 DOMAIN_HOME/lib/application 또는 SERVER_HOME/lib/application에 복사하여 위치시켜야 한다. JEUS 서버가 해당 작업을 수행하기 위해서는 해당 클래스를 로딩해야 하기 때문에 JEUS가 이미 기동되어 있는 상태라면 반드시 재기동해야 한다.

7.3. Jakarta EE 컴포넌트에서 사용

EJB나 서블릿과 같은 Jakarta EE 컴포넌트에서 JEUS Scheduler를 사용할 수 있다. 이때 JEUS Scheduler는 JEUS 서버에서 기동된다.

EJB 2.1 표준에는 EJB Timer Service를 명시하고 있으며, EJB Timer Service를 제공하고 있다. 따라서 EJB 컴포넌트의 경우 Jakarta EE 표준을 준수하려면 JEUS Scheduler 보다는 EJB Timer Service를 사용할 것을 권장한다. 하지만 EJB 외의 Jakarta EE 컴포넌트에서는 EJB Timer Service를 사용할 수 없으므로 JEUS Scheduler를 사용해야 한다.

Jakarta EE 컴포넌트에서 JEUS Scheduler를 사용하는 것은 Standalone JEUS Scheduler를 사용하는 방식과 동일하다. SchedulerFactory 클래스를 이용하여 서버에서 기동되는 Scheduler 객체를 얻어온 후에 필요한 등록 메소드를 호출하여 작업을 등록한다.

JEUS 서버에서 동작하는 JEUS Scheduler에 대해 Thread Pool을 설정할 수 있다. 설정하는 방법에 대해서는 Thread Pool 설정을 참고한다.

다음은 EJB에서 JEUS Scheduler 사용 예제이다.

EJB에서 JEUS Scheduler 사용
public class HelloEJB implements SessionBean {
    private SimpleTask task;
    private ScheduleController taskHandler;
    private boolean isStarted;

    public HelloEJB() {
    }

    public void ejbCreate() {
        task = new SimpleTask("HelloTask");
        isStarted = false;
    }

    public void trigger() throws RemoteException {
        if (!isStarted) {
            Scheduler scheduler = SchedulerFactory.getDefaultScheduler();
            taskHandler = scheduler.registerSchedule(task,
                          2000, 2000, null, Scheduler.UNLIMITED, false);
            isStarted = true;
        }
    }

    public void ejbRemove() throws RemoteException {
        if (isStarted) {
            taskHandler.cancel();
            isStarted = false;
        }
    }

    public void setSessionContext(SessionContext sc) {
    }

    public void ejbActivate() {
    }

    public void ejbPassivate() {
    }
}
public class HelloClient {
    public static void main(String args[]) {
        try {
            InitialContext ctx = new InitialContext();

            HelloHome home = (HelloHome) ctx.lookup("helloApp");
            Hello hello = (Hello) home.create();
            hello.trigger();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

8. Job-list 사용

Job-list는 JEUS 서버에 프로그래밍 방식으로 작업을 등록하지 않고 설정 파일을 이용하여 작업을 등록하는 것이다. Job-list는 JEUS 서버 Scheduler에 등록할 수 있다. Job-list로 작업을 등록하면 작업은 Fixed-rate 방식으로 반복 수행된다.

JEUS 서버에서 동작하는 JEUS Scheduler에 대해 Job-list를 설정할 수 있다. 설정하는 방법에 대해서는 Job-list 설정을 참고한다.