이 글에 소개될 대부분의 작업은 Apache XML 프로젝트의 오픈 소스 툴을 기반으로 한다. (Axis beta 3;WSIF; Web Services Inspection Language (WS-Inspection); Web Services Description Language for Java Toolkit (WSDL4J) API; Universal Description, Discovery and Integration for Java (UDDI4J) API 포함-참고자료). SOAP 애플리케이션의 개방성과 크로스 플랫폼 특징 을 드러내기 위해 펄, 파이썬 PHP로 작성된 클라이언트를 검토하겠다.
먼저 간단한 자바 코드를 이용하여 웹 서비스를 만들어보겠다. 이 샘플 애플리케이션은 Magic Eight Ball 이라고 하는 장난감을 모방한것이다.
자바 버전의 Eight Ball은 무작위로 20개의 스트링 중 하나를 리턴하는 난수(random number) 생성기이다(Listing 1).
Listing 1. EightBall.java에서 답 찾기
import java.util.Random;
import java.lang.Double;
import java.util.Date;
public class EightBall {
static String answers[] = {"Yes.",
"Outlook not so good.",
// The other 17 answers were
// removed for brevity.
"Don't count on it."};
public static String getAnswer() {
return askQuestion("");
}
public static String askQuestion
String question) {
java.util.Random r = new Random
(new Date().getTime());
java.lang.Double d = new Double
((r.nextDouble()*20)-1);
return new String(answers[d.intValue()]);
}
public static void main(String args[]) {
System.out.println(getAnswer());
}
}
|
이 코드에서 가장 재미있는 부분은 웹, 네트워킹, SOAP, XML을 모른다는 점이다. 그럼에도 불구하고 이 코드에 어떤 것도 변경하지 않고 웹 서비스로 만들수 있다.
코드를 실제로 전개하기 전에 세 가지 메소드를 이해해야한다. 주 메소드는 명령행에서 Eight Ball을 호출할 수 있도록 한다. 웹 서비스 클라이언트에서 이것을 사용하지 않을 것이다. getAnswer와 askQuestion 메소드는 웹 서비스로서 노출해야하는 것이다. askQuestion 메소드는 String이 주어질 때, 무작위로 선택된 20 개의 답 중에서 한 개를 리턴한다. getAnswer는 매개변수를 취하지 않고 공백 String으로 askQuestion을 호출한다.
Axis 에서 호스팅되는 웹 서비스로서 코드를 전개하기 위해서는 전개 디스크립터를 만들어야한다. (Listing 2).
전개 디스크립터는 많은 것을 정의한다:
- 서비스 이름(urn:EightBall).
- 서비스 유형 (java:RPC: 자바 코드로 작성된 RPC 스타일의 서비스).
- 서비스를 제공하는 자바 클래스 이름 (EightBall).
- 서비스로 지원되는 메소드 이름(getAnswer와 askQuestion).
용어설명
웹 서비스 툴박스
|
나는 AdminClient 클래스를 사용하여 Axis에서 이 데이터를 프로세스 했다:
일단 Axis가 전개 디스크립터를 프로세싱하면 EightBall.class 파일을 Axis가 찾을 수 있는 디렉토리로 옮긴다. 이 경우 \WEB-INF\classes 디렉토리가 된다. Jakarta Tomcat 4.0.3의 경우 디렉토리는 jakarta-tomcat-4.0.3\webapps 이다.
자바 코드를 웹 서비스로 전개했으면 클라이언트 애플리케이션도 작성할 수 있다. 다양한 언어와 방법으로 가능하다. 클라이언트 애플리케이션을 작성하는 방법에 관계없이 이 애플리케이션은 다음의 사항을 알아야한다:
- 서비스를 호스팅하고 있는 머신 이름.
- 서비스 이름.
- 호출하고자하는 메소드 이름.
- 호출하고자하는 메소드용 매개변수(매개변수의 수와 데이터 타입).
- SOAPAction 필드에 포함되어야 할 것.
첫 번째 애플리케이션은 Axis client API를 사용하여 작성된 자바 클라이언트이다. 다음은Axis API의 네 단계 사용법이다:
- Service 객체 만들기.
- Service 객체를 사용하여 Call 객체 만들기
- Call 객체의 속성 설정하기(메소드 이름, 매개변수).
- Call 객체의 호출 메소드 사용하기.
Listing 3에서는 주목할 만한 것이 몇 가지 있다. 우선, 클라이언트 애플리케이션은 SOAP, XML 등에 대해서는 알아야한다. 그런다음, Service와 Call 객체를 만들고 Call 객체의 속성을 설정한다. 웹 서비스를 호스팅하는 머신 주소를 설정하고 (http://localhost:8070/axis/services/), 서비스 이름을 정의하고 (urn:EightBall), 메소드 이름을 정의 (getAnswer) 하는 것이 포함된다. 모든 속성을 설정했다면, Listing 4의 호출 메소드를 사용할 수 있다.
Listing 3. EightBallClient1 (Axis API 사용)
import javax.xml.rpc.namespace.QName;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.utils.Options;
public class EightBallClient1 {
public static void main(String [] args)
throws Exception {
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(
new java.net.URL
("http://localhost:8070/axis/
services/"));
call.setOperationName(
new QName("urn:EightBall",
"getAnswer"));
|
Listing 4. 호출 메소드 사용하기
try {
System.out.println(call.invoke(
new Object[] { }));
} catch (java.rmi.RemoteException re) {
System.out.println("Error - " + re);
}
|
호출은 Java Object 어레이를 유일한 매개변수로서 전달한다는 것을 주목하라. 자바 코드의 모든것이 궁극적으로는 java.lang.Object에서 상속받기 때문에 이 어레이 안에 모든것을 넣을 수 있다. 첫 번째 클라이언트 애플리케이션에서 getAnswer 메소드를 사용했는데 이것은 매개변수를 갖지 않는다. Java Object의 공백 어레이를 만들고 그대로 둔다는 뜻이된다. 좀더 발전된 클라이언트에서는 좀더 복잡한 아이템을 어레이에 넣을 수 있다.
호출 메소드는 리퀘스트를 포함하는 SOAP envelope을 구현하고 이를 엔드포인트 어드레스로 설정한 머신으로 보낸다. 웹 서비스를 호스팅하고 있는 이 머신은 이 데이터를 SOAP 리퀘스트에서 서비스로 전달한다. 그런다음 그 리퀘스트를 처리하고 응답을 만들어낸다.
마지막으로, 자바 코드 트라이 블록에서 호출 메소드를 사용한다. 분산 애플리케이션이 잘못될 수 있다. 따라서 문제를 예견하는 것이 최상의 방법이다.
다음은 클라이언트 애플리케이션이 명령행에서 어떻게 보이는지를 나타낸것이다:
D:\webservices\eightball>java EightBallClient1 It is decidedly so. |
이것이 최상의 애플리케이션은 아닐지라도 작은 노력을 들인 것 치고는 대단하다:
- 일반적인 코드를 사용하여 네트워크를 통해 메소드를 노출했다.
- 자바 객체의 메소드를 호출하기 위해서 네트워크상에서 누구나 사용할 수 있는 클라이언트 애플리케이션을 작성했다.
- SOAP은 언어중립적이기 때문에 어떤 프로그래밍 언어나 OS에서 자바 객체의 메소드에 액세스할 수 있다.
좀더 세련된 클라이언트 애플리케이션으로 옮겨가기 전에 Axis에는 매우 유용한 트레이싱 툴이 포함되어있다는 것을 기억하길 바란다. 다음은 트레이싱 툴을 시작하는 명령어이다:
tcpmon 클래스에 대한 인자는 SOAP envelope용 모니터 포트는 8070 이라는 것을 나타낸다. tcpmon 클래스는 port 8070로 가는 SOAP envelope을 작성하여 이를 localhost port 8080으로 보낸다. SOAP 응답이 port 8080에서 올 때, tcpmon은 트레이싱 윈도우에 대한 응답을 작성하고 이를 다시 클라이언트로 보낸다. 클라이언트 애플리케이션을 실행할 때 트레이싱 윈도우 모습이다. (그림 1). 그림 1 에서 클라이언트 사이를 왕래할 때 요청-응답 메시지를 볼 수 있다.
그림 1. AXIS SOAP 트레이싱 윈도우(Tracing window)
첫 번째 클라이언트 애플리케이션에서 getAnswer 메소드를 사용했다. 그리고 이것은 어떤 매개변수도 취하지 않았다. 다음 애플리케이션은 askQuestion 메소드를 사용 할 것이다. 이것은 매개변수로서 하나의 String이 필요하다. Call.invoke을 사용하기 전에 약간의 작업이 필요하다. 다행히 작업량은 많지 않다. 다음은 중요한 코드의 일부이다. (Listing 5).
Listing 5. 매개변수와 함께 EightBall 서비스 호출하기
<question xsi:type="xsd:string">
Will Deutschland win the World Cup?
</question>
call.setTargetEndpointAddress(new
java.net.URL
("http://localhost:8070/axis/services/"));
call.setOperationName(new
QName("urn:EightBall", "askQuestion"));
call.addParameter("question",
XMLType.XSD_STRING, ParameterMode.IN);
call.setReturnType(
org.apache.axis.encoding.XMLType.XSD_STRING);
try {
String question =
"Will Deutschland win the World Cup?";
String ret = (String) call.invoke
(new Object[] { question });
System.out.print(question + "\n ");
System.out.println(ret);
}
|
Listing 5는 두 개 정도의 부가 작동을 수행한다는 것을 주목하라. 우선 addParameter 메소드를 사용하여 Call 객체에 새로운 매개변수를 추가한다. 매개변수 이름은 question이고, 이것은 유형 스트링의 하나이고 인풋 매개변수라는 것을 정의한다. 다음으로는 setReturn Type 메소드가 있는데 이는 웹 서비스에 의해 리턴된 데이터가 스트링이라는 것을 Call 객체에게 말해주고있다. 이것은 필요한 모든 속성을 갖춘 Call 객체로 설정한다. 트라이 블록 내부에서 질문을 포함하는 Java String을 만들고 Object 어레이에 있는 스트링을 전달한다. 호출 메소드를 사용할 때 String 자체를 어레이에 넣을 수 있다.
SOAP 리퀘스트가 서버로 갈 때, 매개변수도 포함하고 있다. 매개변수는 다음과 같다. (Listing 6).
Listing 6. 매개변수
String ret = (String) call.invoke
(new Object[] { "Will Deutschland..." });
|
여러 개의 매개변수를 웹 서비스로 보내야한다면 원하는 만큼 addParameter 메소드를 사용할 수 있고 필요한 모든 객체를 호출 메소드가 사용하는 어레이에 넣을 수 있다. 인자의 수나 유형이 맞지 않다면 예외를 갖게된다.
지금 까지는 자바코드로 모든것을 했다. 이것만으로도 충분하지만 SOAP의 목적은 상호운용성이다. SOAP의 상호운용성을 설명하기 위해서 세 가지의 다른 언어로 작성된 클라이언트를 설명하겠다.
펄 클라이언트를 작성은 Pavel Kulchenko의 SOAP::Lite 라이브러리로 할 것이다. Listing 7은 전체 클라이언트 코드이다.
스크립팅 언어답게, 펄 클라이언트는 간단하다. 신택스가 분명히 차이가 있음에도 이 클라이언트는 똑 같은 기본 정보를 정의하고 있다:
- 서비스를 호스팅하고 잇는 머신 주소 (http://local-host:8070/axis/services/).
- 서비스 이름(urn:EightBall).
- 호출하기 원하는 메소드 이름 (askQuestion).
- 호출하기 원하는 메소드용 매개변수 ("Will I win the lottery?").
명령행에서 이 클라이언트를 실행하면 다음의 예상된 결과가 나온다:
Listing 7. 펄 클라이언트 코드
D:\webservices\eightball>eightballclient.pl Perl SOAP client: Signs point to yes. |
다음은 파이썬 클라이언트이다. 파이썬 클라이언트는 SOAP.py 라이브러리를 사용한다. Listing 8은 전체 소스 코드이다.
호스트이름, 서비스 이름 등 같은 항목을 정의한다. 신택스는 다르다. 하지만 결과는 같다:
Listing 8. 파이썬 클라이언트 코드
D:\webservices\eightball>eightballclient.py
Python SOAP client:
Concentrate and ask again.
|
PHP 클라이언트는 HTML 페이지 안에 SOAP 호출을 임베딩한다는 점에서 다소 다르게 느껴진다. 사용자는 주어진 URL (http://localhost/eightballclient.php, in this case)을 요청하고, PHP 라이브러리는 웹 서비스를 호출하고 서비스에서 온 결과를 HTML페이지에 넣는다. Listing 9는 PHP 소스 코드이다.
Listing 9. PHP 클라이언트 소스 코드
<html>
<head>
<title>PHPSOAPclienttest</title>
</head>
<body>
<center>
<h1>WillDeutschland
wintheWorldCup?</h1>
<p>Theanswerfromthe
MagicEightBall:</p>
<p>
<fontsize="+3"color="blue">
<?php
require_once('nusoap.php');
$parameters=array('question'=>'Will
DeutschlandwintheWorldCup?');
$soapclient=
newsoapclient(
'http://localhost:8070/axis/services/');
echo$soapclient->call('askQuestion',
$parameters,'urn:EightBall');
?>
</font>
</p>
</center>
</body>
</html>
|
같은 정보를 사용하고 있지만 웹 서비스에서 온 결과를 HTML 페이지에 임베딩하고 있다. 그림 2는 페이지 모습이다. 이 페이지는 NuSOAP PHP 라이브러리로 구현되었다.
그림 2. EightBall
웹 서비스를 설명하는 Web Services Description Language (WSDL) 파일이 있다면 툴을 사용하여 클라이언트 애플리케이션을 만들 수 있다. Axis는 전개된 서비스에서 WSDL 파일을 자동으로 생성한다. 웹 서비스의 URL은 http://localhost:8080/axis/services/urn:EightBall 이다. Listing 10은 생성된 WSDL 파일의 일부이다.
Listing 10. EightBall WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions...>
...
<wsdl:operation name="getAnswer">
<wsdl:input message=
"intf:getAnswerRequest"/>
<wsdl:output message=
"intf:getAnswerResponse"/>
</wsdl:operation>
...
</wsdl:portType>
<wsdl:binding
name="urn:EightBallSoapBinding"
type="intf:EightBall">
<wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/
soap/http"/>
...
<wsdl:operation name="askQuestion">
...
</wsdl:binding>
<wsdl:service name="EightBallService">
<wsdl:port binding=
"intf:urn:EightBallSoapBinding"
name="urn:EightBall">
<wsdlsoap:address
location=
"http://localhost:8080/axis/
services/urn:EightBall"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
SOAP::Lite의 놀라운 기능 중 하나는 WSDL 파일을 직접 사용할 수 있다는 점이다. 다음은 WSDL 파일에서 웹 서비스에 대한 상세 정보를 얻고 이 정보에 기반한 getAnswer 메소드를 호출하는 펄 클라이언트 (Listing 11)이다.
Listing 11. 펄 클라이언트 WSDL
use SOAP::Lite;
print SOAP::Lite
-> service('http://localhost:8080/axis/
services/urn:EightBall?WSDL')
-> getAnswer(), "\n";
|
이 코드가 펄 클라이언트의 원래 버전보다 그렇게 짧은 것은 아니지만 중요한 차이점을 갖고 있다. 이것은 웹 서비스가 어떻게 구현되었는지에 대한 상세정보를 발견하고 그러한 상세 정보를 사용하여 서비스를 호출한다. 이전의 모든 클라이언트에서 호스트네임, 서비스 이름 같은 상세정보를 하드코딩했다. 이제는 웹 서비스 발견에 대한 두 가지의 접근방식을 설명하겠다.
IBM은 최근 Apache XML Project에 대한 WSIF를 발표했다. WSIF는 개발자와 웹 서비스 호출의 상세를 단절하는 것이다; WSIF를 이용하여, 호출하기 원하는 서비스의 WSDL 디스크립션에 라이브러리를 지정한다. 그리고 프레임웍은 이를 취한다. Listing 12은 웹 서비스를 호출하는 코드의 핵심 부분이다.
Listing 12. WSIF로 웹 서비스 호출하기
WSIFService svc =
ServiceFactory.newInstance().
getService(wsdlLocation);
WSIFPort defPort = svc.getPort();
WSIFOperation myOp =
defPort.createOperation("askQuestion");
WSIFMessage inMsg =
myOp.createInputMessage();
WSIFMessage outMsg =
myOp.createOutputMessage();
WSIFMessage faultMsg =
myOp.createFaultMessage();
myOp.executeRequestResponse
(inMsg, outMsg, faultMsg);
System.out.println("Result:"+
outMessage.getStringPart("return"));
|
Listing 12의 코드를 보면 이것이 호출하고 있는 서비스의 종류를 말하기 힘들다. 디자인 때문이다. WSIF는 여러분이 그러한 상세를 봐야할 필요를 차단한다. 서비스를 호출하는 방법에 대한 정보는 WSDL 파일에서 얻는다. WSIF 라이브러리는 런타임 시 그러한 상세들을 발견한다.
웹 서비스 발견에는 두 가지 중요한 접근방식이 있다. 하나는 WS-Inspection을 사용하는 것이다. WS-Inspection으로 서비스 제공자의 URL을 알아서 서비스 제공자에게 웹 서비스의 모든 리스트를 포함하고 있는 WS-Inspection 문서를 요청한다. 다른 접근방식은 UDDI를 사용하는 것이다. UDDI 스팩은 웹 서비스의 레지스트리가 어떻게 보여지는지를 정의한다. UDDI로 클라이언트 애플리케이션은 서비스 제공자의 URL을 발견한다. 웹 서비스를 공개하고 싶다면 레지스트리에 이것의 디스크립션을 저장하고 UDDI 레지스트리에서 WSDL 디스크립션을 발견하는 클라이언트를 만들고 웹 서비스를 호출한다.
제공하고 있는 서비스 유형과 고객의 필요에 따라 두 가지 방법 모두를 사용할 수 있다. 일반적인 목적으로 서비스를 제공하고 싶다면 이들을 UDDI 레지스트리에 넣는 것이 가장 간단한 방법이다. 소수의 파트너와 고객에게만 제공하고 싶다면 WS-Inspection을 사용하는 것이 가장 간단한 접근방식이다.
두 경우 모두 이전에 클라이언트 애플리케이션을 구현했을 때 필요로했던 정보를 발견하는 메커니즘을 사용하고 있다. 다음은 클라이언트 애플리케이션이 알아야하는 다섯 가지와 웹 서비스 발견에 대한 두 가지 접 근방식을 처리하는 방법이다:
- 서비스를 호스팅하고 있는 머신 이름. WS-Inspection에서 머신의 이름을 안다. 직접 가서 서비스 리스트를 요청한다. UDDI 에서는, 레지스트리로 가서 머신의 이름을 발견한다.
- 서비스 이름. WS-Inspection 또는 UDDI를 사용하고 있는지를 발견한다.
- 호출하기 원하는 메소드 이름. 이를 발견하는 것은 기술적으로는 가능하지만 거의 필요하지 않다.
- 메소드용 매개변수. 메소드 이름을 발견하기로 결정하더라도 클라이언트 애플리케이션은 어떤 종류의 데이터를 웹 서비스로 전달하려는지에 대한 사전 지식이 있어야한다. 툴로는 두 스트링을 주어진 서비스로 전달하는 코드를 만들 수 있다. 하지만 그러한 두 개의 스트링이 무엇인지를 정의하는 비지니스 로직을 작성하는 것은 여러분의 몫이다.
- SOAPAction 필드에 포함되어 있는 것. WSDL 파일에서 이를 발견할 수 있다. 다양한 벤더들은 SOAPAction 필드를 다르게 처리하기 때문에 이 정보를 발견하는 것은 좋은 생각이다. 예를 들어, Axis가 웹 서비스를 호스팅 한다면, SOAPAction 필드는 완전히 무시된다. 만약 이것을 Microsoft .NET이 호스팅하고 있다면, SOAPAction 필드는 특정 방식으로 포맷되어야한다. SOAPAction 필드가 특정 포맷에 있어야 하는 SOAP 툴킷으로 호스팅되는 웹 서비스를 호출할 때 클라이언트 애플리케이션은 그 포맷이 어떤 것인지를 알아야한다.
이 다섯 가지 항목 중에서 1, 2, 5번 항복을 발견하는 것은 쉽다. 3번 항목은 거의 찾지 않고 4번 항목을 발견하는 것은 이치에 맞지 않는다. 웹 서비스가 매개변수로서 두 개의 스트링을 취한다는 것을 WSDL 파일을 통해 발견할 수 있다. 하지만 두 개의 스트링이 무엇이 되어야하는지를 설명하는 비지니스 로직을 작성해야한다. 무작위로 두 개의 스트링을 집어 이를 막 발견한 웹 서비스로 보내는 것은 좋은 생각이 아니다.
WS-Inspection은 웹 서비스 발견을 위한 제안 표준이다. 앞서 설명했듯이 WS-Inspection은 여러분이 서비스를 호스팅하는 머신 이름을 알고 있다고 가정한다. 여러분은 그 머신으로 가서 서비스 리스트를 요청한다. 서비스를 호스팅하고 잇는 머신이 http://localhost/ 라면, http://localhost/inspection.wsil 파일에서 웹 서비스 리스트를 찾는다. Listing 13은 EightBall 서비스를 참조한 간단한 WSIL 파일이다.
Listing 13. WS-Inspection 파일
<?xml version="1.0"?>
<inspection
xmlns="http://schemas.xmlsoap.org/ws
/2001/10/inspection/"
xmlns:wsilwsdl=
"http://schemas.xmlsoap.org/ws
/2001/10/inspection/wsdl/">
<service>
<abstract xml:lang="en-US">
WSDL description for the
Eight Ball service
</abstract>
<name xml:lang=
"en-US">urn:EightBall</name>
<descriptionreferencedNamespace=
"http://schemas.xmlsoap.org/wsdl/"
location=
"http://localhost/EightBall.wsdl"/>
</service>
</inspection>
|
이 파일에서 두 가지 중요한 항목은 서비스 엘리먼트 안에 있는 이름 엘리먼트(urn:EightBall)와 디스크립션 엘리먼트의 위치 애트리뷰트(http://localhost/EightBall.wsdl)이다. WS-Inspection 발견 시나리오에서 클라이언트 애플리케이션은 특정 서비스용 WS-Inspection 파일을 찾고 서비스를 설명하는 WSDL 문서의 URL을 얻는다. WSDL 문서에서 웹 서비스의 상세를 찾고 이를 호출한다.
WS-Inspection이 발견할 수 있도록 웹 서비스를 설정하려면 다음의 세 단계를 거쳐야한다:
- WSDL 파일을 만든다. Axis 툴킷은 이를 수행한다.
- 웹 서버의 문서 루트(root)에 EightBall.wsdl로 저장한다.
- WS-Inspection 파일을 만든다. inspection.wsil 파일을 만들고 이를 웹 서버의 문서 루트에 저장한다.
이 단계를 완성하면 이 서버에서 지원되는 모든 웹 서비스 리스트를 요청하는 클라이언트를 만들어 이들 서비스 중 하나를 선택할 수 있다.
마지막 주제는 UDDI이다. UDDI로 UDDI 레지스트리에 서비스 디스크립션을 저장한다. 클라이언트는 적당한 서비스용 레지스트리를 검색하고 상세항목을 찾고(WSDL 파일) 서비스를 호출한다. 서비스가 실패한다면 클라이언트는 레지스트리로 가서 같은 서비스의 다른 제공자를 찾고 서비스를 호출한다. UDDI 접근방식은 유연하다. 하지만 그만큼 많은 작업이 필요하다.
UDDI 발견은 좀더 복잡하다:
- WSDL 파일을 두 개로 나누기. 하나의 파일은 서비스와의 인터페이스를 설명하고 다른 파일은 구현을 설명한다. 다시말해서 WSDL 파일 인터페이스는 " EightBall 서비스라는 것이 있고 이것은 getAnswer와 askQuestion 이라는 메소드를 가지고 있으며 그러한 메소드에 대한 인풋이 있고 아웃풋이 있다."는 것을 설명한다. 구현 WSDL 파일은 "EightBall 인터페이스를 구현하고 이 서비스 구현에 액세스하는데 필요한 상세가 있다."는 것을 말한다.
- 인터페이스 WSDL 파일을 TModel로 등록하기. TModel은 UDDI 구조체로서 표준을 참조하고 있다. UDDI 스팩은 그들을 tModels로 정의하지만, UDDI4J 라이브러리는 TModel이라는 자바 클래스를 정의한다. 이 글에서는 두 개 모두를 TModels 라고 통칭하겠다.
- 회사를 위한 BusinessEntity 만들기. 웹 서비스를 제공하는 조직을 나타낸다.
- 특정 웹 서비스를 위한 BusinessService 만들기. BusinessService는 BusinessEntity와 TModel 모두를 참조한다.
WSDL 파일을 두 부분으로 나누기
인터페이스 WSDL 파일은 타입, 메시지, portType 엘리먼트를 포함하고 있다. 이것은 웹 서비스
메소드를 정의한다. WSDL 파일 구현은 바인딩, 서비스, 포트 엘리먼트를 포함하고 있으며 WSDL 구현
파일을 포함하기 위하여 반입 구문을 사용한다. 그림 3은 WSDL
파일을 나누는 방법이다.
그림 3. WSDL 파일 나누기
인터페이스 WSDL 파일을 TModel로 등록하기
UDDI 스팩은 표준에 대한 레퍼런스로서 TModel을 정의한다. 이 정의는 완전히 개방적이다. 이 경우,
EightBall 서비스에 대한 인터페이스를 표준으로 정의하고 있다. UDDI 레지스트리에서 TModel
객체를 만들고 그 TModel에 고유 ID를 준다. EightBall 서비스의 구현을 정의할 때, TModel을
참조하여 서비스가 표준을 지원하고 있다는 것을 표시한다. Listing
14는 새로운 TModel을 정의하는 XML 엘리먼트이다.
Listing 14. EightBall XML TModel
<tModel tModelKey="">
<name>EightBall Interface</name>
<description xml:lang="en">
The interface-only definition
of the EightBall service.
</description>
<overviewDoc>
<description xml:lang="en">
The mystical powers of the EightBall,
channeled into Java.
</description>
<overviewURL>
http://localhost/eightball.org/
eightballinterface.wsdl
</overviewURL>
</overviewDoc>
<categoryBag>
<keyedReference
tModelKey="UUID:C1ACF26D-9..."
keyName="uddi-org:types"
keyValue="wsdlSpec"/>
</categoryBag>
</tModel>
|
UDDI4J 라이브러리는 SOAP envelope에 XML 엘리먼트를 넣고 이것을 레지스트리로 보낸다; TModel이 성공적으로 만들어지면, 새로운 TModel의 속성을 설명하는 TModelDetail 엘리먼트를 받는다. 이 속성 중 가장 중요한 것은 TModel의 고유 ID 이다.
BusinessEntity 만들기
그런 다음 BusinessEntity를 만들어 서비스 제공자를 표현한다. BusinessEntity에 여러
항목이 포함될 수 있다; Listing 15는 샘플 프로그램이 사용하는
XML 이다.
Listing 15. EightBall BusinessEntity markup
<businessEntity businessKey="">
<discoveryURLs>
<discoveryURL useType="wsil">
http://localhost/inspection.wsil
</discoveryURL>
</discoveryURLs>
<name>DougCo Software</name>
<description xml:lang="en">
Sample business created for
the UDDI discovery demo.
</description>
<contacts>
<contact useType="Technical contact">
<personName>Doug Tidwell</personName>
<phone useType=
"voice">1-919-555-5583</phone>
<phone useType=
"fax">1-919-555-2389</phone>
<phone useType=
"mobile">1-919-555-9385</phone>
<email>dtidwell@us.ibm.com</email>
<address>
<addressLine>1234 Main Street
</addressLine>
<addressLine>Anytown, TX 73958
</addressLine>
</address>
</contact>
</contacts>
<categoryBag>
<keyedReference keyName=
"uddi-org:iso-ch:3166-1999"
keyValue="US-NC" tModelKey=
"UUID: 4E49A8D6-D5A2-4FC2-93A0-
0411D8D19E88"/>
<keyedReferencekeyName= "ntis-gov:naics:1997"
keyValue="541511" tModelKey=
"UUID: C0B9FE13-179F-413D-8A5B-5
004DB8E5BB2"/>
<keyedReference keyName="unspsc-org:unspsc"
keyValue="43.16.26.05.00" tModelKey=
"UUID: CD153257-086A-4237-B336-6BDCBD
CC6634"/>
</categoryBag>
</businessEntity>
|
BusinessEntity에는 많은 것이 있다. 비지니스에 이름과 디스크립션을 부여했고 담당자를 정의했다. 밑 부분에 세 가지 다른 TModel을 언급하는 참조 부호를 넣었다:
- uddi-org:iso-ch:3166-1999. 지리적 위치를 위한 ISO 이름.
- ntis-gov:naics:1997. North American Industry Classification System.
- unspsc-org:unspsc. United Nations Standard Products과 Services Code.
BusinessService 만들기
BusinessService를 만들 때, 구현용 WSDL 파일을 참조하는 적절한 상세를 제공해야한다. 물론,
BusinessEntity의 ID와 TModel의 ID를 참조할 것이다. (Listing
16).
Listing 16. EightBall BusinessService markup
<businessService serviceKey=""
businessKey="00EB06FC-3C45-42B8-B394-1
F7F35602B2E">
<name>EightBallservice</name>
<descriptionxml:lang="en">
ThepoweroftheMagicEightBall,
channelledthroughcode.
</description>
<bindingTemplates>
<bindingTemplatebindingKey=""
serviceKey="">
<descriptionxml:lang="en">
Tiesthisimplementationtothe
EightBallInterface.
</description>
<accessPointURLType="http">
http://localhost:8080/axis/services/
urn:EightBall
</accessPoint>
<tModelInstanceDetails>
<tModelInstanceInfotModelKey=
"UUID:0B6E1227-329A-4657-89B5-
35DA36CA2554">
<descriptionxml:lang="en">
Implementationofthe
EightBallInterface.
</description>
<instanceDetails>
<overviewDoc>
<overviewURL>
http://localhost/
EightBallImplementation.wsdl
</overviewURL>
</overviewDoc>
</instanceDetails>
</tModelInstanceInfo>
</tModelInstanceDetails>
</bindingTemplate>
</bindingTemplates>
</businessService>
|
BusinessService 정의의 세 가지 중요한 부분은 비지니스 키에 대한 레퍼런스, TModel key, EightBall 서비스 구현을 설명하는 WSDL 문서의 URL 이다.
UDDI를 통해 발견 작동을 수행하는 클라이언트 애플리케이션을 작성하려면 적절한 TModel을 참조하는 BusinessServices를 검색해야한다. 찾게되면 레지스트리에 후속 쿼리를 실시하여 상세를 얻을 수 있다. 궁극적인 목표는 WSDL 파일의 URL을 검색하는 것이다; 일단 그렇게 되면 WSIF를 사용하여 WSDL 파일에서 웹 서비스를 호출할 수 있다. UDDI 발견 코드의 첫 번째 단계는 속성 파일에서 TModel 키를 검색하는 것이다. TModel을 만들었다면 속성 파일에 키를 저장한다. 키를 사용해야 할 경우 파일에서 값을 검색한다. (Listing 17).
Listing 17. 파일에서 키 값 검색하기
TModelBag tmb = new TModelBag();
TModelKey tmk = new TModelKey();
File testFile = new File
("eightball.properties");
if (testFile.exists()){
FileInputStream propStream =
new FileInputStream(testFile);
props.load(propStream);
propStream.close();
}
tmk.setText(props.getProperty("TModelKey");
Vector tModelKeys = newVector();
tModelKeys.add(tmk);
tmb.setTModelKeyVector(tModelKeys);
|
이것은 TModelBag 구조를 만드는데 이것은 주문되지 않은 TModels의 컬렉션이다. 이 구조를 검색 기준의 일부로 사용하게 된다. UDDI 레지스트리에 요청하여 TModelBag의 모든 TModel을 참조하는 BusinessService 객체를 받도록 한다.
UDDI 레지스트리가 매치되었다고 가정하고, 첫 번째를 가져다가 이 서비스와 관련된 바인딩 템플릿을 찾는다(Listing 18). 웹 서비스의 구현을 설명하는 WSDL의 URL을 찾기위해 서비스의 다른 속성을 찾게된다.
Listing 18. EightBall WSDL URL을 찾는 자바코드
ServiceList sl = proxy.find_service
("", null, null, tmb, null, 0);
Vector serviceInfoVector =
sl.getServiceInfos().getServiceInfoVector();
String serviceKey =
((ServiceInfo)(serviceInfoVector.
elementAt(0)).getServiceKey();
BindingDetail bd = proxy.find_binding(null,
serviceKey, tmb, 0);
Vector bindingTemplates=
bd.getBindingTemplateVector();
BindingTemplate bt = (BindingTemplate)
bindingTemplates.elementAt(0);
AccessPoint ap = bt.getAccessPoint();
Vector tModels =
bt.getTModelInstanceDetails().
getTModelInstanceInfoVector();
TModelInstanceInfo tmii=
(TModelInstanceInfo)tModels.elementAt(0);
InstanceDetails id =
tmii.getInstanceDetails();
OverviewDoc od = id.getOverviewDoc();
OverviewURL ou = od.getOverviewURL();
|
OverviewURL은 EightBall 서비스 구현을 설명하는 WSDL 문서의 URL 이다.
간단한 코드를 이용하여 웹 서비스를 만들었다. 서비스를 전개하고 다양한 프로그래밍 언어로 이 서비스를 위한 클라이언트를 작성하고 웹 서비스 발견에 대한 두 가지 접근 방식도 살펴보았다. 이 글을 통해 SOAP, WSDL, UDDI, WSIL, WSIF 등의 다양한 기술도 접했다. 이 글이 웹 서비스 애플리케이션을 구현하는데 디딤돌이 될 수 있기를 희망한다.
- developerWorks worldwide 사이트에서 이 기사에 관한 영어원문.
- 이
글에 사용된 애플리케이션 소스코드.
- Axis.
- 표준 스팩:
- SOAP
Lite library, SOAP.py
library, NuSOAP
library.
- IBM
alphaWorks WSTK 다운로드.