Sistemas Distribuídos Marcelo Lobosco DCC/UFJF Comunicação em Sistemas Distribuídos Aula 06 Agenda Modelo Cliente-Servidor (cont.) Invocação Remota de Método (Remote Method Invocation – RMI) Visão Geral Criando aplicações distribuídas com RMI Escrevendo código do servidor Escrevendo código do cliente Compilando e executando o exemplo Visão Geral RMI provê suporte para comunicação remota entre programas escritos na linguagem de programação Java Java RMI permite que um objeto executando em uma Máquina Virtual Java (MVJ) invoque métodos de um objeto executando em uma segunda MVJ Aplicações RMI geralmente consistem de dois programas separados Cliente e servidor Visão Geral Típico programa servidor cria algum(ns) objeto(s) remoto(s), torna a(s) referência(s) a este(s) objeto(s) acessível(ies) e aguarda o(s) cliente(s) invocarem métodos neste(s) objeto(s) Típico programa cliente obtêm uma referência remota para um ou mais objetos remotos em um servidor e então invoca métodos neste(s) RMI provê o mecanismo pelo qual clientes e servidores se comunicam e passa informações de um lado para o outro Visão Geral Tais aplicações algumas vezes chamadas sistemas de objetos distribuídos (OD). Ações necessárias em um sistema de OD: Localizar os objetos remotos Comunicar com objetos remotos Ler definições de classes para objetos que são passados como parâmetro / retorno Visão Geral Localizar objetos remotos Vários mecanismos podem ser utilizados para obter referência de objetos remotos Aplicação pode registrar seus objetos remotos em um serviço especial de registro de nomes, RMI registry Referências remotas podem ser passadas / retornadas como parte de invocações remotas Comunicar com objetos remotos Detalhes da comunicação com objetos remotos são tratados por RMI Para programador, comunicação remota similar a invocação tradicional de métodos Visão Geral Ler definições de classes para objetos que são passados como parâmetro / retorno RMI permite que objetos sejam passados como parâmetro / retorno RMI provê mecanismos para ler definição da classe de um objeto Java é uma linguagem dinâmica RMI provê mecanismos para transmitir estado de um objeto Visão Geral Visão Geral Figura ilustra aplicação RMI distribuída que usa registro para obter referência para objeto remoto Servidor chama registro para associar (ou ligar – bind) um nome a um objeto remoto Cliente procura por objeto remoto pelo seu nome no registro do servidor e então invoca um método neste objeto Webserver pode ser usado por sistema RMI para ler definição de classes para objetos necessários Tanto do lado do cliente quanto do servidor Visão Geral Interfaces, objetos e métodos remotos Como qualquer aplicação Java, aplicação distribuída composta de classes e interfaces Interfaces declaram métodos Classes implementam métodos declarados na interface, podendo declarar métodos adicionais Em uma aplicação distribuída, implementações podem residir em máquinas virtuais distintas Objetos com métodos que podem ser invocados por diferentes MVJ são chamados de objetos remotos Visão Geral Objeto torna-se remoto quando implementa um interface remota, que possui as seguintes características: Uma interface remota estende a interface java.rmi.Remote Cada método da interface declara java.rmi.RemoteException em sua cláusula throws, em adição a qualquer exceção específica da aplicação Visão Geral RMI trata objetos remotos de forma distinta dos objetos locais quando o objeto é passado de uma MVJ para outra Ao invés de copiar na MVJ o objeto de implementação invocado, RMI passa um stub remoto para um objeto remoto Stub age como representante local (proxy) do objeto remoto Basicamente stub é, para o cliente, a referência remota Cliente invoca método no stub local, que é responsável por repassar a chamada para o objeto remoto Visão Geral Stub para um objeto remoto implementa o mesmo conjunto de interfaces remotas que o objeto remoto implementa Somente métodos definidos na interface remota podem ser chamadas pela MVJ onde clientes executam cliente Máquina do cliente stub servid or Máquina do servidor Criando Aplicações Distribuídas com RMI Passos gerais para desenvolver aplicação distribuída com RMI: Projetar e implementar os componentes de sua aplicação distribuída Compilar os fontes Tornar as classes acessíveis via rede Iniciar a aplicação Criando Aplicações Distribuídas com RMI Projetar e implementar os componentes de sua aplicação distribuída Inicialmente, determine a arquitetura de sua aplicação, definindo componentes locais e componentes acessíveis remotamente: Defina as interfaces remotas: especifica métodos que podem ser invocados remotamente por um cliente Defina tipos dos parâmetros e retorno dos métodos Implemente os objetos remotos: objetos remotos devem implementar uma ou mais interfaces Podem incluir implementações de interfaces e métodos disponíveis apenas localmente Criando Aplicações Distribuídas com RMI Projetar e implementar os componentes de sua aplicação distribuída (cont) Implemente o(s) clientes Compile os fontes qualquer programa java, use javac para compilar os programas fonte Como Declarações de interfaces remotas, suas implementações, outras classes de serviço, clientes Versões anteriores a JSE 5.0 necessitam do rmic Criando Aplicações Distribuídas com RMI Tornar as classes acessíveis via rede Neste passo, torne certas definições de classes acessíveis via rede Como definições para interfaces remotas e seus tipos associados, e definições de classes que precisam ser acessadas por clientes e servidores Geralmente tornam-se acessíveis através de um servidor web Iniciar a aplicação Inclui, além do cliente e do servidor, o serviço de registro de nomes, rmiregistry Criando Aplicações Distribuídas com RMI Passagem de parâmetros Qualquer tipo primitivo, objeto remoto ou objeto local, pode ser passado como parâmetro em chamadas RMI No caso de objeto local, além de acessível via rede, deve ser também serializável Para objeto ser considerado “serializável”, sua classe deve implementar a interface java.io.Serializable Exemplo de Aplicação: Calculadora Distribuída Servidor oferece serviços de uma calculadora Funcionalidade simples: soma, subtração, multiplicação e divisão com dois inteiros Cliente faz uso dos serviços Faz invocação, passando parâmetros e imprimindo resultado Objetivo: ilustrar o funcionamento de RMI Naturalmente aplicação real que overhead proibitivo para Serviço muito simples Calculadora Distribuída: Interface Primeiro passo: projetar interface remota Que serviços serão oferecidos? Quais os parâmetros necessários? Quais os tipos dos valores retornados? Quais exceções podem ocorrer? Calculadora Distribuída: Interface package exemplo; import java.rmi.Remote; import java.rmi.RemoteException; import exemplo.divisaoPorZero; public interface Calculadora extends Remote { float som (float a, float b) throws java.rmi.RemoteException; float sub (float a, float b) throws java.rmi.RemoteException; float mul (float a, float b) throws java.rmi.RemoteException; float div (float a, float b) throws DivisaoPorZero, java.rmi.RemoteException; } Calculadora Distribuída: Interface Ao estender a interface java.rmi.Remote, a interface Calculadora identifica-se como uma interface que pode receber requisições de outras MVJ Qualquer classe que implemente esta interface pode produzir objetos remotos Métodos da interface Calculadora podem ser executados remotamente Logo devem ser capazes de sinalizar exceção java.rmi.RemoteException Sinaliza ocorrência de falha de comunicação ou erro de protocolo Calculadora Distribuída: Implementação da Interface De modo geral, classe que implementa interface remota deve seguir, no mínimo, os seguintes passos: Declarar as interfaces remotas sendo implementadas Definir o construtor para cada objeto remoto Prover uma implementação para cada método remoto declarado nas interfaces remotas Calculadora Distribuída: Implementação da Interface Programa servidor RMI deve: Criar os objetos remotos e Exportá-los para o sistema runtime do RMI Sistema runtime torna-os aptos a receber invocações remotas Este processo de configuração pode ser: Encapsulado em um método da implementação da interface remota ou Incluído completamente em outra classe Roteiro do processo de configuração: Criar e instalar um gerente de segurança Criar e exportar um ou mais objetos remotos Registrar ao menos um objeto remoto no registro de nomes do RMI (ou outro serviço de nomes) Calculadora Distribuída: Implementação da Interface package exemplo; import import import import import import java.rmi.RemoteException; java.rmi.registry.LocateRegistry; java.rmi.registry.Registry; java.rmi.server.UnicastRemoteObject; exemplo.Calculadora; exemplo.divisaoPorZero; public class CalculadoraImpl implements Calculadora{ public CalculadoraImpl() { super(); } Calculadora Distribuída: Implementação da Interface public float som (float a, float b) { return a+b; } public float sub (float a, float b) { return a-b; } public float mul (float a, float b) { return a*b; } public float div (float a, float b) throws divisaoPorZero { if (b == 0) throw new divisaoPorZero(); return a/b; } Calculadora Distribuída: Implementação da Interface public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } try { String name = "Calculadora"; Calculadora calc = new CalculadoraImpl(); Calculadora stub = (Calculadora)UnicastRemoteObject.exportObject(calc,0); Registry registry = LocateRegistry.getRegistry(); registry.rebind(name, stub); System.out.println("Calculadora registrada!"); } catch (Exception e) { System.err.println("Ocorreu excecao: "); e.printStackTrace(); } } } Calculadora Distribuída: Implementação da Interface Declarando a interface remota sendo implementada: public class CalculadoraImpl implements Calculadora Podemos criar objetos remotos da classe CalculadoraImpl Alguns dos seus métodos, entretanto, só podem ser invocados localmente Construtor e método main Calculadora Distribuída: Implementação da Interface Definindo o construtor para o objeto remoto public CalculadoraImpl() { super(); } Em nosso caso, apenas invoca construtor da classe ancestral Apesar de não ser necessário, foi adicionado por questões de clareza de código Calculadora Distribuída: Implementação da Interface Provendo implementações para cada método remoto Classe provê implementações para todos os métodos declarados na interface som, sub, mul, div Passagem de parâmetros em Java segue as seguintes regras Objetos remotos passados por referência Objetos locais passados por cópia Todos os campos, exceto declarados estáticos ou transientes Desde que serializáveis e classes acessíveis (por exemplo, via servidor web) Calculadora Distribuída: Implementação da Interface Implementação do método main Primeira tarefa: instalar o gerente de segurança Protege o acesso aos recursos do sistema Determina, por exemplo, se código pode ter acesso ao sistema local de arquivos ou realizar qualquer outra ação privilegiada Receita de bolo: if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } Calculadora Distribuída: Implementação da Interface Implementação do método main Segunda tarefa: tornar os objetos remotos disponíveis para o cliente Instanciar objeto Exportar objeto, com UnicastRemoteObject.exportObject, de forma que ele possa receber chamadas remotas: Calculadora stub = (Calculadora)UnicastRemoteObject.exportObject(calc,0); Segundo parâmetro indica número da porta TCP a ser ouvida Zero indica porta anônima Porta real escolhida por SO ou RMI Calculadora Distribuída: Implementação da Interface Implementação do método main Método exportObject retorna stub para objeto remoto exportado Observe que tipo do stub deve ser da interface, e não da implementação Antes do cliente invocar um objeto em um objeto remoto, deve primeiro obter referência para objeto remoto Registro faz esse papel Serviço de nomes, que fornece uma referência para objeto remoto, dado um nome Calculadora Distribuída: Implementação da Interface Servidor registra nome no serviço de nomes String name = "Calculadora"; Registry registry = LocateRegistry.getRegistry(); registry.rebind(name, stub); Rebind faz chamada remota para registro localizado na máquina do servidor Como qualquer chamada remota, pode retornar exceção, que deve ser tratada Bloco try/catch faz tratamento Calculadora Distribuída: Implementando o Cliente package exemplo; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class CalculadoraClnt { public static void main(String args[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } Calculadora Distribuída: Implementando o Cliente try { String name = "Calculadora"; Registry registry = LocateRegistry.getRegistry(args[0]); Calculadora calc = (Calculadora) registry.lookup(name); System.out.println("2 + 2 = " + calc.som(2,2)); } catch (Exception e) { System.err.println("Calculadora exception:"); e.printStackTrace(); } } } Calculadora Distribuída: Implementando o Cliente Assim como servidor, começamos declarando gerente de segurança Necessário se cliente fizer download de código do servidor a partir do servidor web if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } Próximo passo: localizar referência para objeto remoto String name = "Calculadora"; Registry registry = LocateRegistry.getRegistry(args[0]); Calculadora calc = (Calculadora) registry.lookup(name); Calculadora Distribuída: Implementando o Cliente Passo final: usar as funcionalidades do objeto remoto, através de seus métodos, da mesma forma que usaríamos as funcionalidades de um objeto local System.out.println("2 + 2 = " + calc.som(2,2)); Calculadora Distribuída: Compilação e Execução Para compilar: Compilar com javac *.java Criar arquivo jar com classes relacionadas ao servidor: jar cvf server.jar Calculadora.class CalculadoraImpl.class divisaoPorZero.class Caso código do servidor seja acessado através de servidor web, disponibilizar arquivo jar em pasta acessível via rede Caso alguma classe do cliente seja acessada pelo servidor, deve-se criar jar e disponibilizálo através da rede Calculadora Distribuída: Compilação e Execução Antes de executar: Criar arquivo contendo políticas de segurança Neste exemplo, daremos todas as permissões de acesso: grant { permission java.security.AllPermission; }; Calculadora Distribuída: Compilação e Execução Para executar: Iniciar rmiregistry (rmiregistry&) Iniciar servidor: java -Djava.security.policy=f:\ calc\exemplo\secpolicy -Djava.rmi.server.codebase=file:\f:calc\exempl o\server.jar CalculadoraImpl Iniciar cliente: java -Djava.security.policy=f:\calc\exemplo\secpoli cy CalculadoraClnt localhost Resultado: 2 + 2 = 4.0 Próxima Aula... Remote Procedure Call (cont.) Questões de Implementação Problemas