RMI
지금까지 객체의 메소드 호출은 같은 JVM 환경의 객체와 객체 사이에서 이루어진 것이었다.
그러면 다른 JVM 에서 동작하고 있는 객체의 메소드를 호출할 수 있을까?
RMI를 사용하면 할 수 있다.
RMI 프로그래밍 방법
- 원격 인터페이스를 정의
- 서버 구현
- 클라이언트 구현
- 자바 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
테스트 실패시 체크 리스트
- loopback adapter 를 사용 중지한다.
- 윈도우를 서버로 사용할 경우, Windows 방화벽의 예외 탭에서 1099(RMI 디폴트 포트번호)를 포트추가 버튼을 이용해 추가한다.
- 공유기 환경에서 우분투를 서버로 사용할 경우 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
