Effective Java: segunda edição

Por Paulo Silveira em 25/07/08

Effective Java Como sabemos, a segunda edição do Effective Java foi publicada. O autor é Joshua Bloch, um dos principais responsáveis pelo generics do Java, e atualmente chief java architect no Google. Esse livro é dividido em 78 itens, cada um com cerca de 3 páginas, atacando um ponto específico do java e orientação a objetos, explicando uma boa ou má prática. Simplesmente incrível, durante a leitura você sempre reconhece muita coisa que já aprendeu durante sua experiência de desenvolvimento.

Essa nova edição está estendida e revista, para cobrir as grandes mudanças do Java 5. Esse, juntamente com outros dois livros (e atualmente incluiríamos também o The Mythical Man-month), são de extrema importância para todo desenvolvedor na nossa opinião.

O Fernando Boaglio tem um resumo em seu blog, sobre todos os itens dessa nova versão. A Vanessa Sabino publicou anos atrás um resumo completo sobre a primeira edição, que você pode conferir na coluna da direita do seu blog.

O Fernando também postou no GUJ um link para uma excelente entrevista do Joshua Bloch, onde ele tenta resumir as más práticas, o java inefetivo: otimização prematura, e escrever o próprio código quando bibliotecas boas já existem. Além disso, Joshua Bloch é categório sobre a grande importância dos testes unitários: “Unit testing is key. And writing your tests first is a great thing.

Lendo essa nova edição e relembrando muito da edição anterior, escolhi aqui quatro itens que considero vitais, e vou falar sucintamente sobre cada um deles. Curiosamente todos os selecionados aqui já existiam na edição anterior, e estão mais relacionados a design que a idiomismos da linguagem, mas isso não tira a importância dos outros aqui não citados. Esses itens são muito debatidos no capítulo de Tópicos em Orientação a Objetos no nosso trienamento de Design e Arquitetura de projetos Java. Vamos a eles:

Item 15: Minimize mutabilidade

Classes imutáveis possuem uma série de vantagens: fáceis de manter, não possuem efeitos colaterais e acima de tudo são thread safe. Uma classe deve ser imutável a não ser que você tenha muito bons motivos para isso. Mesmo se não for possível tornar sua classe imutável, minimize a quantidade de métodos que alteram o estado do objeto. Um objeto previsível é muito mais simples de manter. Joshua Bloch cita String, BigInteger e diz que java.util.Date e java.awt.Point deveriam ter sido criadas imutáveis! Muitas APIs novas abusam da imutabilidade, como a Joda Time, classes wrapper, Money and Time do Eric Evans, etc. Aliás, é com o slogan da imutabilidade que linguagens como clojure e erlang tem chamado tanta atenção. Leia também essa citação no blog do Renato Lucindo.

Item 16: Favoreça composição em vez de herança

Esse é um tópico que já foi discutido anteriormente nesse post. O fato é o seguinte: é muito fácil usar herança de maneira errada, como é o caso de Stack extends Vector e Properties extends Hashtable. Mesmo quando usada corretamente, herança pode causar efeitos colaterais com muita facilidade, sendo que utilizar interfaces e composição pode substitui-la por completo, com o pequeno acréscimo de algumas linhas de delegação. Esse item também é citado no livro Design Patterns como um dos dois princípios básicos do bom design orientado a objetos.

Item 47: Conheça e use as bibliotecas!

Você conhece a ArrayDeque do java 6? Sabia que a java.util.Scanner pode ler facilmente arquivos com formatos caseiros, e já trazer para você doubles, Strings e até mesmo BigDecimals? Que JAXB e JAXWS podem agora ser usados apenas com Java SE? Sabia que a Collections possui hoje métodos para calcular a frequência de um elemento e inverter a ordem de um Comparator?. Conhecer bem a biblioteca padrão do Java pode te salvar de escrever muito código já existente, testado e de qualidade. java.io, java.lang e java.util são APIs que funcionam como base para todo desenvolvedor e merecem um estudo aprofundado.

Item 52: Refira a objetos pelas suas interfaces
Sem dúvida uma boa prática mais que necessária. Através dela conseguimos diminuir muito o acoplamento entre classes, deixando apenas uma fina camada entre elas: as interfaces. Sempre usar InputStream em vez de se acoplar em FileInputStream, sempre usar List em vez de se acoplar a ArrayList. Muitas vezes podemos ir mais longe, nesse último caso Collection pode ser o suficiente, ou até mesmo Iterable! Algumas pessoas levam isso tão a sério que nunca criam uma única classe concreta que não implemente uma interface. Esse item também é citado no livro Design Patterns, e é o outro princípio básico do bom design orientado a objetos desta forma: “Programe voltado a interface, e não a implementação“.

Ainda existem itens fundamentais sobre Enums, Exceptions, Concorrência e Generics. Esse livro é realmente importante na sua cabeceira. Boa leitura!

Melhorando o GUJ: Jetty, NIO e load balancing

Por Fabio Kung em 27/06/08

GUJ2

Durante boa parte da vida do GUJ.com.br, na sua segunda versão (screeshot acima), o site sofreu diversas quedas e passou por muitos períodos de lentidão, mesmo depois de ter migrado para um servidor dedicado. A grande verdade é que por um bom tempo ficamos devendo a devida atenção ao deployment do GUJ. Sempre que um problema acontecia alguém simplesmente reiniciava o servidor, sem investigar as causas reais do problema com profundidade.

Dada a relação próxima que a Caelum sempre teve com o site, já que dois dos fundadores do GUJ são também os fundadores da Caelum, resolvemos assumir de vez a posição de criadores do GUJ. A Caelum agora é a patrocinadora e mantenedora oficial do GUJ. Recentemente andamos gastando algum tempo, tentando acabar de vez com estes problemas que o GUJ a tanto tempo sofre. Felizmente, a melhora já é bem perceptível!

Há algum tempo atrás, o GUJ ficava esporadicamente muito lento. Para resolver estes problemas de lentidão, o primeiro passo foi conseguir um servidor dedicado, pago pelos anúncios espalhados pelo site. Já faz tempo que o GUJ roda neste servidor dedicado e desde então a performance se tornou quase sempre aceitável.

Porém, o servidor dedicado não resolveu todos os problemas, já que java.lang.OutOfMemoryError sempre foi o principal problema enfrentado pelo GUJ. Sempre desconfiamos que o culpado poderia ser o código do próprio GUJ, ou até do JFórum, que deveriam conter algum vazamento de memória.

Alguns desenvolvedores da Caelum já tiveram ótimas experiências passadas com o Jetty, que é um excelente servidor web e servlet contêiner. Foi, inclusive, um dos servidores Java pioneiros a usar conectores NIO (java.nio). O Jetty foi desenhado para ser embutido em outras aplicações Java e portanto é extremamente leve. Consome bem menos memória que o Tomcat, seu concorrente mais conhecido, e não deixa nada a desejar nas outras características.

O servidor dedicado do GUJ tem 2GB de memória RAM e o Tomcat estava configurado inicialmente para ter um heap máximo de 768MB (-Xmx768M). A primeira tentativa foi aumentar o heap máximo (-Xmx1024M), mas mesmo assim o temível OutOfMemoryError insistia em aparecer.

Resolvemos então dar uma chance ao Jetty. Já que ele consome menos memória, acreditamos que os OutOfMemoryError demorariam mais a aparecer. Logo ao subir, o jetty ocupa 4% da memória do servidor. O impressionante é que em duas semanas no ar, o uso total de memória não passou de 12%. Na verdade, o uso de memória estabilizou em 9% do total, porém recentemente fizemos alguns testes de carga no servidor do guj, com mais do que o dobro do número de conexões que o guj recebe hoje nos períodos de pico. Isto fez com que o uso de memória do Jetty pulasse para 12%. Uma diferença enorme da quantidade usada pelo Tomcat, que chegava facilmente a 80%.

uso de memória do Jetty

Já temos algum tempo rodando, sem problemas de memória e com o jetty estável usando 9-12% do total da memória do servidor. Já estamos até pensando em descartar a possibilidade de existir um vazamento de memória no código do GUJ ou do JForum. Isto tornaria o Tomcat culpado pelos problemas de memória!

A possibilidade de vazamentos de memória no Tomcat (estávamos com a versão 6.0.14, sem o APR e com Linux Kernel 2.6.22) deste tamanho é um pouco assustadora. Com a base de usuários que o Tomcat tem, muito possivelmente alguém já teria pego este problema muito antes de nós. Provavelmente o problema é de alguma configuração mal feita no Tomcat do GUJ.

Fato é que o Jetty foi uma tentativa que deu certo e o problema está aparentemente resolvido. O Jetty mostrou um uso mais alto de CPU que o Tomcat, chegando a picos de 60% da capacidade total de processamento do servidor, que possui 2 processadores. Através dos testes de carga que fizemos, temos percebido que o Jetty usa bastante CPU para responder diversas requisições simultâneas. Isso não chega a ser um problema, já que os dois processadores que o servidor possui são mais do que suficientes para atender a quantidade de requisições por segundo que o GUJ recebe hoje.

Apesar de termos algumas suspeitas, ainda não investigamos a razão do alto uso de CPU. Este é um ponto a ser abordado, caso o GUJ tenha problemas com disponibilidade de processamento algum dia.

Mesmo não representando um problema hoje, esta situação preocupa já que o MySQL rodando na mesma máquina também costuma usar bastante CPU. Isto pode se tornar um problema em algum dia que tenha um pouco mais de requisições por segundo do que o comum, já que o uso de CPU chegava as vezes perto do limite. Felizmente, grande parte das requisições ao servidor do GUJ são para conteúdo estático: imagens, JavaScripts, arquivos CSS, download de pdfs (dos artigos), entre outros. Todo esse conteúdo estático era servido pelo Jetty, contribuindo para o alto uso de CPU. Resolvemos então tentar um proxy reverso na frente do Jetty, especificamente para servir este conteúdo estático.

proxy reverso servindo conteúdo estático

Existem diversas alternativas de proxy reverso e a primeira a ser considerada quase sempre é o conhecido servidor web Apache Httpd, com a adição do mod_proxy. É uma excelente solução e existe bastante documentação para fazer tudo funcionar. No entanto, faz um tempo que eu já estava querendo testar o servidor russo Nginx, tão falado pelo pessoal da Engine Yard.

A desvantagem do Nginx para o Apache Httpd é a documentação não tão extensa. Esse é um grande problema para a comunidade do Nginx, que tem se esforçado em traduzir grande parte do que está escrito em russo. Apesar disso, o Nginx é impressionante e superou todas as nossas expectativas. Além de ser extremamente rápido, consome pouquíssimos recursos. Cada um dos processos (1 master + 5 workers) consome na maior parte do tempo apenas 1% de CPU e 0.2% da memória disponível do nosso servidor. Incrível!

O conteúdo estático do GUJ agora é todo servido pelo Nginx; as requisições nem chegam ao Jetty. Além disso, o Nginx, como um bom proxy reverso, oferece diversas otimizações. Uma essencial para o GUJ é o preenchimento automático dos cabeçalhos HTTP de cache, sugerindo aos browsers que façam cache do conteúdo estático. Agora o consumo de CPU diminuiu consideravalmente.

uso de recursos do nginx no GUJ

Não fizemos nenhum comparativo científico de performance, mas a melhora dos tempos de resposta do GUJ está visível. Frequentemente tenho a sensação de que o site está “voando”.

Configuramos também no Nginx a exibição de algumas estatísticas de acesso; tão valiosas ao GUJ. É assustador como o padrão de acesso se repete a cada dia, e a cada semana. Repare como o gráfico de requisições por segundo é praticamente idêntico para cada dia (com exceção do sábado e domingo, que são parecidos entre si).

requisições por segundo no GUJ, em uma semana

Temos também o interesante gráfico de conexões por segundo, que mostra relação média aproximada de 3 requisições por conexão.

conexões por segundo no GUJ, em uma semana

Note que a legenda do gráfico está errada, já que deveria mostrar “connections/sec”. O pequeno pico que dá para ver no gráfico aparece por causa de alguns testes de carga que fizemos neste dia. Pode ser desconsiderado.

Fizemos ainda algumas experiências com múltiplos servidores Jetty por trás do Nginx, funcionando também como balanceador de carga. Espero em breve postar sobre nossa experiência em geral com balanceamento de carga e alguns outros truques que pudemos testar nesta experiência do GUJ e em alguns clientes.

Aproveitando o post, o GUJ recentemente comemorou a marca de meio milhão de mensagens. É um orgulho poder fazer parte desta comunidade, e mais ainda por poder torná-la cada vez melhor.

Os 7 hábitos dos desenvolvedores de WebServices altamente eficazes

Por Paulo Silveira em 18/02/08

Esta semana tive o prazer de palestrar no Café Com Tapioca, evento do CeJUG realizado em Fortaleza. Estavam presentes dois conhecidos evangelistas da Sun: Reggie Hutcherson e Simon Ritter.


DSC01766 DSC01773
DSC01770 DSC01763

O agradecimento fica ao Rafael Carneiro, do PortalJava e do CeJUG, pela organização geral do evento. Agradeço também à equipe do grupo Fortes pela minha vinda ao Ceará: ao Clavius Tales, Igo Coelho, Antônio Israel, Ronaldo Moreira, Tiago Moraes, Rodrigo Maia e tantos outros. Vale reparar como a comunidade java cearense é ativa nos blogs! Nesse sábado também ministrei umworkshop sobre Arquitetura e Design Java aqui em Fortaleza.

Durante esses dias aqui, muitas pessoas perguntaram bastante sobre webservices: em especial JSON e Rest. Compilei alguns dos pontos que foram recorrentes nas discussões e considerados como boas práticas:

Cuidado com a granularidade - a granularidade do seu serviço não pode ser muito fina, caso contrário seus seviços sofrerão dos mesmos problemas que o EntityBean do EJB2 exposto remotamente: uma enorme quantidade de requisições serão disparadas para executar pequenas tarefas, como getters! Seus serviços devem realizar uma quantidade significativa de tarefas, para evitar um número alto de roundtrips!

Exponha serviços, não dados - é comum ouvir a frase “Vou criar webservices para expor os meus dados“. O grande perigo aqui é deixar toda a lógica de negócio na mão do cliente, o que descentralizará seu serviço e forçará o cliente a realizar muitas requisições ao servidor. Devemos expor serviços, e não dados em sua forma mais crua.

Nunca parsear WSDL/SOAP manualmente - antes das ferramentas que trabalham com webservices terem atingido sua maturidade, era muito comum ver por aí as pessoas gerando o SOAP manualmente, através de bibliotecas XML ou às vezes até mesmo concatenando String com o uso de StringBuffer! O SOAP é o protocolo de comunicação, e assim como quando você usa RMI/CORBA e você não enxerga absolutamente nada do JRMP/IIOP, você nunca deveria ter contato direto com o protocolo de comunicação! O SOAP e o WSDL devem ser utilizados por ferramentas, e não pela sua própria aplicação. Hoje em dia qualquer ferramenta como o Apache Axis, Apache CXF, ou mesmo o wsimport, que já vem no JDK 6, auxilia nossa tarefa de gerar Stubs que sabem trabalhar com o SOAP, sem que você nem mesmo precise ve-lo um dia. Se você está usando um servidor de aplicação, esta tarefa é ainda mais fácil, até mesmo para fazer o deploy do serviço.

Não enviar XML dentro de XML - outra prática comumente encontrada em aplicações antigas que usam Webservices: dentro do SOAP é enviado um outro XML como um dos parâmetros, então mesmo usando ferramentas para gerar os stubs você fica com uma String que dentro dela há um XML e este precisa ser parseado por sua própria aplicação. Isto é uma má interpretação do conceito que “Webservices é comunicação via XML“. Concordo que em alguns raros casos isso possa fazer sentido, como por exemplo se o valor do que você quer realmente é um outro XML, mas no geral isto é feito sem necessidade nenhuma, como é o famoso caso do WebService MS Office dos Correios do Brasil.

Considerar protocolo binário - são muitas as reclamações de que o SOAP acaba sendo um XML grande e pesado para ser transportado. Hoje em dia há muitas formas de contornar isso, como o uso do padrão Fast InfoSet para compressão do XML. Uma outra forma seria o uso de protocolos proprietários, como o AMF da Adobe, que é uma opção comum no uso do Flex.

Considerar não usar WSDL/SOAP - pelas diversas críticas a burocracia exagerada do WSDL/SOAP, muitas pessoas estão optando por usar algo mais simples, como o bom e velho XML (POX), JSON ou até mesmo uma forma qualquer de estruturar dados. Caso você precise de simplicidade e velocidade no desenvolvimento com outras plataformas, essa é uma boa opção. Cuidado: muita gente está considerando qualquer webservice que não use WSDL/SOAP como sendo REST. Os webservices do flickr e da Amazon são um bom exemplo: tudo é via GET (as vezes POST) e não há recurso identificado pela URI. Na verdade ele usa apenas um esquema HTTP+XML (POX), considerado apenas em parte como RESTful. Você pode ver que esses webservices são muito diferentes do modelo RESTful do Atom Publishing Protocol. Criar um protocolo realmente REST como este não é fácil: porém teremos esse trabalho bastante simplificado com a JAX-RS (JSR 311), e já podemos ver isso através do Glassfish Jersey.

Considerar usar JSON - JSON é um formato que tem ganho muita popularidade. Não é a toa: além de ser um simples para debugar, parsear e gerar, com javascript basta fazer um eval em um resultado JSON que ele já estará pronto para usar. É uma excelente opção para o consumo via AJAX e criação de mashups. JSON tem ganho bastante força na comunidade Flex, tornando-o uma ótima opção como ponte entre Flex e Java. A escolha por JSON abre portas para muitos tipos diferentes de clientes, e em especial o browser, que é nossa plataforma global.

Os 7 hábitos dos desenvolvedores Hibernate e JPA altamente eficazes

Por Paulo Silveira em 28/01/08

Essa última semana tive a oportunidade de palestrar no RioJUG sobre JPA e Hibernate, onde fui muito bem recebido pelo Guilherme Chapiewski e Magno Cavalcante. Isso ocorreu durante o treinamento de Arquitetura Java que demos para diversos desenvolvedores da Globo.com, e onde tive o prazer de conhecer alguns desenvolvedores e arquitetos, como Vitor Pellegrino, Anselmo Alves, Wesley Silva, Alexandre Gazola, Tiago Motta, entre outros. Também vi o Ettore Luglio e o Daniel Passos.


DSC01736 DSC01709
DSC01691 DSC01750

Infelizmente durante a palestra não tive tempo de mostrar muitos recursos avançados e boas práticas do Hibernate, então vou usar este espaço para tal.

Precisamos conhecer todo pontencial de qualquer ferramenta, framework ou biblioteca que vamos usar em um projeto. Uma ferramenta boa, sem o devido conhecimento, resulta em projetos atrasados, com problemas de performance e desculpas do tipo “O problema é o [Hibernate|Struts|JSP, insira sua tecnologia aqui...], que gera uma quantidade excessiva de [queries|objetos|scriptlets|...] durante [lazy loading|requisições|...]“. Isso vale em especial para ferramentas mais antigas, como JSP e Struts 1. Hoje em dia ambas possuem recursos poderosos que auxiliam em muito o desenvolvimento, mas alguns desenvolvedores acabam não se aprofundando e desconhecem esses detalhes que podem ser vitais no uso de determinadas tecnologias.

Com o Hibernate não é diferente. É muito comum as pessoas culparem o Hibernate pela queda do banco de dados, performance das queries, número de objetos em memória, LazyInitializationException, e outros inúmeros problemas os quais em sua maioria poderiam ter sido evitados com a utilização de alguns recursos, boas práticas e bons hábitos no uso desse framework.

Sem mais demora, os 7 hábitos:

Connection Pool - Usar o pool de conexões embutido com o Hibernate é um erro comum, e a própria documentação diz que você não deve usa-lo em produção! Pode acontecer até connections leak!
A Caelum teve ótimas experiências com o C3P0, e é muito fácil configurá-lo como Provider para o Hibernate.

Second Level Cache - Todos já passamos por situações em que precisamos criar caches para as linhas de banco de dados mais acessadas. Aqui temos diversos problemas: sincronismo, gasto de memória, memory leak, tamanho do cache, política de prioridade da fila (LFU, LRU, FIFO, etc), tempo de expiração e modos de invalidar o cache. Escrever um cache eficiente e seguro é um grande trabalho, imagine ainda dar suporte a um cache distribuído e que possa se aproveitar do disco rígido para não gastar tanta memória? Esse é o papel do second level cache. Você pode usá-lo com diversos providers, sendo o EhCache um dos mais conhecidos.

Query Cache - Um recurso fantástico do Hibernate. No caso de você ter queries que são executadas inúmeras vezes, você pode pedir para o Hibernate fazer o cache do resultado desta query. O interessante é que ele não vai armazenar todos os objetos resultantes, e sim apenas suas primary keys: no momento que ele precisar executar novamente aquela query, ele já tem todos os IDs resultantes, e através destes ele consulta o second level cache, sem fazer um único hit ao banco de dados! Esse cache será invalidado quando alguma das tabelas envolvidas nesta query for atualizada, ou um determinado tempo passar.

Controle do Lazy - Algumas pessoas costumam reclamar do lazy loading, dizendo que em alguns casos teria sido melhor ele carregar tudo em uma única query. Você sempre pode redefinir o comportamento desses relacionamentos quando fizer uma query, através de um eager fetch.

Stateless Session - Algumas vezes precisamos fazer um processamento em batch de objetos, ou mesmo inserir uma quantidade grande deles na base de dados. Em muitos casos uma bulk operation é o suficiente, mas se quisermos manter a Orientação a Objetos, devemos tomar cuidado com a grande quantidade de objetos que ficarão armazenados no first level cache. A StatelessSession resolve esse problema: simplesmente não há first level cache e nenhum objeto se comportará como managed, tendo praticamente o mesmo efeito que chamar entityManager.clear() a cada operação.

Open Session in View - Na arquitetura MVC, muitas vezes renderizamos em nossa view diversas entidades do nosso modelo, e essas podem ter sido carregas pelo Hibernate. Se essas entidades possues relacionamentos lazy, precisamos que a sessão esteja aberta no momento da renderização da View, caso contrário teremos uma LazyInitializaionException ou algum código macarrônico para carregar relacionamentos que nem sempre precisamos. Para isso devemos manter a session aberta através de um filtro, interceptador ou algum outro mecanismo. Isso resulta no pattern Open Session in View e também se aplica ao EntityManager.
O mesmo efeito pode ser obtido através de inversão de controle e injeção de dependências através da anotação @PersistenceContext, que é tratada por containers EJB3 e também por muitos frameworks web, como o Spring. O EJB3 ainda possui o conceito de um contexto de persistência extendido, quem é interessante em casos de conversações longas: o EntityManager usado será o mesmo enquanto aquele stateful session bean não for removido.

Evitando número de queries excessivas (n+1) - Se uma NotaFiscal possui muitos Items, e essa coleção é lazy, gastaremos duas queries para buscar a NotaFiscal e seus respectivos Itens. Mas se temos uma lista de NotaFiscal resultante de uma query, para cada NotaFiscal teremos uma nova query executada para todo getItems invocados. 1 query para listar NotaFiscal, N queries para pegar os relacionamentos: é o problema das n+1 queries. Você deve usar as configurações de batch-size e fetch-size para pedir ao Hibernate carregar as entidades/relacionamentos em blocos em vez de um em um. Você também pode utilizar o second level cache nesses relacionamentos, diminuindo consideravelmente o número de queries disparada.

Essas são apenas alguns dos hábitos, poderíamos ainda falar sobre o bom tratamento de exceções, o cuidado ao fechar todos os recursos abertos pelo Hibernate, o uso de queries nativas, o mapeamento de queries nativas para entidades através do ResultTransformer, filtros de coleções, dynamic insert e update, a criação do seu próprio tipo de persistência, e muitos outros. Conhecer bem o capítulo de performance do Hibernate é fundamental além de um bom começo.

Java 6, as APIs de XML, Webservices e classloaders

Por Paulo Silveira em 17/12/07

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.