quinta-feira, 31 de janeiro de 2013

Passo a passo do RMI

RMI é... velho. É um assunto batido, bem documentado, sem maiores mistérios, mas que é um dos pilares mais importantes do JEE, embora ele esteja caminhando para o esquecimento a passos largos com a chegada de tecnologias como o CDI.

Ainda assim, se você estiver estudando não me custa quase nada dar os toques do que fazer, ;-)

Como imaginado, o assunto aqui é prática. Se quiser teoria, tem aqui.

Comece abrindo o eclipse e criando um projeto chamado rmi_sample1:

Uma vez criado o projeto, vamos criar dois pontos de entrada: um para ser usado quando rodarmos o cliente, outro para quando rodarmos o servidor. Bastam duas classes por hora, cada uma com um método main():

Vamos fazer o seguinte agora: na classe do servidor vamos subir o registro, pois sem ele não poderemos disponibilizar os objetos remotos:

 public static void main(String[] args) throws Exception {
  LocateRegistry.createRegistry(1099);

 }

Outra forma de fazer é chamar na linha de comando o rmiregistry, mas vamos fingir que isso jamais existiu, ;-)

Falemos um pouco sobre o tal "objeto remoto":

  • Deve implementar uma interface que extenda a interface Remote.
  • Deve herdar de UnicastRemoteObject.
  • Deve sobrescrever e chamar ativamente o construtor padrão.

No nosso exemplo vamos fazer um pequeno cadastro de alunos. Crie uma interface chamada GestorAlunos:

package sample.api;

import java.rmi.Remote;

public interface GestorAlunos extends Remote {
 void adicionarAluno();
 String removerAluno();
 void consultarAluno();
 void listar();
}

Você pessoa esperta que é sabe que esta interface não está completa. Mas quando você estiver criando suas coisas você pode ir por esta abordagem também: defina os nomes, para depois definir os detalhes.

Por exemplo, vamos precisar de uma classe chamada Aluno:

Vamos colocar no aluno estes atributos:

package sample;

public class Aluno {

 private int codigo;
 private String nome;
 private double nota1,nota2,nota3;
 private int frequencia;
}

Claro, não esqueça de gerar os getters/setters usando o atalho do eclipse:

Feito isso, volte à interface GestorAlunos e modifique-a:

package sample.api;

import java.rmi.Remote;
import java.util.List;

import sample.Aluno;

public interface GestorAlunos extends Remote {

 Aluno adicionarAluno(Aluno aluno) throws RemoteException;

 String removerAluno(int codigo) throws RemoteException;

 Aluno consultarAluno(int codigo) throws RemoteException;

 List listar() throws RemoteException;
}

Parece bom... muito bom... mas até agora apenas definimos o contrato entre o cliente e o servidor, nada mais.

Esta interface, inclusive, será comum aos seus clientes e ao servidor.

vamos para a diversão, vamos fazer agora a implementação do GestorAlunos:

package sample.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;

import sample.Aluno;
import sample.api.GestorAlunos;

public class GestorAlunosImpl extends UnicastRemoteObject implements
  GestorAlunos {

 public GestorAlunosImpl() throws RemoteException {
  super();
 }

 private static final long serialVersionUID = 1179227415408023062L;

 @Override
 public Aluno adicionarAluno(Aluno aluno) {
  return null;
 }

 @Override
 public String removerAluno(int codigo) {
  return null;
 }

 @Override
 public Aluno consultarAluno(int codigo) {
  return null;
 }

 @Override
 public List listar() {
  return null;
 }

}

Observe que esta implementação faz nada ainda, apenas implementamos os métodos que a interface obriga bem como herdamos de UnicastRemoteObject.

Vamos deixar assim, uma coisa que chamo de wireframe. Vamos seguir para o MainServer novamente e colocar a nossa implementação no registro:

package sample;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

import sample.api.GestorAlunos;
import sample.impl.GestorAlunosImpl;

public class MainServer {

 /**
  * @param args
  */
 public static void main(String[] args) throws Exception {
  LocateRegistry.createRegistry(1099);
  GestorAlunos gestor = new GestorAlunosImpl();
  System.out.println("Servidor rodando");
  Naming.rebind("//localhost/GestorAlunos", gestor);
  System.out.println("Objeto publicado");
 }

}

Feito isso o MainServer passa a ser responsável por subir o registro e em seguida disponibilizar o objeto remoto.

Vamos ao MainClient fazer o código de recuperar a referência pro objeto remoto:

package sample;

import java.rmi.Naming;

import sample.api.GestorAlunos;

public class MainClient {

 /**
  * @param args
  */
 public static void main(String[] args) throws Exception {
  // conseguindo uma referência remota
  GestorAlunos gestor = (GestorAlunos) Naming//
    .lookup("//localhost/GestorAlunos");

  // fazendo coisas com o gestor
  Aluno a = new Aluno();
  a.setNome("Fulano de tal");
  a.setFrequencia(100);
  a.setNota1(10.0);
  a.setNota2(9.5);
  a.setNota3(7.0);
  a = gestor.adicionarAluno(a);
  System.out.println(a);

 }

}

Simples né? Vamos rodar. Primeiro rode o MainServer (botão direito nele, Run As > Java Application)

Em seguida faça o mesmo com o MainClient. Mas veja, algo de diferente ocorreu:

Esse erro é comum entre estudantes, e a solução é super simples. Torne a classe Aluno serializável:

package sample;

import java.io.Serializable;

public class Aluno implements Serializable{

 private static final long serialVersionUID = 1728799560066886492L;

 private int codigo;
 private String nome;
 private double nota1,nota2,nota3;
 private int frequencia;

 public int getCodigo() {
  return codigo;
 }
 public void setCodigo(int codigo) {
  this.codigo = codigo;
 }
 public String getNome() {
  return nome;
 }
 public void setNome(String nome) {
  this.nome = nome;
 }
 public double getNota1() {
  return nota1;
 }
 public void setNota1(double nota1) {
  this.nota1 = nota1;
 }
 public double getNota2() {
  return nota2;
 }
 public void setNota2(double nota2) {
  this.nota2 = nota2;
 }
 public double getNota3() {
  return nota3;
 }
 public void setNota3(double nota3) {
  this.nota3 = nota3;
 }
 public int getFrequencia() {
  return frequencia;
 }
 public void setFrequencia(int frequencia) {
  this.frequencia = frequencia;
 }

}

Salve.

Importante, você vai precisar parar o MainServer novamente, afinal ele ainda está rodando e está com a definição antiga de classe. Agora rode o MainServer novamente. Tudo certo dessa vez, né?

Retornou null, mas não se preocupe ainda: vamos para o GestorAlunosImpl olhar o método adicionarAluno. Vamos modificar essa implementação pra que nos dê algo mais interessante:

package sample.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;

import sample.Aluno;
import sample.api.GestorAlunos;

public class GestorAlunosImpl extends UnicastRemoteObject implements
  GestorAlunos {

 private int seq = 0;
 private List alunos = new ArrayList();

 public GestorAlunosImpl() throws RemoteException {
  super();
 }

 private static final long serialVersionUID = 1179227415408023062L;

 @Override
 public Aluno adicionarAluno(Aluno aluno) {
  aluno.setCodigo(++seq);
  alunos.add(aluno);
  System.out.println("Aluno adicionado com código " + aluno.getCodigo());
  return aluno;
 }

 @Override
 public String removerAluno(int codigo) {
  return null;
 }

 @Override
 public Aluno consultarAluno(int codigo) {
  return null;
 }

 @Override
 public List listar() {
  return null;
 }

}

Que beleza, né? agora ele adiciona numa lista e retorna com um código.

Entretanto, se você rodar o MainClient de novo, o retorno ainda será null. Por que isso?

Nosso GestorAlunosImpl não tem os stubs gerados.

Classicamente gerar stubs era um saco, mas estamos usando eclipse e portando a vida é boa.

Crie na raiz do projeto eclipse um arquivo chamado build.xml que tenha isso:



 
  
 

Aperte F5 na pasta do projeto e tenha uma surpresa, :-)

Agora, volte ao MainServer e rode-o outra vez. E faça a mesma coisa com o MainClient:

Bacana?

Agora voe sozinho, passarinho, que o resto é simples, e se você usar o que você viu aqui só o sucesso lhe espera, ;-)

Sem mais para o momento, apenas boa sorte!

Nenhum comentário :

Postar um comentário