RMI 재정리

파일시스템 com.sun.jndi.fscontext.FSContextFactory com.sun.jndi.fscontext.RefFSContextFactory LDAPv3 com.sun.jndi.ldap.LdapCtxFactory DNS com.sun.jndi.dns.DnsContextFactory NIS com.sun.jndi.nis.NISCtxFactory RMI레지스트리 com.sun.jndi.rmi.registry.RegistryContextFactory WebSphere Application Server V5 CosNaming com.ibm.websphere.naming.WsnInitialContextFactory 일단 이게 바탕이 되어서 RMI가 사용되어지는데요. RMI의 인스턴스를 bind하면 클라이언트에서 lookup으로 가져오는거죠. RMI는 Remote Method Invocation 이니까 당연히 오브젝트 인스턴스는 서버에서 관리되는데 이 인스턴스의 상태가 Stateful 또는 Stateless 로 나뉩니다. 기본적으로 Stateful 상태입니다만, Stateless는 호출할때마다 인스턴스가 새로 생성되겠지요. 자, 이걸 그대로 EJB로 적용해봅시다. 그전에 RMI에서 복잡하게 bind/lookup 하는건 누가 해줄까요? 그건 EJBContainer가 해주는데 “Bean의 인스턴스화”, “JNDI Binding”, “트랜잭션관리”등을 해주니까 편리하다며 EJB가 주목을 받았죠. 물론 아직까지도 유용히 잘 쓰이고 있습니다. EJB의 Entity Bean에는 Stateful Session Bean 과 Stateless Session Bean 이 있는데 이제 슬슬 감이 오죠? (EJBContainer와 클라이언트가 같은 서버라면 @Local 다르다면 @Remote 로 나뉠겁니다.) EJB를 통해 DB관련 처리를 하면 한군데에서 트랜잭션 처리를 하게 되므로 여기저기서 트랜잭션이 발생하는것 보다 관리하기가 쉬우므로 EJB에서는 한군데서 트랜잭션 관리도 해주게 되었지요. 그럼 가장 모태가 되는 RMI에 대해서 간단한 샘플을 만들어 보았습니다.

1. RMI 인터페이스

스터브(stub)라고도 하죠. 클라이언트에서는 lookup 후 가져온 Remote클래스를 여기서의 Stub로 캐스트하여 메소드를 호출합니다.
package jo;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface SampleRMI extends Remote {
	public int doSomething() throws RemoteException;
}

2. RMI 구현 클래스

인터페이스를 구현한 클래스입니다. Skeleton이라고 합니다.
package jo;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class SampleRMIObj extends UnicastRemoteObject implements SampleRMI, Serializable {
	private static final long serialVersionUID = 16308199599625625L;
	// stateful or stateless?
	private int counter;
	public SampleRMIObj() throws RemoteException {
		super();
	}
	@Override
	public int doSomething() throws RemoteException {
		counter++;
		System.out.println("do something here. counter is " + counter);
		return counter;
	}
}
Stateful인지 Stateless인지 확인하기 위해 counter값을 변수로 두었습니다.

3. 등록하기

이제 오브젝트를 공개합니다. rmiregistry를 이용하는데요. rmiregistry.exe 를 실행하여 1099 포트를 listening합니다.
package jo;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
public class RMIRegister {
	public static void main(String[] args) throws Exception {
		SampleRMI rmi = new SampleRMIObj();
		int counter = rmi.doSomething();
		System.out.println("server counter is " + counter);
//		Properties props = new Properties();
//		props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
//		props.put(Context.PROVIDER_URL, "rmi://localhost");
//
//		Context ctx = new InitialContext(props);
//		ctx.bind("sample", rmi);
		Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
		//registry.rebind("sample", UnicastRemoteObject.exportObject(rmi, 0)); //任意ポート
		//registry.rebind("sample", UnicastRemoteObject.exportObject(rmi, 1099)); //1099ポート
		// SampleRMIObjが既にUnicastRemoteObjectを継承しているからこちらを使う。(UnicastRemoteObject.exportObjectは使用しない)
		// http://www.coderanch.com/t/210349/java/java/object-exported-exception-RMI
		registry.rebind("sample", rmi);
		System.out.println("ok. rmi object is registed.");
	}
}
위 샘플에서는 간단하게 Registry 클래스를 이용하였습니다.

4. 사용해보기

이제 분산환경의 여러대의 클라이언트에서 접속해보세요. 클라이언트에서 실행되는게 아니라 서버에서 CPU연산이 되므로 분산환경에서 고려해야할 排他制御등의 문제가 해결되었네요~~
package jo;
import java.rmi.Naming;
import java.rmi.Remote;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
public class RMIClient {
	public static void main(String[] args) throws Exception {
//		Properties props = new Properties();
//		props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
//		props.put(Context.PROVIDER_URL, "rmi://localhost");
//
//		InitialContext ictx = new InitialContext(props);
//		SampleRMI rmi = (SampleRMI) ictx.lookup("sample");
		Remote r = (Remote)Naming.lookup("sample");
		SampleRMI rmi = (SampleRMI) r;
		int counter = rmi.doSomething();
		System.out.println("client counter is " + counter);
	}
}
실행을 할때마다 counter 값이 증가하는걸 알 수 있습니다. 즉, Stateful 이죠. ]]>

Related Posts