Java RMI (Remote Method Invocation)

문영진

부산 광역시 금정구 금사동 57-8 번지 15통 6반

E-mail : yjmoon@sarang.kyungsung.ac.kr

URL : http://sarang.kyungsung.ac.kr/~yjmoon/

Abstract:

기존의 자바 소켓(Socket)으로 대부분의 통신 프로그래밍이 가능했으나 분산되어 있는 객체간의 메시지 교환을 위해서는 프로그래머가 일일이 응용 래벨 프로토콜을 직접 설계 해야 하며 이는 복잡하고 에러를 발생시키는 주된 요인이 되기도 한다. 이러한 Socket 프로그래밍을 대체 할수 있는 것이 원격메소드호출 (Remote Mothod Invocation)이며, RMI 는 분산된 객체들을 마치 local 객체처럼 사용이 가능하다. 본 강의는 RMI 의 개념과, 프로그래 밍 방법 등을 예제를 통해 설명한다.

Keywords:

java, rmi, network

1.서론

인터넷과 인트라넷 제품을 위한 디자인은 항상 객체의 전달에 관한 문제에 직면하게 되며 대부분은 라인 프로토콜의 자체 개발로 많은 시간과 비용이 소요되었다. 특히 이러한 작업은 복잡하고 에러를 발생시키는 주된 요인으로 작용한다. Java RMI 는 이러한 문제들을 해결할수 있는 해결사로서 네트워상에서 객체의 전달을 투명하게 한다. 다시 말해 Java RMI (Remote Method Invocation)은 분산된 객체를 마치 로컬에 있는 객체를 다루듯이 사용할수 있으며 기존의 소켓 프로그래밍과 같은 복잡한 설계 절차와 애러를 발생시키는 요인을 극소화 시켰다. 모든 네트웍 프로그래밍의 자세한 부분, 즉 소켓 API 를 사용해서 구현되는 부분은 RMI package, client stub 그리고 server stub에 의해 숨겨진다.

원격절차호출 RPC(Remote Procedure Call)와는 달리 RMI 는 Java VM 환경에서 실행되므 로 XDR(eXternal Data Representation)과 같이 다른 시스템간에 이식 가능한 형태로 데이타를 코드화는 표준 방법을 필요로하지 않는다.

2. RMI 의 개발 목적

·안정된 원격 호출(remote invocation)의 제공.

·서버에서 애플랫으로의 콜백(callback) 제공.

· 분산모델을 자바환경으로의 통합.

· 분산 객체와 비분산 객체의 명확한 구분.

· 안정된 분산 애플리케이션을 간편하게 만들수 있는 환경제공.

· 자바 런타임환경에의해 제공되는 안전성 유지.

3.자바 분산 객체 모델

3.1. 용어 정리

· 원격 객체(remote object)

다른 호스트에 있는 자바 VM 으로부터 호출되어지는 메소드를 가지고 있는 객체.

· 원격 인터페이스(remote interfaces)

원격 객체의 메소드를 선언하는 자바 인터페이스.

· 원격 메소드 호출(Remote Method Invocation)

원격 인터페이스 안에 정의된 리모트 객체의 메소드를 호출하는 것.

3.2. 분산 모델과 비분산 모델의 차이점

원격 객체의 클라이언트들은 구현 클래스들과 상호작용을 하는 것이 아니라 원격 인터페이스와 상호작용을 한다.

원격메소드 호출시 (원격이 아닌) 독립 변수나 결과값들은 참조가 아닌 복사로서 전달이 된다.

원격 객체는 원격구현이 복사가 되는 것이 아니라 참조값으로 값이 전달된다.

지역 객체 호출보다 원격 객체 호출시 오류가 발생할 확율이 높으므로 클라이언트는 추가로 예외를 처리할 수 있어야 한다.

3.3. RMI 의 Interface 와 Class

java.rmi 안에 정의 되어 있는 인터페이스들과 클래스들은 RMI System의 상태(behavior)들을 기술하고 있다. 밑의 그림은 인터페이스들과 클래스들간의 관계를 나타낸 것이다.

3.3.1 Remote Interface

모든 Remote Interface 는 java.rmi.remote 를 상속 받는다.

public interface Hello extends java.rmi.Remote {

String sayHello() throws java.rmi.RemoteException;

}

몇가지 주의할 점은 다음과 같다.

각 메소드는 반드시 java.rmi.RemoteException으로 예외 처리를 해주어야 한다.

독립변수나 리턴값으로 넘겨지는 remote object는 interface에 정의된 메소드를 구현한 클래스(implementation class)로 선언하지 말고 remote interface로 선언해야 한다.

3.3.2 RemoteException Class

java.rmi.RemoteException 클래스는 RMI 실행시간에 발생할수 있는 예외들의 슈퍼클래스 이다. RMI 시스템을 사용하는 애플리케이션의 견고성을 보장하기 위해서는 remote interface 에 선언된 각각의 메소드에 예외처리를 해주어야 한다. java.rmi.RemoteException은 메소드 호출의 실패, 네트웍크의 고장 또는 서버에 문제가 생겼을 경우 발생한다.

3.3.3 RemoteObject Class 와 슈퍼클래스

RMI 서버 function은 java.rmi.server.RemoteObject와 그 subclass인 java.rmi.server.Unicast

Remote Object에 의해 제공된다.

java.rmi.server.RemoteObject 클래스는 remote object에 사용될수 있는 hashCode, equals, toString 를 제공한다.

object를 생성하고 원격으로 사용할수 있도록 하는 .function은 java.rmi.server.RemoteServer 와 그 subclass에 의해 추상적으로 제공된다.

java.rmi.server.UnicastRemoteObject 클래스는 unicast remote 객체를 정의한다. 이 객체는 서버 프로세서가 살아있을 경우에 참조가 가능하다.

3.4.4 Implementing a Remote Interface

remote interface 를 구현(implemetation) 하기 위한 일반적인 방법은 다음과 같다.

구현하는 클래스는 주로 java.rmi.server.UnicastRemoteObject를 상속받으며 아울러 UnicastRemoteObject의 상위 클래스인 java.rmi.server.RemoteObject와 java.rmi.server.RemoteServer 의 상태를 상속 받는다.

여러개의 remote interface를 구현할수 있다.

다른 원격 구현(remote implementation) 클래스를 상속받을수 있다.

remote interface 에 선언되지 않은 method 를 정의할수는 있으나 그 메소드는 단지 지역(local)에서만 사용이 가능하고 원격으로는 쓰일수 없다.

import java.rmi.*;

import java.rmi.server.UnicastRemoteObject;

public class HelloImpl extends UnicastRemoteObject implements Hello

{

public String sayHello() throws RemoteException

{

......

}

}

3.4.5 원격 객체의 type = 지역 Stub의 type

분산 객체 모델에서 client는 원격 객체의 클래스에 의해 구현되는 원격 인터페이스의 집합을 가지는 stub 객체와 상호 작용을 한다. stub 클래스는 원격 객체가 아닌 보통 클래스는 포함하지 않는다. 클래스 계층 구조에서 만약 C가 B로부터 상속받고 B는 A로부터 상속을 받는다고 가정하자. 그리고 단지 B만이 원격 인터페이스를 구현한다면 stub는 단지 B로부터 생성되지 C로부터는 생성이 되지 않는다. Stub는 하나의 원격 객체로 원격 인터페이스의 집합을 구현하므로 자바시스템의 관점에서는 원격객체와 같은 형을 가지고 있다고 보는 것이다. 따라서 클라이언트는 내장된 자바 연산으로 원격객체의 형을 채크할수 있으며 또한 임의의 원격 인터페이스를 다른 원격 인터페이스로 캐스트할 수 있다.

3.4.6 인자(parameter)의 전달

원격 객체로부터 주고 받는 인자는 serializable한 java형이면 모두 가능하다. 이것은 원시적(primitive)형을 포함하여 원격 java 객체와 java.io.serializabe을 구현한 원격이 아닌 java 객체를 포함한다. Serializable에 관한 자세한 정보는 (Reference)의 "Object Serialization Specification"을 참조하기 바란다. 애플랫의 경우 인자로 쓰이는 클래스나 또는 리턴값이 지역에서 유효하지 않을 경우 AppletClassLoader를 통해 동적으로 로드된다. 애플리케이션의 경우 이러한 클래스들은 디폴트로 class loader또는 RMIClassLoader에 의해 불리워 진다. 보통 객체로서의 인자 전달.

원격메소드호출에서 인자로서 또는 결과값으로 넘겨지는 보통객체는 복사로서 전달된다. 다시말해 RMI 에 의해 불리워진 메소드가 보통객체의 메소드일경우 먼저 원격지로부터 복사가되어 지역으로 옮겨진 후에 그 객체의 메소드가 실행이되는 것이다. 원격객체로서의 인자전달. 원격객체를 인수로 넘길경우 원격객체의 stub가 전달된다.

3.4.7 예외 핸들링

원격메소드에 java.rmi.RemoteException 처리를 해주므로 호출을 하는 쪽에서는 반드시 이 예외를 다룰수 있도록 준비를 해야한다. 원격호출을 하는동안 java.rmi.RemoteException이 발생하면 클라이언트는 호출중에 일어난것인지 그 후에 일어난 것인지 아니면 그 전에 일어났는지에 대한 정보가 거의 없다. 따라서 원격 인터페이스와 그 안에 선언된 메소드들은 이러한 상황을 염두해두고 설계되어야한다.

3.4.8 RemoteObject 클래스에의해 override 되는 메소드

Object 클래스 안에는 equals, hashCode, toString 메소드가 구현되어 있다. 하지만 이들은 원격객체를 위한것이 아니다. 따라서 jaa.rmi.server.RemoteObject 클래스는 이 메소드들을 원격 객체에 알맞게 override 했다.

3.4.9 final 로 선언된 객체

getClass, notify, notifyAll, wait 메소드들은 Object 클래스에서 final로 정의되어 있어 override 되어질수 없다.

3.4.10 원격 객체의 위치

원격 객체의 참조는 java.rmi.Naming 클래스의 URL 관련 메소드에의해 가능하다. 클라이언트가 원격 객체를 호출하기 위해서는 먼저 그 객체에 대한 참조 정보를 얻어야만한다. 보통 원격 객체의 참조는 메소드 호출에 의해 리턴되는 값으로부터 얻어진다. RMI 시스템은 간단한 bootstrap 네임서버를 제공한다. 이를 통해 주어진 호스트의 리모트 객체를 얻을수 있다. java.rmi.Naming 클래스는 lookup, bind, rebind, unbind, list와 같은 URL- based 메소드를 제공한다.

4. RMI 시스템의 구조

RMI 시스템은 stub/skeleton 래이어, remote reference 래이어, transport 래이어로 구성되어 있으며 각 래이어의 경계는 특별한 인터페이스와 프로토콜에 의해 정의되진다. 또한 각 래이어는 서로 독립적이며 따라서 다른 래이어에 영향을 주지 않고 한 래이어의 성질을 바꿀수 있다. 예들 들어 설명하자면, 현재 transport 구현은 TCP를 바탕으로 하고 있으나 다른 래이어에 영향을 주지 않고 UDP 를 바탕으로한 transport 로의 교체가 가능한 것이다.

어떠한 주소공간에서 다른 주소공간으로 객체의 전달을 투명하게 하기 위해서는 자바를 위해 특별히 제작된 Object serialization 기술이 쓰인다. 자세한 내용은 "Object Serialization Specification"을 참조하라.

또 다른 테크닉으로 Dynamic stub loading이 있다. 이는 remote interface를 구현하는 client-side stub 를 지원하기 위해 쓰인다. 예들 들면 정확한 형의 stub 가 클라이언트에서 유효하지 않을 경우 클라이언트가 자바에 내장된 연산자를 캐스팅과 타입채킹에 사용하도록 허용한 다.

.

stub/skeleton

클라이언트가 remote server의 객체를 호출할때 실재로 호출되는 부분으로 stub는 원 격 인터페이스의 구현으로 호출 요청을 remote reference layer를 통해 서버 객체로 보내는 역할을 담당한다. skeleton 은 그 반대로 생각하면 되겠다.

remote reference layer

호출 정보를 분석해서 server 가 단일 객체인지 아니면 여러 사이트에 흩어져 있는 복수개의 객체인지를 판별후 호출을 실행한다. 또한 그 서버가 항상 실행상태인지 아니면 호출시에만 실행이되는지에 따른 참조방법을 추상화 한다. 따라서 상위 래이어에서는 이러한 차이점이 보이지 않게 된다.

transport layer

연결 setup, 연결 관리, 그리고 연결 감시등을 책임지는 래이어다.

각 래이어의 경계는 특별한 인터페이스와 프로토콜에 의해 정의되지며 각 래이어는 서로 독립적이며 따라서 다른 래이어에 영향을 주지 않고 한 래이어의 성질을 바꿀수 있다. 예들들어 설명하자면, 현재 transport 구현은 TCP를 바탕으로 하고 있으나 다른 래이어에 영향을 주지 않고 UDP 를 바탕으로한 transport 로의 교체가 가능한 것이다.

5. Resource

다음은 Java RMI 관련 자료들이 있는 곳이다. 기본적인 프로그래밍과 소스코드 그리고 Socket Programming 과 RMI를 사용한 Performance 평가 등이 있으니 직접 접속해 유용한 정보를 얻을수 있기를 바란다.

JavaSoft (http://chatsubo.javasoft.com/)

RMI specification, Object Serialization 그리고 RMI 를 처음 접하는 프로그래머를 위한 기본 프로그래밍 예제 "Getting start with RMI"를 제공하며 RMI 관련 Project 를 수행하는 회사와 제품에 대한 정보를 얻을수 있다.

rmi-users mail achive (http://chatsubo.javasoft.com/email/rmi-users)

RMI 에 관심이 있는 사람들의 메일을 모아놓은 곳이다. 초급부터 고급까지 다양한 질문과 답변들이 있다. 프로그래밍을 하면서 궁금한 점이 있으면 메일링 리스트에 가입을 하거나, 또는 achive 를 참조한다면 많은 도움이 될 것이다.

Game Client/Server (http://www.digitalfocus.com/ddj/code/)

RMI 를 이용한 간단한 Client/Server 프로그램이다. 네트웍을 이용한 게임도 RMI 를 이용하면 충분히 큰 효과를 얻을것으로 생각된다.

Client/Server Computing using JDBC RMI (http://www.geocities.com/SiliconVally/Park/9841/rmi.html)

RMI는 CGI 와 비교하여 성능의 향상과 함께 데이타베이스와의 persistent한 연결이 가능며, 서버의 처리능려과 로컬의 처리능력을 결합함으로서 자원의 요구를 최소화 시킬수 있다. JDBC 프로그래밍에서 Socket JDBC, RMI JDBC까지 상세한 해설과 코드를 제공한다.

6. Hello World

본문의 예제를 논하기 앞서 먼저 준비해야할 일이 있다. 밑의 내용을 참조하여 자신이 필요한 것이 무엇인지 판단하고 RMI 를 실행할수 있는 환경을 구축해야 한다.

ClientJava VMInstall

Windows 95/NT Netscape 3.011.0.xNetscape plug-in

Windows 95/NT HotJava  1.01.1No installation required

Windows 95/NT appletviewer 1.1No installation required 1.1.1

Solaris Netscape 3.0.11.0.xInstall RMI libraries for 1.02

Solaris HotJava 1.11.1No installation required

Solaris appletviewer 1.1.11.1No installation required

시작하기전에 먼저 작업할 디렉토리를 정한다. 본문의 예제는 jdk1.1.1/mysrc/examples/hello 로 한다.

디렉토리가 없으면 다음과 같이 디렉토리를 만든다.

mkdir $HOME/java/mysrc/examples/hello

1. Remote Interface 정의

Remote method invocation 은 여러가지 이유로 인해 실패할 가능성이 있다. 주된 이유는 네트웩크에 관련된 문제 또는 서버의 문제등을 들수 있다. 이러한 문제는 remote object 사용시 반드시 고려되어야 한다. remote object 라는것을 알리기 위해서 object 는 remote interface 를 구현(implement)해야 한다. 따라서 먼저 remote interface를 선언한다.

인터페이스는 반드시 public으로 선언하고 java.rmi.Remote를 상속받는다. 또한 인터페이스 안에 선언된 메소드는 java.rmi.RemoteException 처리를 해주어야 한다.

-- Hello.java --

package examples.hello;

public interface Hello extends java.rmi.Remote

{

String sayHello() throws java.rmi.RemoteException;

}

2. Implementation Class (remote object)

HelloImpl.java 코드는 Hello World 서버의 역할을 한다.

-- HelloImpl.java --

import java.rmi.*;

import java.rmi.server.UnicastRemoteObject;

// 구현되는 인터페이스를 기술한다.//

public class HelloImpl extends UnicastRemoteObject implemnents Hello

{

private String name;

// 원격 객체의 생성자를 정의한다. //

public HelloImpl (String s) throws RemoteException

{

super();

name = s;

}

// 인터페이스에 선언된 메소드를 구현한다. //

public String sayHello() throws RemoteException

{

return "Hello World!";

}

public static void main(String args[])

{

System.setSecurityManager(new RMISecurityManager());

try

{

HelloImpl obj = new HelloImpl("HelloServer");

// 원격 객체를 등록시킨다. //

Naming.rebind("//sarang.kyungsung.ac.kr/HelloServer",obj);

System.out.println("HelloServer bound in registry");

}

catch (Exception e)

{

System.out.println("HelloImpl err: " + e.getMessage());

e.printStackTrace();

}

}

}

3. Remote 서비스를 이용하는 Applet

Hello World 예제의 부분인 애플랫은 HelloServer 의 sayHello 메소드를 호출한다. 그결과로 "Hello World!" 를 화면에 표시한다.

-- HelloApplet.java --

package examples.hello;

import java.awt.*;

import java.rmi.*;

public class HelloApplet extends java.applet.Applet

{

String message = "";

public void init()

{

try

{

Hello obj = (Hello)Naming.lookup("//" + getCodeBase().getHost() + "/HelloServer");

message = obj.sayHello();

}

catch (Exception e)

{

System.out.println("HelloApplet exception: " + e.getMessage());

e.printStackTrace();

}

}

public void paint(Graphics g)

{

g.drawString(message, 25, 50);

}

}

4. HTML 작성.

<HTML>

<title>Hello RMI</titlt>

<center> <h1>Hello RMI</h1> </center>

The message from the HelloServer is:

<p>

<applet codebase="../.."

code="examples.hello.HelloApplet"

width=500 height=120>

</applet>

</HTML>

$HOME/jdk1.1.1/mysrc/hello 안에는 다음의 4가지 file 있다.

Hello.java - remote interface.

HelloImpl.java - remote object (Applet 의 서버)

HelloApplet.java - Applet 소스 코드.

index.html - Web page.

· Compile

javac -d /$HOME/public_html/codebase *.java

· Stub 와 Skeletons 생성.

rmic -d $/HOME/public_html/codebase examples.hello.HelloImpl

위 명령을 실행한후에 다음 2개의 클래스 파일이 생성된다.

HelloImpl_Stub.class

HelloImpl_Skel.class

· move HTML file

mv $HOME/java/mysrc/examples/hello/index.html

$HOME/public_html/codebase/examples/hello

· Start

start rmiregistry (Win 95)

rmiregistry (Solaris)

디폴트로 1099 포트를 사용하나 다른 애플리케이션이 사용중이라면 rmiregistry 2002 처럼 1099외의 포트를 사용한다.

java -Djava.rmi.server.codebase=http://sarang.kyungsung.ac.kr/~yjmoon/codebase/

examples.hello.HelloImpl

appletviewer http://sarang.kyungsung.ac.kr/~yjmoon/codebase/examples/hello/index.html

참고문헌(Reference)

[1] JavaSoft, Information on configuring the RMI plug-in:

http://chatsubo.javasoft.com/current/doc/rmi/release-notes.html

[2] JavaSoft, The complete Object Serialization Specification:

http://chatsubo.javasoft.com/current/doc/serial-spec/

serialTOC.doc.html

[3] JavaSoft, Getting Started Using RMI:

http://chatsubo.javasoft.com/current/doc/tutorial/getstart.doc.html

[4] JavaSoft, Java Remote Method Invocation Specification:

http://www.javasoft.com:80/products/jdk/1.1/docs/guide/rmi/spec

[5] JavaSoft, Java Remote Method Invocation API:

http://www.javasoft.com:80/products/jdk/1.1/docs/api