Wsdl

From 탱이의 잡동사니
Jump to: navigation, search

Overview

WSDL(Web Services Description Language의 약자)은 웹 서비스 기술언어 또는 기술된 정의 파일의 총칭으로 XML로 기술된다. 웹 서비스의 구체적 내용이 기술되어 있어 서비스 제공 장소, 서비스 메시지 포맷, 프로토콜 등이 기술된다.[1]

Using WSDL

WSDL 파일은 단지 웹 서비스의 내용만이 기술되어 있다. 따라서 이 wsdl 파일을 사용해 프로그램을 작성하기 위해서는 wsdl 파일에서 헤더 파일을 추출하고, 스텁 코드를 생성하는 작업을 추가로 해주어야 한다.

Using WSDL on C

먼저 wsdl 파일로부터 헤더 파일을 추출해야 한다. 여기서는 gsoap 를 사용하기로 한다. 헤더파일 추출 이후, 스텁 코드와 헤더 파일을 이용하여 라이브러리를 만드는 예제이다.

여기에서는 덴마크 통신사 TDC에서 제공하는 SendSms.wsdl, ReceiveSms.wsdl, SmsNotificationManagerService.wsdl 파일 세개를 사용하기로 한다. 각각의 파일들의 원본은 https://cpsapi.tdc.dk/SendSmsService?wsdl, https://cpsapi.tdc.dk/ReceiveSmsService?wsdl, https://cpsapi.tdc.dk/SmsNotificationManagerService?wsdl 에서 확인할 수 있다.

Extract header file

wsdl 에서 헤더 파일을 추출하는 프로그램에는 여러가지가 있지만 여기서는 설치와 사용이 간편한 gsoap 를 사용하기로 한다.

  • Install gsoap
$ sudo apt-get install gsoap
  • Extract header file
$ wsdl2h -c -o SoapEnv.h SendSms.wsdl
$ wsdl2h -c -n SendSms -o SendSms.h SendSms.wsdl
$ wsdl2h -c -n ReceiveSms -o ReceiveSms.h ReceiveSms.wsdl
$ wsdl2h -c -n SmsNotificationManagerService -o SmsNotificationManagerService.h SmsNotificationManagerService.wsdl

위의 명령어를 살펴보면 추출해야 하는 wsdl 파일을 3개인데 반해, 추출한 헤더파일은 4개이다. 정확히는 SendSms.wsdl 파일에서 두 개의 헤더 파일을 추출했다.

그 이유는 실제 컴파일과 라이브러리 구현에 사용할 gsoap를 위해서 기본 header 파일이 필요했기 때문이다. 그리고, 위의 사용한 옵션 중, -c 옵션은 c 언어를 위한 헤더파일 생성 옵션, -n 옵션은 namespace 영역을 구분짓기 위해 사용한 옵션이다.

하나 이상의 wsdl 파일을 사용하는 라이브러리 제작시, -n 옵션이 특히 중요한데, 이유는 -n 옵션 없이 wsdl2h 를 사용할 경우, 서로 혼합된 namespace 영역이 사용되기 때문이다. 서로 혼합된 namespace 영역이 사용되면 생성되는 헤더 파일에 문제가 생긴다. 지정된 namespace 없이 wsdl2h 를 사용하게 되면 매 사용시마다 다른 메소드 prefix 가 지정되기 때문이다.[2]

이 때문에 2 개 이상의 wsdl 파일을 사용하는 라이브러리 작성시, namespace 지정은 반드시 필요한 옵션이다.

Stub code

wsdl2h 를 사용하면 각각의 헤더 파일들이 만들어진다. 이렇게 만들어진 헤더파일들을 이용해 실제 C에서 사용 가능한 코드로 만들어 줘야 한다. 이 작업을 해주는 프로그램이 soapcpp2 이다.

  • soapcpp2
$ soapcpp2 -C -c SoapEnv.h
$ soapcpp2 -C -c -n -pSendSms SendSms.h
$ soapcpp2 -C -c -n -pReceiveSms ReceiveSms.h
$ soapcpp2 -C -c -n -pSmsNotificationManagerService SmsNotificationManagerService.h

가급적 위의 명령어를 makefile 과 같은 곳에 넣어 빌드시 마다 수행되도록 하자. 왜냐하면 soapcpp2 프로그램의 버전때문이다.

soapcpp2 프로그램으로 생성되는 파일은 *.c 파일과 *.h 파일들이다. 즉, 이를 이용하여 라이브러리를 만들게 되는데, 같이 링크 시켜줘야 되는 파일 중 하나가 libgsoap.a 파일이다.

그런데, 만약 *.c 파일과 *.h 파일을 생성할 때 사용한 gsoap 버전과 실제 프로그램에 링크되는 libsoap.a 파일의 버전이 서로 맞지 않는다면 링크 오류가 발생하는 것이다. 간단한 예로, stub 코드를 생성한 곳과 프로그램 전체 빌드하는 곳이 서로 다른 경우를 생각하면 된다. 따라서 가급적 컴파일을 진행 할 때마다, stub 코드를 다시 생성하여, 정확한 버전의 libgsoap.a 파일이 링크되도록 하는 것이다.

그리고, wsdl2h 사용시와 마찬가지로, namespace 영역 구분을 위해 -n 옵션과 -p 옵션을 추가하였다.

Compile

namespace 사용시 별도의 define 이 필요하다.[3]

DEFS                    = -DWITH_OPENSSL -DWITH_NONAMESPACES

마지막으로.. 링크 과정에서 알 수 없는 오류가 계속 발생했다. 이상하게 gsoap 라이브러리 추가시, 자꾸 세그먼트 폴트가 발생하는 것이다. 원인을 못찾다가 링크 라이브러리 제일 마지막 순서에 gsoap 라이브러리를 추가해야 오류가 나지 않는 것을 확인했다. 왜 그러는지는 모르겠으나, 다른 해결 방법을 찾지 못했다.

LIBS            =  -lzmq -lczmq -luuid -ljansson -lssl -lcrypto -lpthread -lcurl -lgsoapssl -lgsoap

WSDL example

<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://www.w3.org/ns/wsdl" 
             xmlns:tns="http://www.example.com/wsdl20sample" 
             xmlns:whttp="http://www.w3.org/ns/wsdl/http"
             xmlns:wsoap="http://www.w3.org/ns/wsdl/soap"
             targetNamespace="http://www.example.com/wsdl20sample">
 
 
<!-- Abstract types -->
   <types>
      <xs:schema xmlns="http://www.example.com/wsdl20sample"
                 xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                 targetNamespace="http://www.example.com/wsdl20sample">
 
         <xs:element name="request">
            <xs:complexType>
               <xs:sequence>
                  <xs:element name="header" maxOccurs="unbounded">
                     <xs:complexType>
                        <xs:simpleContent>
                           <xs:extension base="xs:string">
                              <xs:attribute name="name" type="xs:string" use="required"/>
                           </xs:extension>
                        </xs:simpleContent>
                     </xs:complexType>
                  </xs:element>
                  <xs:element name="body" type="xs:anyType" minOccurs="0"/>
               </xs:sequence>
               <xs:attribute name="method" type="xs:string" use="required"/>
               <xs:attribute name="uri" type="xs:anyURI" use="required"/>
            </xs:complexType>
         </xs:element>
 
         <xs:element name="response">
            <xs:complexType>
               <xs:sequence>
                  <xs:element name="header" maxOccurs="unbounded">
                     <xs:complexType>
                        <xs:simpleContent>
                           <xs:extension base="xs:string">
                              <xs:attribute name="name" type="xs:string" use="required"/>
                           </xs:extension>
                        </xs:simpleContent>
                     </xs:complexType>
                  </xs:element>
                  <xs:element name="body" type="xs:anyType" minOccurs="0"/>
               </xs:sequence>
               <xs:attribute name="status-code" type="xs:anySimpleType" use="required"/>
               <xs:attribute name="response-phrase" use="required"/>
            </xs:complexType>
         </xs:element>
      </xs:schema>
   </types>
 
 
<!-- Abstract interfaces -->
   <interface name="RESTfulInterface">
      <fault name="ClientError" element="tns:response"/>
      <fault name="ServerError" element="tns:response"/>
      <fault name="Redirection" element="tns:response"/>
      <operation name="Get" pattern="http://www.w3.org/ns/wsdl/in-out">
         <input messageLabel="GetMsg" element="tns:request"/>
         <output messageLabel="SuccessfulMsg" element="tns:response"/>
      </operation>
      <operation name="Post" pattern="http://www.w3.org/ns/wsdl/in-out">
         <input messageLabel="PostMsg" element="tns:request"/>
         <output messageLabel="SuccessfulMsg" element="tns:response"/>
      </operation>
      <operation name="Put" pattern="http://www.w3.org/ns/wsdl/in-out">
         <input messageLabel="PutMsg" element="tns:request"/>
         <output messageLabel="SuccessfulMsg" element="tns:response"/>
      </operation>
      <operation name="Delete" pattern="http://www.w3.org/ns/wsdl/in-out">
         <input messageLabel="DeleteMsg" element="tns:request"/>
         <output messageLabel="SuccessfulMsg" element="tns:response"/>
      </operation>
   </interface>
 
 
 
<!-- Concrete Binding Over HTTP -->
   <binding name="RESTfulInterfaceHttpBinding" interface="tns:RESTfulInterface" 
            type="http://www.w3.org/ns/wsdl/http">
      <operation ref="tns:Get" whttp:method="GET"/>
      <operation ref="tns:Post" whttp:method="POST" 
                 whttp:inputSerialization="application/x-www-form-urlencoded"/>
      <operation ref="tns:Put" whttp:method="PUT" 
                 whttp:inputSerialization="application/x-www-form-urlencoded"/>
      <operation ref="tns:Delete" whttp:method="DELETE"/>
   </binding>
 
<!-- Concrete Binding with SOAP-->
   <binding name="RESTfulInterfaceSoapBinding" interface="tns:RESTfulInterface" 
            type="http://www.w3.org/ns/wsdl/soap" 
            wsoap:protocol="http://www.w3.org/2003/05/soap/bindings/HTTP/"
            wsoap:mepDefault="http://www.w3.org/2003/05/soap/mep/request-response">
      <operation ref="tns:Get" />
      <operation ref="tns:Post" />
      <operation ref="tns:Put" />
      <operation ref="tns:Delete" />
   </binding>
 
 
<!-- Web Service offering endpoints for both the bindings-->
   <service name="RESTfulService" interface="tns:RESTfulInterface">
      <endpoint name="RESTfulServiceRestEndpoint" 
                binding="tns:RESTfulInterfaceHttpBinding" 
                address="http://www.example.com/rest/"/>
      <endpoint name="RESTfulServiceSoapEndpoint" 
                binding="tns:RESTfulInterfaceSoapBinding" 
                address="http://www.example.com/soap/"/>
   </service>
</description>

References

  1. http://ko.wikipedia.org/wiki/WSDL
  2. http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc8
  3. http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc19.36