98..Etc/Etc...2008. 11. 12. 17:09
반응형

기존 시스템과 J2EE 애플리케이션 서버를 통합하는 작업을 해본 경험이 있다면 그 작업이 결코 쉽지 않다는 것을 알고 있을 것이다. 커넥션을 관리하고 상이한 데이터 타입들을 변환시켜주는 작업, 애플리케이션 서버와 기업 정보 시스템(EIS) 사이의 단순한 메시지 전송조차 자바 프로그래머의 머리를 아프게 만들 수 있다. 하지만 고맙게도 이제 J2EE 커넥터 아키텍처 1.5가 J2EE 1.4 버전에 포함되어 J2EE 플랫폼을 이종 EIS들과 접속시키기 위한 표준 아키텍처를 제공한다.

J2EE 커넥터 아키텍처를 사용하면 EIS 벤더들이 여러 애플리케이션 서버들을 위한 커넥션 소프트웨어를 직접 제작할 필요가 없다. 또한 애플리케이션 서버 벤더들도 다양한 EIS 패키지들을 위해 자신들의 코드를 다시 작성할 필요가 없다. 이제 벤더들은 단순히 J2EE 커넥터 아키텍처 기준에 맞는 "중간" 코드만 패키지 시켜주면 된다. 이 코드는 애플리케이션 서버와 EIS 자원 간의 통신에 필요한 데이터와 메시지를 위해 표준화된 인터페이스와 클래스를 구현하거나 확장 시켜 준다. 이 중간 코드는 애플리케이션 서버에 직접 연결되며 자원 어댑터라 불린다.

자원 어댑터 모듈은 흔히 자원 어댑터 아카이브 (RAR)이라는 명칭을 가지고 있으며 단순한 JAR 파일이다. 보통 RAR은 다음과 같은 사항들을 포함하고 있다.

  • 자원 어댑터 컨트렉트와 클라이언트 API를 구현하는 자바 클래스 및 인터페이스

  • EIS와의 통신을 위해 필요한 모든 고유 라이브러리

  • 애플리케이션 서버가 자원 어댑터를 설정하기 위해 필요한 설치 정의 파일인 ra.xml. 이 파일은 META-INF 디렉토리에 위치

다음 그림은 애플리케이션 서버와 EIS에서 사용되는 자원 어댑터를 도표로 설명한 것이다.

각각의 구성원들이 다른 요소들과 통신하는 방식을 살펴보기 바란다.

J2EE 커넥터 아키텍처를 구성하는 데는 세 가지의 중요한 통신 브릿지가 있다. 각각에 대해 살펴보도록 하자.

클라이언트 API와 공통 클라이언트 인터페이스

애플리케이션 서버에 연결되는 애플리케이션 컴포넌트들은 반드시 자원 어댑터와의 통신 방식을 가지고 있어야 하며, 공통 클라이언트 인터페이스 (CCI)는 이런 통신 방식을 위해 권장되는 API이다. CCI는 javax.resource.cci 패키지에 구현된 API이다. 이 API를 권장하는 이유는 사용하기가 쉬우면서 필수적이지는 않다는 점 때문이다. 만약 CCI가 제공하는 기능이 부족하다면 자원 어댑터 구현자들은 자유롭게 자신만의 클라이언트 API를 제작하여 애플리케이션 컴포넌트와 통신을 할 수도 있다.

컨테이너-컴포넌트 컨트렉트

컨테이너-컴포넌트 컨트렉트는 애플리케이션 서버와 애플리케이션 컴포넌트들 간의 표준 통신 방식이다. JCA에 관련된 내용은 거의 없는데 예를 들어, 만약 웹 애플리케이션이 Enterprise JavaBeans(EJB) 컴포넌트들을 사용한다면 EJB specification은 곧 각각의 EJB 컴포넌트가 애플리케이션과 통신하는 방법을 정의하는 컨테이너-컴포넌트 컨트렉트가 된다.

시스템 컨트렉트

시스템 컨트렉트는 J2EE 커넥터 아키텍처의 주요 부분이다. 이 아키텍처는 자원 어댑터가 애플리케이션 서버와 적절히 통신하기 위해 따라야 하는 몇 가지의 시스템 컨트렉트를 정의하고 있다. 시스템 컨트렉트를 위한 API는 javax.resource.spi 패키지에 구현되어 있다. J2EE 커넥터 아키텍처의 1.0 버전에서는 세 가지의 컨트렉트를 제시하고 있다.

  • 접속 관리 컨트렉트: EIS와 애플리케이션 서버 간의 접속을 가능하게 하는 인터페이스와 클래스 제공하며 애플리케이션 서버가 필요에 따라 이러한 커넥션들을 풀링할 수 있도록 해준다.

  • 트랜젝션 관리 컨트렉트: EIS에 트랜젝션의 모든 부분을 관리할 수 있는 기능을 제공한다. 이 컨트렉트를 이용하여 EIS를 자원 어댑터 혹은 트랜젝션 관리자로 관리할 수 있다. 그러나 두 가지 방법 중 한 가지 방법으로만 사용할 수 있다. 트랜젝션은 반드시 EIS 외부에서 이루어져야 하며 이를 EIS로 전달해야 한다. 그 반대는 성립되지 않는다.

  • 보안 컨트렉트: 애플리케이션 서버와 EIS 간의 적절한 인증이 이루어져 있는지 확인한다.

아키텍처의 1.5 버전은 자원 어댑터의 기능을 확장 시켜 줄 수 있는 4 가지의 새로운 컨트렉트를 제공한다. 이 새로운 컨트렉트들은 다음과 같다.

  • 생명 주기 관리 컨트렉트: 애플리케이션 서버가 깨끗하게 자원 어댑터를 시작 혹은 종료 시킬 수 있는 방법 제공

  • 작업 관리 컨트렉트: 자원 어댑터가 애플리케이션 서버에서 작업을 실행시킬 수 있도록 인터페이스와 클래스 제공

  • 트랜젝션 유입 컨트렉트: EIS에서 애플리케이션 서버로 트랜젝션 전달 가능 (트랜젝션 관리 컨트렉트의 반대 기능) 또한 EIS가 오작동할 경우를 대비하여 트랜젝션 복구 보조

  • 메시지 유입 컨트렉트: 자원 어댑터가 애플리케이션 서버 내에서 동기 혹은 비동기 메시지를 엔드포인트로 전달 가능

이 4 가지의 새로운 기능들을 좀더 자세히 살펴보도록 하자.

생명 주기 관리 및 작업 관리 컨트렉트

javax.resource.spi 패키지 안의 ResourceAdapter 인터페이스가 바로 자원 어댑터이다. ResourceAdapter 인터페이스에서 생명 주기 관리를 하는 메소드는 start(), stop()의 두 가지가 있다. start() 메소드는 애플리케이션 서버가 자원 어댑터를 시작하려 할 때(예:자원 어댑터를 배치할 때) 호출되며, stop() 메소드는 애플리케이션 서버가 자원 어댑터를 해제할 때 (예:자원 어댑터를 배치 해제할 때) 호출된다.

작업 관리 컨트렉트는 javax.resource.spi.work 패키지의 Work 인터페이스를 확장하는 객체를 생성함으로써 자원 어댑터가 애플리케이션 서버에 작업을 전달할 수 있도록 해준다. Work 인터페이스는 Runnable 인터페이스의 확장이다. Work 인터페이스에는 Runnable 인터페이스에서 상속받고 자신의 쓰레드에서 실행시키는 run() 메소드뿐만 아니라 release() 메소드도 포함되어 있다. 애플리케이션 서버는 release() 메소드를 사용하여 쓰레드가 완료되면 그 자원을 가능하면 빨리 해제할 수 있도록 해준다.

다음은 ResourceAdapter 인터페이스를 사용하여 생명 주기 관리 및 작업 관리 컨트렉트를 사용하는 예시이다.

   public class NexesResourceAdapterImpl implements 
           ResourceAdapter {

      // Ten seconds
      public static final long WORK_START_TIMEOUT = 10000L;
      
      public NexesResourceAdapterImpl() {    }
      public void start(BootstrapContext ctx)
      throws ResourceAdapterInternalException
      {
 
          WorkManager workManager = ctx.getWorkManager();
          Work nexesWorkJob = new NexesWorkImpl();
          WorkListener workListener = 
                  new NexesWorkListenerImpl();

          try {

              // Unlike scheduleWork() or doWork(), this call 
              // blocks until the work has started. If it takes 
              // longer than 10 seconds for the work to start, 
              // the call throws a WorkRejectedException.

              workManager.startWork
                      (nexesWorkJob, WORK_START_TIMEOUT, 
                      new ExecutionContext(), workListener);

          } catch (WorkException e) {
              //  Handle the exception
          }
        }

       public void stop()
       {
          //  Do whatever you need to do here to close down the
          //  resource adapter.
       }  
       
          // Transaction Inflow contract methods omitted. 
          // See the section "Message Inflow and Transaction 
          // Inflow Contracts"

      }

BootstrapContext 인터페이스를 구현하는 start() 메소드로 객체가 전달되는 것을 볼 수 있다. 이 객체는 EIS가 트랜젝션 정보와 작업을 애플리케이션 서버에 전달할 수 있도록 해주는 매우 중요한 객체이다. BootstrapContext 인터페이스에 관련된 상세한 정보는 "메시지 유입 및 트랜젝션 유입 컨트렉트" 섹션을 참조하시기 바란다.

또한 WorkListener 인터페이스에 대한 참조도 확인하기 바란다.

   WorkListener workListener = 
           new NexesWorkListenerImpl();

and the startWork method:

   workManager.startWork
           (nexesWorkJob, WORK_START_TIMEOUT, 
           new ExecutionContext(), workListener);

만약 애플리케이션 서버가 처리 중인 작업에 대한 진행 상황을 보고 받고 싶다면 javax.resource.spi.WorkListener 인터페이스를 구현하는 객체를 생성하면 된다. 그 다음 이 객체를 애플리케이션 서버 상의 WorkManager 객체의 startWork() 메소드를 사용하여 등록하면 된다. WorkManager 인터페이스는 Work 인스턴스를 실행 시킬 수 있는 기능을 제공한다. 객체를 등록함으로써 작업이 거절 혹은 수락되었는지, 수락되었다면 언제 작업이 시작되었고 종료되었는지를 서버가 자원 어댑터에게 알려줄 수 있다. 또한 WorkAdapter 클래스를 확장시킬 수 있는데, 이 클래스는 WorkListener 인터페이스를 구현하고 각각에 대해 빈 메소드를 제공한다. 다음은 WorkListener를 구현하는 간략한 코드이다.

   public class NexesWorkListenerImpl implements WorkListener {

      public void workAccepted(WorkEvent e) {
          //  myAppServerLog.log("Work instance " + e + 
          //      " has been accepted."); 
      }

      public void workRejected(WorkEvent e) {
          //  myAppServerLog.log("Work instance " + e + 
          //      " has been rejected."); 
      }

      public void workStarted(WorkEvent e) {
          //  myAppServerLog.log("Work instance " + e + 
          //      " has been started."); 
      }

      public void workCompleted(WorkEvent e) {
          //  myAppServerLog.log("Work instance " + e + 
          //      " has been completed."); 
      }

}

메시지 유입 및 트랜잭션 유입 컨트렉트

메시지 유입 컨트렉트는 애플리케이션 서버가 메시지 엔드포인트를 활성화/비활성화 시키는 호출에 대해 자원 어댑터가 반응할 수 있도록 해준다. ResourceAdapter 인터페이스의 endpointActivation() 메소드가 엔드포인트 활성화시 호출되며, 이는 자원 어댑터가 메시지 엔드포인트에 메시지를 전달할 때 필요한 설정을 하도록 한다. ResourceAdapterendpointDeactivation() 메소드는 메시지 엔드포인트가 비활성화될 때 호출되며, 이는 리소스 어댑터가 메시지 엔드포인트에게 메시지를 전달하는 것을 멈추게 한다. javax.resource.spi.endpoint 패키지의 MessageEndpointFactory 객체가 endpointActivation 메소드에 전달된다. 자원 어댑터는 이 객체를 사용하여 메시지 엔드포인트를 생성하게 되며, 이러한 엔드포인트들에 대한 모든 정보는 endpointDeactivation() 메소드가 호출되면 자원 어댑터에서 제거되야 한다. 마지막으로 ResourceAdaptergetXAResources() 메소드는 시스템에 오류가 생길 경우에 트랜잭션 자원을 복구하는데 사용된다. endpointActivation(), endpointDeactivation(), getXAResources()메소드들은 ResourceAdapter 인터페이스에 위임된다.

   public class NexesResourceAdapterImpl implements 
          ResourceAdapter {

      //  Lifecycle Contract methods from earlier omitted.

      public XAResource[] getXAResources(ActivationSpec[] specs)
      throws ResourceException
      {
          // This method should either return an array of 
          // XAResource objects that uniquely correspond to the 
          // resource manager given the ActivationSpecs passed 
          // in, or null if it does  not support this par 
          // of the Message Inflow contract.
        
          return null;
      }

      public void endpointActivation(MessageEndpointFactory mef, 
          ActivationSpec as) 
      throws NotSupportedException
      {
          // This is also part of the Message Inflow contract.
          // The idea here is to create a message endpoint 
          // using the MEF's createEndpoint() method, which is 
          // then stored in the ActivationSpec class. This binds 
          // the EIS and the application server together so that 
          // the two can communicate independently of the 
          // messaging style of the EIS.
      }

      public void endpointDeactivation(MessageEndpointFactory mef, 
          ActivationSpec as)
      {
          //  This removes any resources that were created by the 
          //  endpointActivation() method above for the specified
          //  messaging endpoint. The resource adapter must notify
          //  any message providers that the endpoint is no longer
          //  valid
      }

   }

ResourceAdapter 메소드에 전달되는 ActivationSpec 클래스는 JavaBean으로써, 다양한 속성들의 정보를 입수/설정하는 메소드들을 구현한다. 이러한 입수/설정 메소드들 이외에도 구현은 반드시 validate() 메소드를 제공하여 모든 속성들이 적법하게 설정되어 있는지 확인해야 한다. 만약 속성이 적절히 설정되어 있지 않다면 메소드는 InvalidPropertyException을 반환하게 된다. ActivationSpec 객체가 equals()를 오버라이드할 수 없다는 점에 유의하기 바란다.

   public class MyActivationSpec implements ActivationSpec, 
           Serializable {

      public void setMyProperty(MyProperty s) { }
      public MyProperty getMyProperty() { }

      public void validate() throws InvalidPropertyException { }

   }

J2EE 커넥터 아키텍처 버전 1.0의 경우, 자원 어댑터는 자기 자신 혹은 외부 트랜잭션 관리자로부터 EIS에만 트랜잭션 정보를 전달할 수 있었다. 그러나 1.5 버전에 포함된 트랜잭션 유입 컨트렉트에 의해 자원 어댑터가 EIS 트랜잭션 요구를 애플리케이션 서버에 전달할 수 있게 됨은 물론, 생명주기 컨트렉트의 start() 메소드에 전달된 BootstrapContext 오브젝트 또한 사용할 수 있게 되었다. BootstrapContext 인터페이스는 생명주기 관리 contract의 부분에서 잠깐 언급된바 있다. 다음은 BootStrapContext 인터페이스 내의 메소드이다.

   public class NexesBootstrapContextImpl implements 
           BootstrapContext {

      public WorkManager getWorkManager() {
          //  Get the work manager from the application server
      }

      public XATerminator getXATerminator() {
          return new NexesXATerminatorImpl();
      }

      public Timer createTimer() {
          return new Timer();
      }

}

이제 XATerminator 인터페이스에 대해 자세히 알아보자. BootStrapContext 인터페이스의 getXATerminator() 메소드의 반환값에 유의하기 바란다. XATerminator 인터페이스는 트랜잭션 터리를 위해 5가지의 간단한 메소드를 제공한다

   public class NexesXATerminatorImpl implements XATerminator {

      public void commit(Xid xid, boolean onePhase)
              throws XAException { }
      public void forget(Xid xid) throws XAException { }
      public int prepare(Xid xid) throws XAException { }
      public Xid[] recover(int flag) throws XAException { }
      public void rollback(Xid xid) throws XAException { }

   }

자원 어댑터 파일

자원 어댑터 디스크립터 파일인 ra.xml은 매우 쉽게 생성할 수 있다. 이는 단순히 ResourceAdapter 인터페이스를 구현하는 클래스를 파일에 가리키는 것만으로 이루어 진다. 그러면 애플리케이션 서버가 이 객체에 접근하게 된다. J2EE Connector Architecture 1.5 specifications을 참조하면 수신 메시지 클래스를 ActivationSpec 클래스와 묶는 방법 등을 포함하여 자원 어댑터의 배치 디스크립터에 대한 상세한 정보를 얻을 수 있다. 모든 배치 디스크립터와 마찬가지로 ra.xml 파일은 WAR 파일의 WEB-INF 디렉토리 내에 위치하고 있어야 한다.

   <?xml version="1.0" encoding="UTF-8"?>
   <connector xmlns="http://java.sun.com/xml/ns/j2ee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd" 
     version="1.5">

     <display-name>Skeleton Resource Adapter</display-name>
     <vendor-name>Sun Microsystems, Inc.</vendor-name>
     <eis-type>Unknown</eis-type>
     <resourceadapter-version>1.0</resourceadapter-version>

     <resourceadapter>
       <resourceadapter-class>
         com.nexes.ra.NexesResourceAdapterImpl
       </resourceadapter-class>
     </resourceadapter>

   </connector>

J2EE 커넥터 아키텍처에 대한 상세한 정보를 위해서는 J2EE Connector Architecture page를 참조하기 바란다.

"Java EE" 카테고리의 다른 글

Posted by 1010