웹 서비스는 다중 플랫폼과 시스템을 통한 복잡한 문제들과 분산된 프로세스들을 해결하는 것으로 잘 알려져있다. 웹 서비스는 표준 그룹들이 개발한 SOAP, WSDL, UDDI 등의 표준 프로토콜을 사용하여 이를 달성할 수 있었다. 이러한 표준들은 여전히 진화하고 있으며 모든 분야에 쓰일 수 있도록 솔루션을 튼튼히 다져가야 한다. 서비스 간 데이터 구조 어레이 같은 커스텀 데이터 유형을 전송하는 방법 뿐 아니라 바이너리와 다른 복잡한 데이터 파일을 전송하기 위해 첨부파일을 핸들하는 방법을 모색해 나가는 것도 중요하다.
이 글에서는 웹 서비스에서 커스텀 데이터 인코딩과 첨부파일 핸들링에 쓰일 수 있는 지원 프로토콜과 툴을 설명하겠다. 또한 간단한 케이스 스터디를 통해 기존 툴과 표준을 사용하여 파일 업로드/다운로드 웹 서비스를 만드는 방법도 설명하겠다. 본격적인 주제로 들어가기 전에 SOAP과 WSDL이 작동하면서 데이터 인코딩을 어떻게 핸들하는지를 살펴보자.
SOAP 메시지에는 분산환경에서 동배(peer)간 구조화되고 유형화된 정보를 교환하는데 사용될 수 있는 XML 문서 정의가 포함되어 있다. SOAP 메시지는 기본적으로 SOAP 스팩의 Section 5에 정의되어 있는 대로 인코딩된다. 하지만 XML 스키마나 RDF 같은 기타 인코딩 스키마를 사용하는 것에 대한 제한이 없다. 애플리케이션에 의해 스팩이 인식될 수 있는 한 그렇다. 예를 들어 WSDL에 정의된 문자 인코딩 메소드는 XML 스키마 인코딩을 사용한다. SOAP스팩의 Section 5는 XML 스키마의 유형 시스템의 초기 버전의 단점을 보완하도록 만들어졌다. 특별히 어레이와 다른 복잡한 유형 시스템과 같이 SOAP이 필요로 하는 기능들이 그러한 것들이다. 표준 그룹들 사이에서 XML 스키마가 SOAP이 필요로 하는 대부분의 기능들을 지원하기 위해 진화되고있음에도 불구하고 특별한 인코딩 스키마 대안이 필요한 이유에 대한 논란이 일고 있다.
SOAP 메시지 교환은 원격 프로시져 호출(RPC)로서 모델링 될 수 있다. 잘 정의된 메소드 호출 신호에 기반한 잘 정의된 방식의 메시지 교환과 이것의 리턴 값과 기대값이 있는 곳에서 그렇다. SOAP 메시지는 교환된 데이터의 의미가 오직 애플리케이션 레벨(전송자/수신자)에서 알려진 곳에서만 교환된 문서로서 보여질 수 있다.
WSDL (Web services Definition Language)
WSDL은 일단의 SOAP 메시지를 설명하고 이들 메시지들이 SOAP 프로세서들간에 어떻게 교환되는지를 설명한다. 이것은 기업이 그들의 서비스를 설명하고 클라이언트가 SOAP과 같은 저수준 교환 프로토콜(바인딩)을 알 필요 없이 표준 방식으로 서비스를 소비할 수 있도록 한다. 이같이 서비스에 대한 상위의 추상화는 인간의 상호작용을 제한하고 웹 서비스를 위한 프록시의 자동 생성을 가능하게 한다. (이러한 프록시는 정적인 것이 될 수도, 동적인 것이 될 수도 있다.) WSDL은 문서지향의 메시지와 RPC 지향의 메시지 설명이 가능하다.
WSDL에서 가능한 두 종류의 메시지 인코딩 (문자(literal) 인코딩과 SOAP 인코딩)이 있다. 문자 인코딩은 메시지가 XML 스키마에 따라 구성된다는 것을 의미한다. SOAP 인코딩은 SOAP 스팩의 Section 5에서 지정된 규칙을 사용한다. WSDL의 다른 중요한 개념은 메시지가 문서 또는 RPC 매개변수로서 전송될 수 있는 곳에서의 메시지 전송 포맷이다. 문서 전송 포맷은 메시지 부분이 메시지의 바디에 있다는 것을 의미한다. 반면 RPC는 메시지 부분이 특정 서명과 호출의 매개변수만 XML 엘리먼트에 래핑된다는 것을 의미한다. 이러한 전송포맷은 SOAP 메시지 교환에서 SOAP 의미를 확인한다.
이러한 개념들을 바탕으로 SOAP과 웹서비스를 비롯하여 커스텀 유형 매핑과 첨부파일 지원을 포함하는 SOAP과 웹 서비스의 재미있는 부분들을 설명하겠다.
SOAP에서의 유형 매핑은 원시 언어 객체들을 XML로 마샬링 또는 재포맷 함으로서 SOAP 메시지에서 원시 언어 객체들이 전송될 수 있도록 이들을 XML로 마샬링 또는 재포맷하여 원시 언어 객체들을 전송하는 방법을 결정하는데 사용된다. 이러한 유형 매핑은 클라이언트와 서버의 매핑 레지스트리에 저장된다. 대부분의 툴킷은 프리머티브 유형, 콜렉션, 심지어 MIME 첨부파일을 위한 사전정의된 변환기(translator)를 갖추고 있다. 커스텀 클래스 객체를 전송하려면 새로운 시리얼라이저나 디시리얼라이저를 만들거나 클라이언트와 서버에 새로운 유형의 매핑을 등록해야 한다. 그와같은 유형 매핑은 SOAP의 encodingStyle 기능에 기반한것이다.
각각의 유형 매핑은 일반적으로 다음과 같은 정보를 포함하고 있다:
- encodingStyle - 사용되는 인코딩 스타일을 설명하는 URI; 예를 들어, http://schemas.xmlsoap.org/soap/encoding/
- Qname for the XML element - 특정 유형 정의에 주어진 URI; 예를 들어, http://www.w3.org/2001/XMLSchema:int
- 인코딩/디코딩 될 원시 언어 클래스
- 시리얼라이저로 작동할 원시 언어 클래스 이름
- 디시리얼라이저로 작동할 원시 언어 클래스 이름
대부분의 기존 SOAP 툴킷은 SOAP 인코딩 스키마와 XML 스키마에 따라 복잡한 유형 매핑을 지원한다..
커스텀 유형 인코딩을 핸들하기 위해서 새로운 유형 매핑이 서버와 클라이언트에서 정의되어야 한다. 서버사이드에서 Apache SOAP 2.2 툴킷을 사용하여 유형 매핑을 등록하는 두가지 방법이 있다:
- 전개 디스크립터 사용하기.
- 새로운 유형 매핑으로 기본 매핑 레지스트리를 오버라이딩하기.새로운 레지스트리는 SOAPMappingRegistry의 하위클래스가 되어야 한다.
또한 Apache 툴킷을 사용하여 클라이언트 사이드에서 유형 매핑을 등록하는 두 가지 방법도 있다:
- SOAPMappingRegistry의 인스턴스를 만들고
mapTypes()메소드를 사용하여 새로운 유형 추가하기. - 사전 등록된 매핑을 이용하여 SOAPMappingRegistry의 하위클래스를 만들기.
개발자들을 돕기 위해서 아파치 SOAP에는 자바빈 시리얼라이저/디시리얼라이저 클래스가 제공되고 자바빈즈 기술 표준에 따라 구현된 모든 클래스들은 커스텀 코드를 작성하지 않고도 시리얼라이즈 또는 디시리얼라이즈된다. 이러한 자바빈 시리얼라이저/디시리얼라이저 클래스는 시리얼라이즈와 디시리얼라이저 로서 유형 매핑 레지스트리에 사용될 수 있다.
개발자들은 커스텀 핸들링을 수행하기 위해 org.apache.soap.util.xml.Serializer와
org.apache.soap.util.xml.Deserializer 를 구현하여 커스텀 시리얼라이저와
디시리얼라이저를 만들 수 있다.
어떻게 이러한 커스텀 인코딩이 수행될 수 있는지를 예제를 통해 설명하겠다.
때로는, SOAP 메시지를 이미지, 그림, XML 문서 등과 같은 다양한 종류의 첨부파일과 함께 전송해야 한다. 그와 같은 데이터는 종종 특정 바이너리 포맷으로 되어있다. SOAP with Attachment 스팩은 MIME 부분들의 레퍼런싱에 필요한 'MIME multipart/related' 미디어 유형과 URI 스키마 사용에 대해 자세한 설명을 하고 있다. SOAP 첨부파일 지원은 모든 웹 서비스 툴킷에서 사용 수 있는것은 아니다. 마이크로소프트는 DIME(참고자료)에 기반한 첨부파일 솔루션을 제안해왔다.
이제 어떻게 SOAP 메시지가 첨부파일과 함께 구성될 수 있는지와 SOAP 메시지에서 첨부파일을 레퍼런싱 하는 방법을 설명하겠다.
SOAP 메시지 패키지에는 XML 포맷의 기본적인 SOAP 메시지 뿐 아니라 다른 데이터 포맷(gif, jpg, xml)의 엔터티가 포함되어 있다.
그림 1에서 보듯이 SOAP 메시지 패키지는 MIME 범위 내에 삽입된 각 파트와
함께 MIME의 Multipart/related media 유형을 사용하여 구성된다. E각 MIME 파트는 MIME
파트에 삽입된 데이터 유형을 지정하는 Context-Type과 MIME 파트에 사용되는 인코딩을 지정하는
Content Transfer-encoding, MIME 패키지에 있는 모든 내용들을 참조하는 식별자로서
Content-ID 와 Content-Location 등의 헤더 정보를 갖고 있다.
MIME 메시지의 루트(root) 부분에는 text/xml 로 설정된 Content-Type
을 가진 SOAP envelope이 포함되어 있다.
그림 1. SOAP 메시지 패키지
SOAP 헤더와 SOAP 메시지의 바디는 메시지 패키지에 있는 다른 엔터티를 참조할 수도 있다. SOAP 1.1 인코딩 규칙에 따라 SOAP 'href' 속성(그림 1 참조)은 다른 리소스를 참조하는데 사용될 수 있다.
Any resource can be referenced using either Content-ID 또는
Content-Location을 사용하여 참조될 수 있다. 식별자로서 Content-ID
를 사용한다면 스키마 속성(예를들어 href)은 URL scheme CID를 사용해야 한다.( 그림
1).식별자로서 Content-Location을 사용한다면 SOAP with Attachments
스팩에 지정된 규칙을 따라야 한다:
- 절대적인(Absolute) URI 레퍼런스- 절대적 URL은 SOAP envelope의
Content-Location과 'href' 속성으로 지정된다. - 상대적(Relative) URI 레퍼런스- MIME 메시지의 메인 헤더의
Content-Location에 지정된 것 처럼 기본적인 URL이 있다. 모든 상대적 URL은 이러한 기본적인 URL을 사용하여 레퍼런스 된다. - 기본적인 URI가 없는 상대적 레퍼런스- 메인 헤더의
Content-Location으로 지정된 기본적인 URL은 없지만 메시지 기본 URL과 모든 상대적인 URL을 사용하는 것은 이러한 기본적인 URL을 사용하여 레퍼런스 된다.
기본적으로 URI를 분석해야할 지를 결정하는 것은 SOAP 프로세서에 달려있다. 게다가 SOAP envelop에 어떤 URI 레퍼런스가 없는 메시지 패키지의 첨부파일이 있을 수도 있다.
HTTP 바인딩
첨부파일을 보내기 위해 SOAP에 의해 HTTP 바인딩이 사용된다면 HTTP 헤더는 SOAP MIME 패키지 헤더의 Content-Type
정보를 포함하도록 변경되어야 한다. MIME 패키지 헤더의 어떤 기타 헤더 정보도 HTTP 헤더에 나타나지 않는다. Apache는
Content-Transfer-Encoding과 MIME Version 정보 같은
헤더를 무시한다. SOAP envelop 부분과 다른 첨부파일을 포함하는 모든 MIME 파트는 HTTP 바디를 구성한다. 반면
SMTP 바인딩의 경우 모든 멀티파트(multipart) MIME 헤더는 SMTP 헤더의 부분으로 저장된다. 따라서 SOAP
프로세서와 애플리케이션은 다른 종류의 SOAP 바인딩의 헤더 비호환성을 인식해야 한다.
첨부파일이 있는 웹서비스를 설명을 지원하는 WSDL
WSDL 문서에서 보듯이 바인딩 엘리먼트의 인풋/아웃풋 엘리먼트는 확장되고 패키지되었다. 첨부파일이 있는 각 마임 부분은 콘텐트
유형 역시 지정할 수 있다.
Listing 1: WSDL 바인딩 확대하기
<binding name="DocManagementService_Binding" ... />
<operation name="SubmitArrayOfDocuments">
<soap:operation soapAction="http://www.DocManagementService.com/SubmitArrayOfData"/>
<input>
<mime:multipartRelated>
<mime:part>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service"
parts="SubmitArrayOfDocuments_inType1 SubmitArrayOfDocuments_inType2"
use="encoded"/>
</mime:part>
<mime:part>
<mime:content part="SubmitArrayOfDocuments_inType3" type="*/*"/>
</mime:part>
</mime:multipartRelated>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service"
use="encoded"/>
</output>
</operation>
</binding>
|
Apache SOAP 2.2 툴킷의 첨부파일이 포함된 SOAP 지원
Apache SOAP toolkit version 2.2는 W3C의 'SOAP with Attachments Note'에 근거하여 첨부파일이 있는 SOAP을 지원한다. 서비스의 유형과 필요한 제어에 따라 첨부파일을 핸들하는 다양한 기술이 있다:
- javax.activation.Datasource 객체와 javax.activation.DataHandler 객체를 사용하는 첨부파일을 핸들하는 시리얼라이저/디시리얼라이저가 빌트인되어있다. 이는 주로 RPC 기반의 클라이언트-서버 호출에 사용된다.
- 첨부파일 핸들링에 세밀한 제어를 하기위해서는 org.apache.soap.messaging.Message, org.apache.soap.rpc.Call, org.apache.soap.rpc.Response 같이 클라이언트 측에서 사용가능하고, SoapContext 클래스 같은 서버측에서 사용가능한 메소드들이 있다. 이러한 메소드들은 javax.mail.internet.MimeBodyPart를 SOAP 메시지에 추가할 수 있다.
첨부파일이 있는 SOAP을 사용하는 데 있어서 많은 문제점들이 있다:
- SOAP with Attachments 스팩은 'MIME Multipart/related'에 기반을 두고 있다. SOAP 커뮤니티에서는 'MIME Application/Multiplexed' 사용에 대한 논란이 일고있다. 모든 데이터를 한번에 메모리에 로딩하지 않고 첨부파일을 추가함으로서 애플리케이션의 확장성 향상에 도움을 준다.
- 몇몇 툴킷은 SOAP with Attachments 스팩 표준을 지원한다. Apache SOAP 2.2 , WASP 등이 그러한 것들이다.
- 대부분의 SOAP 툴킷은 첨부파일 또는 메시지 같은 대용량의 데이터 커밍을 핸들링하는 스트리밍 모델을 지원하지 않는다. 이것은 SOAP 프로세서에 확장성과 퍼포먼스 문제라는 결과를 초래하게 되었다.
이 구현 스터디를 위해 웹 기반 애플리케이션을 만들어 엔드유저가 그들의 파일을 웹 서버에서 업로드 및 다운로드 할 수 있도록 할 것이다. 이 애플리케이션은 웹 서비스 기반의 호출 모델을 제공한다. 이 웹 서비스 애플리케이션은 사용자가 어느 분량의 파일이라도 서버에 업로딩할 수 있고 그러한 파일에 대해 레퍼런스를 할 수 있고 뿐만 아니라 이러한 레퍼런스를 사용하여 원하는 파일을 다운로드 할 수 있다.
이 예제는 다중의 첨부파일과 MIME 첨부파일을 지원하는 고급 WSDL 문서 구조체를 전송하는 커스텀 유형 매핑을 설명할 것이다. 또한, 어레이로서 커스텀 데이터를 전송하는 방법도 설명한다. 이 간단한 애플리케이션은 세계적인 웹 기반 파일 관리 애플리케이션으로 향상될 수 있다. 하지만 보안, 국제화, 프라이버시, 대규모 애플리케이션에 필요한 퍼포먼스 같은 복잡한 사안은 고려하지 않았다.
프론트엔드 클라이언트와 백엔드 서버의 서비스 설명(WSDL)에는 문서 매니저(Document manager) 애플리케이션에서 나타난 서비스 정보가 포함되어 있다. WSDL 바인딩은 MIME type multipart/related과 MIME 포맷을 지원하도록 확장되었다. 백엔드 WSDL 파일은 Listing 2를 프론트엔드는 Listing 3을 참조하라.
Listing 2: DocumentManagement-Interface.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="DocumentManagementService_Interface"
targetNamespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns=
"http://www.DocumentManagementService.com/DocumentManagementService_interface"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:types=
"http://www.DocumentManagementService.com/DocumentManagementService_interface/types/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<types>
<xsd:schema targetNamespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface/types/"
xmlns="http://www.w3.org/2001/XMLSchema/">
<xsd:complexType name="User">
<xsd:sequence>
<xsd:element name="userName" type="xsd:string"/>
<xsd:element name="password" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DocumentInformation">
<xsd:sequence>
<xsd:element name="userDefinedDocName" type="xsd:string"/>
<xsd:element name="localPathInfo" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArrayOfDocumentInformation">
<xsd:complexContent>
<xsd:restriction base="SOAP-ENC:Array">
<xsd:sequence>
<element minOccurs="0" maxOccurs="unbounded"
name="docInfo" type=" DocumentInformation"/>
</xsd:sequence>
<xsd:attribute ref="SOAP-ENC:arrayType"
wsdl:arrayType="DocumentInformation[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="DocumentReference">
<xsd:sequence>
<xsd:element name="docCreateDateTime" type="xsd:datetime"/>
<xsd:element name="docRefType" type="xsd:string"/>
<xsd:element name="serverDocPath" type="xsd:string"/>
<xsd:element name="localPathInfo" type="xsd:string"/>
<xsd:element name="userDefinedDocName" type="xsd:string"/>
<xsd:element name="docRef" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="ArrayOfDocumentReference">
<xsd:complexContent>
<xsd:restriction base="SOAP-ENC:Array">
<xsd:sequence>
<element minOccurs="0" maxOccurs="unbounded"
name="docRef"
type="DocumentReference"/>
</xsd:sequence>
<xsd:attribute ref="SOAP-ENC:arrayType"
wsdl:arrayType="DocumentReference[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ArrayOfString">
<xsd:complexContent>
<xsd:restriction base="SOAP-ENC:Array">
<xsd:sequence>
<element maxOccurs="unbounded" name="docs"
type="xsd:string"/>
</xsd:sequence>
<xsd:attribute ref="SOAP-ENC:arrayType"
wsdl:arrayType="xsd:string[]"/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
</types>
<message name="InSubmitArrayOfDocumentsRequest">
<part name="SubmitArrayOfDocuments_inType1" type="types:User"/>
<part name="SubmitArrayOfDocuments_inType2"
type="types:ArrayOfDocumentInformation"/>
<part name="SubmitArrayOfDocuments_inType3"
type="types:ArrayOfString"/>
</message>
<message name="OutSubmitArrayOfDocumentsResponse">
<part name="SubmitArrayOfDocuments_outType" type=
"types:ArrayOfDocumentReference"/>
</message>
<message name="InFetchDocumentsRequest">
<part name="FetchDocuments_inType1" type="types:User"/>
<part name="FetchDocuments_inType2" type=
"types:ArrayOfDocumentReference"/>
</message>
<message name="OutFetchDocumentsResponse">
<part name=" FetchDocuments_outType1" type="xsd:string"/>
</message>
<message name="InPingDocumentMangerRequest">
<part name="PingDocumentManger_inType1" type="xsd:string"/>
</message>
<message name="OutPingDocumentMangerResponse">
<part name="PingDocumentManger_outType1" type="xsd:string"/>
</message>
<portType name="DocumentManager_portType">
<operation name="SubmitArrayOfDocuments">
<input message="tns:InSubmitArrayOfDocumentsRequest"/>
<output message="tns:OutSubmitArrayOfDocumentsResponse"/>
</operation>
<operation name="FetchDocuments">
<input message="tns:InFetchDocumentsRequest"/>
<output message="tns:OutFetchDocumentsResponse"/>
</operation>
<operation name="PingDocumentManger">
<input message="tns:InPingDocumentMangerRequest"/>
<output message="tns:OutPingDocumentMangerResponse"/>
</operation>
</portType>
<binding name="DocumentManagementService_Binding_Rpc" type=
"tns:DocumentManager_portType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="SubmitArrayOfDocuments">
<soap:operation soapAction="http://www.DocManagementService.com/SubmitArrayOfData"/>
<input>
<mime:multipartRelated>
<mime:part>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocumentManagement-service"
parts="SubmitArrayOfDocuments_inType1 SubmitArrayOfDocuments_inType2"
use="encoded"/>
</mime:part>
<mime:part>
<mime:content part="SubmitArrayOfDocuments_inType3" type="text/html"/>
</mime:part>
</mime:multipartRelated>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocumentManagement-service"
use="encoded"/>
</output>
</operation>
<operation name="FetchDocuments">
<soap:operation soapAction="http://www.DocManagementService.com/FetchDocuments"/>
<input>
<mime:multipartRelated>
<mime:part>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service"
parts="FetchDocuments_inType1 FetchDocuments_inType2"
use="encoded"/>
</mime:part>
</mime:multipartRelated>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocManagement-service" use="encoded"/>
</output>
</operation>
<operation name="PingDocumentManger">
<soap:operation soapAction=
"http://www.DocumentManagementService.com/PingDocumentManger"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocumentManagement-service"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:DocumentManagement-service" use="encoded"/>
</output>
</operation>
</binding>
</definitions>
|
Listing 3: DocumentManager.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="DocManagementServiceDefinition"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:interface=
http://www.DocumentManagementService.com/DocumentManagementService_interface"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace=
"http://www.DocManagementService.com/DocManagementService" >
<import location=
"http://joshyjjp:4040/DocumentManager/DocumentManager-Interface.wsdl"
namespace=
"http://www.DocumentManagementService.com/DocumentManagementService_interface" />
<service name="DocManagementService">
<documentation>Document Management Web Service:
This service will allow user to manage their documents
</documentation>
<port binding=
"interface:DocumentManagementService_Binding_Rpc"
name="DocManagementService_RPC">
<soap:address location=
"http://joshyjjp:4040/dmsoap/servlet/rpcrouter"/>
</port>
<port binding="interface:DocumentManagementService_Binding_Message"
name="DocManagementService_Message">
<soap:address location=
"http://joshyjjp:4040/dmsoap/servlet/messagerouter"/>
</port>
</service>
</definitions>
|
백엔드 서비스 구현(Listing 4)은 SubmitArrayOfDocuments와
FetchDocuments 같은 액세스 가능한 서비스 메소드가 있는 DocumentManagementImpl
클래스에서 핸들된다. SubmitArrayOfDocuments 서비스는 RPC 기반 SOAP 서비스 메소드로서
SOAPContext (첨부파일 핸들), User (사용자 정보), DocumentInfo
어레이(서버에서 제공된 첨부 문서들)를 포함한 매개변수를 포함하고 있다. FetchDocuments 메소드는
RPC 기반 서비스로서 사용자 정보와 문서 레퍼런스를 포함하고 있다. 문서 레퍼런스 객체에는 이미 서버에 저장된 문서 정보 (docRefId)를
포함하고 있다.
Listing 4: DocumentManagementImpl class
// apache soap
import org.apache.soap.*;
import org.apache.soap.rpc.*;
import org.apache.soap.util.xml.XMLParserUtils;
import org.apache.soap.encoding.SOAPMappingRegistry;
// MIME
import javax.activation.*;
import javax.mail.*;
import javax.mail.internet.*;
// java
import java.io.*;
import java.util.*;
public class DocumentManagementImpl implements DocumentManagement{
/*
Submit an array of documents to the Document Manger; Attached data has to
be retrieved from the SOAPContext.
Once the attached data is retrieved it will be saved to the disk at
the server and a document reference will be returned to the user.
*/
public DocumentReference[] SubmitArrayOfDocuments
(SOAPContext requestContext, User user, DocumentInfo[] localDocInfo)
throws Exception{
DocumentReference [] refArray = null;
try{
// get the document information
int docCount = localDocInfo.length;
if(docCount >0){
// get attachment count
int attachmentCount = requestContext.getCount();
// there docCount docs to be saved and if successful there will be
// that many document reference
// for the client
refArray = new DocumentReference[docCount];
MimeBodyPart mbp = null;
// Get root part
MimeBodyPart rootPart = requestContext.getRootPart();
// Detach Attachments if present
for (int i = 0; i < requestContext.getCount(); i++){
// Get next attachment
mbp = (MimeBodyPart)requestContext.getBodyPart(i);
if (!(mbp.equals(rootPart))){
// the root element is the soap envelope
String type = mbp.getContentType();
String Id = mbp.getContentID();
String cntType = mbp.getContentType();
String location = mbp.getHeader
(org.apache.soap.Constants.HEADER_CONTENT_LOCATION, null);
String name = mbp.getFileName();
Object content = mbp.getContent();
String classType = content.getClass().getName();
if (mbp.isMimeType("text/*")){
System.out.println("The MIME data is in text format");
}else{
System.out.println("The MIME data is in binary format");
}
// save each attachment to the file system
DocumentReference dofRef =
saveDocument(user,localDocInfo[i-1],content);
refArray[i-1]= dofRef; // set the return document reference
}
}
}
}catch(Exception e){
System.out.println
("There is an exception "+ e.toString());
throw e;
}
return refArray;
}
/* Get all the documents that already saved in the server with the respective
document reference number. All the documents should be send back
to the user as attachments. */
public void FetchDocuments (DocumentReference[] docRef){
.............................................................................................
}
private synchronized DocumentReference saveDocument(User user, DocumentInfo
userDefinedDocName, Object content){
// save the document in the server and return a reference to the user.
..............................................................................
}
}
|
서비스 코드에서 보듯이 첨부파일 정보를 얻기 위해 SOAPContext 리퀘스트 객체를 사용한다. SOAPContext 객체에는 바디 부분들이 포함되어 있는데, 이는 SOAP envelope를 포함하고 있는 root 부분을 가지고 있는 MIME 부분의 어레이다. 콘텐트 id, 콘텐트 유형, 위치 등이 포함되어 있는 MIME 바디 부분에서 많은 정보를 얻어올 수 있다.
커스텀 유형 매핑 레지스트리는 Deployment Descriptor.xml 파일 (isd:mapping 섹션 참조)에서 보듯이 전개 디스크립터를 사용하여 설정된다. SOAP 서버는 유형 매핑 정보와 필요한 serialization/de-serialization 클래스를 로딩하고 User, DocumentInfo 같은 커스텀 자바 클래스에 유형의 마샬링/언마샬링을 수행한다.
문서 기반의 메소드들은 첨부파일을 세밀히 제어하기 위해 추가될 수 있다. SOAPContext 'reponse' 객체를 사용하여 MIME 첨부파일을 만들면 된다. SOAPContext 객체에는 SOAP envelope을 포함하고 있는 root 부분을 가진 MIME 부분의 어레이인 바디 부분이 포함되어 있다.
제어 정보(사용자 이름, 업로딩된 파일 정보, 파일 레퍼런스 id)는 XML 문서 (DocumentManger.xml)를 사용하여 서버에 저장된다.
클라이언트 코드는 DocumentInfo, DocumentReference 같은 자바 클래스를 마샬링/언마샬링하기 위해 SOAPMappingRegistry에 유형 매핑 정보를 만들고 다음과 같이 리퀘스트 객체와 함께 이를 바인딩한다:
// set the SOAP mapping registry
BeanSerializer beanSer = new BeanSerializer();
// java bean based standard serializer
SOAPMappingRegistry smr = new SOAPMappingRegistry();
smr.mapTypes(Constants.NS_URI_SOAP_ENC,new QName
("urn:xml-docmanger-webservice", "user"),User.class, beanSer, beanSer);
// set the mapping registry
request.setSoapMappingRegistry(smr);
|
User 같은 클래스의 serialization/decerialization을 위해 빈 시리얼라이저 클래스(BeanSerializer)를 사용한다.
Attachments are created using DataSource, DataHandler
and MimeBodyPart classes and bind to the request object as show below,
// Now create attachments and add to the request
DataSource ds = new ByteArrayDataSource(new File("Compile.bat"), null);
|
Compile.bat 파일은 첨부파일로서 전송된다.
DataHandler dh = new DataHandler(ds);
MimeBodyPart mbp = new MimeBodyPart();
mbp.setDataHandler(dh);
// Add the attachment to the service request
request.addAttachment(mbp);
|
Listing 5는 클라이언트의 구현이다. 코드는 대부분 자기 설명적이다(self-descriptive).
Listing 5: Document client code
// Service Implementation URL
String wsdlSvcImplURL = Util.formatURL(WSTKConstants.SERVER_HOSTNAME,
WSTKConstants.SERVER_PORT,
svcImplUrl);
// get the WSDL document
WSDLDocument wsdlDoc = new WSDLDocument(new URL(wsdlSvcImplURL));
// Get the service proxy you will use to invoke service based on the WSDL document
ServiceProxy serviceProxy = ServiceProxyFactory.getServiceProxy
(serviceName,portName,wsdlDoc);
// SubmitArrayOfDocuments RPC based operation expects 2 input arguments
Object oArgs = new Object[2];
// the parameters are
// 1. user information
User user = new User (userName,userPassword);
// 2. an array of submited document information
DocumentInfo[] docInfo = new DocumentInfo[1];
docInfo[0] = new DocumentInfo("compile.bat","d:\\data");
// Setting up the type mapping registry to handle custom java objects
// setup the serializers;
BeanSerializer beanSer = new BeanSerializer();
// java bean based standard serializer
// set the soap mapping registry
SOAPMappingRegistry smr = new SOAPMappingRegistry();
// input parameters
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:xml-docmanger-webservice", "user"),
User.class, beanSer, beanSer);
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:xml-docmanger-webservice", "documentinfo"),
DocumentInfo.class, beanSer, beanSer);
// output parameters
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:xml-docmanger-webservice", "documentreference"),
DocumentReference.class, beanSer, beanSer);
// Set input arguments
oArgs[0] = user;
oArgs[1] = docInfo;
// Set up the request
SoapServiceRequest request = new SoapServiceRequest
("SubmitArrayOfDocuments", oArgs);
// set the mapping registry
request.setSoapMappingRegistry(smr);
// Now create attachments and add to the request
DataSource ds = new ByteArrayDataSource(new File("Compile.bat"), null);
DataHandler dh = new DataHandler(ds);
MimeBodyPart mbp = new MimeBodyPart();
mbp.setDataHandler(dh);
mbp.setFileName("Compile.bat");
mbp.setHeader
(org.apache.soap.Constants.HEADER_CONTENT_LOCATION, "Attachment-1");
// Add the attachment to the service request
request.addAttachment(mbp);
// Invoke the operation
ServiceResponse sr = serviceProxy.invoke(request);
// Pull the soap response object from the results
Response response = (Response) sr.getOneResult(1);
// If the call to the service failed, then display error information
if (response.generatedFault()){
handleFault(response);
} else {
// Get return value
Parameter parameter = response.getReturnValue();
// Display results
System.out.println("Message returned from service provider: \n");
retRef = (DocumentReference[])parameter.getValue();
for(int i=0;i<retRef.length;i++){
System.out.println("Document reference id = "+retRef[i].getDocRef());
}
}
// Now fetch the documents uploaded to the server by using the document references
........................................................................
// Pull the soap response object from the results
response = (Response) sr.getOneResult(1);
// If the call to the service failed, then display error information
if (response.generatedFault()){
handleFault(response);
}else{
// Get return value
int attachCount = sr.getAttachmentCount();
// Display results
System.out.println("Number of attachments = " +attachCount);
}
}
|
Listing 6은 서비스 전개 디스크립터 이다.
Listing 6: 문서 관리 서비스의 전개 디스크립터
<root>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:DocumentManagement-service" checkMustUnderstands="false">
<isd:provider type="java" scope="Application"
methods="PingDocumentManger SubmitArrayOfDocuments FetchDocuments">
<isd:java class="DocumentManagementImpl" static="false"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
<isd:mappings>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:xml-docmanger-webservice" qname="x:user"
javaType="User"
java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:xml-docmanger-webservice" qname="x:documentreference"
javaType="DocumentReference"
java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="urn:xml-docmanger-webservice" qname="x:documentinfo"
javaType="DocumentInfo"
java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
</isd:mappings>
</isd:service>
</root>
|
XML Protocol 위원회는 새로운 소프트웨어 관례 및 사용 시나리오를 정의하는 작업을 시작했다. 다중 비동기식 메시징, 캐싱, 라우팅, 대용량 데이터의 스트리밍 등이 포함된다. 표준화까지 이르기에는 시간이 걸리겠지만 SOAP 프로세서가 이러한 새로운 기능들을 추가해서 출시되는 것을 기대해 보는 것도 좋은 일이다. BEEP (Blocks Extensible Exchange Protocol)는 단일 TCP 연결을 통해 다중 채널을 지원하는 새로운 프로토콜이다. 비동기식 스트리밍 타입의 데이터 통신에 쓰일 수 있다.
Direct Internet Message Encapsulation (DIME)은 경량의 바이너리 메시지 포맷으로서, 하나 이상의 정의 페이로드를 단일의 메시지 구조체로 캡슐화한다. DIME은 두 분야에 장점을 가지고 있다. 전송자(sender)는 페이로드 크기를 사전에 계산하거나 페이로드를 고정된 크기의 레코드로 청크(chunk)한다. 이는 서버 애플리케이션이 메모리 요구조건을 미리 계산하는데 도움이 되어 보다 나은 퍼포먼스를 이룩하게 된다. URI 메커니즘을 사용하여 새로운 미디어 타입을 지정하는 기능은 애플리케이션이 새로운 미디어 타입용 핸들러를 로딩할 수 있도록 한다. 하지만 이것은 기본적으로 Microsoft 정의 스팩이다.
Apache AXIS(Apache SOAP의 최신 버전)은 현재 알파 단계에 있지만 AXIS 툴킷 소스와 샘플을 좀더 면밀히 검토해야 한다. IBM의 WSTK 3.0 툴킷은 AXIS에서의 SOAP with attachments 구현 변경사항들을 반영하고 있다. RPC 기반 서비스 메소드의 경우 첨부파일들은 Axis DataHandlers로서 서비스로 전송되지만 문서 기반 서비스 메소드의 경우, Message와 Attachments 객체를 포함하고 있는 매개변수로서 MessageContext 객체를 기대한다.
- developerWorks worldwide 사이트에서 이 기사에 관한 영어원문.
-
XML
Protocol Activity
-
Web
Services Activity
-
W3C
and SOAP
-
SOAP
with Attachments
-
XML
Protocol Activity Discussion
-
DIME
-
MIME
-
Web
Services Description Language (WSDL) Explained
-
BBEP
-
Apache
SOAP
-
Apache
AXIS