달력

06

« 2017/06 »

  •  
  •  
  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  •  
2008.07.21 11:06

SAAJ Computing에 관한 독백2008.07.21 11:06


SAAJ는 SOAP with Attachments API for Java의 약자로, 말 그대로 SOAP with Attachment를 Java로 다루기 위한 API 모임입니다. 물론 JAXP나 HTTP로 생짜로 짤 수도 있겠습니만, 아무래도 class library가 있다면 좀 편하겠지요. SOAP with Attachment(이하 SwA)는 Web Service의 표준 protocol인 SOAP이, 첨부 file등을 붙일 수 있도록 확장한 것입니다. 비슷한 것으로는 DIME이 있긴 한데 아무래도 SwA가 더 힘을 받는 것 같습니다. 아울러,


  • SwA가 첨부 파일을 지원하는 mechanism은 e-mail에 file을 첨부하기 위해 고안한 MIME을 이용합니다. 이 사항은 SAAJ coding을 보면서 자세히 살펴보죠.
  • SAAJ가 지원하는 SOAP의 규격은 1.1, 1.2 모두를 지원하며 default는 1.1입니다. 이 글에서 명시적인 SOAP 명세 version이 없으면 1.1입니다.
  • Java SE 5 이하에서 SAAJ를 쓰려면 별도의 jar file이 필요했으나 Java SE 6부터는 기본으로 들어 있습니다. 따라서 본 글은 SAAJ를 Java SE에 포함시킵니다.
  • SOAP 자체에 대한 이해가 필요하시면 SOAP Tutorial을 공부하세요. 간단합니다.

SAAJ에서의 SOAP message 얼개#



SwA Message 얼개는 위 그림과 같습니다. 하나의 SOAP message는 1개의 SOAP part와 0개 이상의 Attachment Part로 나뉘며, SOAP Part는 SOAP Body는 꼭 있어야 하고 SOAP Header는 있어도 되고 없어도 됩니다. Attachment Part는 MIME Header들과 그에 따른 Content(즉 첨부 file)이 있습니다. SAAJ에는 이들 SOAP message, header, body, Attachment에 해당하는 class들이 존재하며 SAAJ를 쓴다는 것은 이들 class의 instance를 얻어 조작하는 것을 뜻합니다.


참고로 Attachment Part가 없는 SwA message는 아래 그림과 같이 SOAP Message 그 자체입니다.


SOAPMessage#


본격적으로 시작하기에 앞서, SAAJ에 관한 class들은 대부분 javax.xml.soap이라는 package 안에 들어있으며, 이 글에서 package에 대한 별도의 명명이 없는 class는 바로 이 javax.xml.soap package에 들어있다고 보시면 됩니다.


먼저 SOAP Message에 해당하는 객체를 획득해 보죠. 이 객체를 얻으려면 먼저 이 객체의 factory class인 MessageFactory 객체를 생성해야 합니다. MessageFactory 객체는 MessageFactory의 static method인 newInstance()라는 method를 호출함으로써 얻습니다. 그리고 우리가 정말로 얻고자 하는 객체인 Message를 표현하는 class는 SOAPMessage라는 class인데 이 class에 대한 객체는 MessageFactory의 createMessage()를 호출하면 얻을 수 있습니다. 이러한 동작을 수행하는 code는 아래와 같습니다.


  1. MessageFactory mf = MessageFactory.newInstance();
  2. SOAPMessage msg = mf.createMessage();

만약 SOAP 1.2 spec을 따르는 SOAP Message를 만들고 싶다면,

  1. MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
    

라고 하면 됩니다.


SOAPPart / SOAPEnvelope#

위에서 언급했듯, SOAP message는 SOAP Part와 Attachment Part로 이루어지고 SOAP Part는 반드시 존재합니다. 이 단락에서는 SOAP Part를 살펴보겠습니다. SOAP Part를 나타내는 class는 SOAPPart입니다. 그리고 SOAPPart 객체는 SOAPMessage 객체의 getSOAPPart() method를 통해 얻습니다. code로는 아래와 같죠.


  1. SOAPPart sp = mf.getSOAPPart();

SOAP spec을 보시면 아시겠지만 SOAP은 SOAP Envelope이라는 구조를 가지고 이 SOAP Envelope이 SOAP Header, SOAP Body를 rkwlrh 있습니다. SAAJ에서는 이 SOAP Envelope을 나타내는 class가 있으며 SOAPEnvelope이라 합니다. 이 SOAPEnvelope 객체는 다음과 같이 얻을 수 있죠.


  1. SOAPEnvelope se = sp.getEnvelope();

SOAP Envelope에 Namespace, Attribute 추가하기#

이 SOAPEnvelope은 SOAP의 <SOAP-ENV:envelope/> element에 대응합니다. <SOAP-ENV:envelope/>이라는 element 이름을 보셔도 알 수 있듯, <SOAP-ENV:envelope/>은 SOAP-ENV라는 XML Namespace prefix가 걸려 있으며, 이 prefix에 대응하는 URI는 "http://schemas.xmlsoap.org/soap/envelope/" 입니다. 그런데 때때로 이 <SOAP-ENV:envelope/>에 또다른 Namespace나 attribute를 추가할 수도 있습니다. 이를 위해 SOAPEnvelope은 Namespace 추가용으로 addNamespaceDeclaration(String prefix, String uri), Attribute 추가용으로 addAttribute(Name attrName, String value), addAttribute(QName attrName, String value)라는 method를 가지고 있습니다. 이들에 대한 code 예시는 아래와 같습니다.


  1. /* schemaLocName은 xsi:schemaLocation 이라는 이름을 나타내는 Name, 또는 QName 객체 */
  2. se.addNamespaceDeclaration("eb", "http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd");
  3. se.addAttribute(schemaLocName, "http://schema.xmlsoap.org/soap/envelope http://www.oasis-open.org/committees/ebxml-msg/schema/envelope.xsd");

위 code는 <SOAP-Env:Envelope/>에 eb라는 Namespace와 xsi:schemaLocation이라는 attribute를 추가하는 일을 합니다. 그런데 addAttribute() method의 첫번째 인자로 들어가는 Name, QName은 무엇하는 객체일까요?


Name / QName#

element나 attribute를 만들기 위해서는 당연히 그 element나 attribute에 대한 이름이 필요합니다. 그런데 XML에서 element나 attribute에 대한 이름은 XML Namespace의 적용을 받습니다. 따라서 element나 attribute에 대한 이름은 단순히 이름을 나타내는 String으로는 부족하고 거기에 prefix, uri에 대한 정보를 함께 가지고 있어야 합니다. 이러한 이름 정보를 표현하기 위해 SAAJ에서는 Name이란 class를 제공하며 이 Name에 대한 객체는 아래 SOAPFactory라는 객체에서 얻을 수 있습니다. code로는 아래와 같습니다.


  1. Name schemaLocName = SOAPFactory.newInstance().createName("schemaLocation", "xsi", "http://www.w3.org/2001/XMLSchema-instance");

위 code는 xsi:schemaLocation이라는 이름을 생성합니다.


이러한 이름을 표현하는 방법에는 Name 대신 javax.xml.namespace.QName을 쓰는 방법도 있습니다. 위 code와 동일한 작용을 하는, QName을 쓰는 code는 아래와 같습니다.


  1. QName schemaLocName = new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi");


SOAPHeader / SOAPBody#

SOAP message에서 실제 data를 탑재하는 부분은 SOAP Header와 SOAP Body입니다. SAAJ에서는 이들을 SOAPHeader, SOAPBody class로 표현하며 이들에 대한 객체는 아래 코드와 같이 얻을 수 있습니다.


  1. SOAPHeader sh = se.getHeader();
  2. SOAPBody sb = se.getBody();

SOAPHeader나 SOAPBody는 각각 <SOAP-ENV:header/>, <SOAP-ENV:body/>에 대응하며 <SOAP-ENV:envelope/>과 마찬가지로 addNamespaceDeclaration(String prefix, String uri), addAttribute(Name attrName, String value), addAttribute(QName attrName, String value)을 써서 필요로 하는 Namespace나 attribute를 추가할 수 있습니다. 그리고 하위 element도 추가할 수 있죠.


SOAP Header / SOAP Body에 data 싣기#

SOAPHeader나 SOAPBody에는 필요한 XML element 등을 만들어서 필요한 data를 SOAP message에 적재할 수 있습니다.


SOAP Header에 data 싣기#

SOAPHeader 객체의 addChildElement(Name), addChildElement(QName)이라는 method가 SOAP Header에 하위 element를 추가하는 일을 수행하며, 추가된 하위 element를 가리키는 SOAPElement 객체를 반환합니다.


아래 code는 SOAP Header에 하위 element로 <eb:CPAId/>라는 element를 추가하고, 거기에 "banca.03.N01.test"라는 text 값을 추가하는 예시입니다.


  1. SOAPElement messageHeader = sh.addChildElement(new QName("http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd", "MessageHeader", "eb"));

위 code에 대응하는 XML 문서는 대략 다음과 같습니다.


  1. <SOAP-ENV:Header xml:eb="http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd">
  2.   <eb:MessageHeader/>
  3. </SOAP-ENV:Header>

이 SOAPElement 객체에도 addChildElement(QName name), addChildElement(Name name), addNamespaceDeclaration(String prefix, String uri), addAttribute(Name attrName, String value), addAttribute(QName attrName, String value)이 있으며, 필요한 하위 Element, Namespace나 Attribute를 추가할 수 있습니다. 또한 SOAPHeaderElement에는 setTextContent(String content)라는 method가 있는데 이는 element에 문자열로 된 내용을 추가합니다.


아래는 <eb:MessageHeader/>에 <eb:CPAId>banca.03.N01.test</eb:CPAId>라는 하위 element를 만드는 code입니다.


  1. messageHeader.addChildElement(new QName("http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd", "CPAId", "eb")).setTextContent("banca.03.N01.test");

SOAPBody에 data 싣기#

SOAPBody에도 addChildElement(Name), addChildElement(QName)가 있으며 이 method들이 반환하는 SOAPElement 객체에 Attribute나 하위 element, content를 추가할 수 있는 것은 SOAPHeader와 같습니다. 그런데 SOAPBody에는 SOAPHeader에 없는 독특한 기능이 있는데, 바로 addDocument(org.w3c.dom.Document doc), addFault() method들입니다.


addDocument(org.w3c.dom.Document doc)는 XML 문서 자체를 한꺼번에 SOAPBody에 적재하는 method입니다. 편리하겠지요?


그럼 addFault()는 뭐하는 것일까요? SOAP spec을 보시면 아시겠지만 어떤 처리를 하다 error가 발생하면 그 error에 대한 정보를 SOAP message에 실을 수 있습니다. 바로<SOAP-ENV:Body/> element 안에 들어가는 <SOAP-ENV:Fault/> element죠. 바로 addFault()가 이 element를 추가하는 역할을 수행합니다. addFault()는 SOAPFault 객체를 반환하며 SOAPFault는 SOAPElement의 자식 class입니다.


AttachmentPart#

AttachmentPart는 쉽게 말해 첨부 file 부분을 말합니다. SwA를 통해 첨부 file을 붙이는 작업은 보통 다음의 4가지 일로 나눌 수 있습니다.


  1. SOAPMessage 객체에서 AttachmentPart 객체 얻어오기
  2. Content 추가하기
  3. Content-ID 설정하기
  4. 가공한 AttachmentPart를 SOAPMessage에 추가하기

하나의 SOAPMessage에 적재할 수 있는 AttachmentPart는 이론적으로는 제한이 없으므로, 추가하고 싶은 만큼 위 작업을 반복하면 됩니다.


각 단계에 대한 code 예시는 아래와 같습니다.


  1. attachment = msg.createAttachmentPart();
  2. attachment.setRawContent(new FileInputStream(contentFiles[inx]),"application/octet-stream");
    attachment.setContentId("<payload-" + inx + ">");
    msg.addAttachmentPart(attachment);

위 예에서는 content를 추가하는 작업으로 setRawContent() method를 썼습니다만 setContent(Object content, String mimeType)을 쓸 수도 있습니다. 이 두 method의 차이는, 전자의 경우는 Stream 형태 그대로 적재합니다만, 후자는 Java 객체로 적재할 data를 받습니다. 만약 Java 객체가 두번째 인자로 주어진 MIME Type과 맞지 않다면 setContent()는 SOAPException을 발생시킵니다. 위 예에서 setContent()를 쓸 경우, "application/octet-stream"이라는 MIME Type은 대응하는 Java 객체가 없기 때문에 SOAPException이 납니다.


만든 SOAP Message를 주고 받기#

SOAPMessage는 다른 system에 보내라고 만든 것이죠? SwA는 만들어진 SOAPMessage를 보내는 것에 대한 API도 제공하고 있습니다. 바로 예시 code로 들어갑시다.


  1. SOAPConnectionFactory scf =  SOAPConnectionFactory.newInstance();
  2. SOAPConnection conn = scf.createConnection();
  3. SOAPMessage responseMsg = conn.call(msg, new URL("http://127.0.0.1:27120/ebXML/msh));

그럼 받는 쪽은 어떻게 할까요? 이것에 대한 해답도 역시 SOAPConnection 객체에 있습니다. SOAPConnection 객체에는 get(Object to)이라는 method가 있는데, 이 method는 to에서 SOAP Message를 보낼 때까지 기다리다가 to에서 SOAP Message를 보내면 이를 받아내죠. to에 실제로 올 수 있는 type은 String 객체나 java.net.URL 객체이며 request를 보내는 곳의 URL을 표현합니다.


SwA 실례#

실제 SOAP with Attachment message가 어떻게 생긴 것인지 이해를 돕기 위해 아래에 실제 내용을 첨부합니다. 이 예에서는 Attachement Part가 두 개입니다.


  1. ------=_Part_0_8089714.1216110540156
    Content-Type: text/xml; charset=utf-8
    Content-Id: <SOAP_Part>

    <?xml version="1.0" encoding="utf-8" ?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schema.xmlsoap.org/soap/envelope http://www.oasis-open.org/committees/ebxml-msg/schema/envelope.xsd"><SOAP-ENV:Header xmlns:eb="http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd" xsi:schemaLocation="http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd"><eb:MessageHeader SOAP-ENV:mustUnderstand="1" eb:id="MessageHeader" eb:version="2.0"><eb:From><eb:PartyId eb:type="BANK_CD">03</eb:PartyId><eb:Role>BANK</eb:Role></eb:From><eb:To><eb:PartyId eb:type="INSR_CD">N01</eb:PartyId><eb:Role>INSR</eb:Role></eb:To><eb:CPAId>banca.03.N01.test</eb:CPAId><eb:ConversationId>20010215-111213-28572</eb:ConversationId><eb:Service>urn:bancassurance</eb:Service><eb:Action>100001_Request</eb:Action><eb:MessageData><eb:MessageId>20010215-111212-287572</eb:MessageId><eb:Timestamp>2008-07-15T17:29:00Z</eb:Timestamp></eb:MessageData></eb:MessageHeader><eb:SyncReply SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next" SOAP-ENV:mustUnderstand="1" eb:id="10056109" eb:version="2.0"/></SOAP-ENV:Header><SOAP-ENV:Body xmlns:eb="http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd" xsi:schemaLocation="http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd http://www.oasis-open.org/committees/ebxml-msg/schema/msg-header-2_0.xsd"><eb:Manifest eb:id="Manifest" eb:version="2.0"><eb:Reference xmlns:xlink="http://www.w3.org/1999/xlink" eb:id="Reference-0" xlink:href="cid:payload-0" xlink:type="simple"><eb:Description xml:lang="ko_KR">IBK-0</eb:Description></eb:Reference><eb:Reference xmlns:xlink="http://www.w3.org/1999/xlink" eb:id="Reference-1" xlink:href="cid:payload-1" xlink:type="simple"><eb:Description xml:lang="ko_KR">IBK-1</eb:Description></eb:Reference></eb:Manifest></SOAP-ENV:Body></SOAP-ENV:Envelope>
    ------=_Part_0_8089714.1216110540156
    Content-Type: application/octet-stream
    Content-ID: <payload-0>

                긍정적인 밥

                                   함민복

    시 한편에 삼만원이면
    너무 박하다 싶다가도
    쌀이 두 말인데 생각하면
    금방 마음이 따뜻한 밥이 되네

    시집 한권에 삼천 원이면
    든 공에 비해 헐하다 싶다가도
    국밥이 한 그릇인데
    내 시집이 국밥 한그릇만큼
    사람들 가슴을 따뜻하게 덥혀줄 수 있을까
    생각하면 아직 멀기만 하네

    시집이 한 권 팔리면
    내게 삼백원이 돌아온다
    박리다 싶다가도
    굵은 소금이 한됫박인데 생각하면
    푸른 바다처럼 상할 마음 하나 없네
    ------=_Part_0_8089714.1216110540156
    Content-Type: application/octet-stream
    Content-ID: <payload-1>

                   가을

                                  함민복

    당신 생각을 켜놓은 채 잠이 들었습니다

참고 문헌#

http://java.sun.com/javaee/5/docs/tutorial/doc/bnbhf.html

이 글은 스프링노트에서 작성되었습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 하얀 말


티스토리 툴바