quinta-feira, 24 de maio de 2007

Configurando um tópico JMS no JBoss

Para configurar um tópico de Java Message Service no JBoss (a versão que estou utilizando é 4.0.5.GA) basta editar o arquivo %JBOSS_HOME%/server/default/deploy/jms/jbossmq-destinations-service.xml iserindo uma tag XML como mostrada abaixo:
<mbean code="org.jboss.mq.server.jmx.Topic" 
name="jboss.mq.destination:service=Topic,name=nomeDoMeuTopico">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
<depends optional-attribute-name="SecurityManager">jboss.mq:service=SecurityManager</depends>
<attribute name="SecurityConf">
<security>
<role name="guest" read="true" write="true"/>
<role name="publisher" read="true" write="true" create="false"/>
<role name="durpublisher" read="true" write="true" create="true"/>
</security>
</attribute>
</mbean>

Não sei bem para que serve cada um dos parâmetros pois estou começando a estudar JMS agora.

Depois de configurar um tópico eu crio um servlet de exemplo que lê um parâmetro e o escreve no tópico como uma mensagem de texto. Observer que o lookup que fazemos no InitialContext para pegar o tópico utiliza o mesmo nome do tópico que configuramos adicionado com o prefixo "topic/":
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Context jndiContext = null;
TopicConnectionFactory topicConnectionFactory = null;
TopicConnection topicConnection = null;
TopicSession topicSession = null;
Topic topic = null;
TopicPublisher topicPublisher = null;

try
{
jndiContext = new InitialContext();

topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup("TopicConnectionFactory");
topic = (Topic) jndiContext.lookup("topic/nomeDoMeuTopico");

topicConnection = topicConnectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topicPublisher = topicSession.createPublisher(topic);

TextMessage mensagem = topicSession.createTextMessage();
mensagem.setText( request.getParameter("m") );
topicPublisher.publish(mensagem);
}
catch (NamingException e)
{
log.error("",e);
}
catch (JMSException e)
{
log.error("",e);
}
finally
{
if (topicPublisher != null) try { topicPublisher.close(); } catch (JMSException e) {}
if (topicConnection != null) try { topicConnection.close(); } catch (JMSException e) {}
}

response.getWriter().print("Mensagem publicada");
}

Para ler as mensagens enviadas ao tópico é preciso definir um listener, implementando a interface MessageListener, como é mostrado no código abaixo:
public class MensagemDeTextoListener implements MessageListener
{
public void onMessage(Message mensagem)
{
TextMessage msg = null;

try
{
if (mensagem instanceof TextMessage)
{
msg = (TextMessage) mensagem;
System.out.println("Leu mensagem: " + msg.getText());
}
else
{
System.out.println("Mensagem de tipo invalido: " + mensagem.getClass().getName());
}
}
catch (JMSException e)
{
log.error("",e);
}
}
}

Finalmente é preciso inscrever uma instancia deste listener como leitor do tópico. No exemplo abaixo é mostrado como inscrever um listener no tópico anteriormente definido, através do método contextInitializer de um ServletContextListener.
private TopicConnection topicConnection;

private void contextInitialized(ServletContextEvent evento)
{
Context jndiContext = null;
TopicConnectionFactory topicConnectionFactory = null;
TopicSession topicSession = null;
Topic topic = null;
TopicSubscriber topicSubscriber = null;
MensagemDeTextoListener topicListener = null;
TextMessage message = null;

try
{
jndiContext = new InitialContext();
topicConnectionFactory = (TopicConnectionFactory)
jndiContext.lookup("TopicConnectionFactory");
topic = (Topic) jndiContext.lookup("topic/mensagemTopic");

topicConnection = topicConnectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
topicSubscriber = topicSession.createSubscriber(topic);
topicListener = new MensagemDeTextoListener();
topicSubscriber.setMessageListener(topicListener);
topicConnection.start();
}
catch (NamingException e)
{
log.error("",e);
}
catch (JMSException e)
{
log.error("",e);
}
}
private void finalizarSubscriber()
{
try
{
topicConnection.close();
}
catch (JMSException e)
{
log.error("",e);
}
}

Pronto, aparentemente o exemplo funcionaria, mas não, quando inicializar seu JBoss, ocorrerá o seguinte erro: javax.jms.IllegalStateException in JBOSS: This method is not applicable inside the application server. See the J2EE spec, e.g. J2EE1.4 Section 6.6. Esse erro se dará exatamente na linha em que você adiciona o listener das mensagens ao tópico.

Ainda não entendi bem o porque isso acontece, mas achei a solução: Basta alterar o arquivo %JBOSS_HOME%/server/default/deploy/jms/jms-ds.xml adicionando o parâmetro Strict à connection-factory de jndi-name JmsXA. Veja abaixo:
<tx-connection-factory>
<jndi-name>JmsXA</jndi-name>
<xa-transaction/>
<rar-name>jms-ra.rar</rar-name>
<connection-definition>org.jboss.resource.adapter.jms.JmsConnectionFactory</connection-definition>
<config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Topic</config-property>
<config-property name="JmsProviderAdapterJNDI" type="java.lang.String">java:/DefaultJMSProvider</config-property>
<config-property name="Strict" type="java.lang.Boolean">false</config-property>
<max-pool-size>20</max-pool-size>
<security-domain-and-application>JmsXARealm</security-domain-and-application>
</tx-connection-factory>

Nenhum comentário:

Postar um comentário