내용 목차
본 장에서는 JAX-RPC 웹 서비스의 표준 웹 서비스 Deployment Descriptor 작성, JAX-RPC 매핑 파일 작성에 대해 설명한다.
JavaEE JAX-RPC 웹 서비스는 webservices.xml이라는 이름을 가진 웹 서비스 서술자를 Java 클래스 Endpoint나 EJB Endpoint를 포함하는 압축 파일(WAR나 JAR)에 포함할 것을 요구한다. webservices.xml을 EJB Endpoint의 경우에는 EJB JAR 파일의 META-INF 디렉터리에, 서블릿 Endpoint의 경우에는 WAR 파일의 WEB-INF 디렉터리에 위치시킨다.
다음은 FileAttachmentService라는 웹 서비스의 webservices.xml 파일이다.
[예 24.1] << webservices.xml >>
<?xml version="1.0"?> <webservices version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee"> <webservice-description> <webservice-description-name> FileAttachmentService </webservice-description-name> <wsdl-file> WEB-INF/wsdl/FileAttachmentService.wsdl </wsdl-file> <jaxrpc-mapping-file> WEB-INF/FileAttachmentService-mapping.xml </jaxrpc-mapping-file> <port-component> <port-component-name>FileAttPort</port-component-name> <wsdl-port xmlns:ns2="urn:FileAttachmentService"> ns2:FileTransferIFPort </wsdl-port> <service-endpoint-interface> filetransfer.FileTransferIF </service-endpoint-interface> <service-impl-bean> <servlet-link>FileAttachmentServlet</servlet-link> </service-impl-bean> <handler> <handler-name>ServerAttachmentHandler</handler-name> <handler-class> filetransfer.ServerAttachmentHandler </handler-class> <init-param> <param-name>directory</param-name> <param-value>/temp</param-value> </init-param> </handler> </port-component> </webservice-description> </webservices>
webservices.xml 파일의 root element는 webservices element이고,
webservice-description element를 하나 혹은 그 이상을 필수 요소로 가진다.
<webservice-description> element는 동일한 WSDL을 사용하는 Java 클래스나 EJB Endpoint의 집합을 서술한다. 즉 패키징 내의 다른 WSDL 각각의 파일 들에 대해서 webservice-description element가 하나씩 존재해야 한다. 예를 들면, 2개의 다른 Java 클래스 Endpoint가 각각 WSDL을 별도로 가지고 하나의 WAR 파일 안에 존재한다면 각각의 JSE를 서술하기 위해서 webservice-description element는 2개가 존재해야 한다. <webservice-description> element는 JavaEE Endpoint를 WSDL 포트 정의, DD 구현, JAX-RPC 매핑 파일, 그리고 Endpoint 인터페이스로 바인딩시켜주는 역할을 하며, 본 장에서는 이들의 관계와 필요성에 대해서 설명한다.
<wsdl-file>
<wsdl-file> element는 WSDL 문서의 WAR나 EJB JAR 파일 내에서의 상대적인 위치를 나타내며, webservices.xml 파일은 WSDL 파일과 같은 WAR나 JAR 파일 내에 위치해야 한다.
<jaxrpc-mapping-file>
<jaxrpc-mapping-file> element는 JAX-RPC 매핑 파일의 위치를 지정한다. JAX-RPC 매핑 파일은 WSDL 파일과 JavaEE Endpoint 간의 매핑을 정의한다. JAX-RPC 매핑 파일에 관한 더 자세한 설명은 “24.2. 웹 서비스의 매핑 파일 작성”을 참조한다.
<port-component>
<port-component> element는 특정한 Java 클래스나 EJB Endpoint를 WSDL 문서 내의 특정한 port element로 매핑하는 역할을 한다. Java 클래스의 경우에 WSDL port 정의와 binding 정의는 웹 서비스를 호스트하기 위해 필요한 서블릿을 생성하는 데 사용된다.
EJB Endpoint의 경우에는 port와 binding 정의는 EJB 컨테이너가 SOAP 메시지를 무상태 빈 객체에 보내기 위해 마셜링하는 데 사용된다.
<port-component-name>
<port-component-name> element는 특정한 Java 클래스나 EJB Endpoint를 지정하기 위한 이름을 제공한다. 이 이름은 webservices.xml 파일 안에서 유일해야 한다.
<service-endpoint-interface>
Java 클래스를 백엔드로 가지는 웹 서비스의 경우, 서비스 Endpoint 인터페이스의 이름을 지정하는 element이다.
EJB Endpoint 웹 서비스의 경우에도 마찬가지로 이 element에서 정의하며, 추가적으로 ejb-jar.xml의 service-endpoint에서도 같은 이름으로 정의해야 한다.
<service-impl-bean>
<service-impl-bean> element는 서비스 배치가 수행되는 시점에 어떤 서비스의 로직 구현 정의가 JavaEE Endpoint가 될 것인지를 정의한다. Java 클래스의 경우에는 web.xml의 servlet 정의를 가리키고, EJB Endpoint의 경우에는 ejb-jar.xml의 session 정의를 가리킨다.
JSE의 경우에는 <servlet-link> element에 정의되며 예는 다음과 같다.
[예 24.2] DocLitEchoService : <<webservices.xml >>
<?xml version="1.0"?>
<webservices …>
<webservice-description>
<webservice-description-name>
DocLitEchoService
</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/DocLitEchoService.wsdl</wsdl-file>
<jaxrpc-mapping-file>
WEB-INF/DocLitEchoService-mapping.xml
</jaxrpc-mapping-file>
<port-component>
<port-component-name>EchoPort</port-component-name>
<wsdl-port xmlns:ns2="urn:DocLitService">
ns2:EchoPort
</wsdl-port>
<service-endpoint-interface>
jeustest.webservices.java2wsdl.doclit.Echo
</service-endpoint-interface>
<service-impl-bean>
<servlet-link>EchoServlet</servlet-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>
<serlvet-link> element의 값은 web.xml 내의 servlet-name의 값과 일치해야 한다.
[예 24.3] DocLitEchoService : << web.xml >>
<?xml version="1.0"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<servlet>
<servlet-name>EchoServlet</servlet-name>
<servlet-class>
jeustest.webservices.java2wsdl.doclit.EchoImpl
</servlet-class>
. . .
</servlet>
</web-app>
위와 마찬가지로 EJB Endpoint 웹 서비스의 경우 <ejb-link>
element를 사용하여 정의한다. 그 예는 다음과 같다.
[예 24.4] AddressBookService : <<webservices.xml >>
<?xml version="1.0"?>
<webservices …>
<webservice-description>
<webservice-description-name>
AddressBookService
</webservice-description-name>
<wsdl-file>META-INF/wsdl/AddressBookService.wsdl</wsdl-file>
<jaxrpc-mapping-file>
META-INF/AddressBookService-mapping.xml
</jaxrpc-mapping-file>
<port-component>
<port-component-name>
AddressBookIFPort
</port-component-name>
<wsdl-port xmlns:ns2="urn:AddressBookService">
ns2:AddressBookIFPort
</wsdl-port>
<service-endpoint-interface>
address.AddressBookIF
</service-endpoint-interface>
<service-impl-bean>
<ejb-link>AddressEJB</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>
<ejb-link> element의 값은 ejb-jar.xml 파일 내의
<ejb-name> element의 값과 일치해야 한다. 그 예는 다음과 같다.
[예 24.5] AddressBookService : << web.xml >>
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar …>
<display-name>AddressEJB</display-name>
<enterprise-beans>
<session>
<display-name>AddressEJB</display-name>
<ejb-name>AddressEJB</ejb-name>
<service-endpoint>
address.AddressBookIF</service-endpoint>
<ejb-class>address.AddressBookEJB</ejb-class>
. . .
</session>
</enterprise-beans>
</ejb-jar>
한 가지 유의해야 할 사항은, <servlet-link>나
<ejb-link>를 통해 연결되어지려는 대상들은 webservices.xml를 포함하는 압축 파일 안에
존재해야 한다는 것이다.
<handler>
메시지 핸들러는 JavaEE Endpoint가 보내고 받는 메시지를 걸러서 가공하는 역할을 한다. webservices.xml 파일 안에서 이 메시지 핸들러들을 설정하고, 처리 순서를 지정할 수 있다(메시지 핸들러에 대한 더 자세한 설명은 “제23장 JAX-RPC 웹 서비스의 SOAP 메시지 핸들러 생성”을 참조한다).
다음은 핸들러를 설정한 webservices.xml의 작성 예이다.
[예 24.6] FileAttachmentService : <<webservices.xml >>
<?xml version="1.0"?>
<webservices version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee">
<webservice-description>
. . .
<port-component>
<port-component-name>FileAttPort</port-component-name>
. . .
<handler>
<handler-name>ServerAttachmentHandler</handler-name>
<handler-class>
filetransfer.ServerAttachmentHandler
</handler-class>
<init-param>
<param-name>directory</param-name>
<param-value>/temp</param-value>
</init-param>
</handler>
</port-component>
</webservice-description>
</webservices>
다음은 <handler> element의 하위 항목에 대한 설명이다.
항목 | 설명 |
---|---|
<handler-name> | 여기 정의되는 값은 webservices.xml 파일 안에서 유일한 값이 되어야 하며, 설정한 핸들러에 대한 식별자 역할을 한다. |
<handler-class> | 핸들러를 구현한 클래스를 정의한다. 여기에 정의 되는 핸들러 클래스는 javax.xml.rpc.handler.Handler 인터페이스를 직접, 혹은 간접적으로 구현하여야 한다. |
<init-param> | 필수 사항은 아니며, 메시지 핸들러의 생성 주기의 시작 단계에서 핸들러를 초기화하기 위해 전달될 파라미터를 정의한다. 런타임 시에 HandlerInfo.getHandlerConfig() 메소드를 실행함으로써 얻어지는 java.util.Map 객체로부터 파라미터들을 가져 올 수 있다. |
<soap-header> | <soap-header> element는 핸들러가 처리해야 하는 SOAP 헤더 블록의 큐 네임(Q Name : Qualified Name)을 정의하며, handler element는 하나 이상의 soap-header element를 포함할 수 있다. |
<soap-role> | <soap-role>는 헤더 블록을 처리할 때 핸들러가 정하는 역할을 정의한다. soap-role 값이 soap-header element와 짝을 이루어서 정의될 때에만 어떤 헤더 블록을 핸들러가 처리하는지 정확하게 알 수 있다. SOAP 메시지는 각각의 헤더 블록을 처리하기 위한 역할을 명시적으로 지정할 수 있다. actor 속성이 정의되어 있으면 지정된 역할을 수행하는 노드만이 헤더 블록을 처리하고, actor 속성이 정의 되지 않은 경우에는 최종적인 메시지 수신자가 헤더 블록을 처리한다. |
JAX-RPC 매핑 파일은 JEUS 웹 서비스에 내장된 JAX-RPC 컴파일러가 WSDL 문서와 웹 서비스 Endpoint를 나타내는 Java 인터페이스의 관계를 이해하는 것을 도와준다. 많은 경우에 WSDL과 Java의 매핑은 매핑 파일이 없어도 큰 문제가 없지만, 명시적인 정의가 필요할 때가 있으며 , 그러한 필요에 의해서 JAX-RPC 매핑 파일이 도입되었다. JAX-RPC 매핑 파일은 JavaEE 웹 서비스 Endpoint나 JavaEE 웹 서비스 클라이언트를 사용할 때마다 필요하며, WSDL 문서와 JAX-RPC 매핑 파일은 일대일 대응한다. 즉, 각각의 WSDL 파일에는 하나씩의 JAX-RPC 매핑 파일이 존재한다.
JAX-RPC 매핑 파일은 다음과 같은 관계에 있는 요소들의 매핑을 정의한다.
XML 복합 타입(Complex Type)과 Java Bean
Fault 메시지와 예외 클래스
WSDL의 portType 정의와 서비스 Endpoint 인터페이스
WSDL의 service 정의와 서비스 인터페이스
JAX-RPC 매핑 파일에 정의되지 않은 것들은 WSDL과 XML의 Java로의 표준 매핑 법칙을 따라서 매핑이 이루어진다. 그리고 매핑 파일에서 정의 되는 것들은 항상 표준 매핑 법칙에 우선한다.
매핑 파일은 상당히 내용이 복잡해 보이고, 크기에 있어서도 단일 설정 파일 중에 큰 편이다.
전체적인 설정 파일의 구성은 다음과 같다.
[예 24.7] JAX-RPC 매핑 파일의 구조
<java-wsdl-mapping…> <package-mapping/> <java-xml-type-mapping/> <exception-mapping/> <service-interface-mapping/> <service-endpoint-interface-mapping> <service-endpoint-method-mapping/> <service-endpoint-method-mapping/> . . . </service-endpoint-interface-mapping> <service-interface-mapping/> <service-endpoint-interface-mapping> <service-endpoint-method-mapping/> <service-endpoint-method-mapping/> . . . </service-endpoint-interface-mapping> </java-wsdl-mapping>
<java-wsdl-mapping>
<java-wsdl-mapping> element는 JAX-RPC 매핑 파일의 최상위 element이며, 다른 매핑 element들을 포함하고 있다.
<package-mapping>
<package-mapping> element는 JAX-RPC 컴파일러가 WSDL에 정의된 여러 가지 타입에 대해 Java 클래스와 인터페이스 정의를 생성하려고 할 경우 사용된다. package-mapping 은 반드시 지정되어야 하는 항목이다. <package-type> element의 값은 Java 패키지의 이름이며 namespaceURI의 값은 지정된 Java 패키지로 매핑되어야 하는 XML 이름 공간이다.
다음은 그 예이다.
<java-wsdl-mapping version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee"> <package-mapping> <package-type>address</package-type> <namespaceURI>urn:AddressBookService</namespaceURI> </package-mapping> . . . </java-wsdl-mapping>
<java-xml-type-mapping>
<java-xml-type-mapping>는 XML Schema 복합 타입(Complex Type)이나 단순 타입(Simple Type)을 사용할 때 필요하며 XML 스키마에 정의된 built-in 타입이 Java로 표준적인 매핑이 된다면 이 element를 사용하지 않아도 된다. 이 element는 XML 스키마와 Java 타입 간의 관계를 정의한다.
다음은 그 예이다.
<java-wsdl-mapping version="1.1"
xmlns="http://java.sun.com/xml/ns/j2ee">
. . .
<java-xml-type-mapping>
<java-type>address.Address</java-type>
<ns1:root-type-qname
xmlns:ns1=”http://java.sun.com/xml/ns/j2ee”
xmlns="urn:AddressBookService">
Address
</ns1:root-type-qname>
<qname-scope>complexType</qname-scope>
<variable-mapping>
<java-variable-name>addr</java-variable-name>
<xml-element-name>addr</xml-element-name>
</variable-mapping>
<variable-mapping>
<java-variable-name>street</java-variable-name>
<xml-element-name>street</xml-element-name>
</variable-mapping>
<variable-mapping>
<java-variable-name>zipcode</java-variable-name>
<xml-element-name>zipcode</xml-element-name>
</variable-mapping>
</java-xml-type-mapping>
. . .
</java-wsdl-mapping>
<exception-mapping>
<exception-mapping>는 WSDL 결함(fault) 메시지를 Java 예외 클래스로 매핑한다. 기본적인 구조는 다음과 같다.
<java-xml-mapping…>
. . .
<exception-mapping>
<exception-type>CLASS_NAME</exception-type>
<wsdl-message>WSDL_MESSAGE_NAME</wsdl-message>
</exception-mapping>
. . .
</java-xml-mapping>
<service-interface-mapping>
<service-interface-mapping>는 WSDL의 service 정의를 JAX-RPC 서비스 인터페이스 타입으로 매핑한다. <service-interface> element는 WSDL의 service 정의를 나타내는 Java 인터페이스의 풀 패키지 클래스 이름을 정의하고, 서비스 인터페이스의 패키지 이름은 package-mapping element에 정의된 패키지 이름과 일치해야 한다.
<port-mapping> element는 서비스 인터페이스의 getPortName() 메소드의 포트 이름을 WSDL의 port element와 대응하게 정의한다.
<java-xml-mapping…>
. . .
<service-interface-mapping>
<service-interface>
address.AddressBookService
</service-interface>
<ns3:wsdl-service-name
xmlns:ns3="http://java.sun.com/xml/ns/j2ee"
xmlns="urn:AddressBookService">
AddressBookService
</ns3:wsdl-service-name>
<port-mapping>
<port-name>AddressBookIFPort</port-name>
<java-port-name>AddressBookIFPort</java-port-name>
</port-mapping>
</service-interface-mapping>
. . .
</java-xml-mapping>
위와 같이 정의될 경우, 생성되는 Java 서비스 인터페이스 정의는 다음과 같다.
[예 24.8] << AddressBookService.java >>
package address; public interface AddressBookService extends javax.xml.rpc.Service { public java.lang.String getAddressBookIFPortAddress(); public address.AddressBookIF getAddressBookIFPort() throws javax.xml.rpc.ServiceException; public address.AddressBookIF getAddressBookIFPort( java.net.URL portAddress) throws javax.xml.rpc.ServiceException; }
<service-endpoint-interface-mapping>
<service-endpoint-interface-mapping>는 서비스 Endpoint 인터페이스를 WSDL의 portType과 binding 정의로 매핑한다. 이 element는 JAX-RPC 컴파일러가 적절한 Endpoint Stub과 Endpoint 인터페이스를 생성할 때 필요한 정보를 제공한다. 또한 WSDL의 operation 과 message part 정의가 어떻게 Java Endpoint 메소드의 정의로 매핑될 것인지에 대한 상세 정보를 제공한다. 다음은 AddressBookService의 매핑 파일에 정의된 예이다.
<java-wsdl-mapping…>
. . .
<service-endpoint-interface-mapping>
<service-endpoint-interface>
address.AddressBookIF
</service-endpoint-interface>
<ns4:wsdl-port-type
xmlns:ns4="http://java.sun.com/xml/ns/j2ee"
xmlns="urn:AddressBookService">
AddressBookIF
</ns4:wsdl-port-type>
<ns5:wsdl-binding
xmlns:ns5="http://java.sun.com/xml/ns/j2ee"
xmlns="urn:AddressBookService">
AddressBookIFSoapBinding
</ns5:wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>add</java-method-name>
<wsdl-operation>add</wsdl-operation>
<wrapped-element/>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>address.PersonInfo</param-type>
<wsdl-message-mapping>
<ns6:wsdl-message
xmlns:ns6="http://java.sun.com/xml/ns/j2ee"
xmlns="urn:AddressBookService">
addRequest
</ns6:wsdl-message>
<wsdl-message-part-name>
parameters
</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-value>
address.PersonInfo[]
</method-return-value>
<ns7:wsdl-message
xmlns:ns7=”http://java.sun.com/xml/ns/j2ee“
xmlns="urn:AddressBookService">
addResponse
</ns7:wsdl-message>
<wsdl-message-part-name>
parameters
</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
. . .
</service-endpoint-interface-mapping>
. . .
</java-wsdl-mapping>