실무개발자를위한 실무교육 전문교육센터학원
www.oraclejava.co.kr에 오시면 보다 다양한 강좌를 보실 수 있습니다.
“Hello World” RMI Application
하나의 RMI Application을 작성 하기 위해서는 3~4개 정도의 자바 파일이 필요 하다.
필요한 파일
- 원격 인터페이스 : 자바 인터페이스
- 원격 인터페이스를 구현한 클래스 : 클래스
- RMI 서버 Application : 클래스
- RMI 클라이언트 Application : 클래스
““ Hello World ” – 원격 인터페이스 “
원격 인터페이스는 원격 객체에 대하여 클라이언트가 호출 할 수 있는 비즈니스 메소드를 정의 한다.
클라이언트는 원격 인터페이스에서 정의된 메소드만 서로 다른 실행환경에서 원격으로 호출 할 수 있다.
이렇게 원격에서 호출 가능한 메소드를 “원격 메소드” 라고 한다.
Hello.java에서는 sayHello라는 원격 메소드를 정의 하고 있다.
//Hello.java
public interface Hello extends java.rmi.Remote {
public String sayHello(String name) throws java.rmi.RemoteException;
}
““Hello World” – 원격 인터페이스를 구현한 클래스 “
HelloImpl.java는 원격 인터페이스 Hello를 구현 한 클래스이며 HelloImpl 클래스의 인스턴스가 원격객체로서 RMI 서버에 의해 이름으로 등록 된다.
“Hello World” Application 에서는 java.rmi.UnicastRemoteObject 를 상속하며 원격 이터페이스를 구현하고 있다.
//HelloImpl.java
import java.rmi.server.*;
import java.rmi.*;
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException {
super();
}
//원격 메소드 구현
public String sayHello(String name) {
return "Hello World ... " + name + "!";
}
}
““ Hello World ” – RMI 서버 Application “
HelloServer.java의 경우 RMI 서버 Application의 기능만을 정의한 단순한 클래스 이다.
HelloImpl 객체를 생성하고 “HelloRemote”라는 이름으로 등록하며 이후 RMI 클라이언트에서의 메소드 호출을 기다린다.
//HelloServer.java
public class HelloServer {
public static void main(String[] args) {
try {
HelloImpl remoteObj = new HelloImpl();
java.rmi.Naming.rebind("rmi://localhost:1099/HelloRemote", remoteObj);
System.out.println("Hello Remote Object bound to the registry and ready to service incoming client calls...");
} catch(java.rmi.RemoteException e) {
System.err.println("Exception occurred during processing incoming method call");
} catch(java.net.MalformedURLException e) {
System.err.println("Check the url String...");
}
}
}
““ Hello World ” – RMI 클라이언트 Application“
RMI 서버 Applcation 에서 등록한 원격객체 “HelloRemote” 에 대한 reference를 얻고 있는 부분을 제외하면 실제 로컬의 런타임 환경에서 생성한 객체와 전혀 다를 바 없는 레퍼런스를 사용한 메소드 호출을 하고 있다.
//HelloClient.java, 실행시 이름을 인자로...
import java.rmi.Naming;
public class HelloClient {
public static void main(String[] args) {
try {
Object obj = Naming.lookup("rmi://localhost:1099/HelloRemote");
Hello remoteObj = (Hello)obj;
String msg = remoteObj.sayHello(args[0]);
System.out.println(msg);
}
catch(java.rmi.RemoteException e) {
System.out.println("Something has gone wrong during remote method call...");
}
catch(java.rmi.NotBoundException e) {
System.out.println("Could't bound...");
}
catch(java.net.MalformedURLException e) {
System.out.println("Check url stirng...");
}
}
}
““ Hello World ” 실행 방법 “
RMI 소스 파일을 컴파일 한다. 원격 인터페이스 구현 클래스에 대한 스텁 생성(JAVA_HOME/bin/rmic 사용)
- rmic HelloImpl
네이밍 서버 데몬 시작
rmiregistry 1099 & (Unix)
start rmiregistry 1099 (Windows)
RMI 서버 실행
- java HelloServer
RMI 클라이언트 실행
- java HelloClient jclee
[실행 결과]
RMI에서 원격객체를 생성하고 이를 이름으로 등록한 후 원격 객체에 대한 클라이언트의 서비스 요청을 기다리는 쪽이 서버 입니다.
원격 객체를 이름으로 찾아 해당 객체가 제공하는 원격 메소드를 호출하는 프로그램이 RMI 클라이언트 이구요~
원격 객체에 대한 레퍼런스(원격 레퍼런스)를 이름을 통해 얻어 낼 수 있도록 rmiregstry 라는 네이밍 서버 데몬을 제공 합니다.
원격 참조(레퍼런스)를 통해 서로 다른 JVM 환경의 원격 메소드를 자신의 로컬 JVM 환경에서 생성한 객체와 다름없이 호출 할 수 있는 메커니즘(stub, skeleton)을 제공 합니다.
원격 객체의 메소드 호출 시 객체를 주고 받을 수 있는 메커니즘을 제공 합니다. (원격 객체 메소드 호출시 사용되는 파라미터나 리턴 타입이 Serializable을 구현했을 경우 객체 직렬화를 이용 가능)
RMI에서 pass-by-value이 경우는 원격 메소드 호출시 사용하는 파라미터나 리턴 타입에 적용 되며 pass-by-reference인 경우는 원격 객체에 대한 레퍼런스 입니다. pass-by-reference는 stub과 skeleton의 통신시에 사용 되구요~
다음 코드에서 remoteObj가 Hello 타입의 원격 레퍼런스 입니다.
//HelloClient.java
Object obj = Naming.lookup("rmi://loclahost:1099/HelloRemote");
Hello remoteObj = (Hello)obj;
개발자 입장에서 볼때 원격객체에 대한 원격참조(레퍼런스)를 얻고 나면 로컬에서 생성한 객체와 동일하게 취급하여 사용 가능 한겁니다.
RMI는 원격 객체에 대한 참조로 stub을 사용 합니다.. 즉 RMI 클라이언트 Application에서 사용하는 원격 객체의 reference는 실제로 stub에 대한 reference 입니다.
하나의 JVM에서 생성한 객체를 다른 JVM에서 그대로 실행 한다는 것은 원칙적으로 불가능 합니다.(주소 공간이 틀리기 때문) , 즉 이것이 RMI에서 원격 객체에 대한 참조를 원격객체를 직접 가리키지 않고 stub을 사용하는 이유 입니다.
예를들면 RMI 클라이언트에서 RMI 서버의 메소드를 호출 한다고 했을 때 먼저 로컬 stub 객체에 먼저 전달된 후에 원격의 skeleton 객체에 전달되고 skeleton이 실제 객체의 비즈니스 메소드를 호출 하는 것 입니다.
원격 메소드 호출시에 사용하는 실매개변수와 리턴되는 데이터의 상태를 원격의 클라이언트/서버 간에 유지할 필요가 없을때는 직렬화(Serialization)를 통해 Pass-by-value로 복사본을 주고 받습니다.(객체나 자바 내장 데이터 타입인 경우 객체 직렬화를 사용 합니다.)
자바 RMI의 경우 서버에서 이름으로 등록한 원격 객체를 클라이언트에서 개발자가 직접 저수준의 네크웍 코드를 직접 작성하지 않고 등록된 이름으로 원격에 존재하는 비즈니스 객체를 참조 할 수 있도록 rmiregistry라는 Naming Server Daemon을 제공 합니다.
아래의 코드는 rmiregistry를 통해 원격 객체 remoteObj를 “HelloRemote”라는 이름으로 등록하는 RMI 서버 Application의 일부분 입니다.
//원격 객체 생성
HelloImpl remoteObj = new HelloImpl();
//생성한 원격 객체를 “HelloRemote”라는 이름으로 rmiregistry에 등록 한다.
java.rmi.Naming.rebind("rmi://localhost:1099/HelloRemote", remoteObj);
앞의 코드에서 처럼 rmiregistry에 등록된 원격 객체에 대한 reference를 얻어내어 원격 메소드를 호출하는 RMI 클라이언트의 부분은 아래와 같습니다.
//rmiregistry에서 “HelloRemote”로 등록된 원격객체의 reference를 Object type으로 얻는다.
Object obj = Naming.lookup("rmi://loclahost:1099/HelloRemote");
//원격 메소드의 호출을 위해 적절한 원격 인터페이스 타입으로 Casting 한다.
Hello remoteObj = (Hello)obj;
//로컬객체에 대한 메소드 호출과 같이 원격메소드를 호출한다.
String msg = remoteObj.sayHello(args[0]);
앞의 코드에서 처럼 자바는 rmiregistry라는 네이밍 서비스를 통해 개발자가 원격 객체에 대해 접근하는 저수준의 메트웍 코드를 사용하지 않고도 이름으로 투명하게 접근 할 수 있는 방법을 제공 하는 것입니다.