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:이 글에는 트랙백을 보낼 수 없습니다

ANT 를 이용한 RMI 컴파일 ( rmic 로 stub 생성하기 )

2007/08/03 11:36

서비 JAVA , ,

Ant를 이용하여 RMI 빌드를 위한 스크립트 이다.

(Language : xml)
<!-- RMI 생성 -->
<target name="rmi" description="RMI Stub 생성" depends="make">
<echo message="* rmic 실행  *"></echo>
 <rmic base="./bin/classes" classname="com.prompt.rfid.rmi.CallableFunctionImpl" classpath="./bin/classes" stubversion="1.2">
   <classpath>
     <fileset dir="${jar.dir}">
      <include name="**/*.jar" />
     </fileset>
     <fileset dir="${lib.dir}">
        <include name="**/*.jar" />
       </fileset>
     <fileset dir="${classes.dir}">
      <include name="**/*.class" />
     </fileset>
   </classpath>
 </rmic>
</target>

다른부분은 특별히 언급 할 필요는 없을 듯 하다.
<rmic .. > 태그의 속성만 설명 하자면
base : rmic로 컴파일된 skel 과 stub이 위치할 경로 지정
classname : rmic 로 stub을 생성할 원격 객체를 지정
                 ( 소스를 지정하는게 아니라 javac에 의해 컴파일된 class 파일을 지정 한다. )
classpath : classname에 지정한 원격 객체 class파일이 위치하고 있는 경로
stubversion : 1.1 과 1.2 중 하나를 선택 할 수 있다. ( http://www.yunsobi.com/blog/61 참조 )
                   1.1을 지정하면 jre 1.3 이전의 방식대로 skel 과 stub 을 모두 생성 한다.
                   1.2를 지정하면 jre 1.4 이후 방식으로 sutb 만 생성한다.
2007/08/03 11:36 2007/08/03 11:36
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

어드민 콘솔 <-> Edge Server RMI통신을 위한 참고 URL

자바 버전이 올라가면서 많은 관련 스펙이 조금씩 수정되어 왔듯이
RMI구현 방법도 이전( JDK 1.3 )과는 조금 달라졌다.

RFID Edge Server와 Edge를 관리하는 Admin Console의 통신 방법으로
RMI를 사용하기로 결정하고 진행하는 와중에 RMI최신 스펙을 알아보던 중
이전과는 많은 부분이 바뀌어 있어서 참고용 링크를 남김.

java.sun.com 의 RMI Search 결과
http://onesearch.sun.com/search/onesearch/index.jsp?charset=utf-8&col=developer-reference&qt=rmi

Java RMI Enhancements in J2SE 5.0
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/index.html

RMI ( Remote Method Invocation ) Code Samples
http://java.sun.com/developer/codesamples/rmi.html?t=rmi&cs=false

Distributed Java Programming with RMI and CORBA..


2007/06/16 15:40 2007/06/16 15:40
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다