A tecnologia RMI - Remote Method Invocation (Invocao de Mtodos Remotos), foi
primeiramente introduzida no Java, no JDK verso 1.1, elevando a programao para redes em um patamar mais elevado. Apesar do RMI ser relativamente fcil, ele pe o desenvolvedor Java frente um novo paradigma, o mundo da computao de objetos distribudos. Este guia prtico vai lhe introduzir esta tecnologia verstil, que melhorou muito desde sua primeira verso. Objetivo O principal objetivo para os criadores (designers) do RMI era permitir os programadores a desenvolverem programas distribudos em Java com a mesma sintaxe e semntica usada em programas no-distribudos. Para isso, eles tiveram que mapear cuidadosamente como classes Java e objetos trabalham em uma nica Java Virtual Machine (JVM) para um novo modelo de como as classes e objetos trabalhariam num ambiente distribudo de computao (mltiplas JVMs). Os arquitetos do RMI tentaram fazer com que o uso dos objetos distribudos em Java fosse similar ao uso de objetos Java locais. Esta seo introduz a arquitetura RMI da perspectiva dos objetos Java remotos distribudos, e explora as diferenas de comportamento com objetos locais. A arquitetura RMI define como os objetos se comportam, como e quando excees podem ocorrer, como a memria gerenciada e como os parmetros so passados e retornados de mtodos remotos. Arquitetura Java RMI A arquitetura RMI estende a segurana e robustez da arquitetura Java para o mundo da computao distribuda. Interfaces: O corao do RMI A arquitetura RMI baseada em um importante princpio: a definio do comportamento e a implementao do comportamento so conceitos separados. RMI permite que o cdigo que define o comportamento e o cdigo que implementa o comportamento permanecerem separados e rodarem em JVMs separadas. Em RMI, a definio do servio remoto codificada usando uma interface Java. A implementao do servio remoto codificada em uma classe. Logo, a chave para se entender o RMI lembrar que as interfaces definem o comportamento e as classes definem a implementao. A classe que implementa o comportamento roda do lado do servidor RMI. A classe que roda no cliente atua como um Proxy para o servio remoto. Veja o seguinte diagrama: O programa cliente faz chamadas de mtodos pelo objeto Proxy, o RMI envia a requisio para a JVM remota e redireciona para a implementao. Qualquer valor retornado pela implementao devolvido ao Proxy e ento ao programa cliente. Arquitetura de Camadas do RMI Com o entendimento da arquitetura RMI num alto nvel, vamos dar uma breve olhada na sua implementao. A implementao do RMI essencialmente feita de trs camadas de abstrao. A camada Stub e Skeleton est abaixo dos olhos do desenvolvedor. Esta camada intercepta as chamadas de mtodos feitas pelo cliente para que a varivel de referncia da interface redirecione essas chamadas para o servio RMI remoto. A prxima camada a Remote Reference Layer. Esta camada sabe como interpretar e gerencias referncias feitas dos clientes para os objetos do servio remoto. A conexo do cliente ao servidor Unicast (uma-para-um). A camada de transporte baseada nas conexes TCP/IP entre as maquinas em uma rede. Usando essa arquitetura de camadas, cada uma das camadas poderia ser facilmente melhorada ou substituda sem afetar o resto do sistema. Por exemplo, a camada de transporte poderia ser substituda por uma camada que implemente conexes UDP/IP, sem afetar as camadas superiores. Nomeando Objetos Remotos Como um cliente acha o servio remoto RMI? Os clientes acham os servios remotos usando o servio de nomeao ou diretrio (naming or directory). Isso parece um pouco redundante, mas o servio de nomeao ou diretrio roda como um endereo bem formado (host:port). O RMI pode usar diferentes tipos de servios de diretrio, incluindo o JNDI. O prprio RMI inclue um simples servio, chamado de RMI Registry. O RMI Registry roda em cada maquina que hospeda o servio remoto, por definio na porta 1099. Numa mquina host, um programa servidor cria um servio remoto, primeiramente criando o objeto que implemente aquele servio. Em seguida ele exporta aquele objeto para o RMI. Quando o objeto exportado o RMI cria um servio que aguarda as conexes do cliente. O servidor registra o objeto no RMI Registry, com um nome pblico. No lado do cliente o RMI Registry acessado atravs da classe esttica Naming. Ela provm o mtodo lookup( ), que o cliente usa para requisitar o registro. Esse mtodo aceita a URL que especifica o nome do servidor e o nome do servio desejado. O mtodo retorna uma referncia remota para o objeto do servio. A URL formada como seguinte: view plainprint? 1. rmi://<host_name>[:port_number]/<service_name>
Usando o RMI Agora vamos trabalhar com um sistema que realmente implementa um sistema com RMI. Vamos criar um aplicativo simples, cliente e servidor, que executa mtodos do objeto remoto. Para tanto no necessitamos de duas mquinas distintas ou com IP distintos. O exemplo pode ser rodado na mesma mquina, pois o RMI sabe como trabalhar com isso, mesmo que o host e o cliente sejam na mesma localidade. Um sistema RMI composto de vrias partes: Definio das interfaces para os servios remotos Implementaes dos servios remotos Arquivos de Stub e Skeletons Um servidor para hospedar os servios remotos Um servio de RMI Naming que permite o cliente achar os servios remotos Um provedor de arquivos de classes (servidor http ou ftp) Um programa cliente que necessita os servios remotos Criando seu aplicativo com RMI Agora iremos, de fato, criar um sistema que implemente o RMI, utilizando-se de um programa cliente e um programa servidor. No utilizaremos um servidor FTP ou HTTP, no entanto utilizaremos os programas na mesma mquina e uma mesma estrutura de diretrios. Os passos a serem seguidos agora so: Escrever e compilar o cdigo Java da interface Escrever e compilar o cdigo Java das implementaes das classes Gerar as classes Stub e Skeleton das classes de implementao Crie um diretrio para salvar todos os seus arquivos de projeto. Voc pode fazer o download do cdigo fonte usado nesse tutorial. Interfaces O primeiro passo, como dito, ser criar a interface e compil-la. A interface define todas as funcionalidades remotas oferecidas pelo servio. Nomeio o arquivo como: Mensageiro.java. view plainprint? 1. import java.rmi.Remote; 2. import java.rmi.RemoteException; 3. 4. public interface Mensageiro extends Remote { 5. 6. public void enviarMensagem( String msg ) throws RemoteException; 7. public String lerMensagem() throws RemoteException; 8. }
Perceba que esta interface estende a classe Remote, e cada assinatura de mtodo declara as funcionalidades do servio, e que podem gerar uma exceo RemoteException. Salve este arquivo (Mensageiro.java) no seu diretrio e compile, com a seguinte linha de comando: view plainprint? 1. javac Mensageiro.java
Implementao Agora, voc dever escrever a implementao para o servio remoto, ou seja, o cdigo a ser executado no ambiente remoto. Nomeia o arquivo como: MensageiroImpl.java. view plainprint? 1. import java.rmi.RemoteException; 2. import java.rmi.server.UnicastRemoteObject; 3. 4. public class MensageiroImpl extends UnicastRemoteObject implements Mensageiro { 5. 6. public MensageiroImpl() throws RemoteException { 7. super(); 8. } 9. 10. public void enviarMensagem( String msg ) throws RemoteException { 11. System.out.println( msg ); 12. } 13. 14. public String lerMensagem() throws RemoteException { 15. return "This is not a Hello World! message"; 16. } 17. }
Salve este arquivo (MensageiroImpl.java) no seu diretrio e compile, com a seguinte linha de comando: view plainprint? 1. javac MensageiroImpl.java
Observe que a classe se utiliza (estende) da classe UnicastRemoteObject para linkar com o sistema RMI. Neste exemplo a classe estende a classe UnicastRemoteObject diretamente. Isto no realmente necessrio, mas essa discuso fica para uma prxima etapa. Quando uma classe estende a classe UnicastRemoteObject, ele deve prover um construtor que declare que ele pode lanar uma exceo RemoteException, pois quando o mtodo super( ) chamado, ele ativa o cdigo em UnicastRemoteObject, que executa o link RMI e a iniciao do objeto remoto. Stubs e Skeletons Gere os arquivos Stubs e Skeletons da classe de implementao que roda no servidor. Para tanto, execute o comando rmic, compilador RMI do JDK. view plainprint? 1. rmic MensageiroImpl
Aps a execuo deste comando, voc deveria ver no seu diretrio os arquivos Mensageiro_Stub.class, Mensageiro_Skeleton.class. Servidor O servio remoto RMI deve ser hospedado em um processo servidor. A classe MensageiroServer um servidor bem simples, que prov servios essenciais. Salve o arquivo como: MensageiroServer.java. view plainprint? 1. import java.rmi.Naming; 2. 3. public class MensageiroServer { 4. 5. public MensageiroServer() { 6. try { 7. Mensageiro m = new MensageiroImpl(); 8. Naming.rebind("rmi://localhost:1099/MensageiroService", m); 9. } 10. catch( Exception e ) { 11. System.out.println( "Trouble: " + e ); 12. } 13. } 14. 15. public static void main(String[] args) { 16. new MensageiroServer(); 17. } 18. }
Salve este arquivo (MensageiroServer.java) no seu diretrio e compile, com a seguinte linha de comando: > javac MensageiroServer.java Cliente O cdigo fonte para o cliente o seguinte. Salve o arquivo como: MensageiroClient.java. view plainprint? 1. import java.rmi.Naming; 2. import java.rmi.RemoteException; 3. import java.rmi.NotBoundException; 4. import java.net.MalformedURLException; 5. 6. public class MensageiroClient { 7. 8. public static void main( String args[] ) { 9. try { 10. Mensageiro m = (Mensageiro) Naming.lookup( "rmi://localhost/Mensag eiroService" ); 11. System.out.println( m.lerMensagem() ); 12. m.enviarMensagem( "Hello World!" ); 13. } 14. catch( MalformedURLException e ) { 15. System.out.println(); 16. System.out.println( "MalformedURLException: " + e.toString() ); 17. } 18. catch( RemoteException e ) { 19. System.out.println(); 20. System.out.println( "RemoteException: " + e.toString() ); 21. } 22. catch( NotBoundException e ) { 23. System.out.println(); 24. System.out.println( "NotBoundException: " + e.toString() ); 25. } 26. catch( Exception e ) { 27. System.out.println(); 28. System.out.println( "Exception: " + e.toString() ); 29. } 30. } 31. }
Salve este arquivo (MensageiroClient.java) no seu diretrio e compile, com a seguinte linha de comando: view plainprint? 1. javac MensageiroClient.java
Rodando o sistema RMI Agora que todos os arquivos do projeto de exemplo foram criados e devidamente compilados, estamos prontos para rodar o sistema! Voc precisar abrir trs diferentes consoles do MS-DOS no seu Windows, ou outro, caso utilize um diferente sistema operacional. Em um dos consoles vai rodar o programa servidor, no outro o cliente e no terceiro o RMI Registry. Inicie com o RMI Registry. Voc deve estar no mesmo diretrio em que esto gravados seus arquivos para rodar o aplicativo. Execute a seguinte linha de comando: view plainprint? 1. rmiregistry
Isso ir iniciar o RMI Registry e rod-lo. No segundo console vamos executar o programa servidor. Voc deve estar no mesmo diretrio em que esto gravados seus arquivos para rodar o aplicativo. Execute o seguinte comando: view plainprint? 1. java MensageiroServer
Isso ir iniciar, carregar a implementao na memria e esperar pela conexo cliente. No ltimo console, rode o programa cliente. Voc deve estar no mesmo diretrio em que esto gravados seus arquivos para rodar o aplicativo. Excute o comando: view plainprint? 1. java MensageiroClient
Se tudo correr bem, que o que esperamos e o que deveria acontecer, a seguinte sada ser gerada nos consoles 2 (servidor) e 3 (cliente). No console 2 (servidor): view plainprint? 1. Hellow World!
No console 3 (cliente): view plainprint? 1. This is not a Hello World! message
isso a. Voc acabou de criar um sistema utilizando a tecnologia RMI. Apesar de voc ter rodado os programas na mesma mquina, o RMI usa a pilha de rede TCP/IP para se comunicar entre as trs diferentes instncias da JVM. Espero que tenham gostado e aprendido com esse pequeno exemplo de como se usar o RMI.