내용 목차
본 장에서는 JAX-WS 2.0을 활용한 웹 서비스의 생성과 클라이언트의 작성 그리고 웹 서비스를 호출하는 내용을 설명한다.
JEUS 6에서는 Java EE 5 방식의 웹 서비스를 지원하며 JAX-WS 2.0은 Java EE 5 웹 서비스의 핵심이다.
JAX-WS 2.0은 기존 JAX-RPC를 대체하는 수단으로 설계되었다. 새로운 모습으로 등장한 JAX-WS의 배경으로는 JAXB 2.0의 등장이 있으며, JAXB 2.0은 모든 XML 스키마 타입을 완전히 지원하게 됨으로써 기존에 존재했던 Java 타입과 XML 타입간의 매핑을 보다 명확하게 정의할 수 있게 되었다. 기존에 JAX-RPC 스펙에 존재하던 Java 타입과 XML 타입 간의 매핑에 대한 의존성을 제거할 수 있게한 원동력이 되었다. 여기에다 SOAP 1.2 메시지를 직접 다룰 수 있게 하는 SAAJ 1.3까지 포함하여, Java Web Services 2.0이라는 새로운 웹 서비스 모델이 등장하게 된다.
From Java 방식의 웹 서비스 생성은 기본적으로 다음과 같은 절차를 따른다.
웹 서비스 Annotation이 포함된 서비스 구현 Bean의 작성
서비스 구현 Bean을 작성할 경우에는 다음과 같은 몇 가지의 필수 제약 조건을 따르게 된다.
javax.jws.WebService Annotation을 포함시켜서 이 클래스가 서비스 구현 Bean임을 명시한다.
웹 서비스 메소드의 인자와 리턴 타입은 JAXB 2.0의 Java와 XML 스키마 간의 매핑 정의와 호환되어야 한다.
웹 서비스 메소드의 인자와 리턴 타입은 java.rmi.Remote 인터페이스를 직접 혹은 간접적으로 구현하여서는 안 된다.
이와 같은 요소들 말고도, javax.jws 패키지에 정의되어 있는 여러 Annotation을 활용하면 메소드의 인자와 리턴 타입, 바인딩 방식 등의 커스터마이징이 가능하다. 그럼 간단한 예제 코드를 통해 실제 서비스 구현 Bean이 어떻게 작성되어야 하는지 살펴 보도록 하자. 다음은 간단한 웹 서비스를 구현한 클래스를 보여준다.
예제 코드는 "JEUS_HOME\samples\getting_started\webservices\from_java\src\java\fromjava\server"에서 찾아볼 수 있다.
[예 11.1] <<AddNumbersImpl.java>>
package fromjava.server; import javax.jws.WebService; @WebService public class AddNumbersImpl { public int addNumbers(int number1, int number2) { return number1 + number2; } }
JAX-WS 2.0에서는 기존 Java 클래스의 웹 서비스로의 공개가 아주 용이하며, 위 <리스트 1>에서 보는 바와 같이 @WebService Annotation을 추가함으로써 기존의 Java 클래스를 쉽게 웹 서비스로 변환하여 만들 수 있다.
다른 벤더 간에 이식 가능한 산출물(Portable Artifact)의 생성
서비스 구현 Bean을 작성하고 컴파일 까지 수행하였다면, JAX-WS 런타임에서 사용할 다른 벤더 간에 이식 가능한 산출물의 생성 작업이 필요하다. 여기서 다른 벤더 간에 이식 가능한 산출물은 JAX-WS 스펙을 준수하는 모든 벤더에서 사용할 수 있는 JAX-WS 툴을 통해 만들어낸 산출물임을 의미ㅎ나다. Java의 파라미터를 실제 WSDL의 메시지로 정확하게 매핑하기 위한 정보 등을 담고 있는 Java 클래스와 WSDL등이 여기에 포함된다. JEUS 6에서는 wsgen이라는 콘솔 스크립트를 제공하며, 이 스크립트는 JEUS_HOME/bin 디렉터리 하위에 존재한다.
wsgen –cp <classpath> -d <destination_dir> fromjava.server.AddNumbersImpl
위와 같이 명령을 실행하면 지정한 경로에 이식 가능한 산출물이 생성됨을 확인 할 수 있다. 위 스크립트를 실행할 때 –wsdl 옵션을 설정하면 WSDL까지 생성이 가능하나 JAX-WS 2.0에서는 웹 서비스 Endpoint에 WSDL을 포함하지 않아도 되므로 여기서 –wsdl 옵션을 설정하지 않도록 주의한다. 다음은 이 산출물들을 묶어서 서버에 배치하는 작업을 진행한다.
웹 서비스 패키징과 배치
현재 작성하고 있는 웹 서비스를 패키징 한다는 것은 서비스 구현 Bean과 서비스 구현 Bean이 참조하고 있는 Java 클래스와 부수적인 배치 서술자들을 WAR 형식으로 묶는 것을 의미한다. 여기서는 이미 앞에서 작성했던 파일인 fromjava.server.AddNumbersImpl 클래스와 wsgen 스크립트를 통해 생성하였던 fromjava.server.jaxws.AddNumbers, fromjava.server.jaxws.AddNumbersResponse 클래스만을 포함한다. 이 클래스들은 WEB-INF/classes 하위에 포함시키면 된다. 패키징 할 파일 이름을 AddNumbers.war로 하여 WAR 패키징을 한 다음, JEUS 6에 배치하려면, JEUS_HOME/webhome/autodeploy에 WAR 모듈을 복사해 두기만 하면 된다.
그러면 실제 이 서비스를 호출 할 수 있는 HTTP 주소는 다음과 같다.
http://localhost:8088/AddNumbers/addnumbers
실제 이 주소로 웹 브라우저에서 호출하면 다음과 같이 성공적으로 웹 서비스가 배치되어 있음을 확인할 수 있다.
JEUS 6에서는 웹서비스 예제를 쉽게 실행해 볼수 있는 방법을 제공한다.
다음과 같이 JEUS_HOME\samples\getting_started\webservices\from_java\ 아래에서 다음과 같이 실행하면 서비스 패키징부터 클라이언트 실행까지 쉽게 할 수 있다.
[예 11.2] 샘플 예제 실행
Buildfile: build.xml -pre-init: init: build_server: do-compile: [echo] Compiling AddNumbers... wsgen: setOSConditions: setToolUnix: setToolWindows: setTools: do-wsgen: [echo] wsgen -cp ./build/classes -keep -d ./build/classes fromjava.server.Add NumbersImpl do-package-war: [copy] Copying 16 files to D:\TmaxSoft\jeus6\samples\getting_started\web services\from_java\build\web\WEB-INF\classes [jar] Building jar: D:\TmaxSoft\jeus6\samples\getting_started\webservic es\from_java\dist\AddNumbers.war build: -pre-deploy: deploy: do-deploy: [echo] Deploying ./dist/AddNumbers.war... setOSConditions: setToolUnix: setToolWindows: setTools: do-deploy-cmd: [echo] jeusadmin example -Ujeus -Pjeus deploy -absolute-path ./dist/AddNumber s.war as AddNumbers [exec] using the following application info : [exec] <applicationType> [exec] <deploy-when-booting>true</deploy-when-booting> [exec] <name>AddNumbers</name> [exec] <path>D:\TmaxSoft\jeus6\samples\getting_started\webservices\ from_java\.\dist\AddNumbers.war</path> [exec] <deployment-type>COMPONENT</deployment-type> [exec] <web-component> [exec] </web-component> [exec] <deployment-target> [exec] <all-targets> [exec] </all-targets> [exec] </deployment-target> [exec] <class-ftp-unit>JAR</class-ftp-unit> [exec] </applicationType> [exec] The WebModule, AddNumbers, is deployed on example_container1 do-deploy-success: getBrowser: launch: [echo] [echo] Trying to launch the browser with the url [echo] http://localhost:8088/AddNumbers/addnumbers [echo] [echo] If the above does not work, you may open a browser and copy and paste the above URL. [echo] build_client: wsimport: setOSConditions: setToolUnix: setToolWindows: setTools: do-wsimport: [echo] wsimport -keep -p fromjava.client -d ./build/classes http://localhost :8088/AddNumbers/addnumbers?wsdl [exec] parsing WSDL... [exec] generating code... do-compile: [echo] Compiling AddNumbers... [javac] Compiling 1 source file to D:\TmaxSoft\jeus6\samples\getting_started\ webservices\from_java\build\classes run: [java] ############################################## [java] ### JAX-WS Webservices examples - fromjava ### [java] ############################################## [java] Testing Java class webservices... [java] Success! all: BUILD SUCCESSFUL Total time: 32 seconds
From Java 방식의 웹 서비스 방식이 이미 작성한 Java RPC 모델을 웹 서비스로 공개하는 것이 초점이라면 From WSDL 방식의 웹 서비스는 서로 간에 통신할 SOAP 메시지를 먼저 정의하고 WSDL를 통해 그 정보를 공유한 다음, 그 정의된 메시지 타입에 맞도록 Java 클래스를 생성하는 것이 초점이라 할 수 있겠다.
일반적인 From WSDL 방식의 웹 서비스 생성은 다음과 같은 과정으로 진행된다.
서비스 Endpoint 인터페이스(Service Endpoint Interface)의 생성
이 과정에서는 이미 공개되어 있는 WSDL을 기반으로 웹 서비스의 Java 인터페이스 파일과 Java 클래스 파일들을 생성한다. 이때 JEUS 6에서 제공하는 wsimport 스크립트를 사용하고, 그 스크립트는 JEUS_HOME/bin이라는 위치에 존재한다.
다음과 같이 "JEUS_HOME\samples\getting_started\webservices\from_wsdl" 디렉터리에서 다음과 같이 실행한다.
wsimport -keep -p fromwsdl.server -d ./build/classes ./web/WEB-INF/wsdl/AddNumber s.wsdl
위와 같이 명령을 실행하면, 지정한 경로에 서비스 Endpoint 인터페이스와 서비스 정의 클래스를 포함한 여러 산출물들이 생성된다.
이 중 생성된 서비스 Endpoint 인터페이스는 다음과 같다. 이렇게 생성된 서비스 Endpoint 인터페이스는 JAX-WS 2.0 런타임에 사용될 정보들을 Annotation 형태로 포함하고 있다.
[예 11.3] <<AddNumbersImpl.java>>
package fromwsdl.server; @javax.jws.WebService(endpointInterface = "fromwsdl.server.AddNumbersPortType", ws dlLocation = "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; } }
서비스 Endpoint 인터페이스의 구현
서비스 Endpoint 인터페이스가 생성이 되었다면, 이 Endpoint 인터페이스를 구현하는 실제 비즈니스 로직을 가지고 있는 서비스 구현 Bean을 작성한다. 서비스 Endpoint 인터페이스를 구현한 서비스 구현 Bean을 작성할 경우에는 @WebService Annotation을 추가해야 하며, 이 Annotation은 서비스 Endpoint 인터페이스를 명시한 endpointInterface 멤버를 속성으로 가지고 있어야 한다.
웹 서비스 패키징과 배치
웹 서비스로 공개하기 위해 WAR 형태로 패키징 하는 작업은 이전의 From Java 방식의 웹 서비스의 패키징 및 배치 작업과 유사하다.
wsimport 스크립트를 통해 생성한 서비스 Endpoint 인터페이스를 포함한 몇 가지의 산출물들과 서비스 구현 Bean을 WEB-INF/classes에 위치시키고 WAR 형태로 패키징한다. 이 때의 WAR 패키지의 이름을 AddNumbers.war라고 하였을 경우 이를 JEUS 6에 배치하게 되면 다음과 같은 주소로 접근할 수 있다(From Java 방식으로 이미 배치하였다면, context 이름이 동일 하므로 이를 제거한 후 다시 배치해야 한다).
http://localhost:8088/AddNumbers/addnumbers
실제 이 주소로 웹 브라우저에서 호출하면 다음과 같이 성공적으로 웹 서비스가 배치되어 있음을 확인할 수 있다.
웹 서비스에 접근하기 위해서는 웹 서비스 클라이언트를 작성해야 하는데 웹 서비스 클라이언트 프로그램의 구동 환경에 따라 Java SE 클라이언트와 Java EE 클라이언트로 구분할 수 있다.
본 절에서는 일반 Java 프로그램과 동일하게 구동되는 Java SE 클라이언트에 대해서만 다루도록 하겠다.
JAX-WS 2.0에서는 기본적으로 웹 서비스 Endpoint에 대응하는 동적 프록시를 내부적으로 생성하여 마치 이를 서비스 Endpoint 인터페이스의 구현체인 것처럼 사용할 수 있다. 따라서 클라이언트 프로그램 개발자는 생성된 프록시를 통해 서비스 Endpoint 인터페이스로 정의된 웹 서비스 메소드를 호출 할 수 있다.
이와 같은 작업을 진행하기 위해서는 서비스 Endpoint 인터페이스와 같은 몇 가지의 산출물들이 필요한데, 이때 필요한 산출물들을 WSDL로부터 생성하기 위해서는 From WSDL 방식의 웹 서비스 생성에서와 마찬가지로 wsimport라는 스크립트 툴을 사용하여야 한다.
다음은 wsimport 사용 예이다. JEUS_HOME\samples\getting_started\webservices\from_wsdl 디렉터리에서 다음과 같이 실행한다.
wsimport –p fromjava.client –d ./build/classes http://localhost:8088/AddNumbers/ad dnumbers?wsdl
서비스 Endpoint 인터페이스와 서비스 클래스가 생성이 되었다면, 이를 사용하는 클라이언트 Java 프로그램을 작성한다.
다음은 웹 서비스 클라이언트 프로그래밍의 한 예를 보여준다. 이 부분은 기존 JAX-RPC에서 제공하던 웹 서비스 클라이언트 프로그래밍 예와 흡사하다. 기본적으로 서비스 인스턴스를 생성한 다음, 그 서비스 인스턴스로부터 서비스 Endpoint 인터페이스를 구현한 프록시 객체를 얻어오는 것이 주요 작업이다. 클라이언트 런타임 내부적으로는 많이 달라졌지만, Java SE 클라이언트에서는 유사하다고 할 수 있다.
예제코드는 JEUS_HOME\samples\getting_started\webservices\from_wsdl\src\java\fromwsdl\client에서 찾아볼 수 있다.
[예 11.4] <<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!