Login Registre-se

Você pode ganhar um iPad 2 na promoção do Javafree

O Portal javafree.org inicia mais uma promoção para os usuários do fórum. Quem publicar mais posts válidos (perguntas ou respostas) entre 16/4 a 13/7 levará para casa um iPad 2 de 16GB!

Clique aqui e saiba mais.
Home > Artigos > Java em Geral >

RMI (Remote Method Invocation)

Publicado por .Christiano. em 16/08/2009 - 74.770 visualizações


comentários: 7

A Invocação Remota à Métodos (RMI) permite que um objeto rodando em uma JVM invoque métodos de um objeto rodando em uma outra JVM.

Para tornar isso possível temos de fazer com que nossos métodos estejam prontos a serem chamados a partir de outros objetos. Após isso teremos nosso objeto passará a ser um "objeto remoto".

Para tanto, deveremos seguir algumas regras que serào citadas ao longo deste tutorial.

Como aplicação de exemplo, desenvolveremos um applet para exibir uma mensagem. A princípio algo considerado banal por muitos.
Mas agora faremos isso com RMI.

Precisaremos de algumas classes/arquivos para desenvolver nosso exemplo.

Aqui estão:

. Mensagem.java - interface remota
. MensagemImpl.java - objeto que implementará nossa classe remota
. MensagemApplet.java - applet que invocará o método exibeMensagem da interface remota.
. Mensagem.html - página que carregará o applet.

Então pratiquemos!

Pelo JAVA, necessitar de um caminho completo com o mesmo nome do pacote da classe, precisaremos criar um diretório que reproduza esse nome.
Nesse caso estaremos utilizando "rmi.exemplo" como nome do pacote. Portanto, deveremos criar dentro do diretório selecionado para trabalho
a estrutura de diretório "rmi/exemplo".

Após criada nossa estrutura de diretório, definiremos a classe remota como uma interface.

Em JAVA, um objeto remoto é a instância de uma classe que implementa a interface "Remote". Nossa interface remota terá as declarações de cada um dos métodos da interface de implementação que serão chamados.

Vale lembrar que uma interface remota tem as seguintes características:


    A interface deverá ser declarada como "public"
    A interface deverá extender a interface "java.rmi.Remote"
    Cada método deverá possuir um "throw" para "java.rmi.RemoteException"
    O tipo de dados de qualquer objeto REMOTO que é passado como argumento ou retorno deverá ser declarado com o tipo da Interface Remota.


Agora que já temos uma visão geral de interface remota, vamos ao código.
Abaixo está o código da nossa interface remota:




A classe de implementação é a classe que contém a estrutura dos métodos que serão chamados pela interface remota.

Nossa classe de implementação deverá, pelo menos:

    Implementar uma interface remota
    Definir o construtor do objeto remoto
    Conter implementações dos métodos que poderão ser chamados


A classe de servidor é a classe que possui o método main (criador da instância da implementação do objeto remoto) e liga a instância a um registro no rmiregistry.

O servidor precisará:

    Criar e instalar um gerenciador de seguraça (security manager)
    Criar uma ou mais instâncias do objeto remoto
    Registrar pelo menos um dos objetos remotos com o "RMI remote object registry"


Vejamos como será o código:




Implementação da interface remota

Quando uma classe implementa uma interface, é estabelecida uma ligacação entre as mesmas.
A classe assume que terá métodos e definições para cada um dos mesmos declarados na interface implementada.

Os métodos da interface são, implicitamente, públicos e abstratos. Se a classe de implementação não preencher estes requisitos, será por definição uma classe abstrata e, caso não seja declarada como tal, o compilador acusará.

A classe de implementação deve declarar qual (quais) interface (s) remota (s) ela implementa.




No trecho acima vimos que a classe MensagemImpl implementa a interface Mensagem.

A classe de implementação pode extender uma classe remota (neste exemplo utilizamos a java.rmi.server.UnicastRemoteObject).

Extendendo a UnicastRemoteObject, nossa classe poderá criar objetos que:

    Utilizam o transporte por sockets RMI para comunicação
    Rodam interruptamente


Construtor do objeto remoto

O construtor de uma classe remota tem a mesma função do construtor de uma classe "normal". Ambos inicializarão e instanciarão a classe.

Porém, a instância do objeto remoto deverá ser "exportada". Quando extendemos java.rmi.server.UnicastRemoteObject ou java.rmi.activation.Activatable, nossa classe será exportada automaticamente quando criada.

Vejamos nosso método construtor:




Podemos notar que:

A chamada ao método super() invoca o construtor da classe java.rmi.server.UnicastRemoteObject, que exportará nosso objeto remoto.

O método construtor trata a exceção do tipo java.rmi.RemoteException, devido à possibilidade de falha durante a "exportação" do objeto remoto.

A chamada ao método super() ocorre automaticamente, ela foi incluída neste exemplo somente para deixar claro que a JVM constrói a superclasse antes da classe.

Pela "exportação" do objeto poder causar uma exceção do tipo java.rmi.RemoteException, deveremos definir um construtor que trate uma exceção do tipo RemoteException. Caso não façamos isto, receberemos o seguinte erro ao compilar nosso código:




Implementação do método remoto

A classe de implementação de um objeto remoto contém o código de implementação de cada um dos métodos remotos declarados na interface remota.

No nosso exemplo declaramos o método exibeMensagem() na interface remota.
Agora veremos sua declaração na classe de implementação.



A classe de implementação pode definir métodos não especificados na interface remota, porém estes métodos só poderão ser chamados pela JVM que está rodando o serviço.

Criação do gerenciador de segurança (Security Manager)

Um gerenciador de segurança precisa estar rodando para garantir que as classes não executem operações às quais elas não foram designadas.

Se um gerenciador de segurança não for especificado, não serão permitidos os carregamentos de classes, exceto as que se encontram no CLASSPATH.

No nosso exemplo não instalamos um gerenciador no cliente já que applet utiliza o gerenciador de segurança do browser.

Qualquer JVM precisa de um gerenciador de segurança para fazer o download do código e os clientes RMI para fazer o download dos stubs.

Abaixo está o código de criação do nosso SM:




Instanciando o objeto remoto

O método main do nosso servidor precisará criar uma ou mais instâncias do nosso objeto remoto.
Faremos isso da seguinte forma:



O construtor exportará o objeto remoto.
Uma vez criado, o objeto está pronto para aceitar conexões.

Registrando o objeto

Para aceitação de chamadas por parte do nosso objeto remoto, precisaremos criar uma referência afim de que o cliente consiga "enxergá-lo".

A RMI já nos fornece um registro de objeto remoto (rmiregistry) que permite que criemos um "link" no formato de URL para nosso objeto.

O RMI registry funciona como um DNS, provendo aos clientes uma referência por nome ao objeto remoto.

Uma vez que o objeto remoto foi registrado no servidor pelo rmiregistry, já teremos nossa referência ao objeto e os clientes poderão fazer chamadas a seus métodos.

Faremos isso da seguinte forma:


Note que:

    O primeiro parâmetro é uma String que identifica a URL de onde está o objeto remoto
    Não precisamos especificar protocolos na URL
    O segundo parâmetro é uma referência ao objeto instanciado da classe de implementação.


Obs.: Se o objeto remoto não estiver na mesma máquina que a classe de implementação, deveremos trocar a string "MensagemServer" por "//servidor/MensagemServer", onde "Servidor" poderá ser o nome ou o IP do host do nosso objeto remoto.

Por questões de segurança, uma aplicação poderá ser carregada/descarregada somente para o registro de seu host, porém, uma pesquisa poderá ser realizada de qualquer servidor.
Isso previne um cliente de remover ou sobrescrever uma de suas entradas no registro do servidor.

Applet cliente

Veremos abaixo como construir o applet que invocará remotamente o método exibeMensagem e exibirá o resultado desta chamada quando o applet carregar



O que acontecerá quando o applet rodar?

O applet buscará a referência do objeto remoto no registro da RMI através do método lookup.
No nosso exemplo utilizamos os métodos getCodBase e getHost para construir essa URL que será informada ao método para localização do servidor.

O applet invocará o método remoto exibeMensagem no objeto remoto que está no servidor.

O applet invocará o método paint para exibir a String de retorno.

Obs.: A URL passada como parametro ao método lookup precisa conter o nome do host.
Caso não contenha, o lookup feito pelo applet apontará para a máquina do cliente e o AppletSecurityManager causará uma exceção devido ao applet não conseguir acessar a mesma (o applet só poder comunicar-se com a máquina onde ele está).


Vejamos agora o código html para a página que executará nosso applet:


Compilando e gerando as Classes

Agora que já criamos todas as nossas classes e o arquivo HTML para chamada do nosso applet, podemos compilá-las. Compilaremos nossas classes normalmente e após isso compilaremos a classe de implementação utilizando o rmic para gerar os Stubs* e Skeletons.

* Stub é um espécie de proxy que ficará junto com a aplicação cliente e permitirá o acesso aos objetos que estão no servidor RMI.

Compilando as classes

Primeiro temos que ter certeza de que o diretório onde estão os fontes das nossas classes e o diretório onde a compilação delas será gerada estão no CLASSPATH. Caso não estejam devemos adicioná-los.

Para compilar é simples. De dentro do diretório onde estão nossos fontes faremos o seguinte:



Isso criará nossas classes dentro do diretório rmi/exemplo (especificado como requisito em uma das colunas anteriores).

Após criadas as classes, deveremos criar os stubs e skeletons. Para isso utilizaremos o rmic, a partir do diretório onde estão nossos fontes, da seguinte forma:


Após isso serão criados os seguintes arquivos:

HelloImpl_Stub.class
HelloImpl_Skel.class


Para rodar a aplicação ainda temos que seguir alguns passos. Vamos lá.

1 - Executar o rmiregistry

O RMI registry funciona domo um DNS, provendo aos clientes uma referência por nome ao objeto remoto.

Geralmente é usado somente para localizar o primeiro objeto que a aplicação precisa comunicar-se, então este objeto proverá o restando do acesso aos outros objetos. Para iniciar o rmiregistry basta digitar na linha de comando o seguinte:


O rmiregistry roda, por default, na porta 1099. Caso queiramos mudar da porta 1099 para a porta 2003, por exemplo, basta executá-lo da seguinte forma:


Se o rmiregistry estiver rodando em outra porta que não a 1099, teremos que especificar no nosso código o número da mesma na URL que passamos para a classe java.rmi.Naming.
Exemplo.:


Toda vez que modificarmos nossa interface remota ou modificarmos/adicionarmos algo na classe de implementação, deveremos reiniciar o rmiregistry.

2 - Iniciar o servidor

Ao iniciar o servidor, deveremos atribuir um valor à propriedade java.rmi.server.codebase para que o stub possa ser carregado automaticamente para o registro e para o cliente.Este valor deverá ser o local onde o stub está.

Obs.: Um stub é carregado dinamicamente para a JVM do cliente somente quando ele não está disponível localmente e a propriedade java.rmi.server.codebase foi definida corretamente para especificar a localização do stub.

Precisaremos também de um arquivo de policy para especificar os privilérios de acesso ao servidor.
Crie um arquivo com o código abaixo e salve-o com o nome de "policy"


Agora poderemos iniciar nosso servidor.
Para tanto executaremos a seguinte instrução, a partir do diretório onde estão os fontes, na linha de comando:



Após executar este comando, receberemos a seguinte mensagem:

Quote:
"MensagemServer rodando."


Obs.:Utilizamos a URL para o codebase no formato "file:///" por estarmos testando o exemplo localmente. Caso quisessemos testar com as classes em um servidor web, bastaria trocarmos a URL para "http://servidor/localDasClasses/".


3 - Executar o applet

Uma vez que já iniciamos o rmiregistry e o nosso servidor, podemos rodar nosso applet. Para rodá-lo basta abrir nosso HTML no browser.

comentários: 7