Last Modified : 2011.01.02

RMI

지금까지 객체의 메소드 호출은 같은 JVM 환경의 객체와 객체 사이에서 이루어진 것이었다.
그러면 다른 JVM 에서 동작하고 있는 객체의 메소드를 호출할 수 있을까?
RMI를 사용하면 할 수 있다.

RMI 프로그래밍 방법

  1. 원격 인터페이스를 정의
  2. 서버 구현
  3. 클라이언트 구현
  4. 자바 RMI 레지스트리 시작 , 서버 시작, 클라이언트 시작
  • Hello.java - 원격 인터페이스
  • Server.java - 원격 인터페이스를 구현한 원격 객체
  • Client.java - 원격 인터페이스의 원격 메소드를 호출하는 클라이언트
1. 원격 인터페이스 정의 - Hello.java

원격 인터페이스에서는 클라이언트에서 원격으로 호출할 수 있는 메소드를 정의한다.
원격 인터페이스는 java.rmi.Remote 를 상속해야 한다.
원격 인터페이스의 원격 메소드는 throws java.rmi.RemoteException 을 선언해야 한다.
RemoteException 이외에 다른 익셉션이 추가로 포함될 수 있다.

Hello.java

package example.hello;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {
	String sayHello() throws RemoteException;
}
2. 서버 구현 - Server.java

Server.java 는 원격 인터페이스 Hello.java 를 구현한다.
원격 인터페이스의 sayHello()를 구현하는데 RemoteException 익셉션을 선언할 필요가 없다.
왜냐하면 원격 객체의 sayHello()가 실제로 RemoteException 을 던지지 않기 때문이다.

Server.java

package example.hello;

import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

public class Server implements Hello {

	public Server(){}
	
	public String sayHello() {
		return "안녕하세요 김종훈입니다.";
	}
	
	public static void main(String[] args) {
		Server obj = new Server();
		try {
			Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
			
			// Bind the remote object's stub in the registry
			Registry registry = LocateRegistry.getRegistry();
			registry.bind("Hello", stub);
			System.out.println("Server ready");
		} catch (Exception e) {
			System.out.println("Server exception: " + e.toString());
		}
	}

}

서버의 메인 메소드는 서비스를 공급하는 원격 객체를 생성해야 한다.
그리고 원격 객체는 자바 RMI 런타임으로 익스포트되어야 한다. 이 과정을 통해 서버에 해당하는 자바 RMI 런타임에 스텁이 만들어진다.
이를 수행하는 코드는 아래와 같다.

Server obj = new Server();
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);

위의 코드로 만들어진 서버측 스텁을 클라이언트에서 찾을 수 있도록 자바 RMI 레지스트리에 등록한다.
관련 코드는 아래와 같다.

Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);

LocateRegistry의 getRegistry()메소드가 인자없이 호출된다면 디폴트 포트인 1099를 사용한다는 의미이다.
따라서 익셉션이 발생한다면 1099포트를 개방한 후 테스트한다.

3. 클라이언트 구현

클라언트는 서버측에 등록된 스텁을 등록된 이름으로 찾아서 클라이언트측 JVM 에 다운로드한다.
그 후 스텁의 sayHello() 메소드를 호출한다.

package example.hello;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {

	private Client() {}

	public static void main(String[] args) {
		String host = (args.length < 1) ? null : args[0];
		
		try {
			Registry registry = LocateRegistry.getRegistry(host);
			Hello stub = (Hello) registry.lookup("Hello");
			String response = stub.sayHello();
			System.out.println("response: " + response);
		} catch (Exception e) {
			System.err.println("Client exception: " + e.toString());
			e.printStackTrace();
		}
	}
}
4. 자바 RMI 레지스트리 시작 , 서버 시작, 클라이언트 시작

윈도우시스템에서의 테스트(자바 클래스 파일 위치가 c:\java\rmi\bin 이라면)

start rmiregistry
start java -classpath c:\java\rmi\bin -Djava.rmi.server.codebase=file:c:\java\rmi\bin/ example.hello.Server
java -classpath c:\java\rmi\bin example.hello.Client 

유닉스 시스템에서의 테스트(자바 클래스 파일 위치가 /home/kim/java/rmi/bin 이라면)

rmiregistry &
java -classpath /home/kim/java/rmi/bin -Djava.rmi.server.codebase=file:/home/kim/java/rmi/bin/ example.hello.Server &
java -classpath /home/kim/java/rmi/bin example.hello.Client

다른 JVM에서 클라이언트 실행(서버 IP가 192.168.0.5 라면)

java  -classpath /home/kim/java/rmi/bin example.hello.Client 192.168.0.5

테스트 실패시 체크 리스트

  1. loopback adapter 를 사용 중지한다.
  2. 윈도우를 서버로 사용할 경우, Windows 방화벽의 예외 탭에서 1099(RMI 디폴트 포트번호)를 포트추가 버튼을 이용해 추가한다.
  3. 공유기 환경에서 우분투를 서버로 사용할 경우 root 계정에서 /etc/hosts 파일을 열어서 127.0.1.1 로 셋팅된 것을 우분투가 실제 할당받은 사설 IP(예:192.168.0.5)로 변경해 준다.

RMI는 EJB의 기반기술이다.
만약 여러분이 웹 프로그래머가 목표라면 RMI 관련 예제를 EJB의 개념을 이해하기 위한 도구로만 사용해도 충분하다.

원문

http://java.sun.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html