Rapidinha: enviando mensagens JGroups no viewAccepted()

August 21st, 2011 by rafaelliu No comments »

No JGroups, enviar mensagens no viewAccepted() é útil quando novos nós precisam passar ao cluster alguma informação ao entrarem no cluster.

No entanto nós do cluster apenas aceitam mensagens de nós que façam parte da sua view, o que pode trazer problemas. Imagine um cluster com view V1={A,B,C} e um novo nó D que entra no cluster. Quando a nova view V2={A,B,C,D} é instalada no nó D, os outros nós A, B e/ou C podem ainda ter a view V1 (views são instaladas sem ordem específica). Isso quer dizer que eles  vão descartar mensagens enviadas por D, já que ele não faz parte do cluster (porque a view V2 ainda não foi instalada).

Para isso é possível usar o protocolo FLUSH [1] (já existe uma configuração padrão no JGroups chamada flush-udp.xml) que resolve o problema de outros nós não terem a mesma view do nó que enviou a mensagem. No entanto é preciso subir outra thread ao enviar mensagens com o FLUSH no view Accepted() [2].

Referências
[1] http://www.jgroups.org/manual-3.x/html/user-channel.html#Receiver
[2] http://community.jboss.org/wiki/SendingOfMessagesInViewAcceptedCallback

Criando um Web Service com WS-Addressing (Apache CXF)

August 16th, 2011 by rafaelliu No comments »

Apache CXF é o projeto da ASF para criação de Web Services utilizando JAX-WS e JAX-RS. Entre outros padrões WS-*, ele dá suporte a WS-Addressing e WS-Security.

O WS-Addressing busca independência da camada de transporte enviando informações de transporte no envelope SOAP. Embora na maioria dos casos seja utilizado HTTP que já supre as necessidades mais básicas como endereços de origem e destino, Web Services podem ser utilizado sobre outros protocolos. Imagine ter acesso a um envelope SOAP solto. Da onde ele veio? Pra onde vai? O servidor já recebeu esse envelope? Sem o WS-Addressing essas informações podem ser descobertas apenas analisando a camada de transporte. E se o protocolo de transporte utilizado não armazenar a origem do consumidor? Como o servidor irá eventualmente responder uma requisição? Com o WS-Addressing é possível utilizar protocolos muito mais simples que, a princípio são “incompletos”.

O WS-Security é o padrão para autenticação, assinatura e encriptação de envelopes SOAP. É um padrão bastante interessante, amplo e flexível. Trabalha com o conceito de tokens. Token são utilizados para a autenticação de usuários e assinatura de dados. O interessante desse padrão é que a utilização de tokens torna o padrão extensível, sendo possível implementar tokens proprietários. O problema é que tanto o cliente quanto o servidor precisam entender a forma do token, portanto a especificação definiu alguns tokens padrão como Username (usuário/senha), X.509, SAML e Kerberos. A utilização de tokens permite a encriptação de mensagens (ou trechos de mensagens) e sua utilização junto com um algoritmo de hash (que também é configurável) permite a assinatura de mensagens (ou trechos de mensagens).

Criando o Web Service

O primeiro passo é criar um Web Service, o que é ridículo muito simples utilizando JAX-WS:

@WebService
public class MeuServico {
 
	@WebMethod
	public void falar() {
		System.out.println("Blah");
	}
 
}

E é isso. O web service está pronto, agora precisamos publicar ele. De novo, a especificação torna isso muito fácil:

Endpoint.publish("http://localhost:9000/MeuServico", new MeuServico());

Basta acessar http://localhost:9000/MeuServico?wsdl e ver o WSDL gerado. Utilizando o SoapUI é possível testar nosso web service.

Utilizando Spring com o CXF

Embora tenha sido muito simples publicar nosso web service utilizando a classe Endpoint, é possível também utilizar o Spring para isso. A utilização do Spring trás algumas vantagens como poder mapear nosso web service no web.xml de uma webapp, poder centralizar as configurações em um XML e poder definir beans que podem ser reutilizados e injetados de forma declarativa. O CXF implementa os padrões WS-* através de interceptors. Vamos utilizar dois interceptors, um para WS-Addressing e outro para WS-Security, que vamos definir no XML do Spring:

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jaxws="http://cxf.apache.org/jaxws"
   xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
 
   <import resource="classpath:META-INF/cxf/cxf.xml" />
   <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
   <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />
 
   <jaxws:endpoint name="testService"
      implementor="net.rafaelliu.services.TestImpl"
      address="http://localhost:9000/test">
 
      <!-- Enable WS-Addressing -->
      <jaxws:features>
         <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing" />
      </jaxws:features>
 
      <!-- Optional, enables logging for inbound -->
      <jaxws:inInterceptors>
         <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
      </jaxws:inInterceptors>
 
      <!-- Optional, enables logging for outbound -->
      <jaxws:outInterceptors>
         <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
      </jaxws:outInterceptors>
   </jaxws:endpoint>
 
 
   <jaxws:client name="testClient"
      address="http://localhost:9000/test"
      serviceClass="net.rafaelliu.services.Test">
 
      <jaxws:features>
         <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing" />
      </jaxws:features>
 
   </jaxws:client>
 
</beans>

Agora o Endpoint pegaremos do ApplicationContext do Spring:

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("./cxf-config.xml");
Endpoint ep = (Endpoint) appContext.getBean("testService");
ep.publish();

Nesse ponto temos um serviço utilizando WS-Addressing no ar. Vamos usar um proxy client do CXF para invocar o web service:

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("./cxf-config.xml");
Test client = (Test) appContext.getBean("testClient");
client.doTest();

Como ficou tudo transparente, é mais do que certo o leitor duvidar que realmente algo está acontecendo. Para tirar a dúvida é possível utilizar o SoapUI mencionado, mas fica como dever de casa descobrir como ele funciona com WS-Addressing :p (quer uma dica?)

Conclusão

O CXF está entre os frameworks que mais acho legais de mexer. Muito tem arquitetado, fácil de usar, extensível e just works. Ele não é o framework de web services mais completo em termos de suporte aos padrões WS-*, mas definitivamente vale uma olhada. Foi possível ver como utilizar o CXF para publicar e consumir web services e como fazer a configuração via Spring. O padrão WS-Addressing não é assim tão comum (ou mesmo “útil”), mas futuramente quero mostrar como usar WS-Security no CXF também, o que é um pouco mais complicado e tornaria o post massante.

Drools no TDC2011

July 7th, 2011 by rafaelliu No comments »

Começou hoje o TDC 2011, evento promovido pela Globalcode com várias áreas de interesse. As palestras irão até domingo e estarei lá sexta, falando sobre JBoss Drools na trilha SOA. Apareçam lá se quiserem bater um papo!

Weld no Tomcat 7

May 29th, 2011 by rafaelliu 10 comments »

Havia escrito há muito tempo atrás como fazer o deploy de uma aplicação Seam no Tomcat. Daquele tempo para cá muita coisa mudou: o Seam junto com outros frameworks de IoC gerou a JSR-299 de Contexts and Dependency Injection (CDI), a JBoss criou o Weld (implementação de referência CDI), o Seam 3 foi lançado baseado na nova especificação.

Com a padronização numa JSR, agora implementações CDI são obrigadas a suportar uma variedade maior de ambientes de forma vendor neutral. O Weld faz isso e entre os ambientes suportados está o Tomcat. Portanto não há necessidade de configurações extras e gambiarras! O próprio projeto Weld provê archetypes maven com várias configuração, para criar uma aplicação para rodar em um servlet container:

mvn archetype:generate -DarchetypeArtifactId=weld-jsf-servlet-minimal \
                       -DarchetypeGroupId=org.jboss.weld.archetypes \
                       -DarchetypeVersion=1.0.0.Beta1 \
                       -DarchetypeRepository=central

E um projeto pronto para ser executado no Tomcat/Jetty será criado. O problema é que, devido a um bug, no Tomcat 7 pode ser que você veja a seguinte exceção:

SEVERE: Exception sending context initialized event to listener instance of class org.jboss.weld.environment.servlet.Listener
java.lang.NoClassDefFoundError: org/apache/AnnotationProcessor
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2820)
	at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1143)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1638)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2820)
	at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1143)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1638)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
	at org.jboss.weld.environment.tomcat.Tomcat6Container.initialize(Tomcat6Container.java:47)
	at org.jboss.weld.environment.servlet.Listener.contextInitialized(Listener.java:184)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4544)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5016)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1035)
	at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:738)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1035)
	at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:289)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
	at org.apache.catalina.core.StandardService.startInternal(StandardService.java:442)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
	at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:674)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:140)
	at org.apache.catalina.startup.Catalina.start(Catalina.java:596)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:303)
	at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:431)
Caused by: java.lang.ClassNotFoundException: org.apache.AnnotationProcessor
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1671)
	at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
	... 36 more

O problema é que o Weld identifica erroneamente o Tomcat 7 como Tomcat 6. Para resolver o problema podemos definir explicitamente que o container que estamos usando é o Tomcat 7. Isso é feito criando-se um arquivo META-INF/services/org.jboss.weld.environment.Container no classpath com o conteúdo:

org.jboss.weld.environment.tomcat7.Tomcat7Container

Note que estamos definindo na mão o container! Ou seja, abrimos mão da portabilidade. Vamos esperar o bug ser corrigido, mas até lá é isso!

Referência: https://issues.jboss.org/browse/WELD-879

Drools 5

February 10th, 2011 by rafaelliu No comments »

Acaba de sair na Java Magazine edição 88 um artigo que escrevi sobre o Drools 5. Quem tiver interesse no assunto pode ter uma prévia no site da DevMedia. O código de exemplo é publicamente acessível, lembrando que é preciso ter o Maven 3 instalado.

http://www.devmedia.com.br/articles/viewcomp.asp?comp=19367

Virtualization Tour + JBoss Roadshow 2010

August 2nd, 2010 by rafaelliu No comments »

Começará em Brasília, o Roadshow desse ano, que passará também por Belo Horizonte, Fortaleza, Curitiba, Porto Alegre, Rio de Janeiro e São Paulo. O evento acontecerá no dia 3 de agosto, no Hotel Mercure, a partir das 8:30.

Participe e descubra como a Red Hat pode ajudar a sua empresa crescer de forma sustentável. Conheça as melhores práticas para otimizar o seu orçamento de TI e aproveitar ao máximo seus investimentos.
Além de sessões com nossos especialistas sobre Virtualização, Cloud Computing e Middleware, diversos clientes apresentarão casos de sucesso locais.

Mais informações no site: www.redhatroadshow.com.br

Configurando o JBoss Messaging em cluster

May 30th, 2010 by rafaelliu 2 comments »

Uma das grandes evoluções do JBoss Messaging sobre o JBossMQ (seu predecessor) foi a robustez da solução em cluster.

Introdução (ou como era com o JBossMQ)

Antigamente, o JBossMQ já era clusterizável (ouch.. neologismo..) mas conseguia isso usando o HA Singleton do JBoss. O JBoss quando configurado em cluster pode ser serviços deployados (ouch de novo..) como singletons através do cluster. Funciona assim: você diz ao JBoss que aquele serviço é um HA Singleton e o JBoss vai garantir a você que em seu cluster haverá sempre um, e apenas um, serviço ativo no cluster (enquanto restar nós no cluster, claro). Essa é uma funcionalidade muito interessante pois permite serviços cluster unaware serem clusterizáveis.

O balanceamento de carga pudia ser feito através dos consumidores: implementando um ReceiverImpl consumidores iriam receber as mensagens a la round robin. Melhor que nada.

JBoss Messaging

O JBoss Messaging é a implementação padrão do JBoss 5. Com o JBoss Messaging a infraestrutura de clusterização melhorou Muito. Podemos agora criar um cluster ativo/ativo, que quando um nó falhar o outro se encarregará de enfileirar suas mensagens. E ainda mais, temos balanceamento de carga de fato: cada conexão aberta usará uma fila diferente, round robin way.

Montando o cluster

Configurar o JBoss Messaging em cluster é extremamente fácil:

  • Ative a clusterização nos arquivos de configuração do JBoss Messaging e configure um banco de dados compartilhado;
  • User um ConnectionFactory e uma Queue/Topic marcados como clusterizados.

Bom, vamos aos bits. As versões usadas seguem:

  • JBoss 5.1.0.GA
  • JBoss Messaging 1.4.3.GA (a que vem por padrão)
Configurando o JBoss Messaging

Para fazer o failover, o JBoss Messaging precisa usar um banco de dados compartilhado. Copie de $JBOSS_HOME/examples/config o XML apropriado sobrescrevendo o $JBOSS_HOME/server/all/deploy/messaging/hsqldb-persistence-service.xml (lembre de configurar/iniciar o banco de dados!). Vou usar o MySQL:

rm $JBOSS_HOME/server/all/deploy/messaging/hsqldb-persistence-service.xml
cp $JBOSS_HOME/examples/config/mysql-persistence-service.xml $JBOSS_HOME/server/all/deploy/messaging

Ative a clusterização:

<attribute name="Clustered">true</attribute>
<attribute name="FailoverOnNodeLeave">true</attribute>

Aponte para apontar para o datasource do seu banco:

<attribute name="DataSource">java:/DefaultDS</attribute>
Criando as filas e factories

Vamos usar o ClusteredConnectionFactory, que é uma factory padrão do JBoss que vá vem configurada em cluster, se quiser ver como é feito: $JBOSS_HOME/server/all/deploy/messaging/connection-factories-service.xml.

Só precisamos criar nossas filas clusterizadas. Adicionem um arquivo $JBOSS_HOME/server/all/deploy/clustered-destinations-service.xml com o seguinte conteúdo:

<?xml version="1.0" encoding="UTF-8"?>
 
<server>
 
   <mbean code="org.jboss.jms.server.destination.QueueService"
      name="jboss.messaging.destination:service=Queue,name=clusteredQueue"
      xmbean-dd="xmdesc/Queue-xmbean.xml">
      <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
      <depends>jboss.messaging:service=PostOffice</depends>
      <attribute name="Clustered">true</attribute>
   </mbean>
 
</server>
Criando os profiles

Se queremos criar um cluster vamos precisar de dois nós! Então copie o profile all e crie dois outros profiles:

cp -r all jms1
cp -r all jms2

Testando

Vamos subir os dois nós usando o serviço de ServiceBinding do JBoss para não haver conflitos de porta ou ip:

./run.sh -c jms1 -Djboss.service.binding.set=ports-01 -Djboss.messaging.ServerPeerID=1
./run.sh -c jms2 -Djboss.service.binding.set=ports-02 -Djboss.messaging.ServerPeerID=2

Agora vamos usar uns programinhas que fiz para produzir e consumir mensagens, ele é bem simples e intuitivo e é empacotado junto com os fontes. Vamos usá-lo para testar nosso cluster.

Iniciamos um consumer para cada nó do cluster:

java -cp JmsSample.jar:$JBOSS_HOME/client/jbossall-client.jar net.rafaelliu.jms.JmsConsumer 127.0.0.1:1100 ClusteredConnectionFactory queue/clusteredQueue
java -cp JmsSample.jar:$JBOSS_HOME/client/jbossall-client.jar net.rafaelliu.jms.JmsConsumer 127.0.0.1:1200 ClusteredConnectionFactory queue/clusteredQueue

E iniciar o nosso producer:

java -cp JmsSample.jar:$JBOSS_HOME/client/jbossall-client.jar net.rafaelliu.jms.JmsProducer 127.0.0.1:1100,127.0.0.1:1200 ClusteredConnectionFactory queue/clusteredQueue 10 1 "Mensagem de teste"

Viram que as mensagens foram balanceadas? Agora podemos tentar o seguinte:

  • fechar um dos consumers: as mensagens são enviadas para o outro consumer;
  • fechar um dos consumers enquanto estão sendo enviadas mensagens: o outro consumer assume a partir da última mensagem;
  • pausar e despausar um consumer: as mensagens vão sendo enfileiradas e quando despausamos, elas são todas consumidas;
  • pausar um consumer e depois fechar: as mensagens vão sendo enfileiradas e quando fechamos, elas são repassadas para o outro nó.

Dá pra brincar muito!

Futuro

Atualmente, sendo empacotado junto com o último Milestone do JBoss 6, o serviço de mensageria foi trocado pelo HornetQ. Isso significa ainda outra evolução na área do MOM para a JBoss. Vamos ver se quando for para GA não escrevo sobre ele. Até a próxima o/

PS: para produção dêem uma olhada nos parâmetros DefaultPreserveOrdering e SuckerPassword.

Binário: http://github.com/downloads/rafaelliu/rafaelliu.net/JmsSample.jar

Abertas inscrições para o JBoss In Bossa 2010

April 10th, 2010 by rafaelliu No comments »

Ainda que tarde, vale a pena anunciar.

Foram abertas as incrições para o JBoss In Bossa 2010 que acontecerá em São Paulo dias 7 e 8 de maio. O evento é o maior da América Latina relacionado a JBoss e contará com a presença de palestrantes internacionais como o Peter Muir (project lead do Seam e do Weld), Benjamin Mestrallet (CEO da eXo Platform) e Mauricio Salatino (CTO da PlugTree).

Veja alguns dos principais assuntos previstos na agenda:

  • O novo JBoss 6
  • Tuning JBoss
  • Java Persistence API
  • Seam
  • Weld
  • Contexts and Dependency Injection
  • Java ServerFaces 2.0
  • Teiid
  • Drools
  • Jopr
  • E a lista continua..

É uma ótima oportunidade para conhecer pessoalmente alguns nomes de peso na comunidade e fazer networking. Se inscreva!

Acompanhe também o evento via @jbossinbossa.

java.lang.IllegalArgumentException: LifecycleId already added for id: SEAM_PORTLET.

March 7th, 2010 by rafaelliu No comments »

It’s almost a tradition now in this blog posting fixes to common Exceptions, so here is another one ;)

This one goes to people using JBoss Portlet Bridge 2.0.0.CR1 (don’t know about other versions). The stacktrace is the following:

 Caused by: java.lang.IllegalArgumentException: LifecycleId already added for id: SEAM_PORTLET.
         at com.sun.faces.lifecycle.LifecycleFactoryImpl.addLifecycle(LifecycleFactoryImpl.java:199)
         at org.jboss.portletbridge.lifecycle.PortletLifecycleFactory.addLifecycle(PortletLifecycleFactory.java:60)
         at org.jboss.portletbridge.lifecycle.PortletLifecycleFactory.(PortletLifecycleFactory.java:48)
         ... 195 more

The long history:

Portlet Bridge uses a custom JSF Lifecycle for handling behaviour specific to portlets. It does so by means of a embedded faces-config.xml in it’s jar. The problem is that it tries to add this SEAM_PORTLET even if it already exists. That means that having 2 Portlet Bridges JAR in your application’s classpath will cause confusion.

That raises a not so trivial scenario. Even tho you have only one Portlet Bridge JAR, depending on how your JBoss classloading configuration is set, this Exception can occur. That’s because JBoss uses temporary folders to unpack Javar Archives in it’s <JBOSS_HOME>/server/default/tmp (or whatever configuration you are using). These folders have unique auto-generated names, so between startups your WAR may be unpacked to a folder in tmp while there’s already an older version there with a different name.

The short history:

Delete your JBoss configuration’s tmp and work (don’t worry, it’s safe) folder or search for some duplicated portletbridge-impl.jar.

Reference: JIRA

WordPress Stats não mostra gráfico de visitas

February 27th, 2010 by rafaelliu 1 comment »

Recentemente atualizei a versão do WordPress e também mudei o host (estou usando o kinghost.net). Depois de ter o blog todo migrado percebi que o plugin WordPress Stats não mostrava mais o gráfico de visitas. Achei que fosse algum problema no plugin e resolvi esperar por uma atualização. Após mais de um mês sem fix resolvi procurar a solução.

Minha versão do WordPress é a 2.9.2 a versão do WordPress Stats é a 1.6.2. Encontrei a solução WordPress Forum, o problema é devido a permissões de acesso definidas pelo arquivo .htaccess do plugin (arquivo de configuração do Apache HTTPD) e depende da configuração Apache de cada host (ou seja, nem todos vão passar por isso).

A solução mais simples é remover o .htaccess do plugin localizado em ftp://<HOST>/www/wp-content/plugins/stats.