Managed Objects

본 장에서는 Jakarta Concurrency에서 제공하는 Managed Objects에 대해서 간략하게 설명하고 사용 예제를 기술한다.

1. ManagedExecutorService

jakarta.enterprise.concurrent.ManagedExecutorService 인터페이스는 Java SE의 java.util.concurrent.ExecutorService 인터페이스를 상속한다. ExecutorService와 동일하게 비동기 작업을 수행하기 위해 이용되고, 애플리케이션 서버는 비동기로 실행되는 작업의 컨텍스트 정보를 유지시켜준다.

리소스 정의 예제

다음은 ManagedExecutorService를 리소스로 정의한 예제이다.

ManagedExecutorService를 리소스로 정의한 예제 : <domain.xml>
<domain>
    ...
    <server>
       <data-sources>
           <data-source>testdb</data-source>
       </data-sources>
       <managed-executor-service>mes1</managed-executor-service>
    </server>

    <resources>
        <managed-executor-service>
            <export-name>mes1</export-name>
            <long-running-task>true</long-running-task>
            <thread-pool>
                <min>10</min>
                <max>20</max>
                <keep-alive-time>60000</keep-alive-time>
                <queue-size>4096</queue-size>
                <stuck-thread-handling>
                    <max-stuck-thread-time>3600000</max-stuck-thread-time>
                    <action-on-stuck-thread>None</action-on-stuck-thread>
                    <stuck-thread-check-period>300000</stuck-thread-check-period>
                </stuck-thread-handling>
            </thread-pool>
        </managed-executor-service>
    </resources>
    ...
</domain>

설정 항목에 대한 설명은 다음과 같다.

  • 기본 항목

    항목 설명

    Export Name

    Managed Executor Service를 JNDI Naming Server에 등록할 때 사용할 이름을 설정한다.

    Long Running Task

    Managed Executor Service에서 동작하는 task가 오래 수행되는지 여부이다. boolean 값으로 설정한다.

  • Thread Pool

    Managed Executor Service에서 사용하는 Thread Pool 설정을 나타낸다.

    항목 설명

    Min

    Thread Pool에서 관리하는 스레드 수의 최솟값이다.

    Max

    Thread Pool에서 관리하는 스레드 수의 최댓값이다.

    Keep Alive Time

    설정한 시간 동안 사용하지 않은 Min 개수를 초과한 스레드를 자동적으로 Thread Pool에서 제거한다. 0으로 설정하는 경우 제거하지 않는다.

    Queue Size

    Thread Pool이 처리할 업무 개체를 저장하는 queue의 크기를 지정한다.

  • Stuck Thread Handling

    스레드가 특정 업무 때문에 일정 시간 계속 점유된 상태일 경우 해당 스레드에 대해 특정한 동작을 취하기 위한 설정이다.

    항목 설명

    Max Stuck Thread Time

    스레드를 Stuck Thread로 판단하는 기준이 되는 값을 설정한다. 설정된 시간 이상 계속 점유된 상태이면 해당 스레드를 Stuck Thread로 간주한다.

    Action On Stuck Thread

    Stuck Thread로 판단된 경우 해당 스레드에 대해 취할 액션을 설정한다. 다음 옵션 중 하나를 선택할 수 있다.

    • None: 아무 액션도 취하지 않는다.

    • Interrupt: java.lang.Thread#interrupt() 호출을 통해 Interrupt 신호를 보낸다.

    • IgnoreAndReplace: Stuck Thread를 무시하고 새로운 스레드로 교체한다. 이후 Stuck 상태에서 풀리면 무시한 스레드는 버려진다.

    • Warning: Log에 Warning과 함께 thread dump를 남긴다.

    Stuck Thread Check Period

    Stuck Thread의 상태를 체크하는 주기를 설정한다.

    User Warning Class

    action-on-stuck-thread를 Warning으로 설정된 경우에 default는 thread dump를 찍어주도록 되어있으나, 사용자가 원하는 작업을 할 수 있도록 직접 클래스를 작성할 경우 이 설정을 사용한다. 해당 클래스는 jeus.util.pool.Warning을 반드시 implement해야 하며, jeus.util.pool.Warning interface는 jclient.jar에서 찾을 수 있다. 클래스를 작성한 후에 사용할 대상 서버의 SERVER_HOME/lib/application에 위치시킨다.

애플리케이션 예제

다음은 ManagedExecutorService를 활용한 애플리케이션 예제이다.

ManagedExecutorService를 활용한 애플리케이션 예제
public class AppServlet extends HTTPServlet implements Servlet {
    // Retrieve our executor instance.
    @Resource(name=mes1”)
    ManagedExecutorService mes;

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                 throws ServletException, IOException {
        ArrayList<Callable> builderTasks = new ArrayList<Callable>();
        builderTasks.add(new AccountTask(reqID, accountID));
        builderTasks.add(new InsuranceTask(reqID, accountID));

        // Submit the tasks and wait.
        List<Future<Object>> results = mes.invokeAll(builderTasks);

        AccountInfo accountInfo = (AccountInfo) results.get(0).get();
        InsuranceInfo insInfo = (InsuranceInfo) results.get(1).get();
        // Process the results
    }
}

2. ManagedScheduledExecutorService

jakarta.enterprise.concurrent.ManagedScheduledExecutorService 인터페이스는 ManagedExecutorService의 모든 기능을 상속받는 동시에 Java SE의 java.util.concurrent.ScheduledExecutorService의 기능을 상속받아 작업의 지연 실행이나 주기적 실행이 가능하도록 기능을 제공한다. 추가적으로 Trigger와 ManagedTaskListener 인터페이스를 통해 작업의 실행을 제어할 수 있도록 한다.

리소스 정의 예제

다음은 ManagedScheduledExecutorService를 리소스로 정의한 예제이다.

ManagedScheduledExecutorService를 리소스로 정의한 예제 : <domain.xml>
<domain>
 ...
 <server>
    <data-sources>
       <data-source>testdb</data-source>
    </data-sources>
    <managed-scheduled-executor-service>mses1</managed-scheduled-executor-service>
 </server>
 <resources>
     <managed-scheduled-executor-service>
         <export-name>mses1</export-name>
         <long-running-task>true</long-running-task>
         <thread-pool>
             <min>10</min>
             <max>20</max>
             <keep-alive-time>60000</keep-alive-time>
             <queue-size>4096</queue-size>
             <stuck-thread-handling>
                 <max-stuck-thread-time>3600000</max-stuck-thread-time>
                 <action-on-stuck-thread>None</action-on-stuck-thread>
                 <stuck-thread-check-period>300000</stuck-thread-check-period>
             </stuck-thread-handling>
         </thread-pool>
     </managed-scheduled-executor-service>
 </resources>
 ...
</domain>

설정 항목에 대한 설명은 다음과 같다.

  • 기본 항목

    항목 설명

    Export Name

    Managed Scheduled Executor Service를 JNDI Naming Server에 등록할 때 사용할 이름을 설정한다.

    Long Running Task

    Managed Scheduled Executor Service에서 동작하는 task가 오래 수행되는지 여부이다. boolean 값으로 설정한다.

  • Thread Pool

    Managed Executor Service에서 사용하는 Thread Pool 설정을 나타낸다.

    항목 설명

    Min

    Thread Pool에서 관리하는 스레드 수의 최솟값이다.

    Max

    Thread Pool에서 관리하는 스레드 수의 최댓값이다.

    Keep Alive Time

    설정된 시간 동안 사용되지 않은 Min 개수를 초과한 스레드를 자동적으로 Thread Pool에서 제거한다. 0으로 설정하면 스레드를 제거하지 않는다.

    Queue Size

    Thread Pool이 처리할 업무 개체를 저장하는 queue의 크기를 지정한다.

  • Stuck Thread Handling

    스레드가 특정 업무 때문에 일정 시간 계속 점유된 상태일 경우 해당 스레드에 대해 특정한 동작을 취하기 위한 설정이다.

    항목 설명

    Max Stuck Thread Time

    스레드를 Stuck Thread로 판단하는 기준이 되는 값을 설정한다. 설정된 시간 이상 계속 점유된 상태이면 해당 스레드를 Stuck Thread로 간주한다.

    Action On Stuck Thread

    Stuck Thread로 판단된 경우 해당 스레드에 대해 취할 액션을 설정한다. 다음 옵션 중 하나를 선택할 수 있다.

    • None: 아무 액션도 취하지 않는다.

    • Interrupt: java.lang.Thread#interrupt() 호출을 통해 Interrupt 신호를 보낸다.

    • IgnoreAndReplace: Stuck Thread를 무시하고 새로운 스레드로 교체한다. 이후 Stuck 상태에서 풀리면 무시한 스레드는 버려진다.

    • Warning: Log에 Warning과 함께 thread dump를 남긴다.

    Stuck Thread Check Period

    Stuck Thread의 상태를 체크하는 주기를 설정한다.

    User Warning Class

    action-on-stuck-thread를 Warning으로 설정된 경우에 default는 thread dump를 찍어주도록 되어있으나, 사용자가 원하는 작업을 할 수 있도록 직접 클래스를 작성할 경우 이 설정을 사용한다. 해당 클래스는 jeus.util.pool.Warning을 반드시 implement해야 하며, jeus.util.pool.Warning interface는 jclient.jar에서 찾을 수 있다. 클래스를 작성한 후에 사용할 대상 서버의 SERVER_HOME/lib/application에 위치시킨다.

애플리케이션 예제

다음은 ManagedScheduledExecutorService를 활용한 애플리케이션 예제이다.

ManagedScheduledExecutorService를 활용한 애플리케이션 예제
public class AppServlet extends HTTPServlet implements Servlet {
    @Resource(name=mses1”)
    ManagedScheduledExecutorService mses;

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
              throws ServletException, IOException {
        Runnable printTask = new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis());
            }
        };
        // printTask가 5초마다 주기적으로 실행됨
        mses.schedule(printTask, 5, TimeUnit.SECONDS);
    }
}

3. ContextService

ExecutorService를 이용하지 않고 ContextService를 통해 Managed Task를 생성하는 방식을 제공한다. 본 기능을 이용하여 사용자는 태스크를 만들 때 컨텍스트에 대해서 신경쓰지 않아도 애플리케이션 서버 내부적으로 작업이 실행될 때 컨텍스트를 유지시켜준다. 좀더 상세하게는 Dynamic Proxy를 이용해서 현재 작업을 실행하기 전에 해당 스레드에 컨텍스트를 설정하고 작업을 수행한다. 모든 작업이 완료되면 컨텍스트를 복귀하는 작업을 대신 수행한다.

리소스 정의 예제

다음은 ContextService를 리소스로 정의한 예제이다.

ContextService를 리소스로 정의한 예제 : <domain.xml>
<domain>
    ...
    <server>
        <data-sources>
            <data-source>testdb</data-source>
        </data-sources>
        <context-service>cs1</context-service>
    </server>

    <resources>
        <context-service>
            <export-name>cs1</export-name>
        </context-service>
    </resources>
    ...
</domain>

설정 항목에 대한 설명은 다음과 같다.

  • 기본 항목

    항목 설명

    Export Name

    Context Service를 JNDI Naming Server에 등록할 때 사용할 이름을 설정한다.

애플리케이션 예제

다음은 ContextService를 활용한 애플리케이션 예제이다.

ContextService를 활용한 애플리케이션 예제
public class AppServlet extends HTTPServlet implements Servlet {
    @Resource(name=cs1”)
    ContextService cs;

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                     throws ServletException, IOException {
        // 일반적인 Runnable task
        Runnable simpleTask = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for (int i = 0; i < 10; i++) { sum += i; }
                System.out.println(sum);
            }
        };

        // 일반 task를 ContextService를 통해서 Contextual task로 만듦
        cs.createContextualProxy(simpleTask, Runnable.class);

        // Java SE에서 제공하는 Executor에 Contextual task를 넘김
        ExecutorService es = Executors.newFixedThreadPool(1);
        es.submit(simpleTask);
    }
}

4. ManagedThreadFactory

jakarta.enterprise.concurrent.ManagedThreadFactory 인터페이스는 Java SE의 java.util.concurrent.ThreadFactory 기능을 상속받아 스레드를 생성하는 기능을 제공한다. 일반적으로 ThreadFactory는 ThreadPoolExecutor를 생성할 때 생성자의 파라미터로 넘기는 곳에 사용한다. 따라서, Java SE의 Concurrency API 상에서도 worker 스레드가 작업을 실행할 때 Task에 대한 컨텍스트를 유지해줄 수 있다.

리소스 정의 예제

다음은 ManagedThreadFactory를 리소스로 정의한 예제이다.

ManagedThreadFactory를 리소스로 정의 예제 : <domain.xml>
<domain>
    ...
    <server>
        <data-sources>
            <data-source>testdb</data-source>
        </data-sources>
        <managed-thread-factory>mtf1</managed-thread-factory>
    </server>

    <resources>
        <managed-thread-factory>
            <export-name>mtf1</export-name>
            <thread-priority>5</thread-priority>
        </managed-thread-factory>
    </resources>
    ...
</domain>

설정 항목에 대한 설명은 다음과 같다.

  • 기본 항목

    항목 설명

    Export Name

    Managed Thread Factory를 JNDI Naming Server에 등록할 때 사용할 이름을 설정한다.

    Thread Priority

    스레드의 우선순위를 설정한다. (기본값: 5)

애플리케이션 예제

다음은 ManagedThreadFactory를 활용한 애플리케이션 예제이다.

ManagedThreadFactory를 활용한 애플리케이션 예제
public class AppServlet extends HTTPServlet implements Servlet {
    // Retrieve our executor instance.
    @Resource(name=mtf1”)
    ManagedThreadFactory mtf;

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // 일반적인 Runnable task
        Runnable simpleTask = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for (int i = 0; i < 10; i++) { sum += i; }
                System.out.println(sum);
            }
        };

        // simpleTask를 ManagedThreadFactory에서 제공되는 thread에서 실행
        mtf.newThread(simpleTask).start();

        // 혹은 ThreadPoolExecutor의 매개변수로 ThreadFactory를 전달
        Executor e = new ThreadPoolExecutor(5, 10, 6L, TimeUnit.MINUTES,
                        new ArrayBlockingQueue<Runnable>(4096), mtf);
        e.execute(new SimpleTask());
    }
}