Olá! Hoje veremos um tópico bastante interessante - RMI . Isso significa Invocação de Método Remoto . Com o RMI, você pode ensinar dois programas a se comunicarem, mesmo que estejam em computadores diferentes. Parece legal? :) Mas não é tão difícil de fazer! Na palestra de hoje entenderemos em que partes consiste a interação RMI e como configurá-la. A primeira coisa que precisamos é de um cliente e um servidor . Você não precisa se aprofundar muito na terminologia informática. No caso do RMI, são simplesmente dois programas. Um deles conterá algum objeto, e o segundo chamará métodos deste objeto. Chamar métodos de um objeto em um programa que está localizado em outro programa - nunca fizemos isso antes! É hora de experimentar! :) Para não se afogar na selva, deixe nosso programa ser simples. Em geral, os servidores costumam realizar algum tipo de cálculo que o cliente solicita. Será o mesmo para nós. Teremos um programa de calculadora simples como servidor. Ela terá apenas um método -
multiply()
. Ele multiplicará os dois números enviados pelo programa cliente e retornará o resultado. Primeiro de tudo precisamos de uma interface:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Calculator extends Remote {
int multiply(int x, int y) throws RemoteException;
}
Por que precisamos de uma interface? O fato é que o trabalho do RMI se baseia na criação de proxies, que você estudou em uma das palestras anteriores . E o trabalho com proxies, como você provavelmente se lembra, é realizado precisamente no nível das interfaces, não nas classes. Existem 2 requisitos importantes para nossa interface!
- Deve herdar a interface do token remoto.
- Todos os seus métodos devem lançar uma RemoteException (isso não é feito automaticamente no IDE, você tem que escrevê-lo manualmente!).
Calculator
. Tudo aqui também é bastante simples:
import java.rmi.RemoteException;
public class RemoteCalculationServer implements Calculator {
@Override
public int multiply(int x, int y) throws RemoteException {
return x*y;
}
}
Não há muito o que comentar aqui :) Agora precisamos escrever um programa de servidor que irá configurar e executar nossa classe de calculadora de servidor. Isso parecerá assim:
import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class ServerMain {
public static final String UNIQUE_BINDING_NAME = "server.calculator";
public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {
final RemoteCalculationServer server = new RemoteCalculationServer();
final Registry registry = LocateRegistry.createRegistry(2732);
Remote stub = UnicastRemoteObject.exportObject(server, 0);
registry.bind(UNIQUE_BINDING_NAME, stub);
Thread.sleep(Integer.MAX_VALUE);
}
}
Vamos descobrir :) Na primeira linha criamos algum tipo de variável string:
public static final String UNIQUE_BINDING_NAME = "server.calculator";
Esta string é o nome exclusivo do objeto remoto . Com este nome o programa cliente poderá encontrar nosso servidor: você verá isso mais tarde. A seguir criamos nosso objeto calculadora:
final RemoteCalculationServer server = new RemoteCalculationServer();
Tudo está claro aqui. O seguinte é mais interessante:
final Registry registry = LocateRegistry.createRegistry(2732);
Essa coisa chamada Registro é um registro de objetos excluídos . “Excluídos” não no sentido de que os apagamos do computador, mas no fato de que os objetos deste registro podem ser acessados remotamente a partir de outros programas :) LocateRegistry.createRegistry()
Passamos para o método o número 2732. Este é o número da porta. Se você não sabe o que é uma porta, pode lê-la aqui , mas por enquanto basta lembrar que este é um número exclusivo pelo qual outros programas podem encontrar nosso registro de objetos (você também verá isso abaixo). Vamos continuar. Vamos ver o que acontece na próxima linha:
Remote stub = UnicastRemoteObject.exportObject(server, 0);
Nesta linha criamos um stub . Um stub encapsula todo o processo de chamada remota dentro de si. Pode-se dizer que este é o elemento mais importante do RMI. O que ela esta fazendo?
- Recebe todas as informações sobre uma chamada remota para um método.
- Se o método tiver parâmetros, o stub os desserializa. Preste atenção neste ponto! Os parâmetros que você passa aos métodos para chamadas remotas devem ser serializáveis (afinal, eles serão transmitidos pela rede). Não temos esse problema - apenas transmitimos números. Mas se você transferir objetos, não se esqueça disso!
- Depois disso, ele chama o método desejado.
UnicastRemoteObject.exportObject()
nosso objeto calculadora do servidor para o método. Desta forma tornamos possível chamar seus métodos remotamente. Só nos resta uma coisa a fazer:
registry.bind(UNIQUE_BINDING_NAME, stub);
Nós “registramos” nosso stub no registro de objeto remoto com o nome que criamos no início. Agora o cliente pode encontrá-lo! Você deve ter notado que no final colocamos o thread principal do programa em suspensão:
Thread.sleep(Integer.MAX_VALUE);
Só precisamos manter o servidor funcionando por muito tempo. Executaremos dois métodos no IDEa ao mesmo tempo main()
: primeiro o do servidor (na classe que ServerMain
já escrevemos) e depois o do cliente (na classe ClientMain
que escreveremos abaixo). É importante que o programa do servidor não fique inativo enquanto iniciamos o cliente, então simplesmente o colocamos em hibernação por um longo tempo. Ainda funcionará :) Agora podemos executar main()
nosso método de servidor. Deixe-o rodar e espere o programa cliente chamar algum método :) Agora vamos escrever um programa cliente! Ele enviará números ao nosso servidor para multiplicar.
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class ClientMain {
public static final String UNIQUE_BINDING_NAME = "server.calculator";
public static void main(String[] args) throws RemoteException, NotBoundException {
final Registry registry = LocateRegistry.getRegistry(2732);
Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
}
}
Ela parece descomplicada. O que está acontecendo aqui? Primeiro, o cliente deve estar ciente do nome exclusivo do objeto cujos métodos irá chamar remotamente. Portanto, no programa cliente também criamos uma variável, public static final String UNIQUE_BINDING_NAME = "server.calculator";
a seguir no método main()
obtemos acesso ao registro de objetos remotos. Para isso, precisamos chamar o método LocateRegistry.getRegistry()
e passar lá o número da porta na qual nosso registro foi criado no programa ServerMain - porta 2732 (esse número foi escolhido como exemplo, você pode tentar usar outro):
final Registry registry = LocateRegistry.getRegistry(2732);
Agora tudo o que precisamos fazer é obter o objeto desejado do registro! É fácil porque sabemos seu nome único!
Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Preste atenção ao tipo de fundição. Convertemos o objeto resultante em uma interface Calculator
em vez de em uma classe concreta RemoteCalculationServer
. Como dissemos no início da palestra, o RMI é baseado no uso de um proxy, portanto a chamada remota está disponível apenas para métodos de interfaces, não para classes. No final, chamamos remotamente um método multiply()
em nosso objeto e imprimimos o resultado no console.
int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
Lançamos o método main()
na classe ServerMain
há muito tempo, é hora de lançar o método main()
no programa cliente ClientMain
! Saída do console: 600 É isso aí! Nosso programa (mesmo dois!) Cumpriu com sucesso sua função :) Se você tiver tempo e vontade, pode diversificar um pouco. Por exemplo, certifique-se de que a calculadora suporta todas as quatro operações padrão, e não números, mas um objeto são passados como parâmetros CalculationInstance(int x, int y)
. Como material adicional, você pode consultar artigos e exemplos - aqui:
Nos vemos nas próximas aulas! :)
GO TO FULL VERSION