Por que eu devo ler este artigo:Este artigo aborda a Java Message Service (JMS 1.1), uma API que permite a comunicação distribuída de aplicações Java de forma assíncrona, através de middlewares orientados a mensagens (MOM). Serão apresentados conceitos relativos a esta forma de comunicação e as principais funcionalidades da API.

O tema abordado é importante para arquitetos e desenvolvedores que necessitam projetar ou manter aplicações que utilizam mensageira como forma de integração e comunicação com sistemas distribuídos.

Guia do artigo

Parte I

Parte II

A comunicação entre diferentes sistemas de software sempre foi uma tarefa desafiadora e pouco trivial. Nos anos 70, com o surgimento das redes de computadores, a comunicação entre mainframes abriu caminho para a computação distribuída.

Com a popularização dos computadores pessoais na década de 80 e o consequente aumento no número de aplicações desenvolvidas em diferentes sistemas operacionais e linguagens de programação, a interconectividade entre aplicações passou a ser ainda mais complexa, envolvendo diferentes plataformas de hardware, software, protocolos e formatos de dados.

A Figura 1 ilustra este cenário de comunicação entre sistemas heterogêneos.

Comunicação entre sistemas heterogêneos
Figura 1. Comunicação entre sistemas heterogêneos

Com o surgimento da internet, a computação distribuída tornou-se ainda mais importante e estratégica para as empresas que procuram criar aplicações cada vez mais escaláveis e flexíveis.

Diante deste cenário, diferentes modelos arquiteturais, técnicas e tecnologias têm sido propostos para a cooperação e troca de informações entre sistemas distribuídos em rede. Alguns modelos arquiteturais incluem os já conhecidos modelos cliente/servidor, multicamadas, peer-to-peer, cluster e ainda computação em grade.

No que diz respeito à comunicação entre as aplicações, esta pode ser síncrona, baseada principalmente no protocolo de comunicação Remote Procedure Call (RPC), ou assíncrona. CORBA, Java RMI, Enterprise JavaBeans, Microsoft DCOM, Microsoft Serviced Components (COM+ .NET), e ainda os Web Services, são exemplos de tecnologias que permitem a comunicação síncrona entre aplicações, enquanto que a comunicação assíncrona pode ser realizada, principalmente, por meio da troca de mensagens intermediadas por middlewares orientados a mensagens.

Este artigo apresentará a API que permite a comunicação assíncrona através de middlewares orientados a mensagens em aplicações Java, e nas próximas seções, antes de abordarmos os detalhes da API, veremos algumas características desta forma de comunicação.

Nota: Embora a troca de mensagens através de middlewares orientados a mensagens seja a principal forma de comunicação assíncrona, a partir da versão 3.1 da especificação EJB também é possível realizar chamadas assíncronas a componentes EJB anotados com @Asynchronous.

Middlewares Orientados a Mensagens

Um middleware orientado a mensagens (MOM) pode ser brevemente descrito como uma categoria de software cujo objetivo principal é permitir a troca de mensagens entre aplicações distribuídas de maneira assíncrona, escalável, segura e confiável.

Um MOM age como um mediador (também chamado de broker) entre aplicações que enviam e recebem mensagens. Internamente estas mensagens são mantidas no que é chamado de destino (destination), uma espécie de repositório para o qual as mensagens são encaminhadas assim que são recebidas pelo MOM, e onde ficam até que sejam entregues à aplicação destinatária.

As aplicações que trocam mensagens através do MOM o fazem sempre por meio de destinos específicos; isto é, tanto a aplicação remetente quanto a aplicação destinatária conhecem previamente o destino para onde a mensagem será enviada e de onde será consumida, respectivamente.

Para exemplificar, pode-se fazer uma analogia deste processo com o processo de troca de correspondências através de caixas postais. Uma determinada agência dos correios poderia ser comparada a um MOM, as caixas postais mantidas pela agência poderiam ser comparadas aos destinos e as correspondências poderiam ser comparadas às mensagens trocadas.

Ao enviar uma correspondência a uma caixa postal, não é necessário conhecer o real endereço do destinatário que vai recebê-la, apenas os dados da caixa postal.

O destinatário, por sua vez, sabe onde encontrar as mensagens a ele endereçadas. Portanto, a presença do destinatário não é um requisito para a entrega da correspondência, uma vez que a correspondência ficará guardada na caixa postal até que o destinatário tenha disponibilidade para buscá-la.

Da mesma forma que a ausência do destinatário no momento da entrega ou a mudança de seu endereço físico real não afetam a troca de correspondências por caixa postal, as mudanças ou eventuais indisponibilidades nas aplicações no momento da entrega não afetam a troca de mensagens através de um MOM.

Esta característica permite que a comunicação aconteça com baixo acoplamento, pois uma aplicação não precisa saber detalhes a respeito de outras aplicações com as quais troca mensagens.

Nem mesmo a disponibilidade da aplicação destinatária no momento da entrega é necessária, e é justamente esta última característica que possibilita o modelo de comunicação assíncrona característico de um MOM.

A Figura 2 ilustra como seria a comunicação entre aplicações heterogêneas por meio de um MOM.

Comunicação entre sistemas heterogêneos intermediada por um MOM
Figura 2. Comunicação entre sistemas heterogêneos intermediada por um MOM

Quanto ao modo de troca de mensagens, os middlewares orientados a mensagens costumam se enquadrar em pelo menos um de dois modelos: Point-to-Point ou Publish/Subscribe.

O Modelo Point-to-Point

O modelo Point-to-Point (PTP) de troca de mensagens é baseado no conceito de filas, no qual cada mensagem é enviada por um produtor a uma fila específica, onde ela fica até que seja posteriormente entregue a um consumidor ou até que expire.

Este modelo garante que uma mensagem seja entregue a um único destinatário (relação “um para um” entre produtor e consumidor). Portanto, mesmo que a fila tenha mais de um consumidor ativo, apenas um receberá cada mensagem. A Figura 3 ilustra este conceito.

Modelo Point-to-Point de troca de mensagens
Figura 3. Modelo Point-to-Point de troca de mensagens

Neste modelo a mensagem é entregue ao consumidor mesmo que este não esteja ativo no momento do envio da mensagem. É esta característica que garante o comportamento assíncrono em uma troca de mensagens.

O Modelo Publish/Subscribe

O modelo Publish/Subscribe (Pub/Sub) de troca de mensagens é baseado no conceito de tópicos, no qual as mensagens são publicadas pelo produtor em um tópico e são entregues automaticamente a todos os consumidores que assinaram o tópico para recebimento de mensagens.

Portanto, ao contrário do modelo PTP, este modelo permite que uma mesma mensagem seja entregue a vários consumidores (relação “um para muitos” entre produtor e consumidor). Este conceito é ilustrado na Figura 4.

Modelo Publish/Subscribe de troca de mensagens
Figura 4. Modelo Publish/Subscribe de troca de mensagens

A garantia de entrega das mensagens aos consumidores dependerá do tipo de assinatura que fazem ao tópico, que pode ser durável ou não. Consumidores com uma assinatura durável têm a garantia de receber até mesmo as mensagens enviadas ao tópico durante um período em que estiveram inativos.

Já consumidores com assinaturas não duráveis somente recebem as mensagens enviadas ao tópico enquanto estiverem ativos.

Vantagens e desvantagens de Middlewares Orientados a Mensagem

Os middlewares orientados a mensagem trouxeram diversos benefícios para a comunicação distribuída entre aplicações corporativas, até então não disponíveis em estratégias síncronas como o RPC. Isto, no entanto, não significa que estratégias de mensageria sejam sempre a melhor opção no cenário de comunicação distribuída.

Ao longo dos anos, ambas as estratégias têm se mostrado adequadas, e o que define o sucesso na utilização de cada uma é a correta escolha para cada contexto de solução.

Com base neste dilema, a seguir são apresentados alguns dos benefícios oferecidos pela comunicação a partir de middlewares orientados a mensagem:

  • Comunicação assíncrona;
  • Garantia de entrega;
  • Persistência das mensagens;
  • Mecanismos de autenticação e autorização na troca de mensagens;
  • Modelo de entrega Point-to-Point ou Publish/Subscribe;
  • Comunicação robusta com tolerância a falhas causadas por conexões lentas, instáveis ou não confiáveis;
  • Roteamento e transformação de mensagens;
  • Suporte a diferentes tipos de protocolos de comunicação, como HTTP/HTTPS, multicast, SSL, TCP/IP e UDP;
  • Suporte a diferentes linguagens de programação.

Mesmo com todas estas vantagens, o modelo de troca de mensagens assíncronas pode apresentar algumas desvantagens, dependendo de certos contextos:

  • Costuma ser um modelo de desenvolvimento mais complexo;
  • Não garante o envio ordenado de mensagens, mas pode garantir a entrega ordenada (a prioridade das mensagens pode influenciar neste comportamento);
  • Inadequado para cenários que exigem o modelo síncrono de comunicação (request/response), dada sua inerente característica assíncrona.

A API Java Message Service

Durante anos os diferentes fornecedores de soluções de middlewares orientados a mensagem disponibilizaram APIs próprias para acesso a seus produtos, que estiveram disponíveis a algumas das principais linguagens de programação. Como consequência, uma aplicação que necessitasse utilizar diferentes produtos desta categoria precisava lidar com APIs incompatíveis. A Figura 5 ilustra este cenário.

Acesso a diferentes middlewares orientados a mensagens através de APIs proprietárias
Figura 5. Acesso a diferentes middlewares orientados a mensagens através de APIs proprietárias

A especificação Java Message Service (JMS) foi criada justamente para definir um conjunto de funcionalidades comuns à maioria dos produtos de mensageria e uma API padronizada que permitisse a aplicações Java utilizarem os serviços de middlewares orientados a mensagens, compatíveis com a API, de uma maneira uniforme, para a criação, envio e recebimento de mensagens.

Como toda especificação, a JMS não é um produto em si, mas sim um conjunto de interfaces e semântica que devem ser implementadas por todo produto que se propõe a ser compatível com a API.

Os produtos compatíveis com a API JMS são chamados de provedores JMS (ou JMS Providers), e atualmente há vários no mercado, como WebSphere MQ (IBM), SonicMQ (Progress Software), FioranoMQ (Fiorano), ActiveMQ (Apache), HornetQ (JBoss), Oracle AQ (Oracle), EMS (TIBCO), OpenJMS (OpenJMS Group) e RabbitMQ (SpringSource), para citar alguns.

A Figura 6 expõe como as aplicações Java podem acessar diferentes provedores JMS através da API JMS.

Nota: O servidor Open Message Queue (OpenMQ) é a implementação de referência da especificação JMS. Desde 2006 este é um projeto open-source e pode ser utilizado como uma aplicação standalone ou embutido em servidores de aplicação. Por sinal, o OpenMQ é o provedor JMS do servidor de aplicações GlassFish. Além de atender completamente a especificação JMS, este servidor, como muitos outros, ainda oferece recursos adicionais.
Acesso padronizado a provedores JMS através da API
Figura 6. Acesso padronizado a provedores JMS através da API

A primeira versão da especificação JMS (1.0) foi lançada em 1998 como resultado do trabalho conjunto entre Sun Microsystems e um grupo de empresas líderes no mercado de middlewares orientados a mensagem em busca de uma API de mensageria padronizada, de nível corporativo.

Durante o período de revisão da especificação, outras empresas, organizações educacionais e até mesmo o governo participaram ativamente com comentários. A ampla aceitação da especificação pelo mercado, bem como o fato de esta ter sido muito bem definida, são pontos fortes da API.

É importante salientar que, embora tenha sido definida em conjunto com várias empresas, a API não é uma mera junção das funcionalidades existentes nos produtos destas empresas, nem mesmo a simples intersecção de suas funcionalidades.

Ao invés disso, a API JMS define o conjunto de funcionalidades e conceitos que são essenciais para a implementação de aplicações sofisticadas baseadas em mensageria.

Em 2002 uma nova versão da especificação (1.1) foi proposta e aprovada através da JSR 914. Esta versão apresentava diversas melhorias em relação à versão anterior. Dentre estas melhorias estava a unificação dos conjuntos de APIs para o desenvolvimento nos modelos Point-to-Point e Publish/Subscribe, que até então já eram suportados, mas tinham hierarquias de interfaces e classes separadas (embora fossem parecidas).

Esta mudança, além de tornar o desenvolvimento mais fácil, permitiu que mensagens para filas e tópicos fossem tratadas em uma mesma transação, uma vez que poderiam ser criadas a partir de uma mesma sessão.

A plataforma J2EE passou a contar com a API JMS em sua versão 1.2. Esta versão exigia que os provedores J2EE compatíveis disponibilizassem apenas as interfaces da API, mas não exigia que estas fossem implementadas. Somente a partir da versão 1.3 da plataforma J2EE é que se tornou uma exigência a implementação completa, por parte dos provedores, da API JMS.

No momento em que este artigo é escrito, uma nova versão da especificação JMS (2.0) está sendo disponibilizada juntamente com o lançamento oficial da Java EE 7. Esta versão da JMS, proposta e aprovada através da JSR 343 em 2011, tem como objetivo atualizar a tecnologia frente às diversas evoluções da plataforma Java e Java EE desde que a versão 1.1 foi lançada, em 2002.

A principal característica da nova versão é o foco na facilidade de desenvolvimento. Para isso, foram promovidas importantes melhorias, como simplificações nas interfaces da API e o aproveitamento de recursos da plataforma Java e Java EE, como CDI (Context Dependency Injection), generics, annotations e autocloseable.

No entanto, a nova versão ainda é compatível com as versões anteriores para suportar aplicações já existentes. Para mais detalhes sobre estas novidades da versão 2.0, leia o artigo “JMS 2.0: A renovação do Java Message Service”, na edição 117 da Java Magazine.

Nota: Embora os modelos Point-to-Point e Publish/Subscribe sejam suportados pela API, estes são tratados como domínios distintos na especificação JMS. Isto é necessário pelo fato de que nem todo provedor JMS (produto de mercado standalone) atende aos dois modelos, mas mesmo assim pode atender e ser compatível com um ou outro domínio da especificação. Provedores Java EE, porém, precisam obrigatoriamente suportar os dois domínios.

Embora a versão 2.0 traga muitas melhorias à especificação JMS, no momento ela só está disponível na implementação de referência da plataforma Java EE 7, o GlassFish 4. A versão 1.1 ainda é a principal versão suportada pelos provedores JMS, e é a realidade dos projetos já existentes e que ainda serão desenvolvidos até que a plataforma Java EE 7 seja amplamente adotada pelo mercado.

Diante desta constatação, este artigo apresentará os pontos mais importantes da API e os principais conceitos relativos à versão 1.1 da JMS. Estes conceitos, relacionados diretamente às interfaces disponibilizadas pela API, estão ilustrados nos diagramas de atividades das Figuras 7 e 8. Estes diagramas abordam praticamente todos os passos necessários para que uma aplicação Java envie e receba mensagens através de um provedor JMS, respectivamente, independentemente do modelo de troca de mensagens adotado.

Típico fluxo de envio de mensagem com a API JMS
Figura 7. Típico fluxo de envio de mensagem com a API JMS
Típico fluxo de recebimento de mensagem com a API JMS
Figura 8. Típico fluxo de recebimento de mensagem com a API JMS

Conceitos como fábricas de conexõ ...

Quer ler esse conteúdo completo? Tenha acesso completo