Java 6, as APIs de XML, Webservices e classloaders
A Sun vem há muito tempo fazendo esforços para facilitar a manipulação de XML e de webservices na plataforma. São tantos projetos, subprojetos e especificações que podemos facilmente nos encontrar perdidos no meio de tantas siglas. Elas são:
JAXP (pacote java.xml
no geral) - Processamento geral de XML, com os já antigos SAX e DOM, além de transformadores (XLST) e XPath. JAXB (pacote javax.xml.bind
) - Assocaição/mapeamento de classes java para XML.
JAX-WS (pacote javax.xml.ws
) - Criação e consumo de webservices. Aliada a especificação de metadados para webservices (pacote javax.jws
), previamente já vista aqui no blog, a JAX-WS tornasse poderosa e fácil de usar. JAXR (pacote javax.xml.registry
) - para acesso aos registros de serviços XMLs, como UDDI. JAX-RPC (pacote javax.xml.rpc
) - era o nome antigo do atual JAX-WS. O JAX-WS mudou de nome e já apareceu como 2.0, essa mundaça foi justificada pelo fato dessa API passar a trabalhar bem mais próxima da API do JAXB, além do óbvio marketing.
E a Sun não para por aí, temos mais especificações: a duvidosa JSR 267 que possibilita um JSP acessar diretamente um webservice (!) através de taglibs e a esperada JSR 311 para trabalhar com serviços de maneira RESTful, oferecendo simples anotações para expor métodos Java através de URI + métodos HTTP.
Não são todos subprojetos de manipulação de xml com java que viraram especificações, e as que viraram nem todas estão no Java SE. A Fast Infoset é uma especificação ISO para representação binária do padrão XML (economizando assim espaço e banda, além de melhorar performance do parsing) possui uma implementação Java, utilizada dentro do Metro, projeto que fornece os recursos de webservices do Glassfish.
Muitas dessas APIs, juntamente com implementações de referência (RIs), agora estão presentes no java SE 6.0, que antes eram opcionais. Qual é o problema disso? Até então diversos servidores de aplicação e frameworks traziam embutido implementações do JAXB, JAX-WS, etc. Ao rodar essas aplicações com o Java 6 o sistema de classloading da plataforma vai primeiro carregar as classes da api padrão, mesmo que você tenha implementações dessas APIS de XML no classpath. O ruim aqui é que muitos servidores de aplicação acabam se amarrando a detalhes de sua própria implementação e versão, como é esse caso do JBoss com o JAX-WS. Quando rodado com o Java 6, temos a seguinte exception quando você tenta acessar um webservice que está implantando no servidor:
java.lang.UnsupportedOperationException: setProperty must be overridden by all subclasses of SOAPMessage at javax.xml.soap.SOAPMessage.setProperty(SOAPMessage.java:424)
Enfrentamos esse problema recentemente, e para resolve-lo usamos o sistema de endosso de jars (endorsed jars) do Java: determinados diretórios podem ser configurados para que alguns pacotes específicos possam ser carregados destes antes do Classloader tentar chegar ao rt.jar.
Um outro problema comum é com o JAXB: o Java 6 vem com a versão 2.0, se você precisar usar a 1.1 ou a 2.1, vai ter problemas. O interessante é que a JAXB do Java SE já foi projetada para ela mesma detectar se o classloading foi correto, ou se partiu de uma versão posterior/anterior a ela, mostrando uma mensagem de erro amigável. Para outras bibliotecas esse problema pode ser muito sutil: o classloader pode acabar carregando parte da biblioteca de uma versão recente, já que algumas classes novas só existem nesse jar, e o restante de uma outra antiga, resultando exceptions como NoSuchMethodError
, que não mostram claramente que o problema é a existência de dois jars de versões diferentes no classpath daquela aplicação.
Podemos ver que mesmo seguindos boas práticas, isolando bibliotecas e não usando a terrível variável de ambiente CLASSPATH, acabamos sempre enfrentando o classloader hell.