XML-RPC Fault 문서 포맷

XML-RPC request 처리 도중 발생하는 오류에 대한 Response 문서 형식은
하나의 값을 갖는 fault 엘리먼트로 기술한다.

[code]
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value><string>No such method!</string></value>
   </fault>
</methodResponse>
[/code]

실제로 처리 실패에는 error code가 부여된 경우가 일반적이므로 아래와 같이 struct로 에러 코드와
에러 문구를 함께 전송할 수도 있다.
[code]
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>code</name>
               <value><int>26</int></value>
            </member>
            <member>
               <name>message</name>
               <value><string>No such method!</string></value>
            </member>
         </struct>
      </value>
   </fault>
</methodResponse>
[/code]
2010/04/26 12:09 2010/04/26 12:09
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC 데이타 형식과 표기 방법

XML-RPC의 데이터 타입
 Type  Value  Examples
int or i4 32-bit integers between - 2,147,483,648 and 2,147,483,647. <int>27<int>
<i4>27<i4>
double 64-bit floating-point numbers <double>27.31415</double>
<double>-1.1465</double>
Boolean true (1) or false (0) <boolean>1</boolean>
<boolean>0</boolean>
string ASCII text, though many implementations support Unicode <string>Hello</string>
<string>bonkers! @</string>
dateTime.iso8601 Dates in ISO8601 format: CCYYMMDDTHH:MM:SS <dateTime.iso8601>
20021125T02:20:04
</dateTime.iso8601>
<dateTime.iso8601>
20020104T17:27:30
</dateTime.iso8601>
base64 Binary information encoded as Base 64, as defined in RFC 2045 <base64>
SGVsbG8sIFdvcmxkIQ==
</base64>


배열 데이타는 data 엘리먼트를 포함하는 array 엘리먼트로 기술한다.
[code]
<value>
   <array>
      <data>
         <value><string>This </string></value>
         <value><string>is </string></value>
         <value><string>an </string></value>
         <value><string>array.</string></value>
      </data>
   </array>
</value>
[/code]

아래는 4개의 integer 값을 갖는 배열을 표현한다.
[code]
<value>
   <array>
      <data>
         <value><int>7</int></value>
         <value><int>1247</int></value>
         <value><int>-91</int></value>
         <value><int>42</int></value>
      </data>
   </array>
</value>
[/code]

Array 구조는 서로 다른 데이터 타입을 포함하는것을 허용한다.
[code]
<value>
   <array>
      <data>
         <value><boolean>1</boolean></value>
         <value><string>Chaotic collection, eh?</string></value>
         <value><int>-91</int></value>
         <value><double>42.14159265</double></value>
   </data>
   </array>
</value>
[/code]

다차원 배열의 경우 간단히 Array 를 중첩하여 표기한다.
[code]
<value>
   <array>
      <data>
         <value>
            <array>
               <data>
                  <value><int>10</int></value>
                  <value><int>20</int></value>
                  <value><int>30</int></value>
               </data>
            </array>
         </value>
         <value>
            <array>
               <data>
                  <value><int>15</int></value>
                  <value><int>25</int></value>
                  <value><int>35</int></value>
               </data>
            </array>
         </value>
      </data>
   </array>
</value>
[/code]
2010/04/26 11:59 2010/04/26 11:59
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC Request 문서 포맷

XML-RCP Request Body 예시
double 타입의 변수를 인자로 하는 circleArea 메소드를 호출하는 XML-RPC 문서의 예.
[code]
<?xml version="1.0"?>
<methodCall>
   <methodName>circleArea</methodName>
      <params>
         <param>
            <value><double>2.41</double></value>
         </param>
      </params>
</methodCall>
[/code]


XML-RPC 헤더
[code]
POST /target HTTP 1.0
User-Agent: Identifier
Host: host.making.request
Content-Type: text/xml
Content-Length: length of request in bytes
[/code]


XML-RPC request 문서 전체 예시
[code]
POST /xmlrpc HTTP 1.0
User-Agent: myXMLRPCClient/1.0
Host: 192.168.124.2
Content-Type: text/xml
Content-Length: 169
<?xml version="1.0"?>
<methodCall>
   <methodName>circleArea</methodName>
      <params>
         <param>
            <value><double>2.41</double></value>
         </param>
      </params>
</methodCall>
[/code]
2010/04/26 11:40 2010/04/26 11:40
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC Response 문서 포맷

[code]
<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><double>18.24668429131</double></value>
      </param>
   </params>
</methodResponse>
[/code]
XML-RPC response는 단 하나 parameter만을 갖는다.
파라미터는 하나 이상의 값을 리턴하기 위해 struct 나 array 형식일 수 있다.
파라미터는 필수항목으로 비록 처리하는 쪽 메소드의 리턴 값이 없을지라도
'정상처리'를 표시하기 위해 boolean의 true (1)을 파라미터로 표현해야 한다.


[code]
HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124
[/code]
XML-RPC의 경우 HTTP1.0 스펙을 지원하지만 HTTP1.1과도 호환이 된다.
Content-type의 경우 'text/xml' 형식만 허용한다.
response의 길이를 bytes 단위로 Content-Length 에 표기한다.


[code]
HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124

<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><double>18.24668429131</double></value>
      </param>
   </params>
</methodResponse>
[/code]
결과적으로 위와 같은 형태의 문서가 XML-RPC 서버로부터 XML-RPC 클라이언트로 전송되고 connection을
끊는다.

2010/04/26 11:36 2010/04/26 11:36
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC로 원격 메소드를 호출하기.

자바 세상에는 서로 다른 버추얼머신 상에서 작동하고 있는 객체 간의 통신을 위해 RMI(Remote Method Invocation)라는
기술을 이용할 수 있습니다. (물론 이 RMI도 근본적으로는 소켓통신을 하게 되지만 입출력 스트림의 마샬링과 언마샬링을 같은
복잡하고 지루한 작업을 하지 않아도 되죠.)

RMI라는것은 간단히 말해 다른 버추얼머신상에 존재하는 객체의 메소드를 마치 자신의 메소드인 것처럼 사용할 필요가 있을 때
이용할 수 있는 기법입니다.
예를 들면, 서버 A는 제품정보를 이용해 복잡한 연산을 거쳐 유사한 제품, 혹은 입력한 제품이 속할 분류를 추론해 내는 서비스를
한다고 가정합시다. 서버 B는 관리자가 제품정보를 등록하고 관리하는 역할을 수행합니다. 이때 서버 B에서 관리자가 제품정보를
등록하기 전 등록하고자 하는 제품의 유사한 제품을 확인하고 싶어합니다.
이런 때, 어떻게 할 수 있을까요?  
우선은 서버 A의 기능을 서버 B에도 구현하는 방법이 있겠군요. 하지만 이 방법은 서버와 개발 리소스의 낭비라고 판단됩니다.
혹은, A서버 기능을 B서버에 구현하지 못하는 상황이 생길 수도 있겠죠.
이럴 경우 RMI는 적절한 구원자가 될 수 있습니다. RMI를 이용하면 A서버에서 수행하는 복잡한 연산은 겉으로 드러내지 않으면서
A서버의 특정 기능을 B서버에서 마치 자신의 기능인양 사용할 수 있도록 해 줄 수 있습니다. (객체의 마샬링과 언마샬링에는
상당한 네트웍, CPU자원이 필요한 건 사실입니다만, 이로써 두 시스템의 자연스런 통합이 가능해 집니다.)

서두가 길어졌네요.

이번 포스팅에는 RMI처럼 원격 메소드를 호출하는 다른 방법에 대해 알아보고자 합니다.
이름하여 XML-RPC.
RPC( Remote Procedure Call)은 RMI라는 단어가 있기 전부터 쓰이던 용어로, 분산환경에서 이기종 프로세스를 네트워크를 통해
호출할 수 있도록 하는 기술입니다. XML-RPC는 RPC프로토콜을 XML로 구현한 것이며, HTTP프로토콜상에서 XML데이터를
인코딩해서 전송합니다.

여기서는 자바 어플리케이션에서 Apache XML-RPC구체를 이용하여 원격 메소드를 호출하는 방법을 알아보겠습니다.
( 이하의 글은 붉은늑대 님의 블로그에서 발췌하였습니다. )

1. XML-RPC의 동작방식
XML-RPC는 HTTP 기반의 클라이언트/서버 방식으로 동작한다. 따라서 특정 어플리케이션이 XML-RPC 서비스를 하기 위해서는
반드시 XML-RPC 서버가 필요하다. XML-RPC 서버는 클라이언트로부터 특정 객체의 메소드 호출 요청을 받고 그 메소드를 실행하는
역할을 한다. XML-RPC API(http://ws.apache.org/xmlrpc/apidocs/)를 보면, WebServer 클래스를 찾아볼 수 있는데 이 클래스가
XML-RPC 서버 역할을 한다.

2. XML-RPC 서버 만들기
XML-RPC 서버는 WebServer 클래스 또는 XmlRpcServer 클래스를 이용해 작성할 수 있다. XML-RPC 서버에 등록되고 관리되는 객체를
Handler라고 하며, XML-RPC 서버 클래스의 addHandler 메소드를 사용해 Client가 접근할 객체를 등록할 수 있다.

예제코드) XML-RPC 서버
[code]
import java.util.Hashtable;
import org.apache.xmlrpc.*;
   
public class JavaServer {
 
    public JavaServer () {
        // Our handler is a regular Java object. It can have a
        // constructor and member variables in the ordinary fashion.
        // Public methods will be exposed to XML-RPC clients.
    }
  
    public Hashtable sumAndDifference (int x, int y) {
        Hashtable result = new Hashtable();
        result.put("sum", new Integer(x + y));
        result.put("difference", new Integer(x - y));
        return result;
    }
   
    public static void main (String [] args) {
        try {
               
            // Invoke me as <http://localhost:8080>.
            WebServer server = new WebServer(8080);
            server.addHandler("sample", new JavaServer());
            server.start();
   
        } catch (Exception exception) {
            System.err.println("JavaServer: " + exception.toString());
        }
    }
}
[/code]
* 위 소스에서는 편의상 JavaServer 객체를 Handler로 등록하였다.


3. XML-RPC 클라이언트 만들기
XML-RPC 클라이언트도 서버와 마찬가지로 매우 간단하게 작성할 수 있는데, XmlRpcClient 클래스를 이용하여 다른 시스템의
객체에 접근할 수 있다. 특정 객체의 메소드 호출은 XmlRpcClient의 execute 메소드를 사용하면 되는데 호출할 객체의 메소드는
dot expression으로 표현되며, 호출할 메소드에 전달할 파라미터는 Vector에 담아서 넘기면 된다. 이 때 메소드의 호출 결과는
Hashtable로 리턴되며 메소드 호출에 실패했을 경우엔 XmlRpcException을 발생시킨다.

예제코드) XML-RPC 클라이언트
[code]
import java.util.Vector;
import java.util.Hashtable;
import org.apache.xmlrpc.*;

public class JavaClient {
  
    // The location of our server.
    private final static String server_url = "http://localhost:8080";
  
    public static void main (String [] args) {
        try {
   
            // Create an object to represent our server.
            XmlRpcClient server = new XmlRpcClient(server_url);
   
            // Build our parameter list.
            Vector params = new Vector();
            params.addElement(new Integer(5));
            params.addElement(new Integer(3));
   
            // Call the server, and get our result.
            Hashtable result =
            (Hashtable) server.execute("sample.sumAndDifference", params);
            int sum = ((Integer) result.get("sum")).intValue();
            int difference = ((Integer) result.get("difference")).intValue();
   
            // Print out our result.
            System.out.println("Sum: " + Integer.toString(sum) +
                               ", Difference: " +
                               Integer.toString(difference));
   
        } catch (XmlRpcException exception) {
            System.err.println("JavaClient: XML-RPC Fault #" +
                               Integer.toString(exception.code) + ": " +
                               exception.toString());
        } catch (Exception exception) {
            System.err.println("JavaClient: " + exception.toString());
        }
    }
}
[/code]
* 위 코드에서는 서버에 sample이라는 이름으로 등록된 객체의 sumAndDifference 메소드를 호출하고 있다.

4. XML-RPC 서버 서블릿 만들기
XML-RPC 서버를 작성하면 별도의 서버 프로세스를 실행해야 한다. 만약 WAS 상에서 동작하는 웹어플리케이션의 경우 웹어플리케이션의
특정 객체에 접근하고 싶은 경우엔 (웹어플리케이션이 동작중인 JVM과 XML-RPC 서버가 동작중인 JVM이 서로 다르기 때문에) 위와 같이
별도의 서버를 작성하여 실행할 수 없다. 이와 같은 경우엔 XML-RPC 서버를 서블릿으로 작성하여 웹어플리케이션에 포함시키면 된다.
이렇게 하면 클라이언트에서 서블릿의 특정 URL로 접근하여 원격으로 메소드를 호출할 수 있다.

예제코드) XML-RPC 서버 서블릿
[code]
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.xmlrpc.*;

public class XmlRpcServlet extends HttpServlet {

    protected XmlRpcServer xmlrpc;
   
    public void init(ServletConfig config) throws ServletException {
        XmlRpc.setEncoding("euc-kr");
        XmlRpc.setKeepAlive(true);
        xmlrpc = new XmlRpcServer();
        xmlrpc.addHandler("hello", new HelloHandler());
        XmlRpc.setDebug(false);
    }

    // doPost , doGet
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        //maintenance of public security
        //String auth = request.getHeader( "Authorization" );
        byte[] result = xmlrpc.execute( request.getInputStream() );
        response.setContentType( "text/xml" );
        response.setContentLength( result.length );
        OutputStream output = response.getOutputStream();
        output.write( result );
        output.flush();
    }
}
[/code]

5. XML-RPC Tips
XML-RPC는 HTTP 상에서 동작하며 전달되는 데이터는 XML로 인코딩된다. 즉, 어플리케이션에서 원격 메소드 호출시 전달할
파라미터나 메소드가 리턴하는 객체의 타입은 반드시 Serializable한 객체여야 한다.



덧 붙임 : 유사한 코드로 PHP에서 xmlrpc의 구현이 궁금 하다면 여기를 방문 해 보자.
2008/01/20 12:25 2008/01/20 12:25
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다