jboss.management.local:* already registered

A exceção é da forma:

javax.management.InstanceAlreadyExistsException: jboss.management.local: already registered.
        at org.jboss.mx.server.registry.BasicMBeanRegistry.add(BasicMBeanRegistry.java:756)
        at org.jboss.mx.server.registry.BasicMBeanRegistry.registerMBean(BasicMBeanRegistry.java:233)
        at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:157)
        at org.jboss.mx.server.Invocation.dispatch(Invocation.java:96)
        at org.jboss.mx.interceptor.AbstractInterceptor.invoke(AbstractInterceptor.java:138)
        at org.jboss.mx.server.Invocation.invoke(Invocation.java:90)
        at org.jboss.mx.interceptor.ModelMBeanOperationInterceptor.invoke(ModelMBeanOperationInterceptor.java:140)
        at org.jboss.mx.server.Invocation.invoke(Invocation.java:90)

O JBoss, atendendo à JSR-77, registra sobre o domínio jboss.management.local todos os pacotes deployados. O problema ocorre quando dois pacotes com o mesmo nome estão presentes. A princípio isso nunca aconteceria, os OS não permitem isso. Mas o JBoss tem a capacidade de fazer deploy de pastas aninhadas, além de poder ser configurado para possuir várias pastas de deploy.

Então a solução mais simples seria alterar o nome dos pacotes (pode ser também uma Persistence Unit ou qualquer outro deployable) conflitantes. Se essa não for uma opção, é possível também desabilitar a implementação JSR-77 do JBoss, que não deve trazer maiores consequências (a não ser que esses MBeans estejam sendo explicitamente usados).

Para tanto basta remover ou desabilitar (adicionando a extensão .rej) o arquivo $JBOSS_CONF/deployers/jsr77-deployers-jboss-beans.xml

Adicionando segurança ao JBoss Messaging

Nesse post quero falar de como prover segurança na troca de mensagens utilizando JBoss Messaging. Segurança é um conceito bem amplo. Em termos da mensagens, o que queremos é:

  • autenticidade: garantia de que a mensagem é de quem achamos que é;
  • integridade: garantia que a mensagem é a que foi escrita originalmente;
  • confidencialidade: garantia que a mensagem não foi lida por mais ninguém.

Também não queremos que qualquer consumidor leia da fila. Portanto, além de todas essas garantias, vamos adicionar autenticação e autorização para restringir o acesso ao nosso provedor JMS.

Como alguns já devem ter notado, grande parte dos nossos requisitos são atendidos utilizado-se Certificados Digitais. E é justamente o que vamos usar.

Utilizando um canal SSL para envio de mensagens

O JBoss Messaging (JBM) utiliza um subprojeto da JBoss chamado JBoss Remoting para comunicação de rede. Para utilizarmos SSL, basta configurar o conector do JBoss Remoting para utilizar um socket SSL. Para nossa sorte a equipe da JBoss já fez isso para nós, basta copiar o arquivo $JBOSS_HOME/docs/examples/jms/remoting-sslbisocket-service.xml para a pasta de deploy.

Será preciso criar um Keystore $JBOSS_HOME/server/default/conf/messaging.keystore com a par de chaves e o certificado. Se o certificado for auto-assinado, será também preciso adicioná-lo Truststore do cliente. A senha do Keystore deve ser “secureexample”.

Para produção é possível alterar configurações de keystore e senha no MBean org.jboss.remoting.security.SSLSocketBuilder, dentro do arquivo remoting-sslbisocket-service.xml que copiamos.

Feito isso, vamos criar um ConnectionFactory que utilize esse conector. Dentro de deploy, crie um arquivo messaging-secure-socket-service.xml:


   

      jboss.messaging:service=ServerPeer
      jboss.messaging:service=Connector,transport=sslbisocket
      
      
         
            /SecureConnectionFactory
         
      
   

Note que o atributo Connector possui o Name do MBean que utiliza o conector SSL. Estamos informando para o JBoss que esse ConnectionFactory deve utilizar um canal SSL. Sempre que o client utilizar um ConnectionFactory obtido pelo lookup de “/SecureConnectionFactory” estará se comunicando em um canal encriptado.

NOTA: cuidado para não utilizar binds JNDI padrões do JBM, definidos em connection-factories-service.xml. Eles usam o jboss.messaging:service=Connector,transport=bisocket, que não utiliza Certificado.

Configurando autenticação/autorização para filas

Para autenticar o client vamos utilizar usuário/senha. O JBM por padrão utiliza o Security Domain “messaging” definido em messaging-jboss-beans.xml, que utiliza datasource DefaultDS. Nesse mesmo arquivo é possível alterar o Security Domain utilizado.

As definições de autorização devem ser feitas por fila. Ao definir uma fila, devemos fazê-lo da seguinte forma:


   

      jboss.messaging:service=ServerPeer
      jboss.messaging:service=PostOffice
      
         
            
            
         
      
   

Note o atributo SecurityConfig. Acho que é tudo bem auto-explicativo, a não ser o create. Esse atributo se aplica a tópico e dá o privilégio do client criar uma subscrição DURABLE.

O para publicar na fila, o client deve obter uma conexão usando a chamada createConnection() que passa usuário e senha:

javax.jms.ConnectionFactory.createConnection(username, password);

NOTA: cuidado ao utilizar os arquivos *-persistence-service.xml que vêm de exemplo no JBoss. Alguns usuário padrões são definidos neles e podem ser utilizados para obter acesso privilegiado. Em um post anterior postei mais informações sobre o JBoss Messaging.

Criando um MDB para consumir

Para consumir mensagens autenticando-se e utilizando o ConnectionFactory sobre SSL, o MDB deve ser configurado com algumas propriedade a mais:

@MessageDriven(activationConfig =
{
   @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
   @ActivationConfigProperty(propertyName="destination", propertyValue="queue/securityTestQueue"),
   @ActivationConfigProperty(propertyName="ConnectionFactoryName", propertyValue="SecureConnectionFactory"),
   @ActivationConfigProperty(propertyName="user", propertyValue="john"),
   @ActivationConfigProperty(propertyName="password", propertyValue="needle")
})
public class QueueConsumer implements MessageListener {
   public void onMessage(Message message) {
      System.out.println(this.getClass().getName() + " -> " + message);
   }
}

That’s it!

Medindo consecutivos tempos de subida do JBoss

Estava com um problema que causava congelamentos de até 10 minutos nas máquinas do JBoss em um cliente. Estes congelamentos eram intermitentes e aconteciam sempre no boot do JBoss.

A fim de fazer testes com várias configurações diferentes e gerar um relatório com dados precisos, escrevi um script que executa o init script do JBoss, espera ele inicializar completamente, grava o tempo de inicialização e mata o processo. Faz isso 50 vezes.

Estou postando ele aqui na esperança de ser útil para mais alguém. Ele é bastante auto-explicativo:

#!/bin/bash
# Notes: all Java processes are going to the KILLED and
# previous probe.log ERASED!

JBOSS_LOG_FILE="/opt/jboss/server/default/log/server.log"
REPEAT=50

START_STRING="Started in"
RESULT_FILE="/root/probe.log-`date +%F`"
> $RESULT_FILE

for i in $(seq 1 $REPEAT); do
	# clean
	killall -9 java
	> $JBOSS_LOG_FILE

	# init and wait
	sh /etc/init.d/jboss start
	while [ "x$( grep "$START_STRING" $JBOSS_LOG_FILE )" == "x" ]; do
		sleep 1
	done

	# log
	cat $JBOSS_LOG_FILE | grep "$START_STRING" >> $RESULT_FILE
done

echo "Done"

Devoxx 2011

EDIT: saiu o artigo na InfoQ BR

De volta do Devoxx! O evento foi muito bom. De longe o maior, mais alto nível, melhor organizado evento que já fui.

Minha palestra aconteceu na quarta e qual foi minha surpresa ao ver o Pete Muir (Seam/Weld/Arquillian), Dan Allen (Seam/Weld/Arquillian) e Aslak Knutsen (Arquillian lead) sentados na primeira fileira!

O mais legal foi ter conversado com eles e eles terem me mostrado que a proposta que apresentei já está no road map do Arquillian. Legal!

O evento foi uma ótima oportunidade para fazer networking. Além do convite para um brainstorm regado a cerveja belga do time do Arquillian, encontrei os brazucas Yara e o Vinícus Senger lá também.

Outra pessoa com quem estava querendo trocar umas idéias é o Mathieu Ancelin, criador do weld-osgi, idéia que eu há muito queria implementar e ele fez eximiamente.

Deve estar saindo nos proximos dias um artigo que escrevi para a InfoQ BR sobre a cobertura do Devoxx. Quando sair post o link aqui. Também disponibilizei os slides da palestra no Slideshare.

Dica: melhorando o tempo de execução do Selenium

Aqui vai uma dica rápida de como agilizar a execução dos seus testes:

FirefoxProfile profile = new FirefoxProfile();
// here's the important part
profile.setPreference("permissions.default.image", 2);

WebDriver driver = new FirefoxDriver(profile);
selenium = new WebDriverBackedSelenium(driver, "http://rafaelliu.net");

Com isso o Firefox não irá requisitar, baixar e renderizar imagens. A diferença varia de acordo a quantidade de imagens na página e hits no cache. Executei um teste no http://g1.globo.com/ com 10 iterações, o que resultou nos seguintes tempos (em milissegundos):

NormalSeleniumTest - 39021ms
TweakedSeleniumTest - 29072ms

Hope that helps!

 

Referência: http://kb.mozillazine.org/Permissions.default.image

Separando logs por aplicação no JBoss

Separar logs de aplicações no JBoss é uma tarefa comum e existem alguns modos de fazer isso:

1. Adicionando uma Category ao $JBOSS_CONF/conf/jboss-log4j.xml pegando todos o package da aplicação e usando um Appender específico. Por exemplo:

   
     
     
   

Embora seja a solução mais fácil, a separação não está perfeita. Como estamos separando por pacotes, um erro no Hibernate por exemplo, ainda vai ser escrito no server.log, mesmo tendo sido lançado pela aplicação.

 

2. Usando Filters é possível criar um TCLFilter para pegar apenas logs de determinada aplicação. Basta criar um Appender específico:

   
      
      
      
      
      
         
      

      
      
         
         
          
      
   

O problema aqui é que, embora o log seja gravado separadamente (inclusive de pacotes do Hibernate, por exemplo), ele será gravado duas vezes: uma vez no blog.log que definimos e uma vez no server.log do JBoss. Isso resulta em mais IO e mais espaço em disco.

 

3. Empacotando um log4j na própria aplicação, em WEB-INF/lib, é possível inverter o classloader e fazer com que as configurações do log4j valham apenas para sua aplicação:



	
		
			net.rafaelliu:loader=blog.war
			java2ParentDelegation=false
		
	
  

Feito isso, é preciso criar o log4j.properties (ou xml) normalmente:

log4j.rootLogger=${jboss.server.log.threshold}, fileout

log4j.appender.fileout=org.apache.log4j.DailyRollingFileAppender
log4j.appender.fileout.File=${jboss.server.log.dir}/blog.log
log4j.appender.fileout.Append=true
log4j.appender.fileout.DatePattern='.'yyyy-MM-dd
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.ConversionPattern=%d %-5p [%c] (%t) %m%n

O log4j faz parse de system properties, que podem ser usadas no arquivo de configuração. Uma observação importante é que não é possível usar classes do JBoss aqui! Isso causaria ClassCastExceptions. Algumas dicas:

  • No JBoss 5, use o jboss.server.log.dir e o jboss.server.log.threshold. Isso facilitará o deploy da sua aplicação em outros ambientes;
  • Use o mesmo Appender do jboss-log4j (provavelmente uma classe da JBoss estará lá, veja o equivalente da Apache). Isso deixa os logs do JBoss e das aplicações sincronizados, facilitando o correlacionamento de erros;

O problema dessa abordagem é que perdemos o “hot deploy” do jboss-log4j.xml, que nos permite alterar o nível de log sem indisponibilidade.

Referências:
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PropertyConfigurator.html
http://viragine.blogspot.com/2009/02/separando-o-log-do-jboss-as-por.html
http://community.jboss.org/wiki/SeparatingApplicationLogs

TDC e Devoxx

Esse mês e o próximo estarei palestrando no TDC e no Devoxx.

A edição Goiânia do TDC vai acontecer dias 29 e 30, e de novo estarei lá palestrando.

Sábado, dia 29/10, às 17:40 estarei falando sobre o Weld/CDI. Será uma palestra introdutória onde quero mostrar alguns recursos que temos e aplicações interessantes deles. Será uma versão mais básica da palestra que farei no Devoxx, e será focada em testes.

No domingo às 14:10 foi mostrar o Drools, a apresentação será similar à do TDC São Paulo, mas vou tentar me organizar melhor para mostrar todas as demos que tinha planejado mostrar 🙂 Apareçam, prometo que vai ser interessante!

Em Novembro estarei na Bélgica para o Devoxx, que será uma semana inteira dedicada a tecnologia com representantes da Red Hat, Oracle, Adobe, Google, HP, IBM, SrpingSource e outros.

Lá vou fazer uma rápida palestra na quinta, dia 17/11, às 13:15. Vou falar como TDD combina com CDI: quais são os problemas enfrentados e quais alternativas temos. Terá um pouco de Weld, portable extensions e Arquillian. Aos brazucas que estiverem por lá, vamos combinar uma cerveja depois!

SumUp do JBossInBossa 2011

EDIT: slides da palestra disponíveis no SlideShare

Foi muito bom! Um dia inteiro com geeks das mais diversas áreas num ambiente de troca de conhecimento incrível. As fotos estão no Picasa.

O local do evento ficou apertado para o tanto de gente que deu. Em algumas apresentações as salas ficaram abarrotadas, como a do @brunorst e @claudio4j falando de Tuning de JBoss e a do @vtcorrea e @g_luszczynski falando de Alta Disponibilidade. Ambas as palestras muito bem criticadas e ambas não pude ver..

O @salaboy abriu o evento com um keynote sobre jBPM5 seguido do @jedgarsilva, com toda sua manha de fazer apresentações, falando sobre Cloud. O @porcelli e o Pedro Igor falaram sobre noSQL e data grids e se complementaram bem o primeiro dando um banho (muito bem dado) de conceitos e o segundo falando de produto mesmo, o Infinispan. Para mim foram as apresentações mais interessantes porque nunca tinha ido atrás desses assuntos, que já estão há um bom tempo ai como buzzwords.

Uma apresentação que eu queria muito ter visto e perdi foi a do @jpviragine falando sobre Federação de Dados com o Teiid. No horário da apresentação dele eu estava na outra sala falando sobre JBoss Portlet Bridge. Uma pena, ainda mais vendo a boa repercussão que teve =/

Teve também Weld e Seam com o @rimolive e a @hannelita, que cobriram muito bem o assunto. O @rafabene e o @osmanlira mandaram muito bem na apresentação de Drools. O @rafaeltuelho falou sobre RHQ, com uma demo que pelo que ouvi foi massa. E restou à Flávia Rainone fechar o evento falando sobre JBoss7.

O evento foi nota 10, mesmo sendo apenas a primeira edição em Brasília. Fiquei muito feliz de ter podido palestrar nele porque o público estava muito interessado e não tem nada melhor do que ver gente interessada perguntando na sua palestra =)

Aos que não foram, recomendo não perderem a chance de se inscrever para a edição de 2012 já já deve tá vindo ai.

Cheers!

Rodando GateIn no Eclipse

Graças ao módulo WCI, o GateIn pode rodar em vários servidores. A JBoss distribui ele em duas formas: baseado no JBoss e baseado no Tomcat. Desenvolver portlets para o GateIn utilizando o empacotamento em JBoss é bem simples, basta adicionar um novo servidor (é preciso ter o módulo WTP, e preferencialmente o JBoss Tools também) JBoss apontando para a instalação do GateIn e está tudo resolvido.

Se você quer utilizar o GateIn com Tomcat é um pouco mais complicado, mas nada preciso adicionar a instalação do Tomcat do GateIn normalmente fazer algumas alterações:

1. Mudar no “Server Location” para “Use Tomcat installation (takes control of Tomcat installation)”

2. No  “Launch Configuration” configurar o “Working directory” com o $TOMCAT_HOME

3. Ainda no  “Launch Configuration” adicionar os parâmetros:

-Djava.security.auth.login.config=../conf/jaas.conf

-Dexo.conf.dir.name=gatein/conf

4. Provavelmente será também preciso alterar limites de memória da JVM e timeouts de subida/descida do WTP.

Para desenvolver no GateIn, tanto no JBoss quanto no Tomcat, pode ser interessante adicionar o parâmetro -Dexo.product.developing=true, que desabilita vários caches e desabilita o merge/compressão de CSS e JS.

WS-Security no CXF

Completando o post anterior (bom, agora no sentido “que veio algum momento antes”), vamos ver algo bem mais útil: como criar um web service utilizando username token para autenticação. Suponho que ficou claro como criar um Web Service e um client para ele usando CFX + Spring e não vou subestimar a inteligência de vocês, vou mostrar só o que é preciso adicionar/alterar:

 
   
   
   
 
   
 
      
       
         
         
            
               
                  
                  
                  
               
            
         
       
   
 
 
   
 
      
         
            
               
                  
                  
                  
                  
               
            
         
      
 
   
 

É possível ver que no client definimos um outInterceptor que irá adicionar os headers com o token Username (um dos possíveis tokens como havia dito no post anterior, poderia ser Kerberos, SAML ou algum certificado) e no serviço definimos um inInterceptor que irá vazer a validação do envelope SOAP que chegar, verificando no header as credenciais.

Ambos os interceptors utilizam Callbacks para recuperar a senha a partir do nome do usuário. No client o callback será utilizado para setar a senha e no serviço será utilizado para recuperar a senha a partir do nome do usuário presente no header, que será comparada com a senha que veio junto do header.

A implementação dos Callbacks é Java puro, provavelmente utilizaria um DB ou LDAP, e é bastante simples:

public class ClientPasswordCallback implements CallbackHandler  {

   @Override
   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        
        if (pc.getIdentifier().equals("rafaelliu")) {
          pc.setPassword("abc123");
        }
   }

}

E para o serviço:

public class ServerPasswordCallback implements CallbackHandler  {

   @Override
   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        
        String password = null;
        if (pc.getIdentifier().equals("rafaelliu")) {
           password = "abc123";
        }
        if (password == null || !password.equals(pc.getPassword())) {
           throw new IOException("wrong password");
        }
   }

}

Uma nota importante é que o callback do serviço é responsável por lançar uma exceção caso o a senha do usuário não seja validade. Isso se deve à implementação do WSS4J, framework também da ASF que o CXF usa para WS-Security. Não testei, mas parece que na versão 2.4 do CXF, ou mais precisamente no WSS4J 1.6, esse comportamento mudou e o callback realmente ficou só um callback.

Bonito hein?