Assim como acontece muito nos Servidores de Aplicação JEE como o IBM WebSphere ou no Oracle WebLogic, o Classloading Isolation no JBoss EAP não é tão simples em alguns casos. Claro que o nosso desejo é sempre gerarmos um EAR/WAR “limpo” de arquivos de configurações, que rode em qualquer servidor de aplicação. Geralmente temos desistido e voltado para o bom e velho Apache Tomcat, o que não é ruim quando precisamos rodar apenas arquivos WAR. Creio que não temos muitos problemas com o Tomcat porque ele não possui (em seu ambiente) muitas APIs ou Frameworks que poderiam provocar conflitos com as utilizadas pelas aplicações. Mas é fato que nem sempre iremos produzir somente pacotes WAR. E quando precisarmos de Segurança, Escalabilidade, Transação e outros tantos serviços presentes na especificação JEE? Sem dúvida que Servidores de Aplicação JEE que se prezem são mais robustos, mesmo para simples aplicações WAR. Assim, se o Classloading Isolation é trabalhoso, vale a pena o esforço (até certo ponto, obviamente).
À medida que eu encontrar problemas e soluções de Classloading Isolation no JBoss, vou atualizando este post. Descrevo abaixo alguns problemas e soluções:
1) Isolando bibliotecas do EAP 6.x para utilizar as que estão definidas na aplicação
Basicamente, basta configurar o arquivo /WEB-INF/jboss-deployment-structure.xml. No exemplo abaixo, a versão do SL4J utilizada pelo EAP é excluída do classloading, para que seja utilizada a versão definida na aplicação:
Esta é a configuração básica de isolamento e deve funcionar na maioria dos casos. Entretanto, para determinadas APIs, o isolamento não é tão simples, como é o caso do JSF (Java Server Faces) e JPA (Java Persistence API).
2) Isolando o Hibernate e o Spring do JBoss EAP 6.x
O JBoss EAP 6.x traz consigo a versão 4.2.x do Hibernate. Se a aplicação estiver utilizando uma versão igual ou inferior a esta, basta excluir o módulo org.hibernate no arquivo jboss-deployment-structure.xml. Se, todavia, a aplicação estiver utilizando uma versão mais recente (4.3.x), existe um BUG neste servidor, associado ao isolamento do JPA (vide próximo item 3).
O JBoss EAP 6.x não possui Spring em sua distribuição. Assim, nenhuma configuração de isolamento é necessária.
3) Isolando a implementação JPA default do EAP 6.3.x
O problema a seguir só ocorre quando utilizamos algumas anotações do pacote javax.persistence. O erro abaixo é proveniente do uso da anotação @JoinColumn
Error: javax.persistence.JoinColumn.foreignKey()Ljavax/persistence/ForeignKey
A causa raiz deste problema é que o EAP 6.3.x é baseado na especificação JEE 6, sendo o JPA 2.0 parte desta especificação. As versões mais recentes do Hibernate (4.3.x) trazem consigo a versão 2.1 do JPA, que é parte da especificação JEE 7. Teoricamente, para resolver o problema, bastava excluir os módulos org.hibernate e javax.persistence.api no arquivo jboss-deployment-structure.xml, mas isso não resolve. Segundo a RedHat, isto é um BUG.
3.1) Uma solução é utilizar a versão do Hibernate igual ou inferior à que está presente na distribuição do EAP 6.3.x. Resolve, não podemos ser impedidos de fazer upgrade de qualquer biblioteca, por causa do servidor.
3.1) Podemos trocar a versão do JPA 2.0 para 2.1 diretamente na distribuição, localizada em \JBOSS_DIST\modules\system\layers\base\javax\persistence\api\main. Também resolve, mas esta solução não é aceitável, visto que as aplicações que utilizarem a versão do Hibernate presentes no EAP 6.x poderão ser afetadas.
3.2) Poderíamos seria seguir os passos descritos neste endereço http://mariemjabloun.blogspot.com.br/2014/03/use-jboss-jpa-21-and-hibernate-43-on.html, mas particularmente achei uma solução muito trabalhosa.
3.3) Enfim, após inúmeras buscas e tentativas, encontrei uma solução razoável para contornar o problema, no próprio site da RedHat: https://access.redhat.com/solutions/404223. Acredito nas versões futuras do EAP, isso deverá ser corrigido.
A solução está abaixo (jboss-deployment-structure.xml):
4) Isolando o JSF do JBoss EAP 6.x
Diferente das demais APIs e Frameworks, para isolar o JSF que vem na distribuição do JBoss EAP 6.x, não adianta informar nos “exclusions” do arquivo jboss-deployment-structure.xml (Item 1). Via de regra, basta adicionar o seguinte context-param no arquivo web.xml da aplicação e o isolamento deve funcionar. Caso isso não resolva, deve-se analisar cada caso isoladamente.
Por exemplo, não consegui utilizar na aplicação a solução Ominifaces (showcase.omnifaces.org). Quando o JBoss inicia, ocorre um erro nas configurações de JSF do Ominifaces. É alguma incompatibilidade de versão (Ominifaces x JBoss x JSF) que não parei para descobrir. Como não estava utilizando muitos recursos desta API, resolvi retirá-la.
Existe teoricamente outras maneiras de utilizar outras versões de JSF, que não estejam na aplicação. A versão de JSF presente no EAP 6.x é 2.1.28 (slot=”main”). O EAP 6.x possui serviços, módulos e injeções que sempre carregam o seu próprio JSF. Caso queiramos uma nova versão do JSF disponibilizada no servidor, devemos configurar novos “slots” nos módulos base do servidor, tal qual o Mojarra mais recente (2.2.11). Em seguida, editando o arquivo jboss-deployment-structure.xml, devemos excluir os módulos principais (slot=”main”) do JSF e informar os novos slots nas dependências.
<exclusions>
<module name=”com.sun.jsf-impl” slot=”main” />
<module name=”javax.faces.api” slot=”main” />
<module name=”org.jboss.as.jsf” slot=”main” />
<module name=”org.jboss.as.jsf-injection” slot=”main” />
</exclusions>
<dependencies>
<module name=”com.sun.jsf-impl” slot=”2.2.8″ export=”true” />
<module name=”javax.faces.api” slot=”2.2.8″ export=”true” />
<module name=”org.jboss.as.jsf” slot=”2.2.8″ export=”true” />
<module name=”org.jboss.as.jsf-injection” slot=”2.2.8″ export=”true” />
</dependencies>
4) Isolando o Expression Language (EL) do EAP 6.3.x
Atualmente, insistindo em utilizar a versão JSF do servidor EAP 6.3.x (sem isolamento), encontramos o seguinte erro:
javax.el.ELException: /login.xhtml: The class ‘AutenticadorBean’ does not have the property ‘logon’
Isso é devido à versão do EL (Expression Language) presente no EAP. Basta utilizar a versão mais recente na API, referenciando-a no pom.xml, conforme abaixo: