Quem nunca se deparou com um ClassNotFoundException na vida ao fazer um deploy?
Normalmente a gente sempre encara esse tipo de problema quando estamos começando a trabalhar com a plataforma Java. É um problema simples, conceitual, mas que muita gente não entende de verdade. Vamos tentar chegar ao porquê desse problema discutindo alguns aspectos no meio do caminho: quando estamos no processo de desenvolvimento, utilizamos uma IDE, certo? Vamos supor que estamos desenvolvendo uma aplicação web em que o Controller (Servlet) vai ser desenvolvido na “unha”. Como todos sabemos, uma Servlet deve extender HttpServlet. Mas HttpServlet faz parte de um JAR que não se encontra no Eclipse e então temos que adicionar a API da Servlet na nossa aplicação. A questão aqui é a seguinte: você adicionou a API da Servlet na sua aplicação, mas como toda solução, vamos precisar deployar (colocar) a mesma em um servidor de aplicação, então vamos considerar o Tomcat. Uma das funções de um servidor de aplicação é prover toda uma infraestrutura para que a aplicação funcione. Então, meus caros, a ServletAPI já não estará lá? Claro que sim! Essa situação abre o leque para duas questões: A primeira que o conceito de JAR Hell entrou em cena, e a segunda é a de que para que precisamos importar uma API para a nossa aplicação se o Tomcat já possuí a maioria das necessárias?
Vamos debater a segunda questão primeiro e vamos inverter os papéis. Nós usamos como exemplo a ServletAPI que já está na library do Tomcat. Mas e se, por exemplo, quiséssemos manipular XML’s e utilizassemos o XStream? A API do XStream não é nativa (não pertence, não vem na livraria padrão) do Tomcat. Então, nesse caso sim faz sentido a importação da API para a nossa aplicação, para que a mesma pertença ao WAR que iremos deployar no servidor. Aonde eu estou querendo chegar? Nas dependências. Qual é a forma correta de acoplar dependências no projeto? Vamos por passo a passo.
O que podemos fazer para não incluirmos dependências que já estão no Servidor de Aplicação no nosso projeto?
Bom, sempre que você associar uma dependência você deve verificar na documentação a lista das que já são nativas do Servidor de Aplicação e verificar se a sua já existe. Ah, dica: o search está desabilitado. (…)
Fala sério, né pessoal… O que fazer então? Importe para o seu projeto a livraria nativa do Servidor de Aplicação. Assim você irá evitar a maioria dos problemas com as dependências. Abaixo temos um exemplo de como utilizar a library do Servidor de Aplicação, que no nosso caso, é o Tomcat:
Abra as propriedades do seu projeto, vão em BuildPath, aba Libraries, e clique em Add Library:
Escolha ServerRuntime
E voa-lá: Escolha o Tomcat! (Lembrando o mesmo só irá aparecer se você já o tiver configurado na Aba Servers do Eclipse)
Agora você pode desenvolver uma aplicação sem a necessidade de inserir a maioria das dependências nativas do Tomcat no seu projeto. Mas e as não nativas? Aí não tem jeito: temos que importar para o projeto. Mas, de repente, um warning novo aparece na sua aplicação:
Um ClassPath Dependency Validator Message, dizendo que a API não irão ser exportada ou publicada no WAR.
Aqui é que corremos o risco de tomar uma ClassNotFoundException. Ah sim, o que é um ClassNotFoundException? É quando você faz um deploy de uma aplicação sem uma library que é necessária para o funcionamento de alguma coisa. Como o JAR não vai ser exportado junto com a aplicação e o mesmo não se encontra no container, vai ser apenas uma questão de tempo até que um cliente quebre a sua aplicação. Como evitar? Importe corretamente. Todos os jar’s externos devem estar na pasta lib da sua aplicação. Não confundam com WEB-INF/lib. Estou falando da pasta lib que se encontra na raiz do projeto. Ou você pode simplesmente usar o QuickFix que está disponível na visualização do warning. O que ele faz: marca a dependência diretamente no arquivo MANIFEST.MF que se encontra na pasta META-INF.
Finalmente! Chegamos na pasta META-INF!
A utilidade da pasta e do arquivo MANIFEST dentro do contexto do projeto é simplesmente a “marcação” do que vai ser publicado junto com o WAR na hora do deploy. E nada mais. O QuickFIx faz isso para você quando o warning aparece: ele simplesmente joga manualmente a declaração dentro do arquivo dizendo que aquele determinado jar vai ser exportado no WAR. Simples, não? Mas acreditem se quiser: eu já vi gente excluindo a pasta com a afirmação de que a mesma não servia para nada. Mas agora você não tem desculpa: você sabe o porque da necessidade da pasta META-INF no seu projeto.
Uma ultima consideração sobre o JAR Hell, que foi citado lá em cima no post: um problema que pode acontecer é o deploy de vários projetos com o mesmo JAR publicado com versões diferentes. Quem disponibiliza as classes para a aplicação é o ClassLoader, que faz parte da JRE e disponibiliza as classes para a JVM poder rodar a aplicação. O que vocês acham que acontece quando o ClassLoader for buscar a classe na sua lista de library’s disponíveis, que possuí dois JAR’s com o mesmo nome? Essa eu vou deixar para vocês pensarem.
Bem, espero que esse post tenha contribuído um pouco com seu knowhow. Admito: eu passei por muitas dificuldades no inicio com dependências e achei legal fazer um post sobre o assunto. Ah sim, para quem interessar, segue alguns links sobre algumas coisas que conversamos aqui:
XStream – Òtimo manipulador de XML’s
Documentação do ClassNotFoundException
Definição do ClassLoader na Wikipedia
Até a próxima!




Escrito por Anderson Torres 

