quarta-feira, 18 de setembro de 2013

Wake Up Android Screen

Salve galera,

Nesse posto pretendo mostrar como "acordar" a tela do Android quando necessário. Um exemplo de uso seria em uma aplicação que usa alarmes. É meio chato quando um alarme dispara no dispositivo e temos que liga-lo e desbloquear a tela para fazer alguma coisa com o alarme. Bom, aqui vou mostrar como fazer isso ao disparar o alarme, para que quando o usuário pegue o dispositivo, a tela já esteja ligada e desbloqueada. Isso tudo é muito simples. Acompanhem o código abaixo:


public class WakeUpAndroid extends Activity{


    private KeyguardManager keyguardManager;
    private PowerManager powerManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Disperta a tela
        powerManager = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "TAG");
        wakeLock.acquire();

        //Desbloqueia a tela
        keyguardManager = (KeyguardManager) getApplicationContext().getSystemService(Context.KEYGUARD_SERVICE);
        keyguardLock = keyguardManager.newKeyguardLock("TAG");
        keyguardLock.disableKeyguard();

    }

    @Override
    public void finish() {
        super.finish();
        this.release();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        this.release();
    }

    private void release(){

        keyguardLock.reenableKeyguard();

        if (this.wakeLock.isHeld()) {
            this.wakeLock.release();
        }

    }

}


Após usar o recurso temos que libera-lo, caso contrario quando o dispositivo "adormecer" não será ativado o bloqueio de tela.

Até a próxima! abrassss

sexta-feira, 6 de setembro de 2013

Recuperar PID de uma JVM especifica

Salve galera,

Já precisaram recuperar o PID de uma JVM especifica? Dependendo de como o aplicativo Java  foi iniciado, e se tivermos mais do que um aplicativo rodando, a missão se torna meio complexa e enrolada. Abaixo um código Java que identifica o PID de uma JVM especifica. Precisamos do utilitário jps do java, encontrado na pasta bin do jdk. Ao executarmos o jps, como saída temos o PID das JVM's em execução, junto com um identificador do processo, que são os argumentos passados para a JVM. Por exemplo, um Glassfish apresenta o nome ASMain ao lado do PID, enquanto o ActiveMQ apresenta o nome aqtivemq.jar ao lado do PID. Então é de responsabilidade do programa que precisa usar o recurso saber o nome gerado de antemão. Para ter essa informação é só rodar o aplicativo que se quer controlar, depois rodar o jps e ver as saidas. Abaixo um código que identifica se determinado aplicativo está sendo executado e pega seu PID



 public boolean hasJavaProcess() throws IOException {


        String glassfishProcessName = "ASMain";
        File file = new File(System.getenv("JAVA_HOME"), "bin");

        if (System.getProperty("os.name").toLowerCase().contains("windows")) {
            file = new File(file, "jps.exe");
        } else {
            file = new File(file, "jps");
        }

        String pid = null;

        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("\"" + file.getAbsolutePath() + "\"");
        InputStream in = process.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = null;

        while ((line = reader.readLine()) != null) {
            if (line.contains(glassfishProcessName )) {
                pid = line.replace(glassfishProcessName , "").replace(" ", "");
                break;
            }
        }
        reader.close()
 
       return pid != null;
    }

Ah, mais uma coisa.. Para uma melhor analise dos processos Java em execução, junto do jps você pode rodar a jvisualvm, que da informações mais detalhadas sobre o que está em rodando.

Até pode parecer meio tosco deselegante, mas todas as soluções que encontrei na net eram piores, ao meu ver, pois dependiam de alguma interação com o sistema operacional, usando um ps no Linux ou o tasklist no Windows. Essa solução que criei foi bem por acaso, pois nem conhecia o jps.. mas pensei que poderia ter alguma coisa útil entre aqueles milhões de programas na pasta do sdk. E assim, solucionei o problema e fiquei bem feliz com isso! ;)

Até a próxima!

ActiveMQ Stop Windows

Salve galera,

Por padrão, quando rodamos o ActiveMQ no Windows ficamos com um prompt "pendurado" na sua execução, e ao darmos um Ctrl+C nesse prompt o shutdown do ActiveMQ é chamado. Mas e quando não temos prompt? Ou quando queremos iniciar e parar o ActiveMQ por meio de uma outra aplicação? O que se faz? Bom, como precisei disso, tive que dar uma vasculhada na documentação. Só para comentar, não precisa nem dizer que nos *nix esse problema não existe, pois já temos o start e stop por padrão no script de inicialização.

A solução para o Windows é iniciar o ActiveMQ ativando o conector jmx como no exemplo abaixo:


    

                     
                       
                    


    




Temos que adicionar o parâmetro useJmx="true" no broker e também em managementContext o parâmetro createConnector="true". Com isso podemos chamar o comando abaixo para parar o ActiveMQ no Windows, sem precisar enviar um Ctrl+C para o prompt.

activemq-admin.bat stop



Espero que ajude! Abraço

Spring Security + Container DataSource

Salve galera,

A coisa anda meio corrida e ultimamente, com isso ando meio sem tempo para escrever coisas novas aqui, mas como tudo é passageiro, até a turbulência, essa semana consegui um tempinho.

Nesse post vou dar uma dica na configuração do Spring Security. Quando usamos um banco de dados para fazer a autenticação do Spring Security precisamos informar um cada source como esse:


    
        
            
            
        
    


    
        
        
        
        
    



Isso funciona muito bem, mas se usamos um DataSource fornecido por um Container teremos a configuração do banco (driver, url, usuário, senha) em vários lugares, como no Container, no persistence.xml, no security-app-context.xml, e claro que não queremos isso. Então, para usar o DataSource do Container nas configurações do Spring Security você deve fazer o seguinte:

Primeiro, configure um bean via jndi-lookup, assim (lookup no glassfish):





E depois informe esse bean para a configuração de segurança do Spring, assim:

    
        
            
            
        
    


Rápido e rasteiro, era isso. Até a próxima!

terça-feira, 21 de maio de 2013

Redes Sociais

Salve galera,

Para quem quiser receber as novidades do blog nas redes sociais, é só me adicionar como amigo ou curtir o fanpage da Mobile Mind.

Ricardo Bocchi
Facebook https://www.facebook.com/ricardobocchi
Twitter https://twitter.com/ricardobocchi

Mobile Mind
Facebook https://www.facebook.com/mobilemindtec
Twitter https://twitter.com/mobilemindtec


Add ai galera! abrasss!

sábado, 4 de maio de 2013

EJB com Spring

Salve galera!

Uma dica sobre como injetar um EJB dentro de um Service do Spring.  Devem ter outras maneiras, mas a que usei foi essa:

Primeiro devemos ter configuraro no arquivo do Spring (applicationContext.xml) os seguintes namespaces:




    



Bom, temos que informar um id para o Bean, o JNDI Name, que é fornecido pelo container. No meu caso, estou usando o Apache Tomee. E depois a interface de negócio. Então, para injetar usamos @Resource


@Service
public class ServiceBO {

    @Resource()
    private EjbLocalInterface ejbRefName;


}


É fácil assim mesmo! Abrasss e até a próxima !

sexta-feira, 3 de maio de 2013

Spring MVC + Paginação

Salve galera!

Hoje vou postar um código  que uso para implementar paginação de dados nas requisições do Spring MVC.

Para fazer a paginação, a classe Paginator recebe:

  • pageSize: Tamanho da página
  • offset: Posição do registro
  • modelMap: Estrutura de dados do Spring MVC que faz o bind com a view
  • repository: JpaRepository do Spring para fazer a consulta
  • sort: Atributo para ordenação
  • BeanLoaderListener: Listener para carregamento de entidade, caso seja necessário procesar alguma referência.
Código do BeanLoaderListener

public interface BeanLoaderListener {

    void load(Object bean);
}

Código do Paginator


public class Paginator {

    public void list(Integer pageSize, Integer offset, ModelMap modelMap, JpaRepository repository, String sort, BeanLoaderListener listener) {

        if (offset == null || offset < 0) {
            offset = 0;
        }

        if (pageSize == null || pageSize == 0 || pageSize > 50) {
            pageSize = 10;
        }

        Pageable requestPage = sort == null ? new PageRequest(offset, pageSize) : new PageRequest(offset, pageSize, new Sort(sort));
        Page page = null;

        page = repository.findAll(requestPage);

        if (listener != null) {
            for (Object it : page.getContent()) {
                listener.load(it);
            }
        }

        modelMap.addAttribute("items", page.getContent());

        Integer next = offset + 1;
        Integer last = offset - 1;

        if (!page.hasNextPage() && page.hasPreviousPage()) {
            next = page.getTotalPages() - 1;
        } else if (!page.hasNextPage()) {
            next = 0;
        }

        if (page.isFirstPage()) {
            last = 0;
        }

        if (last == null && next == null) {
            modelMap.addAttribute("paginated", false);
        } else {
            modelMap.addAttribute("paginated", true);
            modelMap.addAttribute("next", next);
            modelMap.addAttribute("last", last);
        }

        List pages = new LinkedList<>();
        for (int i = 0; i < page.getTotalPages(); i++) {
            pages.add(i);
        }
        modelMap.addAttribute("pages", pages);
        modelMap.addAttribute("offset", offset);
        modelMap.addAttribute("hasNext", page.hasNextPage());
        modelMap.addAttribute("hasLast", page.hasPreviousPage());
        modelMap.addAttribute("pageSize", pageSize);

    }
}

E aqui um exemplo de uso:


    @RequestMapping(value = "/person/{pageSize}/{offset}", method = RequestMethod.GET)
    public String list(@PathVariable("pageSize") Integer pageSize, @PathVariable("offset") Integer offset, ModelMap modelMap) {        
        new Paginator().list(pageSize, offset, modelMap, repository, "name", null);
        return "person/list";
    }    

Aqui um resource jsp usado na paginação (yaml css): /extra/paginator.jsp



<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

     




E aqui usando o resource jsp dentro de uma tela de listagem:


    



E era isso gurizada! até a próxima ;)

sexta-feira, 26 de abril de 2013

Veloster Benchmark

Salve galera,


Como prometido, realizamos os testes de desempenho relacionando ao uso ou não uso do Veloster Framework em projetos Android.

Você pode conferir os resultados dos testes acessando http://www.4minds.com.br/engine/show/37


Até a próxima!
 

quarta-feira, 24 de abril de 2013

Transição de tela Android

Salve galera,

Outra dica simples para deixar as aplicações Android mais elegantes. Adicionando o código abaixo logo após iníciar uma nova Actitivy ou após finalizar uma Activity, temos um efeito de slide na mudança de tela:


    overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);


Por exemplo:
     finish();
     overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
Ou
     Intent it = new Intent(context, AboutActivity.class);
     startActivity(it);
     overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);



Até a próxima!

Android transição de imagem

Salve galera,

Agora vou mostrar uma transição simples de imagem em uma aplicação Android. É simples, mas já da um toque especial na aplicação. Você deve implementar esse código no eventro da troca da imagem:


     
        //imageView é o componente do tipo ImageView que está armazenando a imagem
        final Bitmap b = bitmap; //nova imagem
        final boolean toRight = true; // define para que lado a imagem vai deslizar até sumir

        Animation out = AnimationUtils.makeOutAnimation(this, toRight);

        out.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation anmtn) {
            }

            @Override
            public void onAnimationEnd(Animation anmtn) {

                imageView.setImageBitmap(b);


                Animation in = AnimationUtils.makeInAnimation(ImageActivity.this, toRight);
                imageView.startAnimation(in);
                imageView.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation anmtn) {
            }
        });

        imageView.setVisibility(View.INVISIBLE);
        this.imageView.startAnimation(out);


Simples e com um efeito bacana. Nesse momento, sua aplicação deixa de ser tão "crua" e passa a ser um pouco mais elegante ;)

Até a próxima!

Android Menu

Salve galera,

Vou mostrar como criar um menu em uma aplicação Android. Primeiro vamos criar uma pasta chamada menu dentro da pasta de res do android. Depois vamos criar um arquivo para nosso menu:

res/menu/menu_share.xml


    
       
    

                     
    


Depois, na Activity responsável por nossa tela, vamos adicionar o menu:


 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_share, menu);

        menu.getItem(0).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem mi) {
                share();
                return true;
            }
        });

        menu.getItem(1).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem mi) {
                finish();
                return true;
            }
        });

        return true;
    }


Essa é uma das maneuras de criarmos um menu. O resultado é:



Até mais! abrass

Android Share Action

Salve galera,

Existem vários casos onde queremos apenas disponibilizar uma forma simples e genérica de compartilhar  alguma recurso gerado por nossa aplicação. Esse recurso pode ser uma imagem, um texto ou um video que você queira compartilhar por e-mail, facebook, instagram entre outros. Para isso, a api do Android disponibiliza uma action pronta, onde apenas informamos o recurso e chamamos a action, como no exemplo abaixo:



        Intent it = new Intent(android.content.Intent.ACTION_SEND);
        it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        it.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("caminho_imagem")));
        it.putExtra(Intent.EXTRA_SUBJECT, "Nome da Aplicação");

        if (ImageRepository.path.toLowerCase().endsWith("png")) {
            it.setType("image/png");
        } else {
            it.setType("image/jpg");
        }


        startActivity(Intent.createChooser(it, "Complete a Ação Usando:"));

Nesse caso, estamos disponibilizando uma imagem para compartilhamento.

Mas atenção! temos que ter em mente que as opções de compartilhamento dependerão dos aplicativos instalados no tablet ou no celular. Caso você precise de algum recurso mais avançado e ter maior controle sobre esse compartilhamentos, você deve usar a api que alguns aplicativos disponibilizam, como a api do facebook por exemplo.

Para o momento é isso! abrasss

SyntaxHighlighter blogspot

Salve galera,

Dica rápida para configurar  SyntaxHighlighter aqui no blogspot. Pra quem não sabe, SyntaxHighlighter é para adicionar a sintaxe de programação de forma formatada, facilitando sua leitura com cores diferênciadas e tudo mais.

Entre no seu blog, vá em Editar HTML, que deve estar ao lado de personalizar. Procure a tag </head>, sim a de fechamento mesmo. Logo acima dela, adicione o código:



    
         
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    


Salve e com isso já estará funcionado. Para usar, na postagem você deve entar em modo html e adicionar o código desejado entre as tags:





brush: html indica a sitaxe da linguagem que será usada para formatar. Você pode usar shell, html, groovy, java entre outras.

Importânte: Lembre-se de que cada vez que o stilo do blog for mudado, você deve refazer esse processo.

Aqui está o site do projeto http://alexgorbatchev.com/SyntaxHighlighter/integration.html

Até mais.. abrass

quarta-feira, 17 de abril de 2013

git error: cannot lock existing info/refs

Salve galera,

Erro no Git.

Erro:

error: cannot lock existing info/refs

Solução

sudo rm -rf /var/lock/apache2/DAVLock
sudo service apache2 restart

segunda-feira, 15 de abril de 2013

Grails run another port

Salve galera

Para rodar o servidor do grails em outra porta, usamos o seguinte parametros no start:


grails -Dserver.port=7071 run-app

Só isso! abrass

sexta-feira, 12 de abril de 2013

JMS Bridge JBoss

Salve galera,

Nesse poste vou continuar o assunto JMS, mas agora como configurar uma Bridge no JBoss. No poste http://ricardobocchi.blogspot.com.br/2013/04/jms-bridge.html eu mostrei como configurar uma Bridge JMS no glassfish, com toda sua simplicidade de configuração. Também expliquei alguns conceitos sobre JMS e JMS Bridge, então nesse post, somente escreverei sobre a configuração no JBoss.

A verdade é que tive bastante dificuldade em implementar no JBoss aquela mesma estrutura que fiz no glassfish, pois a versão corrente JBoss As 7.1.1 não tem implementação integrada de Bridge, com isso ela deve ser configurada diretamente no HornetQ, que é o servidor JMS embutido no JBoss. Mas o problema é que a documentação de como integrar tudo, de modo que funcione, na minha opinião, é bem incompleta. Por exemplo, para mim que nunca tinha usado o JBoss ficou meio que inviavel descobrir onde colocar os arquivos do HornetQ de modo que ao subir o JBoss, ele reconhece as configurações de Bridge. Eu queria que a estrutura fosse bem simples, usando o servidor JMS embutido, e não ter que subir um servidor JMS para depois subir um JBoss e fazer a integração. Bom, depois de muito apanhar e não sair do chão com a bendita Bridge, eu descobri do detalhe da não implementação através do JBoss, e descobri que para a nova versão a funcionalidade estaria presente. Com isso, baixei a versão de desenvolvimento do JBoss, compilei e ai sim a configuração de Bridge ficou fácil. Dito isso, vou apenas postar as configurações usadas.

A versão do JBoss usada foi:  jboss-as-8.0.0.Alpha1-SNAPSHOT

Abra o arquivo

jboss-as-8.0.0.Alpha1-SNAPSHOT\standalone\configuration\standalone.xml

Primeiramente, para habilitar JMS no JBoss, temos que adicionar a extenção do subsystem dentro da tag <extensions>:






Agora vamos adocionar o sub sistema de mensageria dentro da tag <profile>:


        
        
            
                true
                102400
                2

                
                    
                    
                        
                    
                    
                    
                

                
                    
                    
                        
                        
                    
                    
                

                
                    
                        
                        
                        
                        
                    
                

                
                    
                        jms.queue.DLQ
                        jms.queue.ExpiryQueue
                        0
                        10485760
                        BLOCK
                        10
                    
                

                
                    
                        
                            
                        
                        
                            
                        
                    
                    
                        
                            
                        
                        
                            
                            
                        
                    
                    
                        
                        
                            
                        
                        
                            
                        
                    
                

                
                    
                        
                        
                    
                    
                        
                        
                    
                
            
            
                
                    
                    
                    replicador
                    replicador123*
                    
                        
                        
                        
                        
                        
                    
                
                
                    
                    
                    replicador
                    replicador123*
                
                AT_MOST_ONCE
                500
                1
                500
                500
                true
            
   
                
                    
                    
                    replicador
                    replicador123*
                
                
                    
                    
                    replicador
                    replicador123*
                    
                        
                        
                        
                        
                        
                         
                
                AT_MOST_ONCE
                500
                1
                500
                500
                true
               
        
     

Com essa configuração foram criados dois topicos: Um para enviar mensagens ao outro servidor, e um para receber mensagens do outro servidor. Em source, vocês devem mudar o ip remoto para suas configurações de teste.

 Não vou explicar os detalhes da criação das filas, pois basta procurar nos links abaixo, e se você tem noção do que é uma fila, olhando as tags você consegue se achar:

JMS JBoss Documentação
Artigo que explica como configurar filas e tópicos de forma detalhada
JMS Bridge JBoss

 Um detalhe importânte, é que devemos criar os usuários no JBoss para acessarmos as filas remotamente via Bridge. Na pasta bin do JBoss, temos o utilitário add-user, onde você cria usuários para gerenciamento do appserver e também os usuários que podem ter acesso as aplicações remotamente via lookup. Nessa configuração que fiz, tenho o usuário replicador com senha replicador123, que faz parte do grupo guest.

Ah, também nessa versão do JBoss, temos que configurar um módulo:



    
        
    
 
 
 
    
        
        

    
 
 
    
       
       
       
       
       
       
       
       
    


Esse módulo, fica em


jboss-as-8.0.0.Alpha1-SNAPSHOT\modules\system\layers\base\br\com\mobilemind\main\module.xml

Pelo que parece, esse módulo precisa ser configurado para que a Bridge funciono. Falo isso, por que a versão ainda está em desenvolvimento, então alguma coisa ainda pode mudar.

Bom, era isso.. depois de apanhar um pouco, venho aqui e compartilho o que conseguie para que talvez alguém possa poupar seu tempo de error idiotas.

Por hora é só, qualquer dúvida é só postar! abrass

domingo, 7 de abril de 2013

JMS Bridge


Salve galera,


Hoje vou escrever sobre um assunto que não domino, mas estou pretendendo usar essa tecnologia em alguns projetos, então uma boa maneira de aprender sobre um assunto é escrever sobre ele, e ver se tudo o que aprendi até agora ficou claro. Vou falar sobre JMS.

JMS

O assunto JMS a muito tempo está na minha "lista" de coisas para aprender antes de morrer (sim, coisa de programador! hahahaha), mas por preguiça e falta de motivação, até agora só havia lido superficialmente sobre o assunto. Nesse post vou explicar o que é JMS e para o que ele serve.

JMS, ou Java Messages Service, segundo a Wikipédia, é uma API da linguagem Java para middleware orientado à mensagens. Através da API JMS duas ou mais aplicações podem se comunicar por mensagens (http://pt.wikipedia.org/wiki/JMS ). JMS é uma maneira simples a padronizada de trocar informações entre sistemas distribuídos, ou entre sistemas remotos. Um bom exemplo seria a replicação de dados entre sistemas, onde sistemas dependem de informações de outros sistemas para tomar alguma decisão.

Motivação

Existem diversas estruturas de sistemas onde a comunicação não pode ser em tempo real, ou não podemos ter um servidor central, seja por falta de internet confiável, alta disponibilidade e outras coisas mais. Nesses casos, cada ponto, ou nó tem sua unidade própria de funcionamento, mas precisa trocar e compartilhar informações com outras unidades.


Sobre JMS

Precisamos saber que uma estrutura JMS tem dois componentes principais. Uma fabrica de conexões com uma fila e uma fila.
As filas podem ser de dois tipos:

Consumidores X Produtores

Quando falamos sobre JMS temos dois papeis principais. Os produtores e os consumidores, que não precisam de muita explicação, pois os nomes produtor e consumidor já são bem sugestivos. Mas para não passar em branco:

O produtor é o cara que solicita uma conexão a fabrica de conexão e envia uma mensagem.
O consumidor é o cara que solicita uma conexão a fabrica de conexões e recebe as mensagens enviadas.

Tipos de Fila

Queue: Esse tipo de fila tem uma relação de 1 x 1 com seu consumidor, isso quer dizer que quando uma aplicação lê o dado da Queue, esse dado é marcado como lido e não é mais entregue para ninguém.

Topic: Esse tipo de fila tem uma relação de 1 x n consumidores, isso quer dizer que podemos ter vários consumidores recebendo a mesmo informação.

Persistente

As filas podem ser persistentes (Persistent) ou não. Uma fila com propriedade de persistência é mais segura que uma não persistente por motivos óbvios. Enquanto uma tem suas mensagens armazenada em um local seguro, como um banco de dados ou arquivo, a outra fica apenas em memória. Então, se os dados da fila não podem ser perdidos com uma queda de luz, ou alguma reinicialização do container, você deve usar uma fila persistente. Por padrão, se nada for configurado, os dados são persistentes para ambas as filas.

Time to Live

Ao escrever uma mensagem na fila, podemos definir um tempo de vida para ela (Time to Live), que define o tempo de vida até que ela expire e saia da fila. Isso também vai depender da importância da mensagem. Por padrão, elas têm tempo de vida infinita.

Durabilidade

O termo durabilidade só se aplica as filas do tipo Topic. Para acessar uma fila desse tipo temos que fazer uma inscrição de conteúdo no servidor JMS, claro que isso é apenas "formalidade" pois para uma Queue fazemos o mesmo processo, mas o que define um Topic são seus possíveis múltiplos leitores. Com isso, nós podemos informar qual a importância do conteúdo na perspectiva do assinante ou leitor. Quando dizemos que a assinatura é durável (Durable) estamos dizendo ao servidor JMS que todo conteúdo gerado deve ser entregue, independente de eu estar on-line ou não. Com isso, todo conteúdo gerado é "guardado" para que eu possa receber agora ou mais tarde, garantindo uma entrega segura e sem perdas. Já uma assinatura não durável (NonDurable) diz que quando eu estiver off-line o conteúdo gerado não precisa ser guardado, logo quando eu ficar on-line vou receber apenas o conteúdo gerado a partir desse momento.

JMS Bridge

Uma ponte JMS é um canal direto de comunicação criado entre duas filas JMS que é gerenciada pelo servidor JMS. Isso quer dizer que se tivermos uma fila A1 no servidor G1 e uma fila B1 no servidor G2 fazendo uma ponte, todas as mensagens escritas em A1 são recebidas em B1 automaticamente. Com isso podemos sempre tratar as filas como sendo locais e não precisamos fazer conexões externas em nossa aplicação, pois o servidor JMS é quem cuida da replicação.

Apesar de outros detalhes, acho que essas são as coisas mais importantes que devemos saber antes de iniciar a parte divertida, ou seja, a programação.

Objetivo

Comunicação entre duas aplicações Java rodando em AppServers distintos (Glassfish). Cada aplicação deve ter uma fila de leitura, onde recebe mensagens de uma aplicação remota, e também deve ter uma fila de escrita, na qual ela vai escrever mensagens para que uma aplicação remota receba.

Problema

Antes de conhecer as pontes do JMS, (JMS Bridge) eu pensava que cada aplicação deveria conectar-se no outro AppServer remoto para poder consumir e produzir os dados na suas respectivas filas. Depois um certo tempo batendo a cabeça tentando fazer essa solução sem obter sucesso, eu abri uma pergunta no GUJI, então apareceu essa solução rápida, limpa e certeira!


Solução

A solução foi usar o próprio servidor JMS embutido no Glassfish para fazer essa ponte, de modo que da perspectiva da aplicação, só existam filas locais. Com isso, a responsabilidade de replicação das filas fica com o AppServer, diminuindo consideravelmente a complexidade do problema.

Desenvolvendo a solução

Primeiramente devemos ter um ambiente de testes, com maquinas virtuais, maquinas real ou duas instâncias de um glassfish rodando na mesma maquina.

Ambiente de teste para esse post
- Duas maquinas. Uma com Windows 7 64 bits e um MAC OS 10.8.2
- Java7
- Glassfish 3.1.2.2 (build 5)

Vamos dar nomes aos bois. Vamos chamar um servidor de  Master e o outro de Slave. No servidor Master, vamos rodar uma aplicação chamada JMSMaster e no servidor Slave vamos rodar uma aplicação chamada JMSSlave.

Vamos acessar o console do servidor Master, http://localhost:4848. Vá em Resources > JMS Resources > Connection Factories. Vá em novo, e coloque os dados abaixo:

Pool Name: jms/ReplicationFactory
Resource Type: javax.jms.TopicConnectionFactory
Descriprion: Replication Factory
Status: enabled

Agora vá em Destination Resouce. Vá em novo e crie duas filas com os dados abaixo:

JNDI Name: jms/ReplicationProducer
Physical Destination Name: ReplicationProducer
Resource Type: javax.jms.Topic
Description: Replication Producer
Status: enabled

JNDI Name: jms/ReplicationConsumer
Physical Destination Name: ReplicationConsumer
Resource Type: javax.jms.Topic
Description: Replication Consumer
Status: enabled

Certo, o que precisamos saber aqui é que:
Pool Name e JNDI Name é o nome do recurso usado para acessa-lo dentro da nossa aplicação. 
Physical Destination Name é o nome usado para configurar a ponte JMS

Agora vamos acessar o console do servidor Slave. Faça o mesmo processo com os dados abaixo:

Connection Factory
Pool Name: jms/ReplicationFactory
Resource Type: javax.jms.TopicConnectionFactory
Descriprion: Replication Factory
Status: enabled

Topic

JNDI Name: jms/ReplicationProducer
Physical Destination Name: ReplicationConsumer
Resource Type: javax.jms.Topic
Description: Replication Producer
Status: enabled

JNDI Name: jms/ReplicationConsumer
Physical Destination Name: ReplicationProducer
Resource Type: javax.jms.Topic
Description: Replication Consumer
Status: enabled

Note que tudo é igual, exceto de que o  Physical Destination Name foi invertido:

Quando escrevermos em ReplicationProducer no Slave, a mensagem vai ser replicada para ReplicationConsumer  no Master.

Quando o Master escrever no ReplicationProducer a mensagem será replicada para ReplicationConsumer no Slave.

Parece meio complexo lendo agora, mas é bem simples.

Nesse momento já temos nossos dois servidor configurados para fazer um JMS Bridge, com isso é hora de programar!

JMSMaster 

Vamos criar um EJB com as seguintes classes:



@Stateless
public class JMSProducerBean implements Serializable {

    @Resource(name = "jms/ReplicationFactory")
    private ConnectionFactory cmf;
    @Resource(name = "jms/ReplicationProducer")
    private Destination destination;
    private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

    public String send() throws JMSException {
        TopicConnection connection = null;
        String date = dateFormat.format(new Date());
        Logger.getGlobal().log(Level.SEVERE, "#> {0} send new replication..", date);
        try {
            connection = ((TopicConnection) cmf.createConnection());
            TopicSession topicSession =
                    connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
            TopicPublisher topicPublisher = topicSession.createPublisher((Topic) destination);
            TextMessage message = topicSession.createTextMessage("Master Node: " + date + " - new replication");
            topicPublisher.publish(message);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

        return null;
    }
}

@MessageDriven(mappedName = "jms/ReplicationConsumer")
public class JMSConsumer implements MessageListener {

    @Override
    public void onMessage(Message message) {

        try {
            TextMessage textMessage = (TextMessage) message;
            Logger.getGlobal().log(Level.SEVERE, "#> receive: {0}", textMessage.getText());
        } catch (Exception e) {
            Logger.getGlobal().log(Level.SEVERE, "#error process receiver.. {0}", e.getMessage());
        }
    }
}

@Stateless
public class TimerSessionBean {

    @EJB
    private JMSProducerBean productorBean;

    @Schedule(persistent = false, second = "*/10", hour = "*", minute = "*", dayOfMonth = "*", year = "*")
    public void myTimer() {

        try {
            productorBean.send();
        } catch (Exception e) {
            Logger.getGlobal().log(Level.SEVERE, "### error send message: {0}", e.getMessage());
        }
    }
}

JMSSlave

@Stateless
public class JMSProducerBean implements Serializable {

    @Resource(name = "jms/ReplicationFactory")
    private ConnectionFactory cmf;
    @Resource(name = "jms/ReplicationProducer")
    private Destination destination;
    private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

    public String send() throws JMSException {
        TopicConnection connection = null;
        String date = dateFormat.format(new Date());
        Logger.getGlobal().log(Level.SEVERE, "#> {0} send new replication..", date);
        try {
            connection = ((TopicConnection) cmf.createConnection());
            TopicSession topicSession =
                    connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
            TopicPublisher topicPublisher = topicSession.createPublisher((Topic) destination);
            TextMessage message = topicSession.createTextMessage("Slave Node: " + date + " - new replication");
            topicPublisher.publish(message);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

        return null;
    }
}

@MessageDriven(mappedName = "jms/ReplicationConsumer")
public class JMSConsumer implements MessageListener {

    @Override
    public void onMessage(Message message) {

        try {
            TextMessage textMessage = (TextMessage) message;
            Logger.getGlobal().log(Level.SEVERE, "#> receive: {0}", textMessage.getText());
        } catch (Exception e) {
            Logger.getGlobal().log(Level.SEVERE, "#error process receiver.. {0}", e.getMessage());
        }
    }
}

@Stateless
public class TimerSessionBean {

    @EJB
    private JMSProducerBean productorBean;

    @Schedule(persistent = false, second = "*/10", hour = "*", minute = "*", dayOfMonth = "*", year = "*")
    public void myTimer() {

        try {
            productorBean.send();
        } catch (Exception e) {
            Logger.getGlobal().log(Level.SEVERE, "### error send message: {0}", e.getMessage());
        }
    }
}

Se você analisar os códigos, vai ver que a única mudança do JMSMaster para o JMSSlave, são as mensagens enviadas. Agora você pode fazer e deploy da aplicação nos seus respectivos servidores e ver a magica acontecer nos logs de cada um. Faças os testes com mensagens duráveis e não duráveis deixando um ou os dois servidores off-line de vez em quando, reiniciando, sem rede, esse tipo de coisa.

Mais uma vez volto a dizer que sou novo nessa tecnologia, e ainda não coloquei essa lógica em produção, pois preciso ainda ver a questão de persistência segura, desempenho e tudo mais. Então, se em algum momento escrevi alguma besteira, fiquem a vontade para me corrigir.

Espero que esse post possa ajudar alguém que esteja procurando uma solução parecida. 

Até a próxima! abrasss