내용 목차
본 장에서는 MIME Attachment를 사용한 메시지 전송 방식에 대해서 설명한다.
웹 서비스를 구현하는 데 있어서 클라이언트 또는 서버로부터 웹 서비스의 파라미터 또는 리턴값은 SOAP 메시지의 Body 부분에 페이로드의 형태로 포함되어 있고 이 메시지가 네트워크를 통해 전송된다. 이때 그러한 파라미터, 리턴값이 사진 또는 음악 파일과 같은 크기가 큰 바이너리 데이터일 경우 그 데이터의 크기가 크면 클수록 효율은 떨어진다.
JAX-WS 웹 서비스는 MTOM(Message Transmission and Optimization Mechanism)과 XOP(XML Binary Optimized Packaging)를 통해 xs:base64Binary 또는 xs:hexBinary와 같은 element로 정의된 바이너리 데이터가 최적화되어 전송되도록 한다. 또한 swaRef라는 스키마를 지원하여 이 스키마로 정의된 XML element의 콘텐츠는 모두 MIME Attachment의 형태로 전송하도록 하는 기능을 제공한다.
JEUS 8 JAX-WS 웹 서비스는 MessageContext 프로퍼티를 사용하여 첨부 파일을 스트리밍 방식으로 송수신할 수 있다. 이 기능은 특히 대용량 첨부 파일을 수신할 때 유용하다.
MTOM은 XOP와 함께 XML binary data such as xs:base64Binary 또는 xs:hexBinary와 같은 XML 바이너리 데이터가 네트워크에 어떻게 최적화되어 전송될 수 있는지에 대해 정의하고 있다.
xs:base64Binary와 같은 XML 타입은 SOAP Envelope에 포함되어 전송되기 마련인데 이러한 타입의 데이터, 이미지나 음악과 같이 데이터의 크기가 커지면 커질수록 효율성은 크게 떨어진다. 이러한 문제를 해결하기 위해 MTOM은 이러한 바이너리 데이터의 크기가 어떤 정해진 값보다 클 경우 MIME Attachment의 형태로 XOP 패키징하여 메시지를 전송한다. 이렇게 xs:base64Binary 또는 xs:hexBinary 타입의 element 값이 MIME Attachment의 형태로 XOP 패키징될 때 그 MIME Attachment의 Content-Type은 xmime:expectedContentType 속성에 지정해줄 수 있고 이는 JAXB에서 지원하는 타입 매핑의 적용을 받는다.
xs:base64Binary 또는 xs:hexBinary 타입의 스키마 element는 xmime:expectedContentType 속성과 함께 사용했을 때 JAXB에서 지원하는 타입 매핑을 적용받아 이 element 값의 크기가 어떤 상한선을 넘게 되었을 때 다음과 같은 MIME 타입으로 MIME Attachment에 포함된다. xmime:expectedType 속성을 사용하지 않으면 보통의 byte[ ]로 매핑되고 이 element 값의 크기가 어떤 상한선을 넘게 되면 Content-Type에 일반적인 application/octet-stream와 같은 값으로 MIME Attachment에 포함된다.
이러한 xmime:expectedContentType의 Java 타입으로의 JAXB 타입 매핑은 다음과 같다.
MIME 타입 | Java 타입 |
---|---|
image/gif | java.awt.Image |
image/jpeg | java.awt.Image |
text/plain | java.lang.String |
text/xml or application/xml | javax.xml.transform.Source |
*/* | javax.activation.DataHandler |
따라서 <element name="image" type="base64Binary"/>와 같은 element는 순수하게 byte[ ] 타입으로 매핑되지만, <element name="image" type="base64Binary" xmime:expectedContentTypes="image/jpeg" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/>와 같은 element는 java.awt.Image 타입으로 매핑된다.
MTOM/XOP 환경을 이용할 수 있도록 WSDL 문서의 wsdl:type에 xs:base64Binary 또는 xs:hexBinary와 같은 타입으로 정의된 element가 xmime:expectedContentType 속성으로 정의된다. 정의된 속성은 wsimport 툴을 통해 JAXB의 특정 타입 매핑으로 서비스 인터페이스 및 Portable Artifact들이 생성되었을 때 클라이언트 및 서버에서는 MTOM/XOP 환경을 동작시킬 수 있다.
서버에서 MTOM을 동작시키기 위해서는 서비스 Endpoint 구현 클래스에 @javax.xml.ws.soap.MTOM Annotation을 추가한다.
@javax.xml.ws.soap.MTOM @WebService (endpointInterface = "com.tmax.mtom.Server") public class ServerImpl implements Server { ... }
다음과 같이 서비스 Endpoint 구현 클래스에 @BindingType Annotation을 추가시켜 MTOM을 동작시키는 방법도 있다.
@BindingType(value=javax.xml.ws.SOAPBinding.SOAP11HTTP_MTOM_BINDING) @BindingType(value=javax.xml.ws.SOAPBinding.SOAP12HTTP_MTOM_BINDING)
클라이언트에서 MTOM을 동작시키기 위해서는 서비스로부터 프록시(proxy) 또는 디스패치(dispatch) 객체를 javax.xml.ws.soap.MTOMFeature라는 파라미터를 통해 얻는 방법이다.
Server port = new ServerService().getServerPort(new MTOMFeature()); javax.xml.ws.Service.createDispatch(..., new javax.xml.ws.soap.MTOMFeature())
다음은 프록시 또는 디스패치가 MTOM이 설정되어있는지 확인하는 메소드이다.
Server port = new ServerService.getServerPort(); SOAPBinding binding = (SOAPBinding)((BindingProvider)port).getBinding(); boolean mtomEnabled = binding.isMTOMEnabled(); binding.setMTOMEnabled(true);
JAX-WS 웹 서비스는 xs:base64Binary 그리고 xs:hexBInary 타입의 element에 대한 Java 오브젝트에 대해서 그 크기가 1Kbyte가 넘으면 클라이언트 또는 서버로부터 전송할 때 MIME Attachment의 형태로 XOP 인코딩되어 메시지에 패키징된다. 그렇지 않으면 SOAP 메시지 안에 포함한다.
XOP 인코딩되어 메시지에 패키징될 때 원래의 element에는 <xop:Include href=...>와 같은 값이 설정되는데 이 element의 href 속성에 들어가는 값은 Attachment의 Content-Id 값이다. 또한 스키마의 xs:base64Binary, xs:hexBinary로 정의된 타입의 xmime:expectedContentTypes 속성의 값이 Attachment의 Content-Type 값으로 설정된다.
Attachment 형태로 취급될 바이너리 데이터의 크기를 설정하는 방법은 다음과 같다.
서버는 @MTOM Annotation로 설정한다.
@javax.xml.ws.soap.MTOM(threshold=3000)
@WebService (endpointInterface = "com.tmax.mtom.Server")
public class ServerImpl implements Server {
...
}
클라이언트에서는 MTOMFeature 클래스를 통해 설정한다.
Server port = new ServerService().getServerPort(new MTOMFeature(3000));
javax.xml.ws.Service.createDispatch(..., new javax.xml.ws.soap.MTOMFeature())
다음은 실제 WSDL의 wsdl:type의 스키마 예제이다.
<element name="Detail" type="types:DetailType"/> <complexType name="DetailType"> <sequence> <element name="Photo" type="base64Binary"/> <element name="image" type="base64Binary" xmime:expectedContentTypes="image/jpeg"/> </sequence> </complexType>
실제 WSDL의 wsdl:type 스키마가 위와 같은 모습일 경우에 네트워크에 전송되는 메시지는 다음과 같다.
Content-Type: Multipart/Related; start-info="text/xml"; type="application/xop+xml"; boundary="----=_Part_0_1744155.1118953559416" Content-Length: 3453 SOAPAction: "" ------=_Part_1_4558657.1118953559446 Content-Type: application/xop+xml; type="text/xml"; charset=utf-8 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <Detail xmlns="http://example.org/mtom/data"> <Photo>RHVrZQ==</Photo> <image> <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:5aeaa450-17f0-4484-b845-a8480c363444@example.org"> </xop:Include> </image> </Detail> </soapenv:Body> </soapenv:Envelope> ------=_Part_1_4558657.1118953559446 Content-Type: image/jpeg Content-ID: <5aeaa450-17f0-4484-b845-a8480c363444@example.org> ╪ α ►JFIF ☺☻ ☺ ☺ █ ♠♠♀¶♀♂♂♀↓↕‼☼¶↔→▼▲↔→∟∟ $.' ",#∟∟(7), 01444▼'9=82<.342 █ C☺ ♀♂♀↑↑2!∟!222222222222222222222222222222222 22222222222 222222 └ ) ¬♥☺" ☻◄☺♥◄☺ ─ ▼ ☺♣☺☺☺☺☺☺ ☺☻♥♦ ♂ ─ ╡► ☻☺♥♥☻♦♥♣♣♦♦ ☺}☺☻♥ ♦◄♣↕!1A♠‼Qa"q¶2?#B▒┴§R╤≡$3bré ▬ ↨↑↓→%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzâäàåçêëèÆôöòûùÿÖÜ óúñѪº¿⌐¬▓│┤╡╢╖╕╣║┬├─┼╞╟╚╔╩╥╙╘╒╓╫╪┘┌ßΓπΣσµτΦΘΩ± ≥≤⌠⌡÷≈°∙· ─
MTOM/XOP가 적용된 WSDL 파일의 예제는 다음과 같다.
[예 10.1] << hello.wsdl >>
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:types="http://tmaxsoft.com/mtom/data"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://tmaxsoft.com/mtom"
targetNamespace="http://tmaxsoft.com/mtom" name="mtom">
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://tmaxsoft.com/mtom/data"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
elementFormDefault="qualified">
<complexType name="DetailType">
<sequence>
<element name="image" type="base64Binary"
xmime:expectedContentTypes="image/jpeg" />
</sequence>
</complexType>
<element name="Detail" type="types:DetailType" />
<element name="DetailResponse" type="types:DetailType" />
</schema>
</wsdl:types>
<wsdl:message name="HelloIn">
<wsdl:part name="data" element="types:Detail" />
</wsdl:message>
<wsdl:message name="HelloOut">
<wsdl:part name="data" element="types:DetailResponse" />
</wsdl:message>
<wsdl:portType name="Hello">
<wsdl:operation name="Detail">
<wsdl:input message="tns:HelloIn" />
<wsdl:output message="tns:HelloOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloBinding" type="tns:Hello">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Detail">
<soap:operation />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloService">
<wsdl:port name="HelloPort" binding="tns:HelloBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
위와 같이 DetailType element의 스키마를 보면 순서대로 base64Binary 타입의 element인 image가 정의되어 있고 이는 MTOM에서 관리할 수 있다. 또한 image 타입은 xmime:expectedContentTypes="image/jpeg"으로 속성이 선언되어 있어 MTOM에서 Attachment로 다루는 경우 그 Content-Type이 image/jpeg가 된다.
본 절에서는 지금까지 구현한 클래스들 및 기타 설정 파일들을 사용해서 핸들러 프레임워크를 실행하는 방법에 대해서 설명한다.
다음과 같이 MTOM를 설정한 서비스를 생성하여 JEUS에 deploy한다.
$ ant deploy
위의 과정이 모두 실행되어 서비스가 정상적으로 deploy되면, 클라이언트를 빌드한다. 클라이언트에서 wsimport의 과정을 거치므로 서비스의 deploy가 모두 완료되었을 때 클라이언트의 구성이 가능하다.
다음과 같이 MTOM를 설정한 클라이언트를 생성하고 서비스를 호출한다. 콘솔에 명령어를 입력하면 클라이언트와 서비스가 정상적으로 메시지를 주고받는 것을 확인할 수 있다.
$ ant run ... Host: localhost:8088 Content-length: 4848 Content-type: multipart/related;type="application/xop+xml";boundary="uuid:23ba19 3c-bd1a-4323-abdd-339bf5c05cd1";start-info="text/xml" Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Soapaction: User-agent: JAX-WS RI 2.2 - JEUS 8 --uuid:23ba193c-bd1a-4323-abdd-339bf5c05cd1 Content-Type: application/xop+xml;charset=utf-8;type="text/xml" Content-Transfer-Encoding: binary <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><Detail xmlns="http://tmaxsoft.com/mtom/data"><image><Include xmln s="http://www.w3.org/2004/08/xop/include" href="cid:dc0fa852-3d46-4bac-9ad4-889d 59c6198a@example.jaxws.sun.com"/></image></Detail></S:Body></S:Envelope> --uuid:23ba193c-bd1a-4323-abdd-339bf5c05cd1 Content-Type: image/jpeg Content-Id: <dc0fa852-3d46-4bac-9ad4-889d59c6198a@example.jaxws.sun.com> Content-Transfer-Encoding: binary ??JFIF ...
JAX-WS 웹 서비스는 WSDL의 wsdl:type 안에 element를 wsi:swaRef라는 스키마 타입으로 정의하고 웹 서비스를 구성하면 전송되는 메시지는 element 값을 MIME Attachment의 형태로 포함시킨다.
실제 그 SOAP Body 메시지의 element 안에는 그 Attachment로의 레퍼런스를 취하는 형태를 지원한다. 이러한 wsi:swaRef 스키마의 element는 javax.activation.DataHandler의 형태의 Java 클래스로 매핑된다.
wsi:swaRef 타입의 XML element는 DataHandler Java 클래스로 매핑되며 실제 네트워크에서는 Attachment의 형태로 전송된다.
예를 들어 다음과 같은 XML 스키마가 WSDL에 정의되어 있다고 가정한다.
<element name="claimForm" type="wsi:swaRef" xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"/>
XML 스키마가 정의된 WSDL 문서를 통해 wsimport 툴으로 웹 서비스를 구성하여 네트워크로 메시지를 전송할 때 메시지는 다음과 같다.
Content-Type: Multipart/Related; start-info="text/xml"; type="application/xop+xml"; boundary="----=_Part_4_32542424.1118953563492" Content-Length: 1193 SOAPAction: "" ------=_Part_5_32550604.1118953563502 Content-Type: application/xop+xml; type="text/xml"; charset=utf-8 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <claimForm xmlns="http://example.org/mtom/data"> cid:b0a597fd-5ef7-4f0c-9d85-6666239f1d25@example.jaxws.sun.com </claimForm> </soapenv:Body> </soapenv:Envelope> ------=_Part_5_32550604.1118953563502 Content-Type: application/xml Content-ID: <b0a597fd-5ef7-4f0c-9d85-6666239f1d25@example.jaxws.sun.com> <?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocaption= "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/application_1_4.xsd" version="1.4"> <display-name>Simple example of application</display-name> <description>Simple example</description> <module> <ejb>ejb1.jar</ejb> </module> <module> <ejb>ejb2.jar</ejb> </module> <module> <web> <web-uri>web.war</web-uri> <context-root>web</context-root> </web> </module> </application>
swaRef가 적용된 WSDL 파일의 예제는 다음과 같다.
[예 10.2] << hello.wsdl >>
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:types="http://tmaxsoft.com/swaref/data"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://tmaxsoft.com/swaref"
targetNamespace="http://tmaxsoft.com/swaref" name="swaref">
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://tmaxsoft.com/swaref/data"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
elementFormDefault="qualified"
xmlns:ref="http://ws-i.org/profiles/basic/1.1/xsd">
<import namespace="http://ws-i.org/profiles/basic/1.1/xsd"
schemaLocation="wsi-swa.xsd" />
<element name="claimForm" type="ref:swaRef" />
<element name="claimFormResponse" type="ref:swaRef" />
</schema>
</wsdl:types>
<wsdl:message name="claimFormIn">
<wsdl:part name="data" element="types:claimForm" />
</wsdl:message>
<wsdl:message name="claimFormOut">
<wsdl:part name="data" element="types:claimFormResponse" />
</wsdl:message>
<wsdl:portType name="Hello">
<wsdl:operation name="claimForm">
<wsdl:input message="tns:claimFormIn" />
<wsdl:output message="tns:claimFormOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloBinding" type="tns:Hello">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="claimForm">
<soap:operation />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloService">
<wsdl:port name="HelloPort" binding="tns:HelloBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
위와 같이 claimForm와 claimFormResponse element는 ref:swaRef로 외부 스키마인 wsi-swa.xsd를 이용하여 swaRef의 데이터 타입으로 정의되어 있으므로 이후 네트워크로 메시지가 전송될 때 Attachment의 형태로 전송된다.
본 절에서는 지금까지 구현한 클래스들 및 기타 설정 파일들을 가지고 핸들러 프레임워크를 실행하는 방법에 대해서 설명한다.
다음과 같이 swaRef를 설정한 서비스를 생성하여 JEUS에 deploy한다.
$ ant build deploy
위의 과정이 모두 실행되어 서비스가 정상적으로 deploy되면, 클라이언트를 빌드한다. 클라이언트에서 wsimport의 과정을 거치므로 서비스의 Deploy가 모두 완료되었을 때 클라이언트의 구성이 가능하다.
다음과 같이 swaRef를 설정한 클라이언트를 생성하고 서비스를 호출한다. 콘솔에서 명령어를 입력하면 클라이언트와 서비스가 정상적으로 메시지를 주고받는 것을 확인할 수 있다.
$ ant run ... Host: localhost:8088 Content-length: 2672 Content-type: multipart/related; type="text/xml"; boundary="uuid:26973efe-2e29-4 36a-8fce-3fa58b6fd91f" Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive Soapaction: User-agent: JAX-WS RI 2.2 - JEUS 8 --uuid:26973efe-2e29-436a-8fce-3fa58b6fd91f Content-Type: text/xml <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envel ope/"><S:Body><claimForm xmlns="http://tmaxsoft.com/swaref/data">cid:40b15647-3c 0b-4449-90ad-29b1667e940b@example.jaxws.sun.com</claimForm></S:Body></S:Envelope > --uuid:26973efe-2e29-436a-8fce-3fa58b6fd91f Content-Id:<40b15647-3c0b-4449-90ad-29b1667e940b@example.jaxws.sun.com> Content-Type: text/xml Content-Transfer-Encoding: binary <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions xmlns:types="http://tmaxsoft.com/swaref/data" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tmaxsoft.com/swaref" targetNamespace="http://tmaxsoft.com/swaref" name="swaref"> ...
JEUS 8 JAX-WS 웹 서비스는 첨부 파일을 포함하는 SOAP 메시지를 송수신할 때 첨부 파일을 메모리로 읽어 처리하기 보다는 스트리밍(Streaming) 방식으로 송수신할 수 있다. 이 기능은 첨부 파일이 대용량일 때 웹 서비스 Endpoint 호출 성능을 향상시킨다. 특히, 첨부 파일이 JVM Heap 크기를 넘는 경우 OutOfMemory Error 없이 첨부 파일 송수신을 가능하게 한다.
본 절에서는 웹 서비스 클라이언트가 Outbound SOAP 메시지에 포함된 첨부 파일을 스트리밍 방식으로 전송하는 방법에 대해서 설명한다.
웹 서비스 클라이언트가 Chunked transfer-encoding을 사용하여 Outbound 요청 메시지를 전송하도록 설정한다. 서비스 포트(port)에 대하여 웹 서비스 오퍼레이션(operation)을 호출하기 전에(즉, Outbound 요청 메시지를 전송하기 전에) 서비스 포트로부터 얻은 요청 MessageContext에 다음의 프로퍼티를 설정한다.
Property Key : "com.sun.xml.ws.transport.http.client.streaming.chunk.size"
Property Value : chunked data size
이 프로퍼티는 JAX-WS HTTP Transport가 HTTP 요청을 Chunked Streaming 방식으로 전송한다. JEUS 8 서버는 Chunked Streaming 방식을 지원한다.
[예 10.3] << AttachmentApp.java >>
Hello port = new HelloService().getHelloPort(
new jeus.webservices.jaxws.api.transport.http.AttachmentFeature());
Map<String, Object> reqCnt = ((BindingProvider)port).getRequestContext();
reqCnt.put("com.sun.xml.ws.transport.http.client.streaming.chunk.size",
new Integer(4*1024));
모든 HTTP 서버가 이 방식을 지원하지는 않는다는 것에 유의한다.