JAVA RMI - Cliente A e B enviar info, servidor dar resposta

Primeiramente, olá e bom dia, haha.

Estou tentando desenvolver uma aplicação com RMI para fins acadêmicos, mas estou com dificuldade em como fazer uma parte da minha ideia.

O que eu queria era fazer um programa, como uma calculadora (por exemplo), onde um Cliente A entraria com um valor (X = 5, por ex) e Cliente B com outro (Y = 10, por ex). Nisso, o servidor replicaria uma mensagem para ambos com o resultado (X + Y = 15).

A minha dúvida é só realmente essa, como eu posso fazer com que Cliente A entre com X, Cliente B com Y e devolver ambos para o servidor para que ele use isso para resolver o problema. Tendo essa ideia, eu consigo ir além e realmente fazer o que eu quero.

Se alguém puder dar um help, agradeço!

R.

3 curtidas

Não é nenhum bicho de sete cabeças fazer o que você quer.
É só você implementar uma estrutura de listeners, só que com a interface Remote.

Interface do serviço que recebe o X e o Y e permite que seja registrado um listener:

package exemplo.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Servico extends Remote {

	void addListener(ServicoListener listener) throws RemoteException;

	void setX(double valor) throws RemoteException;

	void setY(double valor) throws RemoteException;
}

Interface do listener para o serviço acima:

package exemplo.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ServicoListener extends Remote {

	void calculoEfetuado(double resultado) throws RemoteException;
}

Implementação da interface do serviço:

package exemplo.rmi.server;

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

import exemplo.rmi.Servico;
import exemplo.rmi.ServicoListener;

class ImplementacaoServico implements Servico {

	private final List<ServicoListener> listeners = new ArrayList<>();

	private boolean setouX;
	private boolean setouY;

	private double valorX;
	private double valorY;

	@Override
	public void addListener(ServicoListener listener) throws RemoteException {
		listeners.add(listener);
	}

	@Override
	public void setX(double valor) throws RemoteException {
		valorX = valor;
		setouX = true;
		verifica();
	}

	@Override
	public void setY(double valor) throws RemoteException {
		valorY = valor;
		setouY = true;
		verifica();
	}

	private void verifica() {
		if (setouX && setouY) {
			double resultado = valorX + valorY;
			for (ServicoListener listener : listeners) {
				try {
					listener.calculoEfetuado(resultado);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
			setouX = false;
			setouY = false;
		}
	}
}

Implementação do servidor que disponibiliza o serviço acima:

package exemplo.rmi.server;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import exemplo.rmi.Servico;

public class Servidor {

	public static void main(String args[]) {
		try {
			String nomeServico = "MeuServico";
			int porta = 12345;

			Servico servico = new ImplementacaoServico();
			Servico servicoDistribuido = (Servico) UnicastRemoteObject.exportObject(servico, 0);

			Registry registry = LocateRegistry.createRegistry(porta);
			registry.bind(nomeServico, servicoDistribuido);
			System.out.printf("Servico disponivel: %s%n", nomeServico);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Implementação do cliente A que faz o setX:

package exemplo.rmi.client;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import exemplo.rmi.Servico;
import exemplo.rmi.ServicoListener;

public class ClienteA implements ServicoListener {

	public static void main(String[] args) {
		try {
			String nomeServico = "MeuServico";
			int porta = 12345;

			ServicoListener clienteA = new ClienteA();
			ServicoListener clienteAdistribuido = (ServicoListener) UnicastRemoteObject.exportObject(clienteA, 0);

			Registry registry = LocateRegistry.getRegistry(porta);
			Servico servicoRemoto = (Servico) registry.lookup(nomeServico);
			servicoRemoto.addListener(clienteAdistribuido);

			double valor = 20;
			System.out.println("Cliente A enviando: " + valor);
			servicoRemoto.setX(valor);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void calculoEfetuado(double resultado) throws RemoteException {
		System.out.println("Servidor devolveu para Cliente A o resultado: " + resultado);
	}
}

Implementação do cliente B, que faz o setY perceba que o código é análogo ao do cliente A, você pode criar uma abstração para isso:

package exemplo.rmi.client;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import exemplo.rmi.Servico;
import exemplo.rmi.ServicoListener;

public class ClienteB implements ServicoListener {

	public static void main(String[] args) {
		try {
			String nomeServico = "MeuServico";
			int porta = 12345;

			ServicoListener clienteB = new ClienteB();
			ServicoListener clienteBdistribuido = (ServicoListener) UnicastRemoteObject.exportObject(clienteB, 0);

			Registry registry = LocateRegistry.getRegistry(porta);
			Servico servicoRemoto = (Servico) registry.lookup(nomeServico);
			servicoRemoto.addListener(clienteBdistribuido);

			double valor = 10;
			System.out.println("Cliente B enviando: " + valor);
			servicoRemoto.setY(valor);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void calculoEfetuado(double resultado) throws RemoteException {
		System.out.println("Servidor devolveu para Cliente B o resultado: " + resultado);
	}
}
3 curtidas

Staroski, muito obrigado cara!

Era exatamente isso. Agora basta sentar um pouco e quebrar a cabeça pra entender o cód. e depois adaptar pro que necessito. A primeira coisa é procurar saber o que é um listener.

Apenas mais uma dúvida: se eu quisesse por exemplo ter apenas um arquivo Cliente e dentro dele ter ambos os códigos de ClienteA e ClienteB, eu teria que criar uma flag pra que, de alguma maneira, eu encaminhasse quem se conectasse primeiro para uma determinada situação, correto?

Ex: Caso cliente 1 se conecte primeiro, fica como “ClienteA”, caso for o segundo, fica como “ClienteB”
(Pra isso, ao conectar com o servidor, incremento 1 em uma variável flag que controla um if do tipo “if flag = 1”: “encaminha pro cod do ClienteTal…”) (Essa variável ficaria do lado do servidor, correto? Na implementação do serviço)

2 curtidas

Em princípio é isso.

2 curtidas

@staroski Se eu levantasse dois servidores, como poderia start sem iniciar os clientes antes.

Staroski, muito obrigado irmão , suas respostas estão me auxiliando muito.
Tenho mais uma duvida na implementação , pois gostaria de saber uma forma de criar uma redundância ativa entre os servidores.
O meu sistema tenho uma classe utilizada como servidor e o cliente escolhe qual servidor usar apontando para o IP .
Teria alguma forma de utilizar 2 servidores com ips diferentes e utilizar uma redundância ativa, se um sair do ar o outro assume automaticamente?
Desde já agradeço o auxilio.

1 curtida

Nunca fiz, mas eu acho que o mais simples seria, no cliente, encapsular o objeto remoto em uma classe com a mesma interface, de forma que se o objeto encapsulado não for mais acessível, tenta fazer o lookup em outro IP.

1 curtida

Viajando um pouco aqui (talvez exagerando no esforço), acho que além da opção dada pelo @staroski você poderia usar um “load balancer” pra essas chamadas, li alguma coisa sobre isso com EJB [0] há algum tempo, mas não sei se existe suporte a essa abordagem sem EJB… E peloq ue vi agora no link [1], uma discussão de forum, você pode rotear as chamadas com um load balancer de requisições HTTP…

[0] https://docs.oracle.com/cd/E26576_01/doc.312/e24934/rmi-iiop.htm#GSHAG00313
[1] https://devcentral.f5.com/questions/load-balancing-java-rmi-calls

@staroski bom dia sei que esta mensagem tem muito tempo mais gostaria de pedir sua ajuda para resolver um problema que estou tento neste seu exemplo!

Quando a parte de “ImplementacaoServico” está com um falha na instrução do for

Explict type can be replaced witch ´var´
Convert to for (iterator…) {}

O resta está funcionando beleza.

Agradeço desde já!

É porque você está usando Java 12.
O exemplo foi feito há 3 anos utilizando Java 7 ou 8.
É só você adequar seu laço. :wink:

Valeu!