Labs SD >

Invocação de procedimentos remotos com Java RMI

Objectivos

No laboratório:

Documentação

Enunciado

Partindo de um Jogo do Galo (Tic Tac Toe) feito para um cenário local, pretende-se desenvolver uma variante do jogo onde a parte computacionalmente mais pesada é realizada por um servidor remoto. O servidor guarda as variáveis do jogo. O cliente faz a interface com o utilizador.
Tic Tac Toe

Sugestão: nas alíneas seguintes, corra os programas cliente e servidor numa máquina não partilhada com outros grupos para evitar conflitos. Use uma máquina pessoal ou do laboratório e não o sigma.

  1. Descarregue e descomprima o código fonte da aplicação Jogo do Galo/Tic Tac Toe
    (servidorZIP, clienteZIP).
    1. Importe os projectos no Eclipse, recorrendo a File > Import > Maven > Existing Maven Projects.
    2. No ponto de partida o código está todo centralizado no servidor, estando o cliente vazio.
    3. Estude os principais ficheiros com a implementação do jogo (Game.java e TTT.java).
    4. Compile e experimente o jogo na sua versão centralizada (executando mvn compile exec:java).

     

  2. Pretende-se que a classe TTT.java, que implementa o jogo, passe a ser invocável remotamente.
    Dessa forma, permitir-se-á que haja um cliente remoto (que possivelmente corre em máquina diferente que a máquina que serve o jogo) que interage com os jogadores e que invoca as funções do servidor via Java RMI (Remote Method Invocation).
    1. Comece por desenhar os métodos remotos do servidor numa interface chamada TTTService.
      A interface deve expor todas as funções remotas que o cliente precisa de invocar. Para ser uma interface remota, precisa também de herdar de java.rmi.Remote e cada um dos seus métodos deve lançar uma java.rmi.RemoteException.
      Consulte o exemplo de interface remota apresentado no livro para ajuda.
      1. Coloque a nova interface num módulo interfaceZIP
      2. Depois de codificar a interface, compile o código e instale a dependência:
        cd ttt-rmi-interface
        mvn install
      3. Adicione a dependência no pom.xml do servidor e do cliente, pois esta interface terá que ser partilhada por ambos.
            ...
                <dependency>
                    <groupId>example</groupId>
                    <artifactId>ttt-rmi-interface</artifactId>
                    <version>1.0-SNAPSHOT</version>
                </dependency>
            ...
        
      4. Depois de editar os pom.xml, refresque as dependências do Maven no projeto Eclipse
        (right-click, Maven, Update Project..., Force Update of Snapshots/Releases, OK)
    2. No servidor, transforme a classe TTT para que passe a implementar a interface remota TTTService. Para que instâncias desta classe possam ser objectos remotos, modifique a definição da classe TTT para que ela passe a herdar de java.rmi.server.UnicastRemoteObject e acrescente um construtor que lance excepção RemoteException.
      Deverá implementar a lógica dos métodos que o Cliente irá chamar posteriormente.
      Consulte o exemplo da classe shapeListServant apresentado no livro para ajuda.
    3. No servidor, crie uma nova classe TTTServer com método main, onde correrá o servidor.
      No método main deverá:
      1. Instanciar um objecto remoto do tipo TTT
      2. Lançar um rmiregistry (serviço de nomes do RMI) e registar o objecto remoto nesse rmiregistry
      Consulte o exemplo da classe ShapeListServer apresentado no livro para ajuda.
      Não se esqueça de actualizar a mainclass do ficheiro pom.xml de modo a reflectir a classe que possui o método main.
    4. Deverá agora proceder à modificação do cliente para chamar remotamente os métodos definidos anteriormente.
      1. Mover a classe ttt.Game do servidor para o cliente. Implemente um cliente remoto que, com base nos comandos recebidos pela consola local, invoca métodos do jogo remoto. Assuma que ambos os jogadores de cada jogo usam o mesmo cliente.
        Consulte o exemplo do cliente apresentado no livro para ajuda.
      2. Não se esqueça de, na chamada ao método Naming.lookup, definir correctamente o URL que localiza o objecto, na forma: //host:port/name, em que host e port definem a máquina e o porto onde corre o rmiregistry onde foi registado o objecto remoto e name é o nome que foi atribuído ao objecto pelo servidor quando chamou rebind.
      3. Adicione o tratamento adequado às exceções lançadas quando acontece algo inesperado numa invocação remota.
        (Algumas das excepções que podem ocorrer são: java.rmi.UnknownHostException, java.rmi.UnmarshalException, java.rmi.MarshalException, java.rmi.RemoteException e java.rmi.ConnectException)

     

  3. Experimente lançar o servidor e depois um cliente para jogar.
  4.  

  5. Responda às seguintes questões:
    1. Quando se usa SUN RPC é gerado código para converter os dados de e para um formato de rede. O que acontece quando se usa RMI?
    2. Das classes e interfaces Java que usou, quais as que pertencem apenas ao cliente, apenas ao servidor e a ambos?

 

O resto do enunciado será entregue na aula. O objectivo será estender a solução resultante do enunciado acima com mais procedimentos ou modificar alguns dos seus procedimentos actuais.

 

Entrega da solução

Fénix, Avaliação, Projetos, mini Exercício 2 - Java RMI

A solução completa deverá ser submetida no Fénix antes do fim da sua aula de laboratório.
Trabalhos submetidos depois da hora de fim da aula não serão considerados.

Ter atenção ao seguinte:

Como seria um projecto Java RMI mais elaborado?

O projecto desenvolvido ao longo das alíneas apresentadas faz algumas simplificações importantes que normalmente não se observam num projecto real de Java RMI. Entre elas:

  1. Existe apenas uma instância de objecto remoto.
    Normalmente pode existir um número variado de interfaces e classes remotas, assim como de suas instâncias.
  2. Há um processo que aloja o objecto remoto e outro processo que obtém referência remota para essa objecto, numa clara distinção entre servidor e cliente.
    Na prática, um processo pode simultaneamente ser servidor de alguns objectos remotos e ter outras referências remotas (para objectos remotos noutros processos), sobre as qual invoca métodos (agindo também como cliente).
  3. No projecto acima nunca ocorre nenhuma situação de carregamento dinâmico de classes.
    Essa situação poderia, por exemplo, acontecer se um método remoto recebesse ou retornasse um objecto por valor. Nesse caso seria necessário definir alguns aspectos de segurança da JVM (em particular, um Security Manager e uma Security Policy).
  4. O RMIRegistry é lançado internamente pelo servidor na mesma JVM.
    (LocateRegistry.createRegistry()). O RMI Registry é normalmente um serviço autónomo que corre numa máquina virtual Java (JVM) separada do processo que instancia e solitica o registo de um objecto remoto. Neste caso, é necessário que os ficheiros com as interfaces remotas dos objectos a registar no Registry estejam disponíveis num URL definido no parâmetro "codebase" da JVM do processo servidor. Um exemplo que usa RMI Registry como serviço autónomo pode ser consultado aqui:
    servidorZIP, clienteZIP.

© Docentes de Sistemas Distribuídos, Dep. Eng. Informática, Técnico Lisboa