Busca

JSantanna

Meu blog sobre engenharia, tecnologia, ciência da Computação, etc.

Categoria

sistemas distribuídos

Projeto de sistemas distribuídos usando CORBA, parte IV: Final tudo funcionando

Antes de testar a aplicação temos que colocar o servidor de nomes no ar , para isso você abre um terminal de comando e digita “tnameserv -ORBInitialPort 1050 “, veja a porta 1050 foi escolhida por mim por que a porta default do Corba já estava sendo utilizada na minha maquina .

Vai aparecer uma tela mais ou menos assim para você no terminal

Isso indica que o servidor de nomes está ativo , somente ai você vai poder testar o programa.

Outra coisa importante , temos que ter 3 copias dos códigos para que possa funcionar , note que apesar das 3 copias estarem na mesma maquina elas representam 3 programas diferentes que poderiam estar rodando em maquinas completamente diferentes , a estrutura de diretório no meu netbeans ficou assim:

São 3 projetos cada um tem um numero identificando que estação representa , conforme expliquei para vocês não vou explicar como fazer a interface gráfica da aplicação , mas digo que foi feita inteiramente em swing usando o matisse no netbeans , veja o video abaixo de como colocar os 3 projetos para funcionar e testar sua aplicação.



Untitled from João Santanna on Vimeo.

 Comente o post e ajude o blog a melhorar…

Anúncios

Projeto de sistemas distribuídos usando CORBA, parte III : Classe relógio , Classe Servidor e Classe Servant

Vou iniciar o post pedindo desculpas por ter demorado a concluir essa serie de post sobre sistemas distribuídos com CORBA , as disciplinas no doutorado apertaram aqui no DAS e tive que me afastar do blog por um tempo 🙂 .

Para quem chegou agora , esse post é precedido de 2 posts anteriores de um trabalho da disciplina de sistemas distribuídos , projeto esse feito no 2º trimestre de 2011, confira abaixo os 2 posts anteriores:

Para finalizar essa serie de artigos vamos comentar o funcionamento das seguintes classes:

– Classe relógio , que controla a evolução do relógio lógico de lamport .
-Classe servidor de mensagem , que conforme o próprio nome diz é responsável por implementar o servidor de mensagens
– Classe mensagemServant que é o código que utiliza o corba para responder aos requisitos advindos do servidor.

1- Classe Relógio

A classe relógio utiliza um mecanismo chamado se semáforo para controlar o acesso simultâneo aos seus métodos , isso por que o relógio pode ser acessado a partir de diversas classes e para evitar inconsistências somente um classe pode manipular os valores do relógio uma de cada vez, a classe em sí é uma classe simples com 3 métodos : um set do relógio para setar valores do relógio , um get para consultar os valores e um método tic para fazer o relógio evoluir a medida que ocorrerem os eventos .
Veja abaixo o código da classe Relógio.java

package psd8; import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; public class Relogio { private static Semaphore sem = new Semaphore(1); private static int timestamp = 0; public static void initRelogio(){ timestamp = 0; } public synchronized int getTimestamp() { return Relogio.timestamp; } //setTimestamp vai ser usado quando chegar uma mensagem logo ele pode //setar um valor maior que 1 public void setTimestamp(int x) { try { sem.acquire(); } catch (InterruptedException ex) { Logger.getLogger(Relogio.class.getName()).log(Level.SEVERE, null, ex); } Relogio.timestamp = x; sem.release(); } //esse aqui incrementa de 1 em 1 public synchronized int ticRelogio() { try { sem.acquire(); } catch (InterruptedException ex) { Logger.getLogger(Relogio.class.getName()).log(Level.SEVERE, null, ex); } Relogio.timestamp ++; sem.release(); return Relogio.timestamp; } }

O relógio é sempre inicializado com timestamp 0 , já na inicialização temos a declaração do semáforo que aceita como argumento o numero 1 , isso significa que ele aceita 1 processo por vez na zona critica , parte do código que se acessada simultaneamente pode trazer incongruências .
O método getTimestamp retorna o valor atual do relógio logico , a palavra syncronized torna esse método atômico , ou seja ele tem que rodar até o final antes que sua thread possa ser interrompida .

Os métodos setTimestamp e  ticRelogio são auto explicativos e a unica coisa diferente é o uso do semáforo . Para acionar o semáforo usamos o sem.acquire( ) , isso cria uma trava no código e somente quando o processo liberar essa trava outro processo concorrente vai poder entrar na zona critica , para um processo liberar o semáforo precisa usar o sem.release( ) ,  sendo sem uma referencia para um objeto do tipo semáforo , vale ainda notar que o uso dos semáforos deve ocorre dentro de uma clausula try/catch .

2- Classe Servidor de mensagem

O servidor de mensagem roda em modo mulithread por isso quase tudo ocorre no método run , nesse método ele instancia o servidor no nosso exemplo estamos fazendo o software da estação 1 ( são 3 estações ao todo ) logo ele cria um servidor com o nome de Mensagemserv1, uma mesma maquina pode ter mais de um servidor rodando ao mesmo tempo. Finalmente a classe tem um método unbindnoServidor que será utilizado quando se quiser retirar as referencias do servidor no servidor de nomes que é utilizado para localizar os servidores do sistema distribuído , isso é necessário por que quando um servidor sair do ar sua referencia também deve ser apagada do servidor de nomes para que ninguém tente acessa-lo gerando um erro.

Veja abaixo o código da classe ServidorMensagem.java

import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.omg.CORBA.*; import org.omg.CORBA.ORBPackage.InvalidName; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.CannotProceed; import org.omg.CosNaming.NamingContextPackage.NotFound; import org.omg.PortableServer.*; import org.omg.PortableServer.POA; /** * * @author Joao */ public class ServidorMensagem implements Runnable { private String [] args; private ORB orb; MensagemServant ms = new MensagemServant(); public ServidorMensagem(String[] args) { this.args = args; } @Override public void run() { try { Properties props = new Properties(); props.put(“org.omg.CORBA.ORBInitialPort”, “1050”); props.put(“org.omg.CORBA.ORBInitialHost”, “127.0.0.1”); //inicia o ORB orb = ORB.init(args,props); // Busca e inicia o rootpoa POA rootPoa = POAHelper.narrow(orb.resolve_initial_references(“RootPOA”)); rootPoa.the_POAManager().activate(); //cria objeto remoto MensagemServant msgservant = new MensagemServant(); //Cria um objeto distribuido CORBA org.omg.CORBA.Object mservcorba = rootPoa.servant_to_reference(msgservant); Mensagem eRef = MensagemHelper.narrow(mservcorba); //referencia ao servidor de nomes org.omg.CORBA.Object objRef = orb.resolve_initial_references(“NameService”); NamingContext nc = NamingContextHelper.narrow(objRef); //registar no servico de nomes NameComponent [] path ={ new NameComponent(“Mensagemserv1”, “Object”)}; nc.rebind(path, eRef); System.out.println(“servidor 1 inicializado”); } catch (Exception e) { } } public void unbindNoServidor() { org.omg.CORBA.Object objRef; try { objRef = orb.resolve_initial_references(“NameService”); NamingContext nc = NamingContextHelper.narrow(objRef); NameComponent [] path ={ new NameComponent(“Mensagemserv1”, “Object”)}; try { nc.unbind(path); } catch (NotFound ex) { Logger.getLogger(ServidorMensagem.class.getName()).log(Level.SEVERE, null, ex); } catch (CannotProceed ex) { Logger.getLogger(ServidorMensagem.class.getName()).log(Level.SEVERE, null, ex); } catch (org.omg.CosNaming.NamingContextPackage.InvalidName ex) { Logger.getLogger(ServidorMensagem.class.getName()).log(Level.SEVERE, null, ex); } } catch (InvalidName ex) { Logger.getLogger(ServidorMensagem.class.getName()).log(Level.SEVERE, null, ex); } System.out.println(“Termino de comunicacao entre emissores – unbind completo”); } } A maior parte do código está comentada o que explica muitos dos comandos utilizados, vou comentar somente algumas partes mais importantes , na linhas 31 , 32 e 33  se prepara uma string especial para setar  algumas configurações do servidor como porta e endereço IP onde o servidor vai rodar . Na linha 57 se registra o servidor de mensagem e o tipo de dado que ele espera receber  Mensagemserv1 como nome do servidor e object como tipo de dado que ele recebe. Na Linha 58 se usa o comando nc.Rebind para se registar os dados no servidor de nomes .

Ao final da classe e fora do método run temos o método unbindNoServidor para apagar o registro do servidor do servidor de nomes .

3-  Classe MensagenServant

Essa classe é o coração do nosso sistema distribuído , sempre que chegar uma requisição para o servidor o corba vai acionar um o servant correspondente para atender ao pedido , é aqui que grande parte da lógica da aplicação deve ser colocada , essa classe é a maior do programa portanto vamos tentar explica-la por partes .

public class MensagemServant extends MensagemPOA { //Evento ev ; private static boolean comunicacaoPendente = true; private static boolean em1Terminou = false; private static boolean em2Terminou = false; private static boolean em3Terminou = false; private static ArrayList em1buffer = new ArrayList(); private static ArrayList em2buffer = new ArrayList(); private static ArrayList em3buffer = new ArrayList(); private static ArrayList bufferDelivery = new ArrayList(); private Relogio relogio = new Relogio(); private static Semaphore sem1 = new Semaphore(1); private static Semaphore sem2 = new Semaphore(1); private static Semaphore sem3 = new Semaphore(1); private static Semaphore semBD = new Semaphore(1); private static boolean para = false;
Nessa primeira parte temos as estruturas de dados para armazenar as mensagens que chegam são 3 array lists que servem para guardar as mensagens dos 3 emissores além de um array adicional que  é o buffer de delivery , esse será o buffer que deve entregar as mensagens para nossa aplicação na camada de aplicação , é nesse buffer que teremos que ordenar as mensagens por ordem total seguindo a logica do relógio de Lamport. Além disso temos 4 valores booleanos que indicam quando terminam as mensagens que chegam de cada emissor além de uma variável comunicacaoPendente que indica quando todas as mensagens de todos os servidores já tiverem chegado . Os semáforos são para controlar o acesso simultâneo aos buffers  e a variável “para”indica quando tudo estiver terminado inclusive a montagem do buffer de delivery já tiver sido entregue para  a camada de aplicação.

public void sendMensagem(Evento ev) { if (ev.origem == 1) { //se a origem da mensagem for a propia estacao //nao atualiza o relogio,só armazena this.armazenarMensagens(ev); } else { //se for uma mensagem que sinalize final de //transmissão só armazena nao atualiza relogio if (ev.tipo.compareToIgnoreCase(“x”) == 0) { this.armazenarMensagens(ev); } else { //se a mensagem for de outra estaçao atualizar o relogio //evento de recepcao , e depois armazenar no buffer correto //compara o relogio local com o timestap da mensagem que chegou //pega o maior valor e soma + 1 int ajusteRelogio = Math.max(relogio.getTimestamp(), ev.timestamp) + 1; relogio.setTimestamp(ajusteRelogio);//ajusta o relogio local this.armazenarMensagens(ev); } } }
Nessa parte do código temos o método sendMensagem , na verdade o esqueleto desse servant foi construído depois que usamos o compilador corba juntamente com o o arquivo de definição  IDL (Interface Definition Language) lá estava definido que teríamos um método sendMensagem que enviaria mensagem para o servidor , infelizmente o nome não foi bem escolhido mas entenda que toda vez que fazemos um sendMensagem do lado do cliente o servidor recebe e trata essa mensagem usando esse método do servant .
O funcionamento do método é o seguinte , se a mensagem que está chegando for da minha própria estação ( nesse caso estação 1 ) eu simplesmente armazeno a mensagem no buffer da estação 1 já que do ponto de vista da estação 1 eu já atualizei o relógio no cliente localmente , caso contrário se verifica se a mensagem é do tipo x o que finaliza as mensagens enviadas de um determinado servidor e não atualiza o relógio , nesse caso só se armazena a respectiva mensagem no seu buffer , finalmente se a mensagem não for do tipo x e se a origem da mensagem não for eu mesmo o servidor faz o ajuste no relógio usando o algoritmo de Lamport e em seguida armazena a mensagem no buffer correto.

public void armazenarMensagens(Evento ev) { //if para eventos do emissor 1 if (ev.origem == 1) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 1 terminou”); try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em1buffer.add(ev); sem1.release(); em1Terminou = true; } else { try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em1buffer.add(ev); sem1.release(); } } //if para eventos do emissor 2 if (ev.origem == 2) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 2 terminou”); try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em2buffer.add(ev); sem2.release(); em2Terminou = true; } else { try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em2buffer.add(ev); sem2.release(); } } //if para eventos do emissor 3 if (ev.origem == 3) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 3 terminou”); try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em3buffer.add(ev); sem3.release(); em3Terminou = true; } else { try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em3buffer.add(ev); sem3.release(); } } //ativado quando as comunicações terminarem if (em1Terminou && em2Terminou && em3Terminou) { comunicacaoPendente = false; } }
Agora vamos examinar o método de armazenagem nos buffers , o funcionamento é simples e o comportamento está repetido para tratar mensagens dos 3 emissores , por isso vamos explicar o funcionamento de um emissor apenas que vai servir para os outros dois.
O primeiro if testa a origem da mensagem para que possamos separa cada mensagem em seu próprio buffer, depois de testar a origem verificamos se o tipo da mensagem é x o que indicaria mensagem de finalização de envio de um determinado emissor, se um x acabou de chegar o flag indicador de fim de transmissão de um determinada estação é modificado no nosso caso em1Terminou = true , em seguida  utilizamos o semáforo para poder acessar os buffers , isso é necessário porque além do servidor existe a camada de aplicação acessando os buffers a medida que as mensagens estão chegando o uso do semaforo evita erros no acesso deixando um processo por vez acessar os buffers , conforme visto anteriormente o uso de semáforos tem que ser dentro de clausulas try/catch  o único comando dentro da seção critica é o comando para armazenar a mensagem no buffer correspondente .
O comportamento descrito na parágrafo anterior se repete no método , uma copia para cada estação num total de 3 , ao final do método fazemos um teste para saber se as flags das 3 estações sinalizam o termino de transmissão e caso isso ocorra modificamos a flag de sinalização pendente para false indicando que não falta chegar nenhuma mensagem.

public void iniciarMontagem(){ Thread tm = new Thread(new MontarBufferFinal() ); tm.setName(“Thread do montador de buffer”); tm.start(); } public class MontarBufferFinal implements Runnable{ public void run() { Evento e1, e2, e3; int x=0; while(true) { if(x==0){ System.out.println(“aguardando para montar buffer de Delivery”); x++; } if ((!em1buffer.isEmpty() ) && (!em2buffer.isEmpty() ) && (!em3buffer.isEmpty())){ try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e1 = em1buffer.get(0); sem1.release(); try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e2 = em2buffer.get(0); sem2.release(); try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e3 = em3buffer.get(0); sem3.release(); this.montarBufferDelivery(e1, e2, e3); //testa se so sobrou os x if (e1.tipo.compareToIgnoreCase(“x”) == 0 && e2.tipo.compareToIgnoreCase(“x”) == 0 && e3.tipo.compareToIgnoreCase(“x”) == 0) { break; } }//fim do if }//fim do while }//fim do RUN

Nessa parte do código temos o método iniciarMontagem() que serve para instanciar a classe interna MontarBufferFinal que  é uma thread a mais que serve para montagem do buffer de delivery, esse método só serve para iniciar essa thread e mais nada …
A Thread responsavel pela montagem do buffer de delivery funciona no método run da classe MontarBufferFinal , ela tem um laço while infinito, em cada passagem dentro do while é verificado se os buffers não estão vazios ou seja se eles tem mensagens a processar.
Se os bufferes tiverem mensagens usamos semáforos para acessar o primeiro elemento do buffer , veja que como isso é uma thread na verdade recebemos as mensagens , armazenamos no buffer , tratamos os buffers e montamos o buffer de delivery tudo concorrentemente por isso precisamos usar os semáforos para não gerarmos nenhuma incongruência no resultado final.
Depois de pegar um elemento de cada buffer chamamos o método montarBufferDelivery passando as 3 mensagens que acabamos de retirar do inicio de cada buffer como argumento, vamos mostrar e explicar o método montarBufferDelivery a seguir , note que montarBufferDelivery é um método e MontarBufferFinal é uma classe interna que é instanciada como uma thread concorrente .
Ao final fazemos um teste if para ver se as mensagens que acabamos de pegar nos buffers são do tipo x, aqui vale adiantar uma coisa , quando passamos as 3 mensagens o método de montagem do bufferDelivery vai comparar os timestamps das mensagens e ordena-las retirando-as do buffer , uma de cada vez , a medida que o tempo passa os buffers vão esvaziar e só vão sobrar as mensagens de tipo X para sinalizar que terminamos de processar tudo , logo testamos ao final de cada ciclo se só sobraram no buffer mensagens do tipo x , se isso ocorrer damos um break no laço infinito while para que a thread chegue ao final e o processo de montagem termine.

private void montarBufferDelivery(Evento e1, Evento e2, Evento e3) { int minimoTimestamp; //pega o evento com menor timestap e maior prioridade que //se encontra na cabeça da fila. A prioridade é ordenada // da origem de valor mais baixo para o mais alto //dessa forma mensagens vindo da estação 1 tem prioridade // maior que as da estaçao 2 e 3 minimoTimestamp = Math.min(e1.timestamp, Math.min(e2.timestamp, e3.timestamp)); //o aninhamento de if’s garente que eventos com mesmo timestamp // utilizam o criterio de menor ID=Origem para serem utilizados na //montagem do buffer if (e1.timestamp == minimoTimestamp ) { try { sem1.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em1buffer.remove(0)); semBD.release(); sem1.release(); } else if (e2.timestamp == minimoTimestamp ) { try { sem2.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em2buffer.remove(0)); semBD.release(); sem2.release(); } else if (e3.timestamp == minimoTimestamp) { try { sem3.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em3buffer.remove(0)); semBD.release(); sem3.release(); } }

Finalmente chegamos ao método de montagem do buffer de Delivery , nesse método temos primeiramente  o comando :


minimoTimestamp = Math.min(e1.timestamp, Math.min(e2.timestamp, e3.timestamp));

Nesse comando pegamos o menor timestamp entre as 3 mensagens que pegamos dos buffers , lembrando de temos que ordenar as mensagens dentro de buffer de delivery usando timestamps crescentes ( do menor para o maior) em caso de timestamps de mesmo valor vamos usar a ordem no menor numero de estação ou seja de a estação 1 e 3 tiverem uma mensagem com o mesmo timestamp a mensagem da estação 1 vai ser entregue primeiro , eventos com mesmo timestamp vindo de estações diferentes indicam que foram eventos que ocorreram simultaneamente  🙂 .
O conjunto de if’s aninhados garante que caso tivermos eventos de origens diferentes com o mesmo timestamp  vamos seguir a regra adotada ( estação com menor numero tem prioridade) , o teste é o seguinte : if (e1.timestamp == minimoTimestamp ) , a logica aqui é que se o evento que vc esta testando tem timestamp igual ao minimo calculado então ele é o evento que deve ser inserido no buffer de delivery .

Usamos o seguinte comando para armazenar essa mensagem no buffer de delivery :
bufferDelivery.add(em1buffer.remove(0)); 
Note que inserimos a mensagem no buffer de delivery e ao mesmo tempo removemos a mensagem do buffer original , no exemplo a mensagem estaria no buffer do emissor 1  o numero 0 significa que estamos removendo o primeiro elemento do buffer ( da posição 0) . Note no código que usamos 2 semáforos para cada if isso por que além de acessarmos o buffer dos emissores também acessamos o buffer de delivery concorrentemente nesse mesmo comando por isso precisamos de 2 semáforos .

A classe servant ainda conta com métodos para se acessar os buffers e variáveis de controle, temos um método utilitário usado para limpar os buffers ao final do uso do programa ( caso queiramos rodar o aplicativo novamente) .


O código completo da classe servant você consulta a seguir :

import java.util.logging.Level; import java.util.logging.Logger; import org.omg.CORBA.*; import java.util.ArrayList; import java.util.concurrent.Semaphore; /** * * @author Joao */ public class MensagemServant extends MensagemPOA { //Evento ev ; private static boolean comunicacaoPendente = true; private static boolean em1Terminou = false; private static boolean em2Terminou = false; private static boolean em3Terminou = false; private static ArrayList em1buffer = new ArrayList(); private static ArrayList em2buffer = new ArrayList(); private static ArrayList em3buffer = new ArrayList(); private static ArrayList bufferDelivery = new ArrayList(); private Relogio relogio = new Relogio(); private static Semaphore sem1 = new Semaphore(1); private static Semaphore sem2 = new Semaphore(1); private static Semaphore sem3 = new Semaphore(1); private static Semaphore semBD = new Semaphore(1); private static boolean para = false; @Override public void sendMensagem(Evento ev) { if (ev.origem == 1) { //se a origem da mensagem for a propia estacao //nao atualiza o relogio,só armazena this.armazenarMensagens(ev); } else { //se for uma mensagem que sinalize final de //transmissão só armazena nao atualiza relogio if (ev.tipo.compareToIgnoreCase(“x”) == 0) { this.armazenarMensagens(ev); } else { //se a mensagem for de outra estaçao atualizar o relogio //evento de recepcao , e depois armazenar no buffer correto //compara o relogio local com o timestap da mensagem que chegou //pega o maior valor e soma + 1 int ajusteRelogio = Math.max(relogio.getTimestamp(), ev.timestamp) + 1; relogio.setTimestamp(ajusteRelogio);//ajusta o relogio local this.armazenarMensagens(ev); } } } public void armazenarMensagens(Evento ev) { //if para eventos do emissor 1 if (ev.origem == 1) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 1 terminou”); try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em1buffer.add(ev); sem1.release(); em1Terminou = true; } else { try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em1buffer.add(ev); sem1.release(); } } //if para eventos do emissor 2 if (ev.origem == 2) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 2 terminou”); try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em2buffer.add(ev); sem2.release(); em2Terminou = true; } else { try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em2buffer.add(ev); sem2.release(); } } //if para eventos do emissor 3 if (ev.origem == 3) { if (ev.tipo.compareToIgnoreCase(“x”) == 0) { System.out.println(“Emissor 3 terminou”); try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em3buffer.add(ev); sem3.release(); em3Terminou = true; } else { try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } em3buffer.add(ev); sem3.release(); } } //ativado quando as comunicações terminarem if (em1Terminou && em2Terminou && em3Terminou) { comunicacaoPendente = false; } } public static ArrayList getBuffer1() { return em1buffer; } public static ArrayList getBuffer2() { return em2buffer; } public static ArrayList getBuffer3() { return em3buffer; } public static boolean statusComunicacao() { //comunicacao pendente vai retornar true, ate que todos //os emissores terminem de transmitir return comunicacaoPendente; } public static boolean isPara() { return para; } public static void limparBuffers() { em1buffer.removeAll(em1buffer); em2buffer.removeAll(em2buffer); em3buffer.removeAll(em3buffer); bufferDelivery.removeAll(bufferDelivery); em1Terminou = false; em2Terminou = false; em3Terminou = false; para = false; } public static Semaphore getSem1() { return sem1; } public static Semaphore getSem2() { return sem2; } public static Semaphore getSem3() { return sem3; } public static Semaphore getSemBD() { return semBD; } public Evento getValorNoBufferDelivery(){ Evento e = bufferDelivery.remove(0); return e; } public int getTamanhoBufferDelivery(){ return bufferDelivery.size(); } public void iniciarMontagem(){ Thread tm = new Thread(new MontarBufferFinal() ); tm.setName(“Thread do montador de buffer”); tm.start(); } public class MontarBufferFinal implements Runnable{ public void run() { Evento e1, e2, e3; int x=0; while(true) { if(x==0){ System.out.println(“aguardando para montar buffer de Delivery”); x++; } if ((!em1buffer.isEmpty() ) && (!em2buffer.isEmpty() ) && (!em3buffer.isEmpty())){ try { sem1.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e1 = em1buffer.get(0); sem1.release(); try { sem2.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e2 = em2buffer.get(0); sem2.release(); try { sem3.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } e3 = em3buffer.get(0); sem3.release(); this.montarBufferDelivery(e1, e2, e3); //testa se so sobrou os x if (e1.tipo.compareToIgnoreCase(“x”) == 0 && e2.tipo.compareToIgnoreCase(“x”) == 0 && e3.tipo.compareToIgnoreCase(“x”) == 0) { // this.imprimirBufferFinal(); //so para testes break; } //tem que corrigir uma caracteristica que ele armazena sempre o ultimo x enviado }//fim do if }//fim do while }//fim do RUN private void montarBufferDelivery(Evento e1, Evento e2, Evento e3) { int minimoTimestamp; //pega o evento com menor timestap e maior prioridade que //se encontra na cabeça da fila. A prioridade é ordenada // da origem de valor mais baixo para o mais alto //dessa forma mensagens vindo da estação 1 tem prioridade // maior que as da estaçao 2 e 3 minimoTimestamp = Math.min(e1.timestamp, Math.min(e2.timestamp, e3.timestamp)); //o aninhamento de if’s garente que eventos com mesmo timestamp // utilizam o criterio de menor ID=Origem para serem utilizados na //montagem do buffer if (e1.timestamp == minimoTimestamp ) { try { sem1.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em1buffer.remove(0)); semBD.release(); sem1.release(); } else if (e2.timestamp == minimoTimestamp ) { try { sem2.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em2buffer.remove(0)); semBD.release(); sem2.release(); } else if (e3.timestamp == minimoTimestamp) { try { sem3.acquire(); semBD.acquire(); } catch (InterruptedException ex) { Logger.getLogger(MensagemServant.class.getName()).log(Level.SEVERE, null, ex); } bufferDelivery.add(em3buffer.remove(0)); semBD.release(); sem3.release(); } } }//fim da classe interna }
Chegamos ao final desse post , no próximo vamos ver como colocamos o programa de exemplo para funcionar a partir do netbeans ,  algumas classes vão ficar sem explicação não vou detalhar a camada de aplicação uma vez que a ideia aqui é usar o corba , fiz a camada de aplicação usando o matisse X swing do netbeans , vou disponibilizar o codigo em algum repositorio caso alguem queira baixar e estudar o código todo.

Não esqueça de comentar o post.

Web services

Nossa  apresentação sobre Web Services.

Para quem programa em java é um tópico bem interessante , outra tecnologia que torna os serviços de software muito mais dinâmicos.

 Esse seminario foi apresentado na disciplina de sistemas distribuídos no DAS – UFSC 

Enjoy e não esqueça de comentar …
Web Serviceshttp://www.scribd.com/embeds/71357083/content?start_page=1&view_mode=list&access_key=key-7jse60g8cidns04l0x4(function() { var scribd = document.createElement(“script”); scribd.type = “text/javascript”; scribd.async = true; scribd.src = “http://www.scribd.com/javascripts/embed_code/inject.js”; var s = document.getElementsByTagName(“script”)[0]; s.parentNode.insertBefore(scribd, s); })();

Projeto de sistemas distribuídos usando CORBA, parte II : Primeiros passos

1. Um pouco mais sobre o CORBA

O padrão CORBA é um framework que permite que aplicações distribuídas em uma rede (local ou mesmo na internet) comuniquem-se entre si e troquem informações. Estas aplicações podem estar sendo executadas em diferentes plataformas (Intel, Sun, etc) e sistemas operacionais (Windows, Linux, Unix, etc) e podem ter  sido construídas em diferentes linguagens de programação. 
Utilizando o padrão CORBA é possível ter aplicações completamente distribuídas, potencialmente com cada pedaço de software sendo executado em qualquer parte da rede e em qualquer plataforma, sem que o usuário perceba que isto está acontecendo e sem que o desenvolvedor precise se preocupar em criar soluções que resolvam os problemas de interoperabilidade entre os diferentes pedaços da aplicação. Atualmente, diversas ferramentas já suportam o padrão CORBA, como Delphi 4, JBuilder 2, Oracle 8i, Cold Fusion e C++ Builder. Uma grande vantagem de CORBA é ser um padrão diretamente suportado por empresas em todo o mundo e com dezenas de implementações disponíveis, incluindo algumas gratuitas. Na prática, essa padronização significa que você não precisa ficar preso a determinados fornecedores, plataformas ou produtos, como acontece quando são escolhidas soluções proprietárias.
2. ORB

A arquitetura CORBA define o ORB (Object Request Broker) como um módulo intermediário entre cliente e objeto, sendo responsável em aceitar a requisição do cliente, enviá-la para o objeto competente e, assim que disponível a resposta, entregá-la para o cliente.

3.IDL
A CORBA utiliza a IDL (Interface Definition Language), uma linguagem baseada em C++ que não possui algoritmos nem variáveis, ou seja, é puramente declarativa, e, portanto, é independente da linguagem de programação utilizada para acessá-la. Há padrão de IDL definido pelo OMG para C, C++, Java, TTCN, COBOL, Smalltalk, Ada, Lisp, Python e IDLscript. Possibilita a interoperabilidade entre os diversos sistemas, visto a separação que é definida entre interface e execução. A interface de cada objeto é definida de forma bastante específica, enquanto a sua execução (código fonte e dados) permanece oculta para o resto do sistema.

4.Primeiros passos
A primeira coisa que definimos é que já que o CORBA suporta o paradigma de orientação a objetos , vamos “enviar objetos mensagens” de uma estação para outra , isso vai tornar muito mais fácil o tratamento das mensagens quando elas chegarem nas estações , colocamos enviar objetos entre aspas porque o CORBA não faz exatamente isso, mas vamos explicar isso em momento oportuno.
Definido o formato da mensagem precisamos criar o arquivo IDL (Interface Definition Language), que vai definir como vai acontecer a troca de informação entre as estações , no nosso projeto o arquivo ficou da seguinte forma:

struct Evento{
string tipo;
long timestamp;
long origem;
long ordemEnvio;

};

interface Mensagem{

void sendMensagem(in Evento ev);


};

Nomeamos o arquivo IDL como Mensagem.idl , o objeto mensagem está espelhado na Struct Evento , como a IDL é baseada em C++  temos que usar structs quando quisermos manipular objetos , nesse caso nosso objeto será Evento  e ele terá os seguintes campos :

  • tipo: pode assumir ‘e’ no caso de evento interno ou ‘m’ no caso de mensagem sendo enviada para as outras estações.
  • timestamp :  leva o timestamp indicando o momento ( de acordo com o relógio lógico de lamport) quando o evento ocorreu .
  • origem : indica de onde está vindo a mensagem
  • ordem de envio : indica a ordem local do evento ,  1º , 2º , 3º evento a ocorrer na estação “x” e assim sucessivamente.

Para mais detalhes sobre relógios escalares de Lamport , procurar o livro : Sistemas Distribuídos conceitos e projeto – Coulouris etal . Capitulo 11 – Tempo e estados globais.

Depois de definir a struct definimos a interface Mensagem , essa interface será mapeada em diversas classes holder e stubs que mostraremos a seguir , dentro da interface temos um método abstrato sendMensagem ( in Evento ev) … a escolha do nome do método não foi feliz na verdade ele devia ser mudado para receiveMensagem , quando analisarmos o código vai dar para ver isso melhor … continuando o in Evento ev  significa que o método recebe um Evento  ev e que só serve para receber ou seja é um método de entrada de dados ( input ) por isso o in.

O projeto foi todo desenvolvido usando o Netbeans , então criamos um projeto java  e um pacote que chamamos de PSD ( projeto de sistemas distribuídos – olha a criatividade ai 🙂  ) , depois de criar esse projeto criamos o arquivo Mensagem.idl …

O próximo passo é usar o compilador IDL que vai ler o arquivo e gerar as classes auxiliares necessárias, para isso utilizamos o prompt de comando ou o terminal (no meu caso) , entramos na pasta que contem os arquivos e digitamos o comando: idlj -fall Mensagem.idl ( veja a figura abaixo)

O compilador IDL vai gerar todas as classes auxiliares necessárias ao projeto , veja abaixo como ficou a pasta do projeto depois da compilação do arquivo idl.

A partir do arquivo idl ( arquivo de texto ) o compilador cria a classe Evento , arquivos holder e helper além do arquivo POA e Stub , basicamente cada uma tem uma tarefa :

  • POA :  Esta classe abstrata é baseada no esqueleto do servidor. Extende org.omg.PortableServer.Servant, e implementa a interface InvokeHandler e a interface MensagemOperations. A classe de servidor, HelloServant, extende MensagemPOA
  • Holder e Helper : implementam operações necessárias a comunicação em corba .
  • Stub : é a classe responsável por serializar e deserealizar os objetos no momento da transmissão , o corba na verdade não envia objetos de uma estação para outra , ele envia um streaming de bytes que podem depois ser convertidos para objetos java , com o uso do stubs isso fica transparente para o programandor.
  • Operations :  Esta interface contém o método sendMessage() . O idlj coloca as operações definidas na interface IDL dentro este arquivo, que é compartilhado por ambos o stubs e esqueletos.

    Acabou a parte fácil , agora é programar tudo no braço , muita calma nessa hora . Pela especificação do projeto precisamos de uma camada de aplicação e outra de serviços ( cliente / servidor ).

    Na camada de aplicação colocamos uma classe apenas chamada PSD , ela vai ativar na camada de serviço as classes de servidor e emissor como a figura abaixo :

    Acima da PSD temos a camada de GUI ( Graphical User Interface ) que foge do escopo do artigo mas que deve ser mostrada no ultimo post , na camada de servico temos as seguintes classes:

    Servidor : aciona o servidor da aplicação e publica o objeto no servidor de nomes
    Emissor : recebe os eventos do PSD
    Relógio : implementação do relógio lógico de lamport ,  acessado tanto pela classe servant quanto pelo emissor.
    Servant: esse é o servidor propriamente dito , ele vai receber as mensagens e montar o buffer de delivery das 3 estações .

    5. Classe PSD ( camada de aplicação ) :
    Vamos explicar essa classe parte por parte

    package psd8;

    import java.util.ArrayList;
    import java.util.concurrent.Semaphore;
    import java.util.logging.Level;
    import java.util.logging.Logger;

    /**
    *
    * @author Joao
    */
    public class PSD1 implements Runnable{

    /**
    * @param args the command line arguments
    */

    boolean imprimiu = false;
    private ArrayList buffertmp = new ArrayList();
    ServidorMensagem servm;
    MensagemServant ms = new MensagemServant();
    private static ArrayList b1 = new ArrayList();
    private static ArrayList b2 = new ArrayList();
    private static ArrayList b3 = new ArrayList();
    private static ArrayList bufferDelivery =
    new ArrayList();
    private static boolean bufferNaoMontado = true;
    private static Semaphore semBD ;
    private static boolean terminar = false;

    Essa classe é o PSD1 por que tirei da implementação da estação 1 , a primeira coisa importante a dizer é que a classe implementa Runnable , isso por que ela vai funcionar como uma thread , alias esse sistema é cheio de threads o que requer muita atenção e cuidados extras , se vc não entende de threads recomento o artigo do GUJ  Introdução ao sincronismo e monitores .

    A classe também tem vários arraylists b1,b2, b3 é para armazenar as mensagens que chegam das estações , essas mensagens depois vão ser usadas na camada gráfica (GUI) , a mesma coisa acontece com o bufferDelivery que é o buffer onde ocorre a montagem da ordem total de entrega das mensagens segundo os relógios lógicos , na verdade o buffer de delivery é montado na classe servant mas é acessado concorrente mente com a classe PSD , e por isso usamos a classe Semaphore , semáforo são uma das formas de se controlar o acesso concorrente de processos em uma variável no caso o buffer de montagem , isso acontece no nosso projeto por que a medida que as mensagens vão chegando o buffer de delivery é montado e a aplicação vai pegando os valores colocados no buffer , tudo concorrentemente , veja que nesse ponto o complicado não é o sistema distribuído em si mas o acesso concorrente e o uso de varias threads .

    Continuando na classe PSD …

        public void run() {

    this.iniciar();

    }


    public void iniciar( ){

    Esse método run deve ser implementado em cada classe que implemente Runnable quando a thead desse processo começar a rodar é esse método run que vai rodar primeiro , no nosso programa ele chama o método iniciar …

     
    public void iniciar(){
    String[] args=null;

    Relogio.initRelogio();//inicia o relógio logico de Lamport

    //instancia um servidor que ira publicar as mensagens e criar o servant
    servm = new ServidorMensagem(args);
    Thread tservidor = new Thread(servm);
    tservidor.setName("Thread do servidor");
    tservidor.start();
    try {
    tservidor.join();
    //aguarda aqui até a thread do servidor teminar isso é necessario
    //para que não se tente rodar a thread cliente antes de registar
    //os objetos no servidor de nomes
    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }

    //iniciar o montador de bufferFinal
    ms.iniciarMontagem();


    Emissor em = new Emissor(args);
    Thread tcliente = new Thread(em);
    tcliente.setName("Thread do cliente");

    tcliente.start();
    try {
    tcliente.join();
    //aguarda o cliente terminar de transmitir a mensagem

    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }


    //já pronto para enviar mensagens

    //thread para acesso ao buffer de delivery no servant
    new Thread(new GetBufferDelivery()).start();

    //String _tipo, int _timestamp, int _origem, int _ordemEnvio

    ArrayList lista = new ArrayList();
    Evento e1 = new Evento("e",0, 1,1);
    lista.add(e1);
    Evento e2 = new Evento("m",0, 1,2);
    lista.add(e2);
    Evento e3 = new Evento("m",0, 1,3);
    lista.add(e3);
    Evento e4 = new Evento("e",0, 1,4);
    lista.add(e4);
    Evento e5 = new Evento("m",0, 1,5);
    lista.add(e5);
    Evento e6 = new Evento("m",0, 1,6);
    lista.add(e6);
    Evento e7 = new Evento("x",100, 1,7);
    lista.add(e7);

    //enviar eventos
    for (Evento ev : lista) {
    em.enviarMensagem(ev);

    }


    }

    Inicialmente o psd inicializa o relógio lógico ( Relogio.initRelogio(); ) que deve começar com timestamp = 0 e ir avançando conforme os eventos internos e mensagens ocorrem , logo depois o psd instanciá a classe Servidor que é responsável por publicar o objeto remoto no servidor de objetos corba ( falamos dele daqui a pouco) , o servidor é instanciado atravez de uma thread , o comando tservidor.join(); obriga a thread atual aguardar a finalização da thread do servidor , isso é necessário por que logo mais vamos chamar o emissor e não podemos começar a emitir as mensagens sem que o servidor esteja “no ar “.

    Depois que o servidor estiver funcionando iniciamos uma outra thread que é a thread de montagem do buffer delivery( ms.iniciarMontagem();  ) , essa thread está como uma classe interna do servant logo usamos a referencia ao servant para aciona-la  no comando:
     
    O próximo passo é iniciar o emissor de mensagem( Emissor em = new Emissor(args);) , novamente em uma thread (Thread tcliente = new Thread(em); ), ele vai ter o método que possibilita o envio das mensagens . Pelo mesmo motivo do anterior fazemos um join nessa thread , isso por que o emissor deve estar funcionando para que passemos para a próxima etapa do algoritmo que é gerar e enviar os enventos , se o emissor não estiver pronto vamos gerar os eventos e na hora de enviar vai dar erro .

    Depois de inicializar o emissor ( também chamado de cliente ) iniciamos uma outra thread de montagem de buffer localmente ou seja , temos uma classe interna em PSD que vai tentar acessar o buffer de delivery  conforme ele for montado e dessa forma “puxar”os valores da camada de serviço para a camada de aplicação ( new Thread(new GetBufferDelivery()).start(); ).

    Nesse momento estamos com o aplicativo como a figura sugere

    Como já estamos prontos para enviar as mensagens , montamos primeiramente um ArrayList com todos os evento , internos e mensagens , cada um segundo a especificação do problema, aqui cabe algumas explicações , o timestamp de cada evento esta zerado por que vai ser atualizado no momento do envio pela classe Emissor utilizando a classe Relógio . O ultimo elemento da lista é um X isso é um evento que não vai atualizar o relógio ele só é um sinalizador que a estação 1 já enviou tudo que tinha para enviar o timestamp dele é 100 para não interferir na hora de ordenar os eventos , na verdade qualquer numero alto serviria nesse caso . Finalmente o For final envia um a um os evento ao emissor , tanto os internos “e”quanto os externos “m”, talvez ai a gente tenha perdido pontos no projeto já que um evento externo não precisava ser enviado só atualizado o relógio mas enviamos mesmo assim ….

    O restante do código tem métodos gets e sets para as variáveis , um método que pega os semáforos usados nos buffers , um método que limpa todos os buffers ao final da operação para que a estação comece a transmitir tudo novamente, um método para desconectar do servidor de localização de objetos corba, um método utilitário que imprime os valores que estão nos buffers e  finalmente a classe interna que puxa concorrentemente os valores do buffer de delivery

      

    public void desconectar(){

    servm.unbindNoServidor();

    }

    public void getSemaforos(){
    semBD = MensagemServant.getSemBD();
    }

    public static void insertValorB1(Evento e) {
    b1.add(e);
    }

    public static void insertValorB2(Evento e) {
    b2.add(e);
    }

    public static void insertValorB3(Evento e) {
    b3.add(e);
    }

    public static void insertValorbufferDelivery(Evento e) {
    bufferDelivery.add(e);
    }



    //metodos para uso da Interface grafica
    public static ArrayList getB1() {
    return b1;
    }

    public static ArrayList getB2() {
    return b2;
    }

    public static ArrayList getB3() {
    return b3;
    }

    public static ArrayList getBufferDelivery() {
    return bufferDelivery;
    }

    public static boolean isBufferNaoMontado() {
    return bufferNaoMontado;
    }



    public static void imprimirDadosPSD(){
    System.out.println("Imprimindo buffer de Delivery no PSD!!!!!!!!");
    System.out.println("Tamanho do Buffer:"+ bufferDelivery.size() );
    for (Evento ev : bufferDelivery) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }

    System.out.println("Imprimindo Buffer 1:");
    for (Evento ev : b1) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }

    System.out.println("Imprimindo Buffer 2:");
    for (Evento ev : b2) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }
    System.out.println("Imprimindo Buffer 3:");
    for (Evento ev : b3) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }
    bufferNaoMontado = false;
    }


    public void limparBuffers(){
    b1.removeAll(b1);
    b2.removeAll(b2);
    b3.removeAll(b3);
    bufferDelivery.removeAll(bufferDelivery);
    bufferNaoMontado = true;
    terminar = false;

    }



    public class GetBufferDelivery implements Runnable{

    public void run() {
    getSemaforos();

    while(true){




    if(ms.getTamanhoBufferDelivery() != 0 ){
    // puxar elemento por elemento

    try {
    semBD.acquire();
    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }



    Evento e = ms.getValorNoBufferDelivery();
    if(e.tipo.compareToIgnoreCase("x")==0){
    terminar = true; //termina thread e nao adiciona eventos nos buffers
    }else{
    bufferDelivery.add(e);
    if (e.origem == 1) {
    b1.add(e);
    } else if (e.origem == 2) {
    b2.add(e);
    } else if (e.origem == 3){
    b3.add(e);
    }
    }

    semBD.release();

    }


    if(terminar){
    imprimirDadosPSD();
    break;
    }


    }//fim do while


    }

    Vale a pena comentar o código da classe interna (GetBufferDelivery) , essa classe testa se o buffer de delivery no servant não está vazio , quanto não estiver vazio ela vai no servant e pega um evento no BFDelivery , claro que para isso ela aciona o semáforo correspondente para só ela conseguir acessar o BFD , cada valor puxado é adicionado no BufferDelivery local da classe PSD e também nos buffers individuais de cada emissor ( b1, b2, b3) , o processo termina quando a classe puxar um evento tipo x no BFD do servant .
    Por enquanto é isso , nos próximos post vamos ver como funciona o relógio e as demais classes terminando com a classe de GUI , o código completo do PSD1 segue abaixo , até o próximo post.

    CODIGO COMPLETO DA CLASSE PSD1

    package psd8;

    import java.util.ArrayList;
    import java.util.concurrent.Semaphore;
    import java.util.logging.Level;
    import java.util.logging.Logger;

    /**
    *
    * @author Joao
    */
    public class PSD1 implements Runnable{

    /**
    * @param args the command line arguments
    */

    boolean imprimiu = false;
    private ArrayList buffertmp = new ArrayList();
    ServidorMensagem servm;
    MensagemServant ms = new MensagemServant();
    private static ArrayList b1 = new ArrayList();
    private static ArrayList b2 = new ArrayList();
    private static ArrayList b3 = new ArrayList();
    private static ArrayList bufferDelivery = new ArrayList();
    private static boolean bufferNaoMontado = true;
    private static Semaphore semBD ;
    private static boolean terminar = false;






    public void run() {

    this.iniciar();

    }


    public void iniciar(){
    String[] args=null;

    Relogio.initRelogio();//inicia o relogio logico de Lamport


    servm = new ServidorMensagem(args);
    Thread tservidor = new Thread(servm);
    tservidor.setName("Thread do servidor");
    tservidor.start();
    try {
    tservidor.join();
    //aguarda aqui até a thread do servidor teminar isso é necessario
    //para que não se tente rodar a thread cliente antes de registar
    //os objetos no servidor de nomes
    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }

    //iniciar o montador de bufferFinal
    ms.iniciarMontagem();


    Emissor em = new Emissor(args);
    Thread tcliente = new Thread(em);
    tcliente.setName("Thread do cliente");

    tcliente.start();
    try {
    tcliente.join();
    //aguarda o cliente terminar de transmitir a mensagem

    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }


    //já pronto para enviar mensagens

    //thread para acesso ao buffer de delivery no servant
    new Thread(new GetBufferDelivery()).start();

    //String _tipo, int _timestamp, int _origem, int _ordemEnvio

    ArrayList lista = new ArrayList();
    Evento e1 = new Evento("e",0, 1,1);
    lista.add(e1);
    Evento e2 = new Evento("m",0, 1,2);
    lista.add(e2);
    Evento e3 = new Evento("m",0, 1,3);
    lista.add(e3);
    Evento e4 = new Evento("e",0, 1,4);
    lista.add(e4);
    Evento e5 = new Evento("m",0, 1,5);
    lista.add(e5);
    Evento e6 = new Evento("m",0, 1,6);
    lista.add(e6);
    Evento e7 = new Evento("x",100, 1,7);
    lista.add(e7);

    //enviar eventos
    for (Evento ev : lista) {
    em.enviarMensagem(ev);

    }


    }




    public void desconectar(){

    servm.unbindNoServidor();

    }

    public void getSemaforos(){
    semBD = MensagemServant.getSemBD();
    }

    public static void insertValorB1(Evento e) {
    b1.add(e);
    }

    public static void insertValorB2(Evento e) {
    b2.add(e);
    }

    public static void insertValorB3(Evento e) {
    b3.add(e);
    }

    public static void insertValorbufferDelivery(Evento e) {
    bufferDelivery.add(e);
    }



    //metodos para uso da Interface grafica
    public static ArrayList getB1() {
    return b1;
    }

    public static ArrayList getB2() {
    return b2;
    }

    public static ArrayList getB3() {
    return b3;
    }

    public static ArrayList getBufferDelivery() {
    return bufferDelivery;
    }

    public static boolean isBufferNaoMontado() {
    return bufferNaoMontado;
    }



    public static void imprimirDadosPSD(){
    System.out.println("Imprimindo buffer de Delivery no PSD!!!!!!!!");
    System.out.println("Tamanho do Buffer:"+ bufferDelivery.size() );
    for (Evento ev : bufferDelivery) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }

    System.out.println("Imprimindo Buffer 1:");
    for (Evento ev : b1) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }

    System.out.println("Imprimindo Buffer 2:");
    for (Evento ev : b2) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }
    System.out.println("Imprimindo Buffer 3:");
    for (Evento ev : b3) {
    System.out.println( ev.tipo +ev.ordemEnvio + ev.origem +":"
    + ev.timestamp +","+ev.origem );
    }
    bufferNaoMontado = false;
    }


    public void limparBuffers(){
    b1.removeAll(b1);
    b2.removeAll(b2);
    b3.removeAll(b3);
    bufferDelivery.removeAll(bufferDelivery);
    bufferNaoMontado = true;
    terminar = false;

    }



    public class GetBufferDelivery implements Runnable{

    public void run() {
    getSemaforos();

    while(true){




    if(ms.getTamanhoBufferDelivery() != 0 ){
    // puxar elemento por elemento

    try {
    semBD.acquire();
    } catch (InterruptedException ex) {
    Logger.getLogger(PSD1.class.getName()).log(Level.SEVERE, null, ex);
    }



    Evento e = ms.getValorNoBufferDelivery();
    if(e.tipo.compareToIgnoreCase("x")==0){
    terminar = true; //termina thread e nao adiciona eventos nos buffers
    }else{
    bufferDelivery.add(e);
    if (e.origem == 1) {
    b1.add(e);
    } else if (e.origem == 2) {
    b2.add(e);
    } else if (e.origem == 3){
    b3.add(e);
    }
    }

    semBD.release();

    }


    if(terminar){
    imprimirDadosPSD();
    break;
    }


    }//fim do while


    }

    }

    }

    Projeto de sistemas distribuídos usando CORBA, parte I : O projeto

    Na disciplina de Sistemas distribuídos (2º Trimestre -PPGEAS- UFSC – 2011) , tive que fazer um projeto em CORBA para 3 estações , a idéia era termos 3 estações funcionando em maquinas diferentes enviando mensagens uma para as outras, além disso utilizaríamos o conceito de relógios escalares de Lamport para poder ordenar os eventos e mensagens para cumprir a ordem total na entrega das mensagens na camada de aplicação , vou criar uma serie de posta explicando como fizemos o projeto e as dificuldades e detalhes da implementação.

    Em tempo , foi por isso que passei um bom tempo sem publicar nada , a disciplina de sistemas distribuídos  e muito extensa e por isso tive que me dedicar e fiquei sem tempo para o blog

    Vamos começar com uma explicação simples sobre o CORBA, segundo a wikipedia :
    “CORBA (abreviado de Common Object Request Broker Architecture) é a arquitetura padrão criada pelo Object Management Group para estabelecer e simplificar a troca de dados entre sistemas distribuídos heterogêneos. Em face da diversidade de hardware e software que encontramos atualmente, a CORBA atua de modo que os objetos (componentes dos softwares) possam se comunicar de forma transparente ao usuário, mesmo que para isso seja necessário interoperar com outro software, em outro sistema operacional e em outra ferramenta de desenvolvimento. CORBA é um dos modelos mais populares de objetos distribuídos.”

    Os posts não vão se aprofundar muito no corba já que isso daria um livro inteiro , mas sempre que achar necessário vamos explicar uma ou outra característica do CORBA.

    O corba é rival do RMI do Java ou seja funciona com base no principio do RPC ( Remote Procedury Call – Chamada de procedimento remoto), o corba é mais complicado que o RMI mas em compensação tem implementações em diversas linguagens diferentes enquanto o RMI é só em java.

    Bom voltando ao projeto de sistemas distribuídos … o enunciado do projeto era o seguinte :

    Implemente um mecanismo baseado em relógios escalares (relógios lógicos de Lamport) que defina a entrega de mensagens obedecendo ordem Total em estações. Considere três estações (ou processos) sujeitas aos seguintes eventos de emissão:

    S1 ( e11, m21, m31, e41, m51, m61)
    S2 ( e12, m22, e32, m42, m52, e62, m72)
    S3 ( m13, m23, e33, e43, m53, m63)

    Todos os eventos de emissão de mensagens descritos nas seqüências acima são produzidos em suas estações (ou processos) respectivamente segundo as ordens locais indicadas pelos seus índices. Os eventos eij são internos e eventos mij correspondem à mensagens enviadas a todas estações (às três consideradas). No processo de envio timestamps são concatenados às mensagens usando os relógios lógicos. Os eventos recepção não estão anotados nas seqüências acima, mas são importantes no funcionamento dos relógios lógicos (devem ser atualizados quando de recepções de mensagens).

     Premissas:
            Comunicação ponto a ponto confiável com ordem FIFO entre estações.
     Condições para as decisões de entrega:
           Decisões de ordem em cada nó baseadas em uma fila por emissor (ver
    desenho abaixo);
           A simulação dos eventos internos eij deve corresponder a simples ativação
    dos respectivos Relógios Lógicos dos respectivos processos Pj.
           A ordem total na liberação das mensagens mij em cada processo toma
    como base os valores de timestamp.

     Implementar a partir de objeto aplicação correspondente a dependência
    da mensagem m53 como sendo precedida pela emissão dos eventos evento interno e32 e mensagem m21.

     Mostre no final dos eventos simulados as filas de delivery de mensagens nas três estações, com as mensagens difundidas ordenadas por estampilhas do mecanismo implementado.


    A partir das especificações do projeto teríamos que criar 3 aplicativos cliente/servidor utilizando o Corba na versão java que já vem na SDK do java SE.

    Inicialmente criamos 3 aplicativos exatamente iguais só fazendo algumas distinções para caracteriza-los como rodando em estações diferentes , ao final tivemos que implementar varias modificações no aplicativo padrão da 3º estação para atender ao item de projeto : Implementar a partir de objeto aplicação correspondente a dependência da mensagem m53 como sendo precedida pela emissão dos eventos evento interno e32 e mensagem m21.

    Acompanhe nos próximos posts como fizemos a implementação.

    Sistemas distribuídos: Introdução ao JAVA RMI

    Objetos remotos são objetos cujos métodos podem ser chamados remotamente, como se fossem locais.
    O Cliente precisa, de alguma forma, localizar e obter uma instância do objeto remoto . Depois que o cliente tem a referência, faz chamadas nos métodos do objeto remoto usando a mesma sintaxe que usaria se o objeto fosse local. Objetos remotos abstraem toda a complexidade da comunicação em rede.

    Java RMI (Remote Method Invocation) pode ser implementado usando protocolos e infraestrutura próprios do Java (JRMP e RMI Registry) ou usando IIOP e ORBs, próprios do CORBA. JRMP – Java Remote Method Protocol : Pacotejava.rmi-RMI básico Ideal para aplicações 100% Java.

    Um objeto remoto previamente registrado é obtido, através de servidor de nomes especial: RMI Registry. O RMI Registry permite que os objetos publicamente acessíveis através da rede sejam referenciados através de um nome.

    Uma aplicação distribuída com RMI tem acesso transparente ao objeto remoto através de sua Interface Remota. A “Interface Remota” é uma interface que estende java.rmi.Remote , A partir da Interface Remota e implementação do objeto remoto o sistema gera objetos (proxies) que realizam todas as tarefas necessárias para viabilizar a comunicação em rede.


    A “Interface Remota” é uma interface que estende java.rmi.Remote . A partir da Interface Remota e implementação do objeto remoto o sistema gera objetos (stubs) que realizam todas as tarefas necessárias para viabilizar a comunicação em rede.
    Veja os 10 passos para a construção de um exemplo mínimo do uso de Java RMI .

    1. Definir a interface
    2. Implementar os objetos remotos
    3. Implementar um servidor para os objetos
    4. Compilar os objetos remotos
    5. Gerar stubs e skeletons com rmic
    6. Escrever, compilar e instalar o(s) cliente(s)
    7. Instalar o stub no(s) cliente(s)
    8. Iniciar o RMI Registry no servidor
    9. Iniciar o servidor de objetos
    10. Iniciar o cliente


    Organização dos arquivos:
    Antes de começarmos sugiro que você crie uma pasta para o projeto e dentro desta pasta crie duas subpastas como nome cliente e servidor. Precisamos separar os códigos do cliente do código do servidor. Estou contando nesse exemplo que primeiramente você vai criar o cliente e servidor rodando na mesma maquina por isso vou utilizar o endereço de loopback da interface de rede , depois explico como levar os arquivos para outra maquina para testa-los na rede .


    1. Definir a interface Remota
    Declare todos os métodos que serão acessíveis remotamente em uma interface Java que estenda java.rmi.Remote.  Todos os métodos devem declarar throws java.rmi.RemoteException. Isto deve ser feito para cada objeto que será acessível através da rede.
    import java.rmi.*; 

    public interface Mensagem extends Remote {
    public String getMensagem() throws RemoteException;
    public void setMensagem(String msg) throws RemoteException;
    }

    2. Implementar os objetos remotos
    Cada objeto remoto é uma classe que estende a classe java.rmi.server.UnicastRemoteObject e que implementa a interface remota criada no passo 1.  Todos os métodos declaram causar java.rmi.RemoteException inclusive o construtor, mesmo que seja vazio.

    import java.rmi.server.*; 
    import java.rmi.*;


    public class MensagemImpl extends UnicastRemoteObject implements Mensagem {

    private String mensagem = "Inicial";

    public MensagemImpl() throws RemoteException {}

    public String getMensagem() throws RemoteException {
    return mensagem;
    }
    public void setMensagem(String msg) throws RemoteException {
    mensagem = msg;
    }
    }

    3. Implementar um servidor para os objetos
    Crie uma classe que crie uma instância do objeto a ser servido e  Registre-a (bind ou rebind) no serviço de nomes.

    import java.rmi.*;

    public class MensagemServer {

    public static void main(String [ ] args) throws RemoteException{

    Mensagem mens = new MensagemImpl( );
    try{
    Naming.rebind("mensagem", mens) ;
    System.out.println("Servidor no ar : Nome do objeto servido: mensagem");
    } catch( Exception ex){

    }

    }

    }

    O try catch no comando rebind é necessário porque um erro nessa operação dispara uma exceção MalformedURLException.

    4. Compilar os objetos remotos
    Para esse passo abra um terminal de comando entre na pasta onde seus arquivos .java estão armazenados e compile os arquivos.

    5. Gerar stubs e skeletons com rmic

    Ainda na pasta do servidor use o rmic( RMI Compiler ) para gerar os stubs , nas ultimas versões do RMI os arquivos skeletons não são mais gerados .Execute o rmic sobre as implementações do objeto remoto
    já compiladas: no nosso caso rmic MensagemImpl. O rmic vai gerar um arquivo stub no nosso exemplo o MensagemImpl_Stub.class .

    6. Escrever, compilar e instalar o(s) cliente(s)Escreva uma classe cliente que localize o(s) objeto(s) no serviço de nomes (java.rmi.Naming) . Após obter uma instância remota de cada objeto ,podemos usar o objeto, chamando seus métodos.
    import java.rmi.*; 
    public class MensagemCliente {
    public static void main(String[] args) throws Exception {

    Object obj = Naming.lookup("rmi://127.0.0.1/mensagem");

    Mensagem mens = (Mensagem) obj;
    System.out.println("Mensagem recebida: "+ mens.getMensagem());
    mens.setMensagem("Fulano esteve aqui!");

    }
    }

    Depois de salvar o arquivo na pasta cliente copie o arquivo da interface remota( Mensagem.java) que está na pasta servidor( é isso mesmo usamos a mesma definição de interface remota para os dois lados , aplicação cliente e servidor ) para a mesma pasta e compile os dois arquivos.

    7. Instalar o stub no(s) cliente(s)
    Copie o arquivo Stub gerado pelo rmic que estão na pasta servidor , o java tem um mecanismo de download de classes stubs mas a configuração desse mecanismo foge ao escopo desse post.

    8. Iniciar o RMI Registry no servidor
    O RMIRegistry é um serviço de nomes de objetos remotos é ele que vai atender ao lookup da aplicação cliente . Você deve rodar o comando dentro da pasta onde se encontra os arquivos .class.
    Neste exemplo será preciso iniciar o RMIRegistry no diretório onde estão os stubs e interface Remote . Isto é para que o RMIRegistry veja o mesmo CLASSPATH que o resto da aplicação. Em aplicações RMI reais isto não é necessário, mas é preciso definir a propriedade java.rmi.server.codebase contendo os caminhos onde se pode localizar o código.

    9. Iniciar o servidor de objetos
    Entre na parta servidor e execute o servidor , note que enquanto isso o RMIRegistry tem que continuar ativo portanto não feche o terminal do RMIRegistry. Abra outro terminal para colocar o servidor no ar.

    10. Iniciar o cliente 
    Entre na pasta cliente e coloque o cliente para rodar , na primeira vez que ele rodar ele deve retornar e imprimir a seguinte string na tela Mensagem recebida : inicial  . Se você rodar o cliente mais uma vez ele pega a mensagem atualizada Mensagem recebida : Fulano esteve aqui . Ou seja se você examinar o codigo no primeiro acesso ao objeto remoto o cliente executa a operação mens.setMensagem(“Fulano esteve aqui!”) mudando dessa forma a String armazenada no objeto remoto , quando você executa o comando pela segunda vez ele pega essa nota String já atualizada.


    Observações


    Antes de rodar as aplicações ( cliente e servidor ) tenha certeza que você tem todos os arquivos necessários ,  implementar primeiramente os programas da maneira que foi sugerido facilita inicialmente o desenvolvimento e teste da aplicação , para testar o cliente e o servidor em maquinas diferente basca pegar o conteúdo da pasta cliente e implantar em uma nova maquina , depois entre no codigo fonte da aplicação cliente e modifique o numero IP da maquina onde está rodando o RMIRegistry , recompile os arquivos e pode testar . Abaixo veja a lista dos arquivos que devem estar presentes tanto na pasta servidor quanto cliente antes de você rodar a aplicação.

    Fonte : Curso Programação Orientada a Objetos usando Java da empresa Argonavis , Unidade 20 Fundamentos de Objetos remotos (java.rmi). Link

    Por favor poste seus comentarios  🙂

    Sistemas distribuidos: Enviando mensagens TCP Parte II – Servidor

    Igual ao servidor UDP esse servidor só vai receber a mensagem e envia-la de volta , ou seja é um servidor de ECO mas com boa vontade basta fazer algumas alterações que vc tem um servidor de chat 🙂 .

    Server TCP:

    import java.net.*;
    import java.io.*;
    public class TCPServer {

    public static void main(String[] args) {

    try {
    int serverPort = 7896;
    ServerSocket listenSocket = new ServerSocket(serverPort);
    //socket de escuta o socket que vai atender as requisições

    while(true){
    Socket client = listenSocket.accept( );
    Connection c = new Connection(client);
    //precisamos criar a classe conection que vai
    //inicializar nossas threads para atender a cada requisição
    }
    } catch (IOException e) {
    System.out.println("Listem :" + e.getMessage( ));
    }

    }//fim do main
    }//fim da classe

    Depois de finalizado a classe servidor precisamos criar a classe adicional
    Connection que vai gerar uma thread para cada cliente que solicitar conexão com o servidor .

    import java.net.*;
    import java.io.*;

    public class Connection extends Thread {
    DataInputStream in;
    DataOutputStream out;
    Socket client;

    public Connection(Socket client) {

    try {
    this.client = client;
    in = new DataInputStream( client.getInputStream( ) );
    out = new DataOutputStream(client.getOutputStream( ) );
    this.start( );

    } catch (IOException e) {
    System.out.println("Connectio :" + e.getMessage( ));
    }
    }//fim do construtor


    @Override
    public void run( ){
    //metodo run que cria a nova thread
    //e implementa o servidor de eco

    try {
    String data = in.readUTF( );
    System.out.println("Dados que Chegaram:" + data);
    out.writeUTF(data);
    } catch (Exception e) {
    System.out.println("Erro: " + e.getMessage( ));
    }finally{
    try{
    client.close( );
    }catch(IOException ex){
    System.out.println("Close Falhou");
    }
    }

    }//fim do run

    }//fim da classe

    Para testar o servidor vá no menu executar e mande “Limpar e construir o projeto principal ” com isso o servidor vai ser empacotado em um jar , depois basta abrir um terminar e executar o jar , em seguida abra o projeto do cliente rodando o projeto a seguir e veja as duas partes se comunicando .

    Sistemas distribuidos: Enviando mensagens TCP Parte I – Cliente

    Aproveitando que estou fazendo a disciplina de  sistemas distribuídos no Doutorado continuo minha serie de posts sobre o assunto , dessa vez vamos conectar dois sistemas usando TCP , abaixo segue o codigo do cliente implementado e testado , lembre que para passar os dois argumentos para testar o cliente você precisa seguir as mesmas instruções de configuração que foram passadas no meu post sobre envio de mensagens UDP , veja o post antigo aqui.

    Código do cliente:

    import java.net.*;
    import java.io.*;

    public class TCPCliente {

    public static void main(String[] args) {
    //argumentos fornecem a mensagem e o nome de host do destino
    //argumento 0 é a mensagem , o 1 é o ip de destino do servidor

    Socket s = null;

    try{
    int serverPort = 7896 ; // porta do servidor
    s= new Socket(args[1], serverPort); // ip e porta do servidor
    DataInputStream in = new DataInputStream(s.getInputStream( ) );
    DataOutputStream out = new DataOutputStream(s.getOutputStream( ) );

    out.writeUTF(args[0] );
    String data = in.readUTF( );
    System.out.println("Dado recebido: " + data);


    }catch(UnknownHostException e){
    System.out.println("Sock: " + e.getMessage( ) );
    }catch(EOFException ex){
    System.out.println("EOF: " + ex.getMessage( ));
    }catch(IOException ex){
    System.out.println("IO: " + ex.getMessage( ) );
    } finally{
    if(s!=null)
    try{
    s.close( );
    }catch(IOException ex){
    System.out.println("Close Falhou");
    }
    }


    }//fim do main
    }//fim da classe

    Antes de testar como fizemos no cliente UDP você deve implementar primeiro o servidor e na hora de usar passar dois parâmetros o 1º parâmetro  tem que ser a mensagem , o 2º parâmetro é o numero IP do servidor , se você estiver testando tudo na mesma maquina use o endereço de loopback 127.0.0.1 .

    Como enviar mensagem via UDP em JAVA – PARTE II – Server

    Continuando o post anterior agora vamos ver a implementação do servidor UDP em Java:

    Server de mensagem UDP

    import java.net.*;
    import java.io.*;

    public class UDPServer {

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {
    // TODO code application logic here

    try {
    //criacao do socket UDP datagrama
    //6789 é o valor da porta a ser usada pelo servidor
    DatagramSocket aSocket = new DatagramSocket(6789);
    //tamanho do buffer de recebimento de mensagem
    byte[] buffer = new byte[1000];

    //loop infinito
    while (true) {
    //criacao do pacote
    DatagramPacket request = new DatagramPacket(buffer, buffer.length);
    //receive bloqueante , o programa fica parado aqui até receber um pacote
    aSocket.receive(request);
    // preparar pacote para envio de resposta nada mais que um eco
    DatagramPacket reply = new DatagramPacket(request.getData(), request.getLength(), request.getAddress(), request.getPort());
    //envia mensagem de eco
    aSocket.send(reply);
    }

    //tratamento de exceção
    } catch (SocketException e) {
    System.out.println("Socket: " + e.getMessage());
    } catch (IOException e) {
    System.out.println("IO:" + e.getMessage());
    }

    }//fim do main
    }//fim da classe


    Para colocar o server para rodar primeiramente vá no menu executar e rode a opção Limpar e Construir o projeto principal , o Netbeans vai gerar a seguinte mensagem:

     Note que a ultima mensagem do compilador é o comando que você deve rodar no terminal para que o servidor fique rodando , abra um terminal copie o comando e rode no terminal :

    Agora o server já está rodando , basta agora voltar ao Netbeans e rodar o cliente para ver se tudo funcionou .

    Crie um website ou blog gratuito no WordPress.com.

    Acima ↑