Understanding classloaders

Havia algum tempo que eu vinha estudando sobre o problema que é conhecido como classloader hell e, como todo problema complexo, levei tempo razoável para dar ao menos uma solução viável.

Pesquisei bastante em sites (Understanding J2EE … Loading Architectures, Classloaders) e blogs como o de Daniel que tem o ótimo post Classloading … que me auxiliaram no desafio imposto a mim mesmo. E após várias noites mal dormidas e deadlines da minha monografia estourados, resolvi que ia deixar essa pesquisa para depois.

Algum tempo depois, após ouvir uma conversa de dois colegas de trabalho – eles se perguntavam qual a lógica de carregamento dos .jar no Tomcat–, resolvi continuar minha pesquisa.

Enfim vamos ao que interessa:

Muitos programadores certamente nunca se depararam com o problema tratado nesse post. Se você nunca se perguntou como uma classe é carregada ou nunca enfrentou problemas de ClassCastException onde se tem certeza de que o casting está correto, provavelmente esse post não é interessante para você no momento, mas recomendo que leia pois um dia precisará desses conhecimentos.

De início, você deverá entender que tipo e classe são coisas diferentes (por mais que nós, desenvolvedores, utilizemos tais termos como sendo uma coisa só). Um tipo (java.lang.reflect.Type) é o nome completo de uma entidade formal JAVA (o que comumente chamamos de classe) que não é a mesma coisa de uma classe (java.lang.Class). Para o dia-a-dia do desenvolvedor pode-se utilizar esses dois termos se referindo à mesma coisa sem medo de errar, pois Class implementa Type, então uma Class é um Type. Mas para nossa abordagem, vamos diferenciar um do outro, uma Class para nós será um Type carregado por um Classloader, ou seja, uma classe é um tipo dentro de um contexto específico. Exemplo: com.example.Person é um tipo e não uma classe, este só será uma instância de um java.lang.Class depois de carregado por um ClassLoader.

A nossa abordagem será a de um servidor de aplicação, no caso o Tomcat. Num ambiente J2SE, os classloaders são arranjados numa árvore. Normalmente, quando um é solicitado a um classloader que carregue uma class ou um resource, o mesmo delega a solicitação para o classloader pai e somente no caso de o pai não encontrar tal class ou resource é que o classloader filho procura em seu repositório. Num servidor de aplicação Java o processo acontece de forma um pouco diferente.

       Bootstrap
          |
       System
          |
       Common
      /      \
 Catalina   Shared
             /   \
        Webapp1  Webapp2 ...

Bootstrap – É o classloader que contém as classes básicas de execução providas pela JVM além de quaisquer classes de arquivos JAR presentes no diretório de extensões ($JAVA_HOME/jre/lib/ext).

System – É o classloader é normalmente inicializado carregando do conteúdo apontado pela váriável de ambiente CLASSPATH, e todas as classes carregadas pelo classloader System será visível dentro do Tomcat (inclusive as web applications). Porém, como padrão do script de inicialização do Tomcat 5 ($CATALINA_HOME/bin/catalina.sh ou %CATALINA_HOME%\bin\catalina.bat) ignora o conteúdo dessa variável, e carrega as classes através do System dos repositórios abaixo:
•    $CATALINA_HOME/bin/bootstrap.jar
•    $JAVA_HOME/lib/tools.jar
•    $CATALINA_HOME/bin/commons-logging-api-x.y.z.jar
•    $CATALINA_HOME/bin/commons-daemon.jar
•    jmx.jar – The JMX 1.2 implementation.

Cada um desses tem sua utilidade definida. Os mais importantes são o primeiro e o segundo, o primeiro (bootstrap.jar) tem o método main() que inicializa o Tomcat e os classloaders dos quais a aplicação depende, já o segundo contém basicamente o compilador javac que converterá arquivos JSP em Servlets.

Common – Este classloader contém classes adicionais que sao visíveis internamente no Tomcat e em todas as webapps.

Catalina – Este classloader é usado para incluir todas as classes e recursos necessários para implementar o próprio Tomcat 5. Essas classes e recursos são INVISÍVEIS para as aplicações web.

Shared – O Shared classloader é o lugar certo onde se deve por as class e resources que se queira acessar de qualquer web application (diretório $CATALINA_HOME/webapps). Porém se você deseja que as classes internas do Tomcat 5 tenham acesso a esses recursos você deve por diretório carregado pelo Common classloader.

WebappX – Para cada web application implantado numa instância do Tomcat 5 um classloader é criado. Todas as classes e recursos no diretório /WEB-INF/classes além das classes e recursos presentes nos arquivos JAR dentro do diretório /WEB-INF/lib são visíveis para o container da aplicação web porém invisível para as demais.

Como dito antes, os classloaders de uma aplicacação web diverge um pouco do modelo de deletação Java 2 – não por acaso, mas por que a Servlet Specification, versão 2.3, seção 9.7.2 Web Application Classloader. No início do post, expliquei rapidamente como funciona o modelo de delegação padrão Java 2, agora vejamos como acontece num servidor de aplicação: quando uma requisição de carregar uma classe do WebappX  classloader (classloader da aplicação em questão) é processado, este classloader irá primeiro procurar em seu repositório local (nos diretórios da aplicação web /WEB-INF/classes e depois /WEB-INF/lib) ao invés de delegar ao classloader pai. Vale salientar que classes que são partes da JRE não podem ser overriden (sobrecarregada, sobreposta) e que todos os outros classloaders do Tomcat 5 utilizam o modelo usual de delegação.

Para simplificar e facilitar o entendimento, abaixo há um esquema que mostra a ordem que as classes e recursos sao carregados no Tomcat 5:
•    Bootstrap
•    System classloader
•    /WEB-INF/classes: classes da sua aplicação web
•    /WEB-INF/lib/*.jar: jars da sua aplicação web
•    $CATALINA_HOME/common/classes
•    $CATALINA_HOME/common/endorsed/*.jar
•    $CATALINA_HOME/common/i18n/*.jar
•    $CATALINA_HOME/common/lib/*.jar
•    $CATALINA_BASE/shared/classes
•    $CATALINA_BASE/shared/lib/*.jar

Por enquanto é isso. Brevemente farei um post com exemplos de classloaders em ação.

Tags: , , ,

2 Responses to “Understanding classloaders”

  1. Ordenando JAR loading no Tomcat 5 « public class {} Says:

    [...] public class {} developer stuff « Understanding classloaders [...]

  2. Adicionando e ordenando outros classloaders do Tomcat5 « public class {} Says:

    [...] Há algumas semanas publiquei um post que ensinava como ordenar os JARs das aplicações web (toda e qualquer aplicação dentro do diretório webapps do tomcat) através da manipulação do WebappClassloader, porém fiquei devendo falar sobre os outros classloaders presentes no tomcat, particularmente os que mais interessam são o common classloader e o shared classloader – não sabe o que são classloaders, ou não sabe o papel de cada um desses classloaders no tomcat? -> Understanding classloaders. [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.