저자 Rama Pulavarthi
핸들러는 JAX-WS(Java API for XML-Based Web Services) 2.0 런타임 환경에 쉽게 플러그인하여 인바운드/아웃바운드 메시지의 추가 프로세싱을 수행할 수 있게 하는 인터셉터이다. JAX-WS는 프로토콜 핸들러와 논리 핸들러 등 두 종류의 핸들러를 정의하는데, 프로토콜 핸들러는 SOAP와 같은 프로토콜 전용 핸들러로서 메시지 헤더와 같이 프로토콜 부분을 포함하여 메시지의 어떤 부분이라도 액세스 또는 변경할 수 있다. 한편 논리 핸들러는 프로토콜을 가리지 않으며(protocol-agnostic), 프로토콜의 메시지 부분을 변경할 수 없다. 또한 논리 핸들러는 메시지의 페이로드에 대해서만 작용한다.
본 팁에서는 JAX-WS에서 사용할 SOAP 프로토콜 핸들러와 논리 핸들러의 작성 방법에 대해 알아보도록 한다.
핸들러의 기본 원리
SOAP 핸들러는 일반적으로 SOAP 헤더와 같은 SOAP 특정 정보를 처리하는 데 사용된다. 예를 들어, SOAP 핸들러는 필수 크리덴션이 메시지에 포함되어 있을 경우 메시지 내의 보안 헤더를 처리하여 요청을 엔드포인트에 전달할 수 있다. 논리 핸들러는 처리 과정에서 SOAP 헤더에 대한 액세스가 필요치 않은 경우나, 페이로드의 검증, REST(Representational State Transfer) 방식의 웹 서비스 등에 흔히 사용된다. 이 외에도 논리 핸들러는 JAXB(Java API for XML Binding)를 이용하여 페이로드를 처리할 수 있으며, 사용자가 JAXBContext 오브젝트를 가지고 있다면 논리 핸들러로 손쉽게 메시지의 내용을 변경할 수도 있다. 또한 SOAP 핸들러에서 SAAJ(SOAP with Attachments API for Java)를 사용하여 Source 오브젝트나 SOAPMessage 오브젝트를 처리하는 대신 JAXB 오브젝트를 얻고 이와 관련하여 자바 메소드를 호출할 수 있다.
핸들러는 핸들러가 인바운드/아웃바운드 메시지를 액세스하고 수정하는데 사용되는 메소드를 제공하는 메시지 컨텍스트를 통해 호출되며, 메시지 컨텍스트 또한 핸들러가 처리할 수 있는 속성을 가진다. 이 속성들은 메시지에 지정되지 않은 추가 정보 또는 메타데이터를 전달하는 데 사용될 수 있고, 추가 정보는 핸들러와 서비스 구현간 또는 핸들러와 웹 서비스 클라이언트간에 교환이 가능하다. (JAX-WS의 메시지 컨텍스트에 관한 자세한 내용은 A little bit about Message Context in JAX-WS(영문)를 참조할 것.)
SOAP 핸들러는 javax.xml.ws.handler.soap.SOAPHandler를 확장하는데 사용되고, JAX-WS 스펙은 SOAP 바인딩을 위한 SOAPHandler 클래스를 정의한다. SOAP 핸들러 호출 시 요청에서 SOAPMessageContext 오브젝트가 지정되는데, 이 SOAPMessageContext 오브젝트는 SOAPMessage(SOAP 메시지를 위한 클래스) 액세스를 위한 메소드를 제공한다. 특히, SOAPMesageContext 내의 getMessage() 메시지는 SOAPMessage를 검색하고, SOAPMessage 획득 후에 사용자는 SAAJ를 사용하여 이를 처리할 수 있다.
논리 핸들러는 javax.xml.ws.handler.LogicalHandler를 확장하고, 메세지를 교환할 경우에는 SOAP 바디의 컨텐츠가 페이로드를 생성하고, HTTP 상의 XML을 사용할 경우에는 주요 메시지 부분의 XML시지 컨텍스트와 메시지 페이로드에 대한 액세스를 제공한다. HTTP 상의 SOAP를 사용하여 메시지를 컨텐트가 페이로드를 생성하게 된다. 논리 핸들러가 호출될 때는 요청에서 LogicalMessageContext 오브젝트가 지정되는데, LogicalMessageContext 내의 getMessage() 메소드는 LogicalMessage를 리턴한다. LogicalMessage는 프로토콜 중립형 XML 메시지를 표현하며 메시지의 페이로드에 대한 액세스를 제공하는 메소드를 포함한다.
다음 그림은 메시지 컨텍스트와 이를 이용하여 검색할 수 있는 오브젝트, 그리고 그 오브젝트에 해당하는 메시지의 부분들간의 관계를 보여준다.

논리 핸들러는 핸들러 체인 내에서 SOAP 핸들러와 공존이 가능하다. 런타임 과정에서 핸들러 체인을 재배열함으로써, 아웃바운드 메시지와 관련하여 논리 핸들러가 SOAP 핸들러보다 먼저 실행될 수 있도록 한다. 인바운드 메시지의 경우에는 SOAP 핸들러가 논리 핸들러보다 먼저 실행된다.
다음 그림은 요청/응답 과정에서 논리 핸들러와 SOAP 핸들러가 어떻게 호출되는지를 보여주고 있다.

SOAP 메시지 핸들러 작성하기
먼저 간단한 SOAP 핸들러를 살펴보기로 하자. 본 팁에는 예제 패키지가 첨부되어 있으므로 예제 패키지를 다운로드하여 압축을 푼다. 이 때, 두 개의 소스 파일이 생성되는데, 그 중 하나인 SOAPLoggingHandler는 웹 서비스에 대한 호출을 기록하는 SOAP 핸들러이다. 다음은 SOAPLoggingHandler의 코드 단편이다.

모든 SOAP 핸들러와 마찬가지로, SOAPLoggingHandler는 SOAPHandler 인터페이스를 구현한다. SOAPLoggingHandler는 또한 handleMessage(), handleFault(), close() 등 세 가지 메소드를 구현한다는 점에 유의할 것. SOAPHandler 인터페이스는 Handler 인터페이스의 서브인터페이스이고, handleMessage(), handleFault(), close() 등의 메소드는 Handler 인터페이스에서 정의되는 세 가지 메소드로, 모든 기본 핸들러에서 구현되어야 한다. 메소드 사용 방식은 다음과 같다.
handleMessage()는 통상적인 인바운드/아웃바운드 프로세싱을 위해,handleFault는 메시지에 프로토콜 오류가 포함된 경우에 호출된다.close( )는 각 웹 서비스 호출을 위한 모든 핸들러의 메시지 프로세싱이 완료된 후에, 즉 SOAP 메소드 교환 패턴(MEP)이 완료된 후에 호출된다.
로깅은 handleMessage()와 handleFault() 메소드 모두가 호출하는 핸들러의 logToSystemOut() 메소드에서 이루어진다.

logToSystemOut() 메소드는 SOAPMessageContext 속성 MESSAGE_OUTBOUND_PROPERTY을 액세스하고, SOAPMessageContext 메소드 getMessage()를 사용한다는 점에 유의할 것. MESSAGE_OUTBOUND_PROPERTY의 값은 메시지 방향을 나타내는데, 참의 값은 메시지가 아웃바운드임을 의미하고 거짓의 값은 인바운드 메시지를 나타낸다. logToSystemOut()은 속성 값을 기준으로 하여 "Outbound message" 또는 "Inbound message"를 프린트한 다음 getMessage()를 사용하여 SOAP 메시지를 얻어 이를 프린트한다.
논리 메시지 핸들러 작성하기
이름에서 알 수 있듯이, 예제 패키지에 포함된 LogicalLoggingHandler 파일은 논리 메시지 핸들러로, SOAPLoggingHandler와 마찬가지로 웹 서비스에 대한 호출을 기록한다. 다음은 LogicalLoggingHandler의 코드 단편이다.

모든 논리 핸들러와 마찬가지로, LogicalLoggingHandler는 LogicalHandler 인터페이스를 구현할 뿐 아니라 모든 핸들러와 마찬가지로 handleMessage(), handleFault(), close() 등의 메소드를 구현한다.
로깅은 handleMessage()와 handleFault() 메소드 모두가 호출하는 핸들러의 processMessage() 메소드에서 이루어진다.

processMessage() 메소드는 논리 핸들러를 위한 논리 메시지 컨텍스트를 이용하여 메시지 페이로드를 액세스/처리하고, LogicalMessage 메소드 getPayload()를 호출하여 메시지의 페이로드를 획득한다. 한편 페이로드는 Source 오브젝트로 리턴된다.
processMessage()의 코멘트 아웃 부분에 특히 유의할 것. 경우에 따라서는 페이로드에 수정이 이루어진 후에 LogicalMessage 메소드 setPayload()를 호출해야 할 수도 있는데, getPayload()을 통해 리턴되는 소스는 JAX-WS 런타임에 의해 결정된다. 리턴되는 소스가 DOMSource인 경우에는 밀폐형(encapsulated) DOM 트리를 수정하면 해당 메시지 페이로드가 변경되는데, 이 경우 이어서 setPayload()를 호출할 필요가 없다. 하지만 다른 종류의 리턴 소스의 경우에는 메시지에 대한 읽기 전용 액세스를 제공하므로 수정 후에 setPayload()를 호출해야 한다.
사용자는 또한 호출 내의 JAXBContext 오브젝트를 getPayload()에 전달하여 페이로드를 JAXB 오브젝트로 획득할 수 있다. 관련 예는 다음과 같다.

리턴되는 오브젝트와 메시지 페이로드 간에는 관련성이 없다는 점에 유의할 것. 페이로드를 변경하려면 setPayload()를 호출해야 한다.
플러그인과 관련하여 유연성을 제공하는 핸들러는 사용자 애플리케이션의 기능 향상에 크게 도움이 될 것이다. 다른 JAX-WS 핸들러의 예를 보려면 Stephen DiMilla 블로그의 Handler example using JAXWS 2.0 내용을 참조할 것.
저자 소개
Rama Pulavarthi는 썬 마이크로시스템즈 Java Web Services 그룹 기술팀 소속으로, 현재 JAX-WS 레퍼런스 구현 개발에 참여하고 있다. 이전에는 JAX-RPC를 위한 Software Quality Engineering 프로젝트를 이끈 바 있다.
"Java EE" 카테고리의 다른 글
- Attach API (댓글 18개 / 트랙백 1개) 2007/09/03
- JSP 2.0 EXPRESSION LANGUAGE (댓글 1개 / 트랙백 0개) 2004/02/05
- 환경 엔트리를 이용해서 배포의 사용자 정의하기 (댓글 2개 / 트랙백 0개) 2003/12/24
- JAX-WS를 이용한 웹 서비스 개발 (댓글 1개 / 트랙백 0개) 2006/01/18
- EJB 2.1로 메시지 구동 빈 이용하기 (댓글 1개 / 트랙백 0개) 2005/05/18
- EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지 (댓글 0개 / 트랙백 1개) 2008/07/09
- 컴포넌트 시스템과 클래스 로더 경계 (댓글 1개 / 트랙백 0개) 2004/10/05
- JAX-WS Dispatch 및 Provider API를 이용한 문서 처리 (댓글 4개 / 트랙백 0개) 2006/09/15
- POJO를 Persistent Entity로 변환하기 (댓글 1개 / 트랙백 0개) 2005/12/27
- SAAJ 소개 (댓글 1개 / 트랙백 0개) 2005/06/08
댓글을 달아 주세요
자바는 알면 알수록 재미가 있어지네요. 좋은 자료 잘 활용하겠습니다.
2007/09/13 22:43좋은 정보 감사해요~
2007/09/19 04:17