웹 서비스 사용하기

본 장에서는 JAX-WS 2.2를 활용한 웹 서비스의 생성과 클라이언트의 작성, 그리고 웹 서비스를 호출하는 내용을 설명한다.

1. 웹 서비스 생성

JEUS 9에서는 Jakarta EE 8 방식의 웹 서비스를 지원하며 JAX-WS 2.2는 Java EE 6 웹 서비스의 핵심이다.

JAX-WS는 기존 JAX-RPC를 대체하는 수단으로 설계되었다. 새로운 모습으로 등장한 JAX-WS의 배경으로는 JAXB 2.0의 등장에 있으며, JAXB 2.2부터는 모든 XML 스키마 타입을 완전히 지원하게 됨으로써 기존에 존재했던 Java 타입과 XML 타입 간의 매핑을 보다 명확하게 정의할 수 있게 되었다. 기존에 JAX-RPC 스펙에 존재하던 Java 타입과 XML 타입 간의 매핑에 대한 의존성을 제거할 수 있게한 원동력이 되었다.

또한 SOAP 1.2 메시지를 직접 다룰 수 있게 하는 SAAJ 1.3까지 포함하여, Java Web Services 2.0이라는 새로운 웹 서비스 모델이 등장하게 된다.

1.1. From Java 방식

From Java 방식의 웹 서비스 생성은 기본적으로 다음과 같은 절차를 따른다.

  1. 웹 서비스 Annotation이 포함된 서비스 구현 Bean의 작성

    서비스 구현 Bean을 작성할 경우에는 다음과 같은 몇 가지의 필수 제약 조건을 따르게 된다.

    • jakarta.jws.WebService Annotation을 포함시켜서 이 클래스가 서비스 구현 Bean임을 명시한다.

    • 웹 서비스 메소드의 인자와 리턴 타입은 JAXB 2.0의 Java와 XML 스키마 간의 매핑 정의와 호환되어야 한다.

    • 웹 서비스 메소드의 인자와 리턴 타입은 java.rmi.Remote 인터페이스를 직접 또는 간접적으로 구현해서는 안 된다.

    이와 같은 요소들 외에도, jakarta.jws 패키지에 정의되어 있는 여러 Annotation을 활용하면 메소드의 인자와 리턴 타입, 바인딩 방식 등의 커스터마이징이 가능하다. 간단한 예제 코드를 통해 실제 서비스 구현 Bean의 작성 방법을 설명한다.

    다음은 간단한 웹 서비스를 구현한 클래스에 대한 예제로 코드는 다음의 경로에서 찾아볼 수 있다.

    JEUS_HOME/samples/getting_started/webservices/from_java/src/java/fromjava/server
    웹 서비스 예제(From Java 방식) : <AddNumbersImpl.java>
    package fromjava.server;
    
    import jakarta.jws.WebService;
    
    @WebService
    public class AddNumbersImpl {
        public int addNumbers(int number1, int number2) {
            return number1 + number2;
        }
    }

    JAX-WS에서는 기존 Java 클래스의 웹 서비스로의 공개가 아주 용이하며, 위 <AddNumbersImpl.java> 에서 보는 바와 같이 @WebService Annotation을 추가함으로써 기존의 Java 클래스를 쉽게 웹 서비스로 변환하여 만들 수 있다.

  2. 타 벤더 간에 이식 가능한 산출물(Portable Artifact)의 생성

    서비스 구현 Bean을 작성하고 컴파일까지 수행했다면, JAX-WS 런타임에서 사용할 타 벤더 간에 이식 가능한 산출물의 생성 작업이 필요하다. 여기서 타 벤더 간에 이식 가능한 산출물은 JAX-WS 스펙을 준수하는 모든 벤더에서 사용할 수 있는 JAX-WS 툴을 통해 만들어낸 산출물임을 의미한다. Java의 파라미터를 실제 WSDL의 메시지로 정확하게 매핑하기 위한 정보 등을 담고 있는 Java 클래스와 WSDL 등이 여기에 포함된다.

    JEUS에서는 wsgen이라는 콘솔 스크립트를 제공하며, 이 스크립트는 JEUS_HOME/bin 디렉터리 하위에 존재한다.

    wsgen –cp <classpath> -d <destination_dir> fromjava.server.AddNumbersImpl

    위와 같이 명령어를 실행하면 지정한 경로에 이식 가능한 산출물이 생성됨을 확인할 수 있다. 위 스크립트를 실행할 때 –wsdl 옵션을 설정하면 WSDL까지 생성이 가능하나 JAX-WS에서는 웹 서비스 Endpoint에 WSDL을 포함하지 않아도 되므로 여기서 –wsdl 옵션을 설정하지 않도록 주의한다. 다음은 이 산출물들을 묶어서 서버에 배치하는 작업을 진행한다.

  3. 웹 서비스 패키징과 배치

    현재 작성하고 있는 웹 서비스를 패키징한다는 것은 서비스 구현 Bean과 서비스 구현 Bean이 참조하고 있는 Java 클래스와 부수적인 DD를 WAR 형식으로 묶는 것을 의미한다. 여기서는 이미 앞에서 작성했던 파일인 fromjava.server.AddNumbersImpl 클래스와 wsgen 스크립트를 통해 생성하였던 fromjava.server.jaxws.AddNumbers, fromjava.server.jaxws.AddNumbersResponse 클래스만을 포함한다. 이 클래스들은 WEB-INF/classes 하위에 포함시키면 된다. 패키징할 파일 이름을 AddNumbers.war로 하여 WAR 패키징을 한 다음, JEUS에 배치한다.

    실제 이 서비스를 호출할 수 있는 HTTP 주소는 다음과 같다.

    http://localhost:8088/AddNumbers/addnumbers

    해당 주소로 웹 브라우저에서 호출하면 다음과 같이 성공적으로 웹 서비스가 배치되어 있는 것을 확인할 수 있다.

    figure webservice
    성공적으로 배치된 From Java 방식의 웹 서비스
샘플 예제 쉽게 실행하기

JEUS에서는 웹 서비스 예제를 쉽게 실행해 볼 수 있는 방법을 제공한다.

JEUS_HOME/samples/getting_started/webservices/from_java/ 아래에서 다음과 같이 jant 명령어를 실행하면 서비스 패키징부터 클라이언트 실행까지 쉽게 할 수 있다.

%JEUS_HOME%/samples/getting_started/webservices/from_java> jant

빌드가 정상적으로 되면 웹 브라우저가 열리면서 결과를 확인할 수 있다.

1.2. From WSDL 방식

From Java 방식의 웹 서비스 방식이 이미 작성한 Java RPC 모델을 웹 서비스로 공개하는 것이 초점이라면 From WSDL 방식의 웹 서비스는 서로 간에 통신할 SOAP 메시지를 먼저 정의하고 WSDL를 통해 그 정보를 공유한 다음, 그 정의된 메시지 타입에 맞게 Java 클래스를 생성하는 것이 초점이라 할 수 있다.

일반적인 From WSDL 방식의 웹 서비스 생성은 다음과 같은 과정으로 진행된다.

  1. 서비스 Endpoint 인터페이스(Service Endpoint Interface)의 생성

    이 과정에서는 이미 공개되어 있는 WSDL을 기반으로 웹 서비스의 Java 인터페이스 파일과 Java 클래스 파일들을 생성한다. 이때 JEUS에서 제공하는 wsimport라는 콘솔 스크립트를 사용하고, 그 스크립트는 JEUS_HOME/bin이라는 위치에 존재한다.

    JEUS_HOME/samples/getting_started/webservices/from_wsdl 디렉터리에서 다음과 같이 실행한다.

    wsimport -keep -p fromwsdl.server -d ./build/classes  ./web/WEB-INF/wsdl/AddNumbers.wsdl

    위와 같이 명령어를 실행하면, 지정한 경로에 서비스 Endpoint 인터페이스와 서비스 정의 클래스를 포함한 여러 산출물들이 생성된다.

    이 중 생성된 서비스 Endpoint 인터페이스는 다음과 같다. 이렇게 생성된 서비스 Endpoint 인터페이스는 JAX-WS 런타임에 사용될 정보들을 Annotation 형태로 포함하고 있다.

    웹 서비스 예제(From WSDL 방식) : <AddNumbersImpl.java>
    package fromwsdl.server;
    
    @jakarta.jws.WebService(endpointInterface = "fromwsdl.server.AddNumbersPortType",
    wsdlLocation = "WEB-INF/wsdl/AddNumbers.wsdl",
    targetNamespace = "urn:AddNumbers", serviceName = "AddNumbersService",
    portName = "AddNumbersPort")
    public class AddNumbersImpl {
    
        public int addNumbers(int number1, int number2) {
            return number1 + number2;
        }
    }
  2. 서비스 Endpoint 인터페이스의 구현

    서비스 Endpoint 인터페이스가 생성이 되었다면, 이 Endpoint 인터페이스를 구현하는 실제 비즈니스 로직을 가지고 있는 서비스 구현 Bean을 작성한다. 서비스 Endpoint 인터페이스를 구현한 서비스 구현 Bean을 작성할 경우에는 @jakarta.jws.WebService Annotation을 추가해야 하며, 이 Annotation은 서비스 Endpoint 인터페이스를 명시한 endpointInterface 멤버를 속성으로 가지고 있어야 한다.

  3. 웹 서비스 패키징과 배치

    웹 서비스로 공개하기 위해 WAR 형태로 패키징하는 작업은 이전의 From Java 방식의 웹 서비스의 패키징 및 배치 작업과 유사하다.

    wsimport 스크립트를 통해 생성한 서비스 Endpoint 인터페이스를 포함한 몇 가지의 산출물들과 서비스 구현 Bean을 WEB-INF/classes에 위치시키고 WAR 형태로 패키징한다. 이때의 WAR 패키지의 이름을 AddNumbers.war라고 할 경우 이를 JEUS에 배치하게 되면 다음과 같은 주소로 접근할 수 있다. From Java 방식으로 이미 배치하였다면 컨텍스트 이름이 동일하므로 이를 제거한 후 다시 배치해야 한다.

    http://localhost:8088/AddNumbers/addnumbers

    실제 이 주소로 웹 브라우저에서 호출하면 다음과 같이 성공적으로 웹 서비스가 배치되어 있는 것을 확인할 수 있다.

    figure webservice1
    성공적으로 배치된 From WSDL 방식의 웹 서비스
샘플 예제 쉽게 실행하기

JEUS에서는 웹서비스 예제를 쉽게 실행해 볼 수 있는 방법을 제공한다.

JEUS_HOME/samples/getting_started/webservices/from_wsdl/ 아래에서 다음과 같이 jant 명령어를 실행하면 서비스 패키징부터 클라이언트 실행까지 쉽게 할 수 있다.

%JEUS_HOME%/samples/getting_started/webservices/from_wsdl> jant

빌드가 정상적으로 되면 웹 브라우저가 열리면서 결과를 확인할 수 있다.

2. 웹 서비스 클라이언트 작성

웹 서비스에 접근하기 위해서는 웹 서비스 클라이언트를 작성해야 하는데 웹 서비스 클라이언트 프로그램의 구동 환경에 따라 Java SE 클라이언트와 Jakarta EE 8 클라이언트로 구분할 수 있다.

본 절에서는 일반 Java 프로그램과 동일하게 구동되는 Java SE 클라이언트에 대해서만 다루도록 하겠다.

2.1. Java SE 클라이언트의 작성

JAX-WS에서는 기본적으로 웹 서비스 Endpoint에 대응하는 동적 프록시를 내부적으로 생성하여 마치 이를 서비스 Endpoint 인터페이스의 구현체인 것처럼 사용할 수 있다. 따라서 클라이언트 프로그램 개발자는 생성된 프록시를 통해 서비스 Endpoint 인터페이스로 정의된 웹 서비스 메소드를 호출할 수 있다.

이와 같은 작업을 진행하기 위해서는 서비스 Endpoint 인터페이스와 같은 몇 가지의 산출물들이 필요한데, 이때 필요한 산출물들을 WSDL로부터 생성하기 위해서는 From WSDL 방식의 웹 서비스 생성에서와 마찬가지로 wsimport 툴을 사용해야 한다.

다음은 wsimport 사용 예로 JEUS_HOME/samples/getting_started/webservices/from_wsdl 디렉터리에서 실행한다.

wsimport –p fromwsdl.client –d ./build/classes http://localhost:8088/AddNumbers/addnumbers?wsdl

서비스 Endpoint 인터페이스와 서비스 클래스가 생성이 되었다면, 이를 사용하는 클라이언트 Java 프로그램을 작성한다.

다음은 웹 서비스 클라이언트 프로그래밍의 한 예를 보여준다. 이 부분은 기존 JAX-RPC에서 제공하던 웹 서비스 클라이언트 프로그래밍 예와 비슷하다. 기본적으로 서비스 인스턴스를 생성한 다음, 그 서비스 인스턴스로부터 서비스 Endpoint 인터페이스를 구현한 프록시 객체를 얻어오는 것이 주요 작업이다. 클라이언트 런타임은 내부적으로는 많이 달라졌지만, Java SE 클라이언트에서는 유사하다고 할 수 있다.

예제 코드는 JEUS_HOME/samples/getting_started/webservices/from_wsdl/src/java/fromwsdl/client에서 찾아볼 수 있다.

Java SE 클라이언트 : <AddNumbersClient.java>
package fromwsdl.client;

public class AddNumbersClient {

    public static void main(String[] args) {
        AddNumbersPortType port = new AddNumbersService().getAddNumbersPort();

        int number1 = 10;
        int number2 = 20;

        System.out.println("##############################################");
        System.out.println("### JAX-WS Webservices examples - fromwsdl ###");
        System.out.println("##############################################");
        System.out.println("Testing Java class webservices from WSDL...");
        int result = port.addNumbers(number1, number2);
        if (result == 30) {
            System.out.println("Success!");
        }
    }
}

위와 같이 클라이언트 프로그램을 작성한 후 실제 이를 컴파일하여 실행하면 다음과 같은 결과가 나타난다.

[java] ##############################################
[java] ### JAX-WS Webservices examples - fromwsdl ###
[java] ##############################################
[java] Testing Java class webservices from WSDL...
[java] Success!