Fundamentos do Serviço de Mensagens Java (JMS) e sua Aplicação

Visão geral do JMS

O Java Message Service (JMS) é uma API que padroniza a comunicação assíncrona entre aplicações Java. Diferente de chamadas remotas síncronas, o JMS atua como um intermediário que desacopla os participantes, permitindo que produtores e consumidores de mensagens não precisem se conhecer diretamente. As mensagens são enviadas a um destino intermediário e, a partir dali, encaminhadas aos interessados.

Por que utilizar mensageria assíncrona?

Em cenários distribuídos, muitas vezes uma aplicação precisa notificar outras sobre eventos, sem que haja dependência direta ou mesmo conhecimento prévio entre elas. Por exemplo, um serviço na Europa pode gerar atualizações que interessam a dezenas de sistemas espalhados pelo mundo. Se cada um precisasse ser contactado diretamente, a complexidade seria enorme. O JMS resolve esse problema fornecendo um ponto de troca comum, similar a um correio eletrônico corporativo.

Além disso, o modelo também é adequado para aplicações baseadas em eventos, como chats online, em que o servidor publica informações e todos os clientes conectados ao tópico as recebem automaticamente. Ao contrário de RMI, o destinatário não precisa estar ativo no momento do envio; ele receberá as mensagens quando se conectar, desde que a assinatura seja durável.

Vantagens do modelo JMS

  • Assincronismo: Os consumidores não precisam requisitar ativamente novas mensagens; o provedor as entrega assim que estão disponíveis.
  • Confiabilidade: O JMS garante que uma mensagem será entregue uma única vez, evitando duplicações acidentais.

Modelos de comunicação

O JMS define dois esquemas principais de troca de mensagens, que podem ser implementados isoladamente ou em conjunto pelo provedor.

Modelo ponto a ponto (filas)

Nesse modelo, a comunicação é baseada em uma fila. Cada mensagem produzida é entregue exclusivamente a um único consumidor. As principais características são:

  • Uma mensagem é consumida por apenas um receptor.
  • Produtores e consumidores não estão temporalmente acoplados; um pode enviar mesmo que o outro esteja offline, e a mensagem permanece na fila até ser lida.
  • Após o recebimento, o consumidor envia uma confirmação (acknowledgement).

Modelo publicação/assinatura (tópicos)

Aqui, as mensagens são enviadas a um tópico e entregues a todos os assinantes ativos. As propriedades incluem:

  • Uma mensagem pode ser recebida por vários assinantes ao mesmo tempo.
  • Normalmente, o assinante precisa estar ativo no momento da publicação para receber a mensagem. Contudo, é possível criar assinaturas duráveis que armazenam as mensagens até que o assinante se reconecte.

Mecanismos de recebimento

Há duas formas de obter as mensagens enviadas:

  • Síncrona: O consumidor invoca o método receive(), que bloqueia a execução até que uma mensagem chegue ou o tempo limite seja atingido.
  • Assíncrona: Um MessageListener é registrado no consumidor. Assim que a mensagem chega, o método onMessage() do listener é acionado automaticamente.

Principais componentes da API JMS

Os blocos básicos que compõem uma aplicação JMS são:

Objetos administrados

São pré‑configurados pelo administrador do sistema e disponibilizados via JNDI. Os dois objetos centrais são:

  • ConnectionFactory: Fábrica responsável por criar conexões com o provedor JMS.
  • Destination: Representa o destino (fila ou tópico) para onde as mensagens são enviadas ou de onde são recebidas.

Exemplo de obtenção desses objetos via JNDI:

QueueConnectionFactory fabricaConexao = (QueueConnectionFactory) contexto.lookup("minhaFabrica");
Queue filaEntrada = (Queue) contexto.lookup("Fila_Entrada");
Queue filaSaida = (Queue) contexto.lookup("Fila_Saida");

Conexão

Uma conexão representa um canal virtual com o provedor. Obtida a partir da fábrica:

Connection conexao = fabricaConexao.createConnection();
// ... uso ...
conexao.close();

Sessão

Uma sessão é um contexto single‑thread que cria produtores e consumidores. Ela também define o modo de confirmação. Exemplo:

Session sessao = conexao.createSession(false, Session.AUTO_ACKNOWLEDGE);

Produtor de mensagens

Criado a partir da sessão para enviar mensagens a um destino:

MessageProducer produtor = sessao.createProducer(destino);
produtor.send(mensagem);

Consumidor de mensagens

Para receber mensagens de um destino:

MessageConsumer consumidor = sessao.createConsumer(destino);

Listener de mensagens

Implementa a interface MessageListener e é registrado no consumidor para tratamento assíncrono:

consumidor.setMessageListener(new MessageListener() {
    public void onMessage(Message msg) {
        // processar a mensagem
    }
});

Estrutura de uma mensagem JMS

Toda mensagem JMS é composta por três partes:

  • Cabeçalho: Campos padronizados como JMSMessageID, JMSTimestamp, JMSDestination, etc., usados pelo provedor para roteamento e controle.
  • Propriedades: Atributos customizados que as aplicações podem definir para filtragem ou metadados adicionais.
  • Corpo: O conteúdo da mensagem, que pode ser de diferentes tipos:
    • TextMessage – dados textuais.
    • ObjectMessage – um objeto Java serializado.
    • BytesMessage – sequência binária.
    • StreamMessage – fluxo de tipos primitivos Java.
    • MapMessage – conjunto de pares chave/valor.

Exemplos práticos

A configuração de um ambiente JMS normalmente envolve um servidor de aplicações ou um broker independente. Um cenário comum é utilizar o ActiveMQ como provedor, acessado por meio de recursos registrados em JNDI no Tomcat. Um fluxo ponto a ponto resumido seria:

// Configuração de contexto JNDI e obtenção de recursos
Context contexto = new InitialContext();
QueueConnectionFactory fabrica = 
    (QueueConnectionFactory) contexto.lookup("ConnectionFactory");
Queue fila = (Queue) contexto.lookup("fila/pedidos");

// Criação de conexão, sessão e produtor
QueueConnection conexao = fabrica.createQueueConnection();
QueueSession sessao = conexao.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender emissor = sessao.createSender(fila);

// Envio de uma mensagem de texto
TextMessage mensagem = sessao.createTextMessage("Novo pedido #1020");
emissor.send(mensagem);

// Finalização
emissor.close();
sessao.close();
conexao.close();

No lado do consumidor, o recebimento assíncrono poderia ser implementado como:

QueueReceiver receptor = sessao.createReceiver(fila);
receptor.setMessageListener(msg -> {
    if (msg instanceof TextMessage) {
        TextMessage tm = (TextMessage) msg;
        System.out.println("Recebido: " + tm.getText());
    }
});
conexao.start(); // inicia a entrega de mensagens

Provedores de código aberto como ActiveMQ, HornetQ e OpenJMS implementam essas especificações e podem ser integrados facilmente com aplicações Java corporativas.

Tags: JMS Java Message Service Mensageria Assíncrona ActiveMQ JNDI

Publicado em 10-22 08:54