본문 바로가기

Dev.../웹서비스

[펌] 웹 서비스로써의 Java 오브젝트를 위한 JAX-RPC 활용하기

출처 : http://kr.sun.com/developers/techtips/e2004_0824.html

웹 서비스로써의 Java 오브젝트를 위한 JAX-RPC 활용하기

JAX-RPC 는 Java API for XML-based Remote Procedural Calls이다. 몇 년이 지나긴 했지만 즉시 acronym RPC를 떠올릴 수도 있다. RPC(Remote Procedural Call)는 한 시스템의 컴포넌트가 네트워크를 통해 리모트 시스템의 컴포넌트에게 메시지를 전달할 때 일어난다. 이 장거리 커뮤니케이션 기술과 EJB(Enterprise JavaBeans),JMX(Java Management eXtensions ), RMI(Java Remote Method Invocation) APIs 핵심과는 매우 가까운 관계가 있다.

EJB, JMX, RMI와 달리 JAX-RPC는 .NET 같은 자바 활용 플랫폼이 아닌 곳에서 컴포넌트를 호출할 수 있으며, 이는 데이터 전송 매커니즘이 XML이기 때문에 가능하다. Java와 .NET 연산 플래폼 간의 연계를 위한 움직임은 WS-I 또는 WebServices Interoperability(웹서비스 호환성)로 불리고 있다. WS-I는 W3C (World Wide Web Consortium)로 제어되며 XML-RPC 를 구축하기 위하여 XML, HTTP, SOAP, MIME , WSDL (Web Services Definition Language) 같은 다른 W3C 기술의 호스트를 결합시킨다. W3C에 의해 유지되는 관계로 표준 XML-RPC는 충분히 플랫폼-중립적이어서 Java와 .net이 밀접하게 호환되도록 시스템이 설계되는 것이 가능하다.

JAX-RPC는 SOAP 1.1 와 WSDL 1.1의 자바 표준 구현이다. JAX-RPC를 이해하기 위해서는 WSDL 또한 이해해야 한다. WSDL은 단지 웹서비스의 스트럭처와 비헤이비어를 서술하는 전문화된 XML 언어일 뿐이므로 걱정하지말기 바란다. 웹서비스에서의 WSDL 파일은 Enterprise JavaBean 컴포넌트에서의 디플로이먼트 디스크립터와 같은 역할을 한다, 이 둘은 컨테이너 내부에서 컴포넌트 스터브를 생성하는 데 사용하는 인터페이스와 오브젝트 구현을 XML 의미로 서술한다. WSDL 파일은 또한 외부로 통하는 포트와 웹 서비스를 전달하는 전송 프로토콜을 규정한다.

전송상의 데이터가 XML로 캡슐화되어있기 때문에 서로 다른 플랫폼의 클라이언트들에는 원격 서비스와 커뮤니케이션하는 표준 방식이 있다. XML과 원시 언어 또는 복잡한 데이터 간의 모든 플랫폼 지정 데이터의 변환은 JAX-RPC 컨테이너에 의해 자동적으로 핸들링된다. 또한 JAX-RPC는 포트 80을 통해 커뮤니케이션을 허용하는 전송 프로토콜로 HTTP를 사용해서 SOAP를 호출할 수 있다. 이것은 JAX-RPC 웹서비스가 Tomcat과 같은 웹서비스를 제공하는 서블렛 컨테이너의 다른 애플리케이션 컴포넌트 같이 구동할 수 있다는 것을 의미한다.

웹서비스 분석

웹서비스가 어떻게 구축되는 지 알기 위한 첫번째 단계는 WSDL 용어를 이해하는 것이다. 이 언어는 웹서비스 컨셉에 관해 높은 단계의 "분석적인 참조"가 된다. 추상성이 증가되는 순서대로 각각의 용어에 대해서 알아보자.

  • 타입(type)은 Java 타입을 서술한다. wsdl:types 요소는 간단한 표준 XML 타입이나 좀 더 복잡한 타입을 표현할 수 있는 XML 정의를 포함한다.
  • 메시지(message)는 이름과 타입 매개변수를 매핑하는 부분들로 구성되어 있다.
  • 연산(operation)은 Java에서 메서드를 서술한다. 연산은 매개변수를 서술하는 입력 메시지와 Java 메서드의 리턴된 타입을 서술하는 출력 메시지로 구축된다.
  • 포트타입(port type)은 SEI(Service Endpoint Interface)를 캡슐화하고 정의한다. 포트 타입은 연산을 위해 SEI의 메서드를 매핑한다.
  • 바인딩(binding)은 포트 타입을 특정 프로토콜에 매핑한다. 이 매핑이 포트 타입의 비헤이비어로 대체될 수 있기 때문에 바인딩은 주어진 포트 타입의 지정 프로토콜 비헤이비어를 기술하기도 한다. 예를 들어 SOAP 프로토콜을 통해 포트타입을 호출하면 HTTP로 같은 포트 타입을 호출한 것과는 다른 비헤이비어가 생성된다. (예를 들어 SOAP 프로토콜을 통해 호출된 포트타입의 비헤이비어는 HTTP로 같은 포트 타입을 호출했을 때와 다르다.) SOAP의 경우에 하나 이상의 오류 정의는 이 바인딩으로 포트 타입에 매핑될 것이다.
  • 포트(port)는 URL과 같은 특정 공개 주소로 바인딩을 매핑한다.
  • 서비스(service)는 포트들의 모음을 캡슐화한다.

따라서 WSDL에서의 웹서비스는 실제로 공개적으로 접근 가능한 포트와 포트 타입 연산 간의 프로토콜 바인딩 모음인 것이다. 이에 대한 친숙한 예로, J2EE에서 서블렛 컨테이너의 URL과 Service Endpoint Interface 구현 오브젝트간의 SOAP 바인딩 모음을 들 수 있다. 혹은 더 간단히, 웹서비스는 Java 인터페이스 오브젝트와 URL 간의 매핑이므로 메서드 혹은 절차 호출을 원격으로 수행할 수 있게 한다는 사실에서도 알 수 있다.

서비스 디스크립터 작성

J2SE 1.4.2 SDK, Tomcat 5.0 for Java WSDP 1.4, Java WSDP 1.4를 설치하자. 이 기술들이 있는 웹페이지에서 설치 명령어를 찾을 수 있다. 그러나 설치 순서를 기억해야 한다. Java와 Tomcat for JWSDP를 먼저 설치한다면 JWSDP 설치가 간단해질 것이다. Java WSDP 1.4를 설치하는 동안 브라우저에는 이 제품을 통합하도록 권장하는 웹컨테이너 옵션 선택 창이 뜰 것이다. JWSDP 1.4 다운로드를 위한 Tomcat이 이미 설치되어 있다면 웹컨테이너 메뉴에 추가하면 된다. 그러면 JWSDP는 자동적으로 Tomcat 설치 디렉토리에 통합될 것이다.

Tomcat 웹서비스 설치에는 새로 출시된 모든 Java 웹서비스 API에 대한 샘플이 포함되어있다. 그러나 이에 대한 문서는 매우 부족한 관계로 이 테크팁에서는 그 샘플들 중 JAX-RPC 예제를 수정한 버전을 이용하겠다. 수정된 예는 서버의 현재시간을 보여주는 간단한 웹 서비스로 특별하진 않지만 중요한 요점을 가르쳐줄 것이다. 사용자가 웹서비스를 직접 작성할 때 이것과 비교하면 좋다.

예제 애플리케이션을 다운로드하고 HelloWorld 디렉토리 옆의 <JWSDP_HOME>/jaxrpc/samples에 압축을 푼다. 그 후에 /etc디렉토리를 찾는다. /etc디렉토리에서 TimeService.wsdl이 파일을 찾을 수 있을 것이다.

이 WSDL 파일을 열 때 앞서 설명한 용어들을 재빨리 기억할 필요가 있다. 가장 높은 단계인 wsdl:definitions 요소와 여러 wsdl:message요소들을 발견하게 될 것이다.

   <?xml version="1.0" encoding="UTF-8"?>   <!-- TimeService.wsdl -->    <definitions      name="TimeService"      targetNamespace="http://time.org/wsdl"      xmlns:tns="http://time.org/wsdl"      xmlns="http://schemas.xmlsoap.org/wsdl/"      xmlns:ns2="http://time.org/types"      xmlns:xsd="http://www.w3.org/2001/XMLSchema"      xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">    <message name="TimeSEI_sayTimeBack">    <part name="String_1" type="xsd:string"/>   </message>       <message name="TimeSEI_sayTimeBackResponse">    <part name="result" type="xsd:string"/>   </message>   ...

중요한 것이 몇 가지 있다. 먼저 http://time.org namespace 는 완전히 지어낸 것이며 웹서비스를 생성할 때 실제 네임스페이스와 교체하게 될 것이다. 둘째로 파일은 각각 결국 같은 연산에 매핑되는 두 개의 메시지를 정의한다. 각 메시지 이름은 TimeSEI_로 시작한다. TimeSEI는 타임서버 엔드포인트 인터페이스를 의미하며 존재하지 않는 Java Service Endpoint Interface 오브젝트를 참조한다. 마지막으로 sayTimeBacksayTimeBackResponse를 모두 보유하고 있는 것은 약간 장황하고 복잡하게 보일 수도 있지만 다음의 포트 타입과 연산 정의(operation definition) 문맥을 고려할 때 이들을 보유 해야하는 이유를 알게 될 것이다.

   <portType name="TimeSEI">     <operation name="sayTimeBack" parameterOrder="String_1">       <input message="tns:TimeSEI_sayTimeBack"/>       <output message="tns:TimeSEI_sayTimeBackResponse"/>     </operation>   </portType>

연산 정의시 기억해두어야 할 것은 각 연산은 반드시 하나가 아닌 두개의 메시지를 포함해야 한다는 것이다. 이는 WSDL과 JAX-RPC이 SOAP 프로토콜을 이용하여 연산하도록 구축되어있기 때문이다. SOAP 메시지들은 한쪽 방향으로만 작용하지만 RPC는 반드시 양방향 비헤이비어이다. 연산과 연산으로 매핑된 Java 메서드는 입력 매개변수와 리턴되는 값을 정의한다. SOAP에 잘 매핑하기 위해, 양방향 커뮤니케이션에는 두개의 개별적인 메시지가 필요하다.

portType 의 이름이 TimeSEI인 것에 주목하자. 다시말해, 이것은 존재하지 않는 Service Endpoint Interface에 대한 매핑이다. 위의 정의에 따르면 TimeSEI 인터페이스 오브젝트는 다음과 같은 서명을 갖고있는 단일 공개 메서드를 노출한다.

   public String sayTimeBack(String) throws RemoteException;  

메시지와 포트 타입 정의를 읽음으로써 이 모든 정보를 조합할 수 있다.

자, 이제 SOAP 프로토콜 바인딩에 대해 알아보자.

   <binding name="TimeSEIBinding" type="tns:TimeSEI">     <operation name="sayTimeBack">       <input>         <soap:body            encodingStyle=            "http://schemas.xmlsoap.org/soap/encoding/"            use="encoded"            namespace="http://time.org/wsdl"/>       </input>       <output>         <soap:body            encodingStyle=            "http://schemas.xmlsoap.org/soap/encoding/"           use="encoded"            namespace="http://time.org/wsdl"/>       </output>       <soap:operation soapAction=""/>     </operation>         <soap:binding        transport="http://schemas.xmlsoap.org/soap/http"        style="rpc"/>   </binding>

이 바인딩은 code>sayTimeBack 연산에서의 입력물과 출력물 메시지 바디의 인코딩 스타일을 정의한다. 이 연산은 SOAP을 이용하여 인코딩되며 다른 연산 메시지와 충돌되는 것을 막기위해 특별한 네임스페이스를 할당한다. 마지막으로 바인딩은 RPC 바인딩 스타일과 SOAP 호출이 HTTP가 되게 하는 전송 메커니즘을 선언한다. 남는 것은 서비스 정의 자체이다.

   <service name="TimeService">     <port name="TimeSEIPort" binding="tns:TimeSEIBinding">       <soap:address location="REPLACE_WITH_ACTUAL_URL"/>     </port>   </service>   </definitions>

서비스 정의를 TimeService라 이름 짓고 그 포트를 바인딩에 매핑한다. 이로써 간단한 웹 서비스 정의가 완료되었다.

서비스 구현

자, 이제 서비스 구현을 작성할 시간이다. 두가지 다른 접근법을 사용할 수 있다. 사용자가 직접 SEL 스터브를 작성하여 웹 아카이브의 구현 클래스와 수동으로 패키징하거나 Java WSDP 1.4에 제공되는 ANT build를 사용할 수 있다. ANT는 JWSDP 1.4 환경의 이점을 살려 SEI 스터브를 자동적으로 생성할 수 있다. 두번째 접근법이 훨씬 빠르므로 기본적인 JAX-RPC 프레임워크의 복잡성을 다룰 시간적 여유가 생긴다.

이 단계를 완성하기 위해 반드시 작성해야 하는 몇 가지 파일이 있다. SEI 구현 클래스부터 시작하자.이 예제에서 사용한 것은 TimeOnServer/src/server/time/TimeImpl이다. 다음은 이 클래스의 컨텐츠이다.

   package time;   import java.util.Date;   public class TimeImpl implements time.TimeSEI,      java.rmi.Remote {       public String sayTimeBack(java.lang.String str) {           Date date = new Date(System.currentTimeMillis());           String result = " Hello, " + str                + ".  The time on the server is "                + date.toString();           return result;       }   }

이 간단한 클래스는 time.TimeSEI인터페이스와 java.rmi.Remote 인터페이스를 구현한다. TimeSEI는 포트 타입 정의로 선언되었었다는 사실을 상기하자. 그러나 인터페이스는 여전히 작성되지 않았다. 이 인터페이스는 JWSDP ANT build로 생성할 수 있으며 구현 클래스와 같은 패키지 안에 있어서 임포트할 필요가 없다.

그러나 JAX-RPC 레퍼런스 구현을 위해 특별한 디스크립터 파일을 작성해야 할 필요가 있다. 이것은 컨테이너에 필요한 사항이므로 TimeImpl 클래스에 대한 포트 타입 정의로부터 어떻게 TimeSEI 레퍼런스를 매핑하는지 알 수 있다.

   <!-- jaxrpc-ri.xml -->   <webServices       xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"       version="1.0"       targetNamespaceBase="http://time.org/wsdl"       typeNamespaceBase="http://time.org/types"       urlPatternBase="/ws">       <endpoint           name="Time"           displayName="Time Service"           description="A simple web service"           wsdl="/WEB-INF/TimeService.wsdl"           interface="time.TimeSEI"           implementation="time.TimeImpl"           model="/WEB-INF/model-wsdl-rpcenc.xml.gz"/>       <endpointMapping           endpointName="Time"           urlPattern="/time"/>   </webServices>

endpoint 요소는 그것의 SEI로의 경로와 구현 클래스를 포함한 서비스의 속성을 설명하며, 관리 툴을 위해 엔드포인트에 대한 몇가지 기본 메타데이터를 정의한다. 또한 endpointMapping은 서비스 엔트포인트로 URL 패턴을 바인딩한다.

실제 웹 서비스를 위해 이 파일을 형성하여 프로젝트를 정확하게 정의했는지 확인 하기 위해 web.xml 파일을 체크해야 한다. 이로써 서비스를 구축하고 디플로이할 준비가 완료된 것이다. PATH 세팅이 ANT (<JWSDP_HOME>/apache-ant/bin)로의 경로를 포함하고 있도록 확인해야 한다. 그 후 커멘트 라인 인터페이스 안의 /TimeOnServer 디렉토리를 탐색하여 다음의 커멘드를 입력하자.

   ant  

ANT는 TimeSEI 인터페이스를 생성할 것이다. 그것의 구현 스터브 클래스도 생성하며 새로운 웹 서비스의 상호작용을 핸들링 하기 위해 SOAP 요청과 반응 구조를 다중 분류하는 배열 역시 생성할 것이다. ANT는 모든 관련된 파일들을 WAR 파일(jaxrpc-TimeOnServer.war)에 조합하면 완료된다. 이 아케이브를 tomcat_jwsdp/webapps 디렉토리에 복사하여 startup.bat 파일을 더블 체킹하거나 /tomcat_jwsdp/bin 디렉토리에서 startup.sh스크립트를 구동하여 Tomcat을 기동시킬 수 있다.

이 때, 충분히 운용할 수 있는 웹 서비스를 보유하고 있어야 하지만, 테스트를 할 때는 웹 서비스에 클라이언트를 구동하는 것으로 충분하다.

간단한 클라이언트 구현

예제 애플리케이션에는 간단한 클라이언트를 포함하고 있다. 이를 구동하기 위해서는 /TimeOnServer 디렉토리의 커멘드 라인 인터페이스에 다음 커멘드를 입력해야한다.

   ant run-client

이 커멘드는 클라이언드 클래스들을 생성하고 컴파일하며 클라이언트를 구동한다. 모든 것이 성공적이라면 클라이언트를 구동하여 출력 시 마지막 몇 줄은 다음과 같이 나타나야 한다.

   run-client:        [java]  Howdy, stranger.         The time on the server is Sun Aug 01 01:01:46 CDT 2004   BUILD SUCCESSFUL   Total time: 20 seconds

어떻게 JAX-RPC 서비스가 작용되는지 좀 더 자세히 알고 싶으면 예제의 소스코드를 읽고 생성된 WAR 파일 안의 Service Endpoint Interface나 SOAP 요청/반응 오브젝트 같은 클래스 파일을 읽자. 프로젝트의 구축환경을 사용자 정의하고 싶다면 build.xml file에 프로젝트 이름을 업데이트하는 것을 잊지 말기 바란다. 또한 Understanding your JAX-RPC SI Environment, Part 2 도 참조하기 바란다. 이 아티클은 몇 가지 개발, 디플로이먼트, 혁신 시나리오를 제공하고 있다.

.
.

컴포넌트 시스템과 클래스 로더 경계

어느 곳에서나 소프트웨어 개발자들은 단일 애플리케이션 개발 전략을 넘어서서 상호작용(interoperating) 애플리케이션 시스템 개발로 나아가고 있다. 현재 엔터프라이즈 개발자들이 당면하고 있는 문제들 중 하나는 최근의 WAR 와 EAR 파일 등 Web and Enterprise Archives 같은 단일 애플리케이션 디플로이먼트 구조에서 느슨하게 결합된 애플리케이션 컴포넌트 시스템으로 어떻게 옮겨갈 것인가 이다. 개발자들은 J2EE 플랫폼의 기본 기능과 호환됨과 동시에 코드 재사용을 늘리고 애플리케이션 복잡성을 줄이기 위해 컴포넌트 기반 애플리케이션 전략을 사용하길 원한다. JARs, WARs, EARs 등과 같이 플러그할 수 있는 컴포넌트 아카이브를 개발하는 것이 이 전략의 관건이 될 것이다.

그러나 J2EE에서의 컴포넌트 기반 애플리케이션 시스템 개발과 배포를 둘러싼 많은 함정이 도사리고 있다. 중요 함정 중 한가지는 Java 버추얼 머신에서 클래스 로더의 경계를 교차하여 기능하기 위한 컴포넌트 시스템을 얻는데 따르는 어려움이다.

클래스 로더는 환경 구획의 의미로 생각하면 된다. 명심해야 할 것은 클래스 로더는 하위 클래스 로더들을 생성할 수 있으며, 따라서 하위환경을 만들어낼 수 있다는 것이다. 이 하위 클래스 로더 환경의 클래스들은 윗 계급인 시스템 클래스 로더쪽을 볼 수 있지만 더 하위 계급의 클래스들은 볼 수 없다.

이런 종류의 다단계적 환경 구조는J2EE 서버 환경에서 매우 보편적이며 심지어 J2EE 기술을 둘러싼 다양한 규격에는 권장되고 있는 사항이다. 예를 들어 서블렛들은 각각 WAR 파일에 패키지화되어 있으며, 오버롤 시스템의 각각의 클래스 로더 컨텍스트에 디폴트값으로 주어져있다. 이는 WEB-INF/lib 디렉토리에 포함되어 있는 JAR 파일은 다른 웹 아카이브의 JAR 파일 안에 저장되어 있는 클래스들에 접근하지 못한다는 것을 의미한다. 단일 애플리케이션에 패키징하기 위해 WAR을 사용할 때 이 배열은 허용가능하나 올인원 전략을 넘어서서 좀 더 컴포넌트 기반 애플리케이션 아키텍처로 나아간다면 바로 문제가 되어버릴 것이다.

이런 문제들의 증상은 확실하지 않을 때가 많으므로, 클래스 로더 경계를 교차하여 컴포넌트를 전달하지 못한다는 것을 진단하기는 매우 어렵다. 예를 들어 웹 애플리케이션 아카이브의 서블렛들 사이로 전달시키고자 하는 Foo라는 이름의 오브젝트가 있다고 하자(어느 한 웹 아카이브 안의 서블렛에서부터 다른 웹 파카이브의 서블렛으로 전달됨을 의미). Foo컴포넌트를 위한 클래스 파일은 JAR 파일에 패키징되어 있으며 JAR 파일의 동일한 복사본이 각 웹 아카이브의 /WEB-INF/lib 디렉토리 안에 있다. 다음의 코드를 작성하자.

   /* This code runs in a servlet in WAR #1 */   SystemScopeObjectCache cache =        SystemScopeObjectCacheFactory.getInstance();   WARScopeFoo foo = new WARScopeFoo ();   System.out.println(foo);   cache.addToCache("myFooObject", foo);   /* This code runs in a servlet in WAR #2 */   SystemScopeObjectCache cache =        SystemScopeObjectCacheFactory.getInstance();   Object o = cache.getFromCache("myFooObject");   try {     // the following throws a ClassCastException!      WARScopeFoo foo = (WARScopeFoo)o;            } catch(ClassCastException e) {     e.printStackTrace();   }

각 웹 아카이브의 서블렛에 접근가능한 WARScopeFoo 오브젝트를 위한 컴포넌트 캐시를 쉽게 생성할 수 있다. 그러나 만약 서블렛 A가 오브젝트의 인스턴스를 중앙 캐시로 전달하고, 다른 WAR의 서블렛 B가 캐시로부터 인스턴스를 꺼내 Object에서 WARScopeFoo로 전달하려한다면 시스템은 ClassCastException을 전달하게 될 것이다.

이 상황은 클래스 로더를 고려한 것이 아니라면 아무 의미가 없다. 서블렛 A 에서 참조하는 WARScopeFoo 클래스는 서블렛 B에서 참조하는 WARScopeFoo클래스와 전혀 연관 없는 다른 클래스 로더로부터 생성된다. 정확히 얘기하자면, 그 둘은 의도적으로 전혀 연관되어 있지 않다. 이것은 같은 서블렛 컨테이너에서 구동되는 웹 애플리케이션 간의 네임스페이스 보전성을 강화하기 위한 안전한 매커니즘이다.

클래스 로더가 충돌한다는 또 다른 신호는 시스템의 단일 클래스에서 다중 인스턴스를 찾았을 때이다. (단일 클래스에는 단 한 개의 클래스 인스턴스가 생성되어야 한다.) 기술적으로 단일 인스턴스는 그 클래스 로더 안에서만 독자적일 뿐이다. 따라서 클래스 로더 단계에서 단일성에 의지하는 것은 위험하다. 다음의 예제를 보자.

   /* MyServlet in WAR #1 */   WARScopeSingleton cache = WARScopeSingleton.getInstance();   WARScopeFoo foo = new WARScopeFoo ();   cache.add("myFooObject", foo);   System.out.println(cache.length()); //output is 1   /* MyOtherServlet in WAR #2 */   WARScopeSingleton cache = WARScopeSingleton.getInstance();   System.out.println(cache.length()); //output is 0!

이 예제에서 코드는 단일 클래스에서의 오브젝트들을 저장한다. 그러나 단일 클래스에서의 스코프는 WAR 클래스 로더를 필연적으로 발생시킨다.

다행히 엔터프라이즈 Java 개발자들을 위해 이런 장애들을 극복할 몇 가지 방법이 있다. 첫번째 단계는 어떤 클래스 로더 경계가 늘 존재하는 것이 보장되는지 확인하고 그에 대한 전략을 계획하는 것이다. 모든 J2EE 환경에는 세 개의 클래스 로더 레벨이 구축되어 있다. 시스템 단계의 컨텍스트는 대부분 VM을 교차하며 J2SE와 J2EE 플랫폼의 클래스와 호환된다. 이것은 애플리케이션 서버가 자체적으로 구동하는 레이어이다.

다음 단계는 Enterprise Archive 컨텍스트인데, 이는 엔터프라이즈 애플리케이션의 모든 JAR와 WAR를 포함하고 있다.

마지막 단계는 Web Archive 컨텍스트이며, 이는 WAR 파일 /WEB-INF/classes디렉토리의 모든 파일과 /WEB-INF/lib 디렉토리의 모든 JAR 파일을 포함하고 있다. WAR 파일 안에 로딩된 모든 클래스가 상호 접근 가능함은 물론이고 EAR 파일과 System 클래스 로더의 클래스들과도 접근가능하지만 다른 WAR 파일 안에 로딩된 클래스들과는 접근될 수 없다.

따라서 웹 아카이브 간의 사용자 비즈니스 오브젝트를 공유하고자 한다면 오브젝트의 JAR 파일을 EAR 클래스 로더 컨텍스트에 위치시키고 WAR 파일의 /WEB-INF/lib 디렉토리는 피하는 게 좋다. 다음은 그 예이다.

   /* MyServlet in WAR #1 */   EARScopeCache cache =EARScopeCache.getInstance();   EARScopeFoo foo = new EARScopeFoo();   cache.add("MyFooObject", foo);   /* MyServlet in WAR #2 */   EARScopeCache cache = EARScopeCache.getInstance();   Object o = cache.get("myFooObject");   EARScopeFoo foo = (EARScopeFoo)o; //SUCCESS!

이 작업의 이유는 각각의 서블렛은 상위 계급의 클래스 로더를 추구하고 WAR 클래스 로더 컨텍스트에 클래스들을 로딩하는 대신 같은 EARScopeCacheEARScopeFoo오브젝트를 찾는다는 데에 있다.

그러나, EAR 파일을 조합하는 것은 서블렛 규격의 참조 구현인 Tomcat 같은 어떤 컨테이너 에서는 옵션이 아니다. Tomcat은 EAR 파일에서 클래스들을 로딩하는 정보처리 기능이 없으며 따라서 다중 웹 아카이브에 클래스들을 교차할 수 있게 만들지 못한다. 그러나 Tomcat은 일반 클래스 로더를 보유하여 /common디렉토리의 모든 JAR 파일을 모든 웹 아카이브 컨텍스트위에 직접적으로 존재하여 접근가능한 클래스 로더 스페이스에 로딩한다.

생각해볼 수 있는 다른 기술로는 서로 다른 컴포넌트 하위 시스템 간에 데이터를 왕복전달하기 위해Java 배열을 사용하는 것이 있다. 두개의 애플리케이션으로부터의 컴포넌트가 바이트로 배열된 데이터를 저장할 수 있는 공유 장소가 있고, 버전 충돌이 잠재적으로 있다는 사실을 배제하면 이 전략은 빠르고 효과적이다.

'Dev... > 웹서비스' 카테고리의 다른 글

[펌] XSL 기초  (0) 2005.02.13
[펌] XMLBeans를 이용한 xml binding  (0) 2005.02.13
[펌] [WEB-SERVICE] - [AXIS] 세팅하기  (0) 2005.02.13
[펌] WSDL  (0) 2005.02.13
[펌] Getting Started with XMLBean  (0) 2005.02.13