제7장 핸들러 프레임워크

내용 목차

7.1. 개요
7.2. 핸들러 체인의 우선순위
7.3. 핸들러 클래스 구성
7.3.1. 핸들러 클래스의 선언
7.4. 핸들러 클래스 설정
7.4.1. Java 클래스로부터 웹 서비스 구성
7.4.2. WSDL로부터 웹 서비스 구성
7.4.3. 클라이언트의 구성
7.5. 핸들러 체인을 사용하는 웹 서비스의 예제
7.6. 웹 서비스의 핸들러 프레임워크 실행

웹 서비스의 핸들러 프레임워크에 대한 기본 개념과 구성에 대해서 알아보고 예를 통해 설정 방법을 설명한다.

7.1. 개요

JAX-WS 웹 서비스는 핸들러를 위한 보다 쉬운 플러그인 형태의 프레임워크를 제공하는데, 이는 JEUS 6 웹 서비스의 런타임(Runtime) 시스템 기능을 보다 향상 시킬 수 있다. 이러한 핸들러의 종류로는 다음의 2가지로 나뉠 수 있다.

  • 논리적 핸들러(Logical Handler)

    프로토콜에 무관하게 메시지의 페이로드(payload)에 접근할 수 있는 핸들러이다.

  • SOAP 핸들러(SOAP Handler)

    헤더를 포함한 SOAP 메시지 전체에 접근할 수 있는 핸들러이다.

[그림 7.1] Relationship between the message contexts

Relationship between the message contexts

핸들러 프레임워크는 다음과 같은 기준에 의해 사용될 수 있다.

  • 전체 SOAP 메시지를 필요로 할 경우, SOAP 핸들러를 사용한다.

  • SOAP 메시지의 XML 문서 페이로드만을 필요로 할 경우에는 논리적 핸들러를 사용한다.

기타 다른 경우에는 Endpoint 클래스에서 처리하도록 웹 서비스를 구성하며 특별히 Java 오브젝트를 필요로 하는 경우에는 JEUS EJB에서 지원하는 인터셉터(Interceptor)를 사용한다. JEUS EJB 인터셉터(Interceptor)에 대해서는 "JEUS EJB 안내서"를 참조한다.

7.2. 핸들러 체인의 우선순위

핸들러 체인은 다음의 그림과 같이 와이어(Wire) 상에서 나가는(Outbound) 메시지인 경우 모든 로지컬 핸들러가 SOAP 핸들러보다 앞서 처리된다. 반대로 들어오는(Inbound) 메시지인 경우 모든 SOAP 핸들러가 로지컬 핸들러보다 앞서 처리된다.

주의

즉, 클라이언트 혹은 서비스 Endpoint 측에서는 프로그래밍상 논리적 핸들러가 SOAP 핸들러보다 앞에 설정되어 있어도 결국 서비스 생성 혹은 호출하는 경우에는 모든 논리적 핸들러는 SOAP 핸들러를 앞서 처리된다.

[그림 7.2] Handler Framework

Handler Framework

7.3. 핸들러 클래스 구성

본 절에서는 사용자가 어떻게 핸들러 클래스를 구성하는지에 대해 알아보고 핸들러 클래스에서 사용되는 메시지 컨텍스트(MessageContext) 클래스에 대해 설명한다.

7.3.1. 핸들러 클래스의 선언

사용자는 핸들러 클래스를 구성하기 위해 논리적 핸들러 혹은 SOAP 핸들러 인터페이스를 구현하는 클래스를 작성한다. 각각의 핸들러를 구성한 모습은 다음과 같다.

[예 7.1] << MyLogicalHandler.java >>

public class MyLogicalHandler implements
    LogicalHandler<LogicalMessageContext> {
    public boolean handleMessage(
        LogicalMessageContext messageContext) {
        LogicalMessage msg = messageContext.getMessage();
        return true;
    }
}

[예 7.2] << MySOAPHandler.java >>

public class MySOAPHandler implements
    SOAPHandler<SOAPMessageContext> {
    public boolean handleMessage(SOAPMessageContext messageContext) {
        SOAPMessage msg = messageContext.getMessage();
        return true;
    }
}

위와 같이 논리적 핸들러, SOAP 핸들러 두 가지는 공통적으로 Handler 인터페이스를 구현하고 있는데, Handler 인터페이스는 handlerMessage( ) 그리고 handleFault( ) 메소드를 지니고 있다.

두 가지 메소드는 공통적으로 MessageContext 클래스를 상속하는 객체를 파라미터로 넘겨 받는데, 그 객체는 현재 핸들러로 들어온 메시지가 들어오는(inbound) 것인지 나가는(outbound) 것인지를 구분하는 역할을 한다.

또한 이러한 사용자 핸들러 클래스에는 @PostConstruct Annotation과 @PreDestroy Annotation을 사용할 수 있는데 사용하는 예는 다음과 같다.

[예 7.3] << MyLogicalHandler.java >>

public class MyLogicalHandler implements
    LogicalHandler<LogicalMessageContext> {
    @PostConstruct
    public void methodA() { }

    @PreDestroy
    public void methodB() {}
}

위와 같이 선언된 MyLogicalHandler에서 @PostConstruct Annotation으로 선언된 메소드인 methodA는 이 핸들러가 생성된후, 그리고 @PreDestroy Annotation으로 선언된 메소드인 methodB는 이 핸들러가 없어지기 전에 호출된다.

7.4. 핸들러 클래스 설정

본 절에서는 이렇게 구성한 사용자 핸들러를 웹 서비스에 적용시키는 방법에 대해 설명한다.

7.4.1. Java 클래스로부터 웹 서비스 구성

Java 클래스로부터 웹 서비스를 구성할 때에는 서비스 Endpoint 구현 클래스에 @HandlerChain Annotation으로 설정하여 wsgen 툴을 이용해서 웹 서비스를 구성한다. 다음은 그 예이다.

[예 7.4] << MyServiceImpl.java >>

@WebService
@HandlerChain( file="handlers.xml")
public class MyServiceImpl {
    ...
}

위와 같이 @HandlerChain Annotation에 설정한 handlers.xml의 모습은 아래와 같다. 실제 서버 사이드 핸들러의 설정은 handlers.xml이라는 메타 데이타 파일을 통해 설정된다.

다음은 handlers.xml 파일의 예이다.

[예 7.5] << handlers.xml >>

<?xml version="1.0" encoding="UTF-8"?>
<jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
    <jws:handler-chain>
        <jws:handler>
            <jws:handler-class>fromjava.handler.TmaxHandler</jws:handler-class>
        </jws:handler>
    </jws:handler-chain>
</jws:handler-chains>


7.4.2. WSDL로부터 웹 서비스 구성

WSDL로부터 웹 서비스를 구성할 때에는 웹 서비스를 구성할 WSDL 문서에 간접적으로 바인딩 사용자화를 설정하여 wsimport 툴을 이용, 웹 서비스를 구성한다.

다음은 외부 파일을 이용하여 WSDL 문서에 간접적으로 바인딩을 사용자화하는 예이다.

<bindings xmlns="http://java.sun.com/xml/ns/jaxws">
    <handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
        <handler-chain>
            <handler>
                <handler-class>fromwsdl.handler.TmaxHandler</handler-class>
            </handler>
        </handler-chain>
    </handler-chains>
</bindings>

위와 같이 바인딩 사용자화 파일에 handler-chains element가 추가된 것에 주목한다.

또한, 다음과 같이 handler-chains element 아래에 handler-chain element가 여러 개가 올 수도 있다.

<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
    <handler-chain>
        <service-name-pattern xmlns:tm="http://tmaxsoft.com">
            tm:Tmax*Service
        </service-name-pattern>
        <handler>...</handler>
    </handler-chain>

    <handler-chain>
        <port-name-pattern xmlns:tm="http://tmaxsoft.com">
            tm:TmaxPort
        </port-name-pattern>
        <handler>...</handler>
    </handler-chain>

    <handler-chain>
        <protocol-bindings>##SOAP11_HTTP</protocol-bindings>
        <handler>...</handler>
    </handler-chain>
</handler-chains>

위와 같이 여러 개의 handler-chain element가 하나의 handler-chains element로 구성될 때에는 어떤 핸들러에 서비스 이름이나 포트 이름 혹은 프로토콜과 같은 속성을 부여할 수 있다.

7.4.3. 클라이언트의 구성

위 서비스의 구성 중 WSDL로부터 웹 서비스를 구성하는 방법과 동일하다.

7.5. 핸들러 체인을 사용하는 웹 서비스의 예제

본 절에서는 로그를 출력하는 사용자 SOAP 핸들러를 구현하여 웹 서비스에 이용하는 간단한 예제를 살펴본다.

논리적 핸들러 클래스에 속하는 javax.xml.ws.handler.LogicalHandler 혹은 SOAP 핸들러에 속하는 javax.xml.ws.handler.SOAPHandler 클래스는 추상 인터페이스인 javax.xml.ws.handler.Handler를 상속하고 있다. 이 2가지 클래스를 구현하는 클래스를 구현함으로써 우리가 원하는 핸들러를 생성할 수 있다.

여기서 구현할 핸들러 클래스인 LoggingHandler는 SOAP 핸들러인 javax.xml.ws.handler.soap.SOAPHandler를 구현하는 클래스이다.

[예 7.6] << LoggingHandler.java >>

public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {

    public Set<QName> getHeaders() {
        return null;
    }

    public void close(MessageContext messageContext) {

    }

    public boolean handleFault(SOAPMessageContext smc) {
        return true;
    }

    public boolean handleMessage(SOAPMessageContext smc) {
        Boolean inboundProperty = (Boolean) smc
                .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        System.out.println("\n##############################################");
        System.out.println("### JAX-WS Webservices examples - handler  ###");
        System.out.println("##############################################");

        if (inboundProperty.booleanValue()) {
            System.out.println("\nClient message:");
        } else {
            System.out.println("\nServer message:");
        }

        SOAPMessage message = smc.getMessage();
        try {
            message.writeTo(System.out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}


위와 같이 이 핸들러에서는 이 핸들러에 들어오는 메시지가 서버로 들어오는 메시지인지 클라이언트로부터 나가는 메시지인지를 검사하고 출력한다.

다음은 이 예제의 서비스 부분 Java 클래스이다. 서비스 부분에서 @HandlerChain Annotation을 사용하여 서버의 핸들러 부분을 설정하는 모습을 확인할 수 있다. 그 아래는 이러한 @HandlerChain에 등록된 파일인 handlers.xml의 모습이다.

[예 7.7] << handlers.xml >>

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
    <handler-chain>
        <handler>
            <handler-class>fromjavahandler.common.LoggingHandler</handler-class>
        </handler>
    </handler-chain>
</handler-chains>


[예 7.8] << AddNumbersImpl.java >>

@WebService
@HandlerChain(file = "handlers.xml")
public class AddNumbersImpl {

    public int addNumbers(int number1, int number2) {
        return number1 + number2;
    }
}


다음은 이 예제의 클라이언트 부분 Java 클래스이다. 클라이언트 부분에서는 WSDL 문서를 가지고 클라이언트를 생성시킬 때 사용하는 wsimport 툴에 바인딩 사용자 선언을 추가한다. 그 아래에서 이러한 바인딩 사용자 선언의 한 가지 예를 본다.

[예 7.9] << AddNumbersClient.java >>

public class AddNumbersClient {

    public static void main(String[] args) {
        AddNumbersImpl port = new AddNumbersImplService()
                .getAddNumbersImplPort();
        port.addNumbers(10, 20);
    }
}


[예 7.10] << custom-client.xml >>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:8088/AddNumbers/addnumbers?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
    <bindings node="wsdl:definitions"
        xmlns:jws="http://java.sun.com/xml/ns/javaee">
        <jws:handler-chains>
            <jws:handler-chain>
                <jws:handler>
                    <jws:handler-class>
                        fromjavahandler.common.LoggingHandler
                    </jws:handler-class>
                </jws:handler>
            </jws:handler-chain>
        </jws:handler-chains>
    </bindings>
</bindings>


7.6. 웹 서비스의 핸들러 프레임워크 실행

지금까지 구현한 클래스들 및 기타 설정 파일들을 이용하여 핸들러 프레임워크를 실행하는 방법은 다음과 같다(기타 서비스 Endpoint 인터페이스의 구현 클래스 및 기타 설정 파일들은 앞 장에서 설명한 예제의 내용과 동일하다).

다음과 같이 핸들러 프레임워크를 설정한 서비스를 생성하여 JEUS에 디플로이한다.

C:\>ant build deploy

위의 과정이 모두 실행되어 서비스가 정상적으로 디플로이되면, 클라이언트를 빌드하고 호출한다. 클라이언트에서 wsimport의 과정을 거치므로 서비스의 디플로이가 모두 완료되었을 때 클라이언트의 구성이 가능하다.

다음과 같이 핸들러 프레임워크를 설정한 클라이언트를 생성하고 서비스에 호출을 한다. 콘솔에서 입력하면 메시지를 주고 받는 모습이 LoggingHandler에 의해 서비스, 클라이언트 양측의 화면에 모두 나타나는 것을 알 수 있다.

C:\>ant run

...

run:

     [java] ##############################################
     [java] ### JAX-WS Webservices examples - handler  ###
     [java] ##############################################

     [java] Client message:
     [java] <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:B
ody><ns2:addNumbers xmlns:ns2="http://server.fromjavahandler/"><arg0>10</arg0><a
rg1>20</arg1></ns2:addNumbers></S:Body></S:Envelope>
     [java] ##############################################
     [java] ### JAX-WS Webservices examples - handler  ###
     [java] ##############################################

     [java] Server message:
     [java] <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:H
eader/><S:Body><ns2:addNumbersResponse xmlns:ns2="http://server.fromjavahandler/
"><return>30</return></ns2:addNumbersResponse></S:Body></S:Envelope>

...

BUILD SUCCESSFUL