Back-End

9 abr, 2010

Usando ActiveMQ através de Ajax e REST

Publicidade

ActiveMQ é um broker de mensageiro que implementa a especificação java para mensageiro a JMS 1.1. Apesar de velhinha, a
API é muito simples e extremamente importante, porque ajuda muito a
desenvolver aplicações de alta disponibilidade e que podem escalar.

Você pode usar o ActiveMQ sozinho em modo standalone ou até mesmo
embarcado na sua aplicação, também é possível utilizar através do seu
container jee, como o JBoss por
exemplo, através do uso de um resource adapter.

Neste artigo, vou mostrar como consumir e produzir mensagem JMS sem ter que
usar a API JMS de forma direta. Isso é possível e pode ser feito de
diversas maneira no ActiveMQ. Vamos ver aqui como fazer isso
usando Javascript e Ajax,
e depois usando REST.

Por que acessar o ActiveMQ com Ajax/JS e REST?

Desta forma, aplicações web de diversas linguagens, como CGI,
Pearl e ASP, podem tirar proveito do uso de messageria. Esta é uma forma
bem fácil de realizar integração de sistemas, já que utiliza padrões
conhecidos, como Javascript, XML e o protocolo HTTP.

Ambientes corporativos podem tirar muito proveito destas
funcionalidades, já que é comum ter aplicações web escritas nas mais
diversas linguagens. Outro aspecto importante é que esta integração
possibilita um processamento em tempo de runtime realtime e, ao
mesmo tempo, todas as mensagens podem ser persistidas e você pode
consumi-las em Java, por exemplo, sem perder escalabilidade.

Mas não é perigoso, em termos de segurança, fazer isso?

Não é porque as configurações de transporte, localização do broker
ficam do lado do servidor. Para que seja possível utilizar estes
recursos é necessária uma aplicação Web(war) no lado do servidor, e é nesta
aplicação (no web.xml) que ficam as configurações.

Logo, não há como um cliente descobrir o endereço real do broker, você
ainda poderia criar um usuário e senha especial no ActiveMQ com acesso a
alguns recursos apenas. O cliente especifica o nome das filas e dos tópicos na hora de enviar e receber as mensagens, mas tudo isso pode ser
controlado do lado do servidor.

Configurando o web.xml para suporte a Ajax e REST

Vamos começar vendo as configurações que devem ser feitas no lado do
server. Então confira o arquivo web.xml abaixo.

<?xml version="1.0" encoding="UTF-8"?>

<web-app id="WebApp_activemq_ajax_rest" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
<servlet>
<servlet-name>AjaxServlet</servlet-name>
<servlet-class>org.apache.activemq.web.AjaxServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet>
<servlet-name>MessageServlet</servlet-name>
<servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>destinationOptions</param-name>
<param-value>consumer.prefetchSize=1</param-value>
</init-param>
</servlet>

<context-param>
<param-name>org.apache.activemq.brokerURL</param-name>
<param-value>tcp://127.0.0.1:61616</param-value>
</context-param>

<servlet-mapping>
<servlet-name>AjaxServlet</servlet-name>
<url-pattern>/amq/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MessageServlet</servlet-name>
<url-pattern>/message/*</url-pattern>
</servlet-mapping>


<filter>
<filter-name>session</filter-name>
<filter-class>org.apache.activemq.web.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>session</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

Perceba que existe o context param chamado
org.apache.activemq.brokerURL, aqui você indica a configuração default
de transporte através de um URL que indica como conectar no broker.
Estou usando o ActiveMQ standalone, basta baixar da internet e
subir o broker, estou usando url, transporte e porta padrão.

Depois, existem dois servlets que estão sendo registrados, são eles:
AjaxServlet e MessageServlet. O primeiro serve para você poder acessar o
ActiveMQ usando Ajax/Javascript e o segundo é para integração com REST.
Os dois tem os seus mapeamentos configurados no XML.

Enviando uma mensagem com Ajax

Para isso, precisamos importar os arquivos javascript do ActiveMQ. Você
vai precisar dos arquivos abaixo:

Confira página jsp abaixo, mais especificamente as funções
javascript que fiz para enviar e receber mensagens usando a api js do
ActiveMQ.

<script type="text/javascript" src="javascript/amq.js"></script>
<script type="text/javascript">amq.uri='/activemq-web-ajax-rest-1.0-SNAPSHOT/amq';</script>
<script type="text/javascript"></p><p> function sendActiveMQQueueMessage(msg){
try{
var xmlMessage = "<message><text>" + msg + "</text></message>";
amq.sendMessage("channel://js_ajax_queue",xmlMessage);
document.getElementById("divResposnse").innerHTML = "ActiveMQ Ajax Client: message[" + msg + "] sent to *js_ajax_queue* ";
}catch(e){
document.getElementById("divResposnse").innerHTML = "Error sending message to ActiveMQ. Error: " + e;
}
}
</script>

O parâmetro amq.uri indica o caminho completo para o seu servlet ajax,
sendo que o que vem primeiro é o context root da sua aplicação; na
maioria das vezes vai ser o próprio nome do seu war. Esta função
javascript só recebe a mensagem porque dentro da função estou definindo para qual fila quero mandar a mensagem, isto é feito com:
channel://js_ajax_queue, sendo que você poderia dizer
topic://nome_topico. Usei channel porque quero que a mensagem vá para
um fila.

Agora, confira abaixo como receber mensagem com ajax. Isso é feito no
mesmo “estilo” de um message listener como você faria em java ou dentro
de um MDB. Confira o código javacript abaixo.

<script type="text/javascript" src="javascript/amq.js"></script>
<script type="text/javascript">amq.uri='/activemq-web-ajax-rest-1.0-SNAPSHOT/amq';</script>
<script type="text/javascript">

var messageHandler =
{
rcvMessage: function(message)
{
document.getElementById("divResposnseHandle").innerHTML = "Message arrived: <b>" + message.textContent + "</b>";
}
};
amq.addListener("msghID","channel://js_ajax_queue",messageHandler.rcvMessage); </p><p></script>

Este método é um estilo call back: quando a mensagem chegar, a função js
chamada rcvMessage será invocada. Por padrão há um timeout de 10 segundos para
que isso ocorra, do contrario, na próxima vez que o script rodar, ele
tenta de novo, você pode fazer um poll forçado se quiser usando por
exemplo: amq._startPolling()

Enviando mensagem com REST

Basicamente vamos usar o objeto XMLHttpRequest para realizar esta
tarefa. Quando acessamos o mesmo URL, mas com métodos diferentes (HTTP),
estaremos efetuando operações diferentes. Em resumo, POST para mandar
mensagens e GET para receber.

O URL é montando assim:

http://localhost:8070/activemq-web-ajax-rest-1.0-SNAPSHOT/message/FilaX?type=queue&body=MinhaMSG

Primeiro, em azul, é o endereço do servidor e porta. Depois, em rosa, é o
context root da aplicação web que está expondo o ActiveMQ. Em cinza, o
mapeamento do servlet REST. FilaX, que está em vermelho, é o nome da destination que
pode ser uma fila ou tópico; neste caso é um fila, porque em preto está
escrito type = queue. Por fim, também em azul, o parametro body recebe a
mensagem a ser enviada.

Então, agora que você entendeu o padrão de URL, vamos ver o código
javascript para enviar mensagem usando REST.

<script language="Javascript">

function sendRESTMessage(queueName,message) {
try{
var xmlHttpReq = false;
var self = this;

if (window.XMLHttpRequest) {
self.xmlHttpReq = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}

var xmlMessage = "<xml><message>" + message + "</message></xml>";
var httpURL = "message/" + queueName + "?type=queue&body=" + xmlMessage;

self.xmlHttpReq.open('POST', httpURL, true);
self.xmlHttpReq.setRequestHeader("Content-Type", "text/xml")

self.xmlHttpReq.onreadystatechange = function() {
if(self.xmlHttpReq.readyState==4){
alert("Ajax call back " + self.xmlHttpReq.responseText );
}
}
self.xmlHttpReq.send(null);
alert("Message: " + message + " sent to queue: " + queueName);

}catch(e){
alert("Error: " + e);
}
}

A função js que criei, chamada sendRESTMessage, recebe o nome da fila e a
mensagem. Para chamar a função e mandar a mensagem seria algo do tipo:
sendRESTMessage(‘rest_queue’,txtMessage.value);

Para receber mensagens é bem parecido, só que usamos GET ao invés de
POST, uma função js de call back é usada para receber as mensagens,
confira o código abaixo.

<script language="Javascript"></p><p>    function receiveRESTMessage(queueName) {
try{
var xmlHttpReq = false;
var self = this;

if (window.XMLHttpRequest) {
self.xmlHttpReq = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}

var httpURL = "message/" + queueName + "?type=queue";

self.xmlHttpReq.open('GET', httpURL, true);
self.xmlHttpReq.setRequestHeader("Content-Type", "text/xml")

self.xmlHttpReq.onreadystatechange = function() {
if(self.xmlHttpReq.readyState==4){
alert("Message Arrived: " + self.xmlHttpReq.responseText );
}
}
self.xmlHttpReq.send(null);

}catch(e){
alert("Error: " + e);
}
}

</script>

A função receiveRESTMessage recebe o nome da fila na qual você quer pegar
mensagens e, quando a mensagem chega, um alert com o conteúdo aparece na
tela, você pode infocar a função se quiser fazer um poll.

No servidor, você vai precisar de algumas dependências para que os
servlets de ajax e rest funcione, confira as dependências do maven abaixo.

<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-web</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
</dependency>
</dependencies>

Se você quiser o fonte completo, pode baixar do meu repositório do subversion aqui.

Abraços e até a próxima.