Escolar Documentos
Profissional Documentos
Cultura Documentos
(v.1.1)
Este documento resultou do tratamento e compilação de informação vária sobre JAVA RMI,
existente em livros da especialidade, acetatos das aulas, URLs da Web e outras fontes. Esta
versão teve a colaboração dos alunos Sandra Melo e Carlos Castro do curso de SIG.
Referências:
Tutorais:
• http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp
• http://www.guj.com.br/java.tutorial.artigo.37.1.guj
• http://www.genady.net/rmi/v20/docs/installation/configure_jdk.html
• http://java.sun.com/javase/technologies/core/basic/rmi/whitepaper/index.jsp
Livro:
JAVA RMI
O JAVA RMI surgiu logo com a versão Java JDK 1.1, e tem vindo a sofrer pequenas alterações
à medida que novos pacotes JAVA são lançados. Embora a portabilidade de soluções
desenvolvidas não esteja saudavelmente garantida entre versões JAVA (entenda-se, JVM -
Máquinas Virtuais JAVA), a arquitectura RMI assenta num padrão de desenvolvimento que se
tem respeitado.
Apresentação
1
máquina. Praticamente todas as “virtudes” da tecnologia JAVA são mantidas, como é o caso do
portabilidade, segurança e polimorfismo. Interagir com sistemas legados (via JNI, JDBC, etc.),
com outros objectos existentes, etc., é uma tarefa suportada. Os arquitectos do RMI tentaram
fazer com que o uso dos objectos distribuídos em Java fosse similar ao uso de objectos Java
locais.
A arquitectura RMI define como os objectos se comportam (através de JAVA Interfaces), como
e quando excepções podem ocorrer, como a memória é gerida e como os parâmetros são
passados e retornados entre métodos remotos.
Em RMI, a definição do serviço remoto é codificada através de uma Interface Java, enquanto a
sua implementação (desse mesmo serviço remoto) é codificada numa classe. Logo, a chave
para se entender o RMI é recordar que as Interfaces definem o comportamento e as classes
definem a implementação.
O programa cliente faz chamadas de métodos pelo objecto Proxy, o RMI envia a requisição
para a JVM remota e redirecciona para a implementação. Qualquer valor devolvido (return)
pelo servidor é devolvido ao Proxy e depois ao programa cliente.
Um objecto RMI, ao poder ser evocado remotamente, tem de ser implementado seguindo as
seguintes especificações RMI:
• Extende UnicastRemoteObject
2
• Possui métodos públicos distribuídos que lidam com (throw) RemoteException
• Possuem um construtor que lida com RemoteException
• Possuem um interface separado que descreve a assinatura dos métodos remotos e
extende java.rmi.Remote.
Implementação
A próxima camada é a Remote Reference Layer. Esta camada sabe como interpretar e gerir as
referências feitas dos clientes para os objectos no serviço remoto. A conexão do cliente ao
servidor é Unicast (uma-para-um).
Cada uma das camadas pode ser facilmente melhorada ou substituída sem afectar o resto do
sistema. Por exemplo, a camada de transporte poderia ser substituída por uma camada que
implemente conexões UDP/IP, sem afectar as camadas superiores.
O RMI pode usar diferentes tipos de serviços de directório, incluindo o JNDI (Java Naming and
Directory Interface). O próprio RMI inclui um simples serviço, chamado RMI Registry (no JDK
>=4.0 corresponde ao programa rmiregistry) que executa em cada máquina que hospeda o
serviço remoto, por defeito, na porta 1099.
3
Numa máquina host, o servidor cria o objecto que implementa um dado serviço remoto
pretendido. Depois regista-o no RMI Registry, com um nome público.
No lado do cliente o RMI Registry é acedido através da classe estática Naming. Ela possui,
entre outros métodos, o método lookup( ), que o cliente usa para procurar o registro. Esse
método aceita a URL que especifica o nome do servidor e o nome do serviço desejado. O
método retorna uma referência remota para o objecto do serviço. A URL tem o seguinte
aspecto:
rmi://<host_name>[:port_number]/<service_name>
Para este tópico vamos utilizar o exemplo Mensageiro (também analisado nas aulas e cujo
código se encontra na página da disciplina), utilizado pelos tutoriais Java na demonstração de
vários processos de desenvolvimento. Para isso não necessitamos obrigatoriamente de duas
máquinas distintas ou com IP distintos. O exemplo pode ser executado na mesma máquina,
pois o RMI sabe como lidar com isso, mesmo que o host e o cliente sejam no mesmo local.
É possível sugerir uma notação capaz de orientar melhor a escrita de código. Assim, no nosso
exemplo, vamos ter:
4
Sufixo _stub e MensageiroImpl_Stub.class Classes que mapeiam os métodos no
skel cliente e no servidor
MensageiroImpl_Skel.class
Um exemplo RMI
Vamos criar um sistema teste que implemente o RMI, utilizando um programa cliente e um
programa servidor. Não vai ser utilizado um servidor FTP ou http e os programas estão
alojados na mesma máquina com a mesma estrutura de directórios.
• Definir a Interface
– Servidor
• MensageiroImpl.java (implementação)
• Mensageiro.java (interface)
• StoreServer.java (main)
• Outras classes
– Cliente
• MensageiroClient.java
• Mensageiro.java
Interface
O primeiro passo, deverá ser então a criação da interface e compilá-la. A interface define todas
as funcionalidades remotas oferecidas pelo serviço. Nomeie o ficheiro como Mensageiro.java.
import java.rmi.Remote;
import java.rmi.RemoteException;
5
public interface Mensageiro extends Remote {
Anote que esta interface extende a classe Remote, e cada assinatura de método ao declarar as
funcionalidades do serviço, prevê a existência de serem geradas excepções RemoteException.
Guarde este ficheiro no seu directório e compile, com a seguinte linha de comando:
javac Mensageiro.java
Implementação do interface
Agora é necessário implementar cada um dos serviços remotos, ou seja, o código a ser
executado no ambiente remoto. Nomeie o ficheiro como MensageiroImpl.java.
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
Guarde este ficheiro no seu directório e compile, com a seguinte linha de comando:
6
javac MensageiroImpl.java
Observe que a classe utiliza (extende) a classe UnicastRemoteObject para linkar com o
sistema RMI. Neste exemplo a classe extende a classe UnicastRemoteObject directamente.
Quando uma classe extende a classe UnicastRemoteObject, ela deve disponibilizar um
construtor que declare que ele pode lançar uma excepção RemoteException, pois quando o
método super( ) é chamado, ele activa o código em UnicastRemoteObject, que executa o link
RMI e a iniciação do objecto remoto.
Gere os ficheiros Stub e Skeleton da classe de implementação que corre no servidor. Para isso
1
execute o comando rmic, compilador RMI do JDK (segundo o JRMP – Java Remote Method
Protocol).
rmic MensageiroImpl
Existem contudo outras formas de gerar os stubs, dependendo da versão JAVA a utilizar:
Após a execução deste comando, você deverá ver no seu directório os ficheiros
Mensageiro_Stub.class, Mensageiro_Skeleton.class. Todas as classes devem ser compiladas
(com javac) antes da utilização do rmic.
Assim, após a geração de stubs, a estrutura típica de ficheiros de uma aplicação RMI ficará:
– Servidor
• MensageiroImpl.java (implementação)
• Mensageiro.java (interface)
• StoreServer.java (main)
1
http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/rmic.html
7
• Outras classes
• MensageiroImpl_Stub.class
– Cliente
• MensageiroClient.java
• Mensageiro.java
• MensageiroImpl_Stub.class
Servidor
O serviço remoto RMI deve ser hospedado num processo servidor. A classe MensageiroServer
é um servidor bem simples, que implementa somente os serviços pretendidos. Guarde o
ficheiro como MensageiroServer.java.
import java.rmi.Naming;
public class MensageiroServer {
public MensageiroServer() {
try {
Mensageiro m = new MensageiroImpl();
Naming.rebind("rmi://localhost:1099/MensageiroService", m);
}
catch( Exception e ) {
System.out.println( "Trouble: " + e );
}
}
Guarde este ficheiro (MensageiroServer.java) no seu directório e compile, com a seguinte linha
de comando:
javac MensageiroServer.java
Cliente
8
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.NotBoundException;
import java.net.MalformedURLException;
Nota:
Naming.lookup( "nome_servidor" )
Neste caso é procurado, por omissão, na porta 1099, no localhost. A sintax mais adequada
deverá ser contudo,
9
Naming.lookup(“rmi://host:port/name”);
Neste caso é possível indicar o IP da máquina e a porta disponibilizada para tal. No nosso
exemplo poderia então ser, por exemplo:
Naming.lookup( "rmi://127.0.0.1:99/MensageiroService" );
Guarde este ficheiro (MensageiroClient.java) no seu directório e compile, com a seguinte linha
de comando:
javac MensageiroClient.java
Para testar um exemplo de RMI, talvez o melhor seja utilizar três consolas diferentes do MS-
DOS.
Numa das consolas vai executar o programa servidor, numa outra execute o RMI Registry e na
última execute o Cliente.
Inicie o RMI Registry. Você deve estar no mesmo directório em que estão gravados os
ficheiros. Execute a seguinte linha de comando:
start rmiregistry
10
Aparecerá então uma consola DOS na qual está a ser executado o RMI registry.
Na segunda consola vamos executar o programa servidor. Você deve estar no mesmo
directório em que estão gravados os ficheiros para executar o aplicativo. Execute o seguinte
comando:
java MensageiroServer
Este comando inicia o servidor, carrega a sua implementação na memória e espera pela
conexão com o cliente.
Na última consola, execute o programa cliente. Você deve estar no mesmo directório em que
estão gravados os ficheiros. Para isso execute o seguinte comando:
java MensageiroClient
Se tudo correr bem, nas consolas 2 (servidor) e 3 (cliente) deverão ocorrer as seguintes
mensagens.
Na consola 2 (servidor):
“Hellow World!”
Na consola 3 (cliente):
Tratamento de segurança
Este processo, embora não muito simples, foca-se na análise das classes que gerem a
segurança do JAVA, neste caso essencialmente a classe System.setSecurityManager. Para
isto, o código no processo Cliente deverá conter a seguinte instrução
System.setSecurityManager(new RMISecurityManager());
Para que consiga ser executado, as condições de acesso e permissão poderão ser
definidasem ficheiros externos (policy files) e referidos na altura da execução dos processos. O
comando seguinte refere um ficheiro (policy.all) no qual estarão definindo todas as regras de
11
segurança pretendidas (convém aprofundar mais este assunto, lendo sobre “Implementação de
segurança em aplicações Java”).
Interface
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
12
{
public static void main(String[] argv)
{
System.setSecurityManager(new RMISecurityManager());
try
{
myRMIImpl implementation = new myRMIImpl("myRMIImplInstance");
System.out.println("Servidor escuta...");
}
catch (Exception e)
{
System.out.println("Exception occurred: " + e);
}
}
}
Cliente
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.Date;
int x = myServerObject.sum(2,3);
System.out.println("Soma de 2 com 3 = :" + x);
}
catch(Exception e)
{
System.out.println("Exception occured: " + e);
System.exit(0);
}
System.out.println("RMI connection successful");
}
13
}
Repare-se que neste último caso a classe main() do Servidor está definida, não na classe que
implementa o interface mas sim na classe instância.
Repare-se também que neste exemplo é possível indicar, na altura da execução do cliente, o
IP da máquina onde está a executar o servidor.
2
Desenvolver aplicações RMI com Eclipse
Caso queiras utilizar o IDE Eclipse para escrever o código JAVA, segue um pequeno tutorial de
como instalar o plugin necessário.
2
Capítulo criado com a colaboração do aluno Carlos Castro ( 3º ano de SIG)
3
Não confunda JDK com JRE. Este último refere-se somente à máquina virtual
14
Figura 1
Figura 2
Tem de verificar que a localização da a máquina virtual (JRE) deverá corresponder à PATH
onde instalou o JDK.
15
e) Se tiver de alterar a localização, por defeito, do JRE, seleccione o registo corrente
(checkbox) e click em “Edit…”. (Figura 3)
Figura 3
Nota:
Por vezes a portabilidade JAVA fica comprometida sempre que estejam várias versões JDK ou
mesmo JRE instaladas. Isto pode acontecer, por exemplo, se compilar com uma versão (com
javac) e for executar (com java) com outra versão distinta…para evitar isto, certifique-se que
todas as variáveis de ambiente estão devidamente configuradas: PATH, CLASSPATH e
JAVA_HOME.
Configuração do Plugin
• Colocar o conteúdo da pasta features e plugins dentro das respectivas com o mesmo
nome na directoria do eclipse.
16
Utilização
• Criar um novo projecto JAVA como se tratasse de uma aplicação Java normal. Uma
boa prática neste caso é criar Packages para agrupar classes. Sendo assim, criamos
(por exemplo) 3 Packages, Cliente, Servidor e Interface.
• Antes de compilar a aplicação, clicar com o botão direito do rato por cima do nome do
projecto no Package Explorer, escolher RMI e depois Enable Stubs Generation (isto
para activar a geração automática do Stub - "rmic")
• Para compilar e correr a aplicação, basta clicar com o botão direito novamente sobre o
nome do projecto, depois escolher Run As, e finalmente RMI Application. Irá surgir uma
mensagem em que se pede que seja escolhida uma classe main (escolhemos a classe
que contem o servidor). De seguida fazemos um Run novamente mas seleccionamos a
classe Cliente.
Trata-se de desenvolver aplicações RMI sem a ajuda de qualquer IDE específico mas somente
através de um editor de texto (situação comum para ambientes linux ou mesmo os menos
adeptos ao Windows).
Neste caso, uma sugestão de organização de ficheiros e criação de scripts é apresentado nos
seguintes tópicos:
Organização de ficheiros:
onde, na pastas:
17
no nosso caso:
no nosso caso
no nosso caso
Batch Files:
Para facilitar a compilação poderão ser criadas algumas batch files (ou scripts) tais como se
apresentam a seguir:
build.bat
usada para compilar todo o código
@rem preparar exemplo de RMI
@by lufer
18
endlocal
server.bat
usada para iniciar o rmiregistry e a aplicação servidor
@rem executa o servidor.
cd build\classes
cd ..\..
client.bat
usada para iniciar a aplicação cliente
@rem Run the SampleServer client
cd build\classes
java -Djava.security.policy=policy.all MensageiroClient
cd ..\..
Conclusão
Se tudo correu bem, acabou de criar um sistema utilizando a tecnologia RMI. Apesar de ter
executado os programas na mesma máquina, o RMI usa a pilha de rede TCP/IP para
comunicar entre as três diferentes instâncias da JVM.
4
Mais informação em http://java.sun.com/javase/technologies/core/basic/rmi/whitepaper/index.jsp
19
• Para concluir, é fácil de programar e é robusta.
Esta tecnologia compete directamente com a especificação CORBA e com a tecnologia Web
Services.
Quiçá uma das desvantagens, entre outras, seja a dependência exclusiva do JAVA.
Março, 2007
20