Documentação
-
Capítulo 5.5 Java RMI do livro principal da cadeira
(Coulouris, "Distributed Systems: Concepts and Design")
Exercício a resolver até ao fim da aula
No laboratório anterior transformamos uma implementação do Jogo do Galo (Tic Tac Toe) numa aplicação distribuída utilizando o gRPC.
Analogamente, neste exercício iremos utilizar o Java RMI para estudar outra forma de transformar uma implementação do Jogo do Galo numa aplicação distribuída.
Partindo de uma versão Jogo do Galo feita para um cenário local,
semelhante à versão utilizada no laboratório anterior,
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.

Sugestão: nas alíneas seguintes, corra os módulos cliente e servidor
numa máquina não partilhada com outros grupos para evitar conflitos de portos.
Utilize uma máquina pessoal ou do laboratório e não o sigma.
-
Descarregue e descomprima o código fonte da aplicação
Jogo do Galo/Tic Tac Toe
adaptada a este exercício.
-
Veja como o programa está estruturado em três módulos: server, client e interface.
Cada módulo tem um POM próprio.
-
Importe os projectos no Eclipse, recorrendo a File > Import > Maven > Existing Maven Projects
-
No ponto de partida, o código está todo centralizado no módulo server, estando o client vazio.
O módulo interface será usado num próximo passo.
- Estude os principais ficheiros com a implementação do jogo
(Game.java e TTT.java do módulo server).
-
Esta implementação é muito semelhante à utilizada como base no laboratório anterior.
No entanto, alguns métodos já são sincronizados.
-
Porque é que esta sincronização é necessária?
-
Compile e experimente o jogo na sua versão centralizada
(executando mvn compile exec:java).
-
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 interage com os jogadores invocando os métodos do servidor via
Java RMI (Remote Method Invocation).
Naturalmente, o cliente poderá correr numa máquina diferente da máquina que serve o jogo.
-
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,
ou seja, play, checkWinner e currentBoard.
Para ser uma interface remota, precisa 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 referência.
-
Coloque a nova interface no módulo interface (ficheiro TTTService.java).
-
Depois de codificar a interface,
compile o código e instale o módulo no seu repositório local:
cd interface
mvn install
-
Adicione a dependência no pom.xml do servidor e do cliente,
pois esta interface terá de ser partilhada por ambos:
...
<dependency>
<groupId>example</groupId>
<artifactId>ttt-rmi-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
...
-
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
-
No servidor, transforme a classe TTT para que passe a
implementar a interface remota TTTService criada anteriormente.
-
Para que as instâncias desta classe possam ser objetos remotos,
modifique a definição da classe TTT de forma a herdar de
java.rmi.server.UnicastRemoteObject
e acrescente um construtor que lance excepção RemoteException.
-
Esta classe deverá implementar a lógica dos métodos que o cliente irá chamar posteriormente, tal como definidos na interface TTTService.
Consulte o exemplo da classe
shapeListServant apresentado no livro para referência.
-
No servidor, crie uma nova classe TTTServer que implementa a função main e
onde correrá o servidor.
Na função main deverá:
-
Instanciar um objeto remoto do tipo TTT.
-
Lançar um rmiregistry (serviço de nomes do RMI) e
registar o objeto remoto nesse rmiregistry utilizando a classe java.rmi.registry.LocateRegistry.
-
Atualize a mainclass definida no pom.xml para refletir a classe que possui o método main.
Consulte o exemplo da classe
ShapeListServer apresentado no livro para referência.
-
Deverá agora proceder à modificação do cliente de forma a chamar remotamente os métodos definidos anteriormente na interface TTTService.
-
Mova 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 referência.
-
Não se esqueça de, na chamada ao método Naming.lookup,
definir correctamente o URL que localiza o objeto, 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 objeto remoto e name é o nome que foi
atribuído ao objeto pelo servidor quando chamou rebind.
-
Adicione o tratamento adequado às exceções lançadas quando acontece
algo inesperado numa invocação remota.
As exceções de comunicação e tratamento de dados que podem ocorrer são todas subclasses de java.rmi.RemoteException. Exemplos:
- java.rmi.UnknownHostException
- java.rmi.ConnectException
- java.rmi.UnmarshalException
- java.rmi.MarshalException
-
Experimente lançar o servidor e depois um cliente para jogar.
-
Responda às seguintes questões:
-
Quando se usa gRPC é gerado código para converter os dados de e
para um formato de rede.
O que acontece quando se usa RMI?
-
Das classes e interfaces Java que utilizou,
quais são 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:
- Só são aceites trabalhos de estudantes que estiveram
presentes no laboratório.
- Deverá também incluir um ficheiro instrucoes.txt
com resumo da funcionalidade implementada e com instruções para
colocar o programa a funcionar como esperado.
Por exemplo:
- A funcionalidade pedida foi total/parcialmente implementada
...
- O servidor deve executar com o seguinte comando:
mvn install exec:java
- O cliente deve executar com o comando:
mvn compile exec:java
- Assegure-se que a solução é enviada em formato ZIP e que não
contém código compilado (faça mvn clean antes de
comprimir).