Mécanisme de chargement des classes de Tomcat

et partage de librairies versionnées entre plusieurs applications

Tomcat


précédentsommairesuivant

III. PARTAGE DE LIBRAIRIES VERSIONNES

III-A. Dépendances possibles entre les catégories de classes

On peut distinguer les classes en trois catégories : celles qui sont propres à l'application, celles qui appartiennent à des librairies versionnées et qui seraient partageables entre plusieurs applications, et celles qui font parties des librairies Common, System ou Bootstrap.

Le diagramme ci-dessous représente les dépendances possibles entre catégories de classes.

Image non disponible

Les classes de Common, System ou Bootstrap ne peuvent avoir de dépendances que vers d'autres classes de la même catégorie. Tandis que les clases de l'application ou des librairies versionnées peuvent avoir une dépendance sur la même catégorie ou sur l'une des autres catégories.

On obtient donc les dépendances possibles suivantes.

Numéro Catégorie dépendante Catégorie de dépendance
1 Common, System ou Bootstrap Common, System ou Bootstrap
2 Application Application
3 Application Common, System ou Bootstrap
4 Application Versionnee
5 Versionnee Versionnee
6 Versionnee Common, System ou Bootstrap
7 Versionnee Application

III-B. Cas pratique de dépendance entre classes

Image non disponible

Le cas pratique couvre les différentes dépendances possibles entre catégories.

Numéro de dépendance Catégorie dépendante Catégorie de dépendance Classe dépendante Classe de dépendance Mêmes repositories/librairies
1 Common Common CommonClasse1 CommonClasse2 Oui
1 Common Common CommonClasse3 CommonClasse4 Oui
2 Application Application ApplicationClasse1 ApplicationClasse2 Oui
2 Application Application ApplicationClasse3 ApplicationClasse4 Oui
3 Application Common ApplicationClasse2 CommonClasse3 Non
4 Application Versionnee ApplicationClasse3 VersionnneeClasse7 Non
4 Application Versionnee ApplicationClasse4 VersionnneeClasse3 Non
5 Versionnee Versionnee VersionnneeClasse1 VersionnneeClasse2 Oui
5 Versionnee Versionnee VersionnneeClasse3 VersionnneeClasse4 Oui
5 Versionnee Versionnee VersionnneeClasse5 VersionnneeClasse6 Oui
5 Versionnee Versionnee VersionnneeClasse7 VersionnneeClasse8 Oui
5 Versionnee Versionnee VersionnneeClasse6 VersionnneeClasse1 Non
6 Versionnee Common VersionnneeClasse8 CommonClasse1 Non
7 Versionnee Application VersionnneeClasse5 ApplicationClasse1 Non

Certaines dépendances ne sont pas représentées : celles entre la Servlet de l'application et sa classe mère HttpServlet (Common) par exemple.

III-C. Implémentation du cas pratique

III-C-1. Implémentation des classes

Les classes du cas pratique ont toutes les mêmes formes. Elles ont leur nom qui comprend une abréviation de leur catégorie (Common, Versionnee ou Application), "Classe" et le numéro dans le diagramme. Si c'est une classe versionnée, on trouve la valeur de cette version. Comme on le verra juste après pour l'implémentation des interfaces du cas pratique, on trouve un Calendar et un Throwable initialisés statiquement. Chaque classe implémente également une méthode d'instance "methode()" qui est vide dans le cas d'une classe sans dépendance et qui comprend un appel vers la classe de dépendance si dépendance.

 
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.caspratique;

public class [CategorieClasse]Classe[NumeroClasse] {

    public static final String VERSION = "[NumeroVersion]";  // Si la classe est versionnée
    public static final java.util.Calendar CALENDAR = java.util.Calendar.getInstance();
    public static final Throwable THROWABLE = new Throwable();

    public void methode() {
        new [ClasseDeDependance]().methode();   // Si la classe a une dépendance
    }
}

La classe "CommonClasse2" n'a pas de version et n'a pas de dépendance.

CommonClasse2.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.caspratique;

public class CommonClasse2 {

    public static final java.util.Calendar CALENDAR = java.util.Calendar.getInstance();
    public static final Throwable THROWABLE = new Throwable();

    public void methode() {
    }
}

La classe "VersionneeClasse8" a une version (ici, on considère que c'est la version 1.0) et a une dépendance vers la classe "CommonClasse1".

VersionneeClasse8.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.caspratique;

public class VersionneeClasse8 {

    public static final String VERSION = "1.0";
    public static final java.util.Calendar CALENDAR = java.util.Calendar.getInstance();
    public static final Throwable THROWABLE = new Throwable();

    public void methode() {
        new CommonClasse1().methode();
    }
}

III-C-2. Implémentation des librairies

Les librairies du Common et celles propres à l'application ne font l'objet que d'un seul JAR. Tandis que les librairies versionnées font l'objet de plusieurs JAR suffixés avec le numéro de version.

Chaque librairie comporte une ressource texte dans le même package que les classes.

Librairie Nom du jar Versions Nom ressource texte Contenu ressource texte Téléchargement JAR Téléchargement Sources
1ère lib. du Common commonLibA   commonA.txt commonA ici (Miroir) ici (Miroir)
2ème lib. du Common commonLibB   commonB.txt commonB ici (Miroir) ici (Miroir)
Lib. propre à l'Appli applicationLib   application.txt application ici (Miroir) ici (Miroir)
1ère lib. versionnée versionneeLibA-[NumeroVersion] 1.0
2.0
versionneeA.txt versionneeA-1.0
versionneeA-2.0
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)
2ème lib. versionnée versionneeLibB-[NumeroVersion] 1.0
2.0
3.0
versionneeB.txt versionneeB-1.0
versionneeB-2.0
versionneeB-3.0
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)
3.0 (3.0 Miroir)
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)
3.0 (3.0 Miroir)
3ème lib. versionnée versionneeLibC-[NumeroVersion] 1.0
2.0
versionneeC.txt versionneeC-1.0
versionneeC-2.0
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)
1.0 (1.0 Miroir)
2.0 (2.0 Miroir)

III-C-3. Implémentation des applications

L'implémentation des applications du cas pratique consiste en quatre applications avec des dépendances différentes.

Chaque application se compose (mis à part les classes "ApplicationClasse3" et "ApplicationClasse4") d'un descripteur de déploiement "web.xml" et d'une Servlet "ApplicationServlet[numeroApplication]".

web.xml
CacherSélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>application[numeroApplication]</display-name>

	<servlet>
		<servlet-name>racine</servlet-name>
		<servlet-class>com.developpez.rpouiller.partagelibrairiesversionnees.caspratique.ApplicationServlet[numeroApplication]</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>racine</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>
ApplicationServlet[numeroApplication].java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.caspratique;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ApplicationServlet[numeroApplication] extends HttpServlet {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS");

    private ClassLoader classLoader;

    /**
     * Retourne les informations d'element de Stack correspondant à une classe ApplicationServletX.
     */
    private String getApplicationServlet(final Throwable pThrowable) {
        if(pThrowable == null) {
            return "&nbsp;";
        }

        final StringBuffer lStringBuffer = new StringBuffer();
        final StackTraceElement[] StackTracesElement = pThrowable.getStackTrace();
        for(int i=0;i<StackTracesElement.length;i++) {
            final String lClassName = StackTracesElement[i].getClassName();
            final int lPosDernierPoint = lClassName.lastIndexOf('.');
            final String lNomClasse = (lPosDernierPoint >= 0)?lClassName.substring(lPosDernierPoint + 1):lClassName;
            if(lNomClasse.startsWith("ApplicationServlet")) {
                lStringBuffer.append(lNomClasse + "." + StackTracesElement[i].getMethodName() + 
                        "(" + StackTracesElement[i].getLineNumber() + ")");
            }
        }
        return lStringBuffer.toString();
    }

    /**
     * Retourne la partie "intéressante" d'une URL d'un ClassLoader.
     */
    private String getFinChemin(final URL pURL) {
        try {
            String lChemin = pURL.toString();
            final int lPosTomcat = lChemin.indexOf("Tomcat");
            if(lPosTomcat != -1) {
                lChemin = "...." + lChemin.substring(lPosTomcat);
            }
            
            return lChemin.replaceAll("%20", "&nbsp;");
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    /**
     * Retourne une ligne d'informations pour une classe donnée.
     */
    private String getInformations(final Class<? extends Object> pClasse, final String pVersion, 
            final Calendar pDateInit, final Throwable pOrigineInit) {

        final StringBuffer lStringBuffer = new StringBuffer();

        final ClassLoader lClassLoader = pClasse.getClassLoader();
        lStringBuffer.append("<TR><TD>");
        lStringBuffer.append(pClasse.getSimpleName());
        lStringBuffer.append("</TD><TD>");
        lStringBuffer.append(pVersion==null?"&nbsp;":pVersion);
        lStringBuffer.append("</TD><TD>");
        lStringBuffer.append(DATE_FORMAT.format(pDateInit.getTime()));
        lStringBuffer.append("</TD><TD>");
        lStringBuffer.append(getApplicationServlet(pOrigineInit));
        lStringBuffer.append("</TD><TD>");
        if(lClassLoader == classLoader) {
            lStringBuffer.append("idem</TD><TD>idem");
        }
        else {
            lStringBuffer.append(lClassLoader.getClass().getSimpleName());
            lStringBuffer.append("</TD><TD>");
            if(lClassLoader instanceof URLClassLoader) {
                final URLClassLoader lURLClassLoader = (URLClassLoader)lClassLoader;
                final URL[] lURLs = lURLClassLoader.getURLs();
                for(int i=0;i<lURLs.length;i++) {
                    lStringBuffer.append(getFinChemin(lURLs[i]) + "<BR/>");
                }
            }
            else {
                lStringBuffer.append("&nbsp;");
            }
        }
        lStringBuffer.append("</TD></TR>");

        classLoader = lClassLoader;

        return lStringBuffer.toString();
    }

    /**
     * Retourne une ligne d'informations pour une ressource donnée.
     * @throws IOException 
     */
    private String getInformationsRessource(final String pNomRessource) throws IOException {

        final StringBuffer lStringBuffer = new StringBuffer();
        lStringBuffer.append("<TR><TD>");
        lStringBuffer.append(pNomRessource);
        lStringBuffer.append("</TD><TD>");
        lStringBuffer.append(getFinChemin(Thread.currentThread().getContextClassLoader().getResource(pNomRessource)));
        lStringBuffer.append("</TD><TD>");
        final InputStream lInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(pNomRessource);
        if(lInputStream == null) {
            lStringBuffer.append("<I>null</I>");
        }
        else {
            final BufferedReader lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
            lStringBuffer.append(lBufferedReader.readLine());
        }
        lStringBuffer.append("</TD></TR>");

        return lStringBuffer.toString();
    }

    /**
     * Affiche les informations sur les classes.
     */
    @Override
    protected synchronized void doGet(final HttpServletRequest pRequete, final HttpServletResponse pReponse) 
            throws ServletException, IOException {

        new ApplicationClasse3().methode();
        new VersionneeClasse5().methode();

        final StringBuffer lStringBuffer = new StringBuffer();

        lStringBuffer.append("<HTML><BODY>");
        lStringBuffer.append("<TABLE BORDER=\"1\" CELLSPACING=\"0\">");
        lStringBuffer.append("<TR>");
        lStringBuffer.append("<TH>Classe</TH><TH>Version</TH><TH>Heure d'init</TH><TH>Origine de l'init</TH>");
        lStringBuffer.append("<TH>ClassLoader</TH><TH>URLs du ClassLoader</TH>");
        lStringBuffer.append("</TR>");

        classLoader = null;
        lStringBuffer.append(getInformations(CommonClasse1.class, null, CommonClasse1.CALENDAR, CommonClasse1.THROWABLE));
        lStringBuffer.append(getInformations(CommonClasse2.class, null, CommonClasse2.CALENDAR, CommonClasse2.THROWABLE));
        lStringBuffer.append(getInformations(CommonClasse3.class, null, CommonClasse3.CALENDAR, CommonClasse3.THROWABLE));
        lStringBuffer.append(getInformations(CommonClasse4.class, null, CommonClasse4.CALENDAR, CommonClasse4.THROWABLE));
        lStringBuffer.append(getInformations(ApplicationClasse3.class, null, ApplicationClasse3.CALENDAR, ApplicationClasse3.THROWABLE));
        lStringBuffer.append(getInformations(ApplicationClasse4.class, null, ApplicationClasse4.CALENDAR, ApplicationClasse4.THROWABLE));
        lStringBuffer.append(getInformations(ApplicationClasse3.class, null, ApplicationClasse3.CALENDAR, ApplicationClasse3.THROWABLE));
        lStringBuffer.append(getInformations(ApplicationClasse4.class, null, ApplicationClasse4.CALENDAR, ApplicationClasse4.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse1.class, VersionneeClasse1.VERSION, VersionneeClasse1.CALENDAR, 
                VersionneeClasse1.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse2.class, VersionneeClasse2.VERSION, VersionneeClasse2.CALENDAR, 
                VersionneeClasse2.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse3.class, VersionneeClasse3.VERSION, VersionneeClasse3.CALENDAR, 
                VersionneeClasse3.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse4.class, VersionneeClasse4.VERSION, VersionneeClasse4.CALENDAR, 
                VersionneeClasse4.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse5.class, VersionneeClasse5.VERSION, VersionneeClasse5.CALENDAR, 
                VersionneeClasse5.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse6.class, VersionneeClasse6.VERSION, VersionneeClasse6.CALENDAR, 
                VersionneeClasse6.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse7.class, VersionneeClasse7.VERSION, VersionneeClasse7.CALENDAR, 
                VersionneeClasse7.THROWABLE));
        lStringBuffer.append(getInformations(VersionneeClasse8.class, VersionneeClasse8.VERSION, VersionneeClasse8.CALENDAR, 
                VersionneeClasse8.THROWABLE));

        lStringBuffer.append("</TABLE>");

        lStringBuffer.append("<TABLE BORDER=\"1\" CELLSPACING=\"0\">");
        lStringBuffer.append("<TR><TH>Ressource recherchée</TH><TH>URL de la ressource</TH><TH>Contenu de la ressource</TH></TR>");
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/commonA.txt"));
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/commonB.txt"));
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/application.txt"));
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/versionneeA.txt"));
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/versionneeB.txt"));
        lStringBuffer.append(getInformationsRessource("com/developpez/rpouiller/partagelibrairiesversionnees/caspratique/versionneeC.txt"));
        lStringBuffer.append("</TABLE>");

        lStringBuffer.append("</BODY></HTML>");

        pReponse.getWriter().print(lStringBuffer.toString());
    }
}

Les dépendances sont définies dans le tableau ci-dessous.

Application Dépendances Téléchargement WAR Téléchargement Sources
Application1 commonLibA, commonLibB, applicationLib, versionneeLibA-1.0, versionneeLibB-1.0 et versionneeLibC-1.0 ici (Miroir) ici (Miroir)
Application2 commonLibA, commonLibB, applicationLib, versionneeLibA-1.0, versionneeLibB-2.0 et versionneeLibC-2.0 ici (Miroir) ici (Miroir)
Application3 commonLibA, commonLibB, applicationLib, versionneeLibA-2.0, versionneeLibB-3.0 et versionneeLibC-1.0 ici (Miroir) ici (Miroir)
Application4 commonLibA, commonLibB, applicationLib, versionneeLibA-2.0, versionneeLibB-1.0 et versionneeLibC-2.0 ici (Miroir) ici (Miroir)

III-D. Lancement des applications sans partage des librairies versionnées

III-D-1. Lancement de l'application 1

L'URL pour accéder à l'application est : http://localhost:8080/application1/

Le résultat est :

Image non disponible
Image non disponible

III-D-2. Lancement de l'application 2

L'URL pour accéder à l'application est : http://localhost:8080/application2/

Le résultat est :

Image non disponible
Image non disponible

III-D-3. Lancement de l'application 3

L'URL pour accéder à l'application est : http://localhost:8080/application3/

Le résultat est :

Image non disponible
Image non disponible

III-D-4. Lancement de l'application 4

L'URL pour accéder à l'application est : http://localhost:8080/application4/

Le résultat est :

Image non disponible
Image non disponible

III-D-5. Bilan du lancement des applications

Les classes des librairies communes sont initialisées une seule fois, donc elles sont chargées une seule fois également. Ce qui est logique, puisqu'elles sont chargées par le ClassLoader Common.

Les classes de l'application (classes simples et librairies) sont initialisées au lancement de chaque application. Elles sont chargées par le ClassLoader de la Webapp, donc elles sont chargées plusieurs fois.

C'est exactement la même chose pour les classes des librairies versionnées.

Les ressources appartenant à des librairies communes, d'application ou versionnées sont chargées depuis les librairies communes ou depuis les librairies qui sont dans les WEB-INF/lib de chaque application.

III-E. Solution de partage des librairies versionnées

III-E-1. Paramétrage du partage des librairies

Le système de partage doit pouvoir être paramétré. Voici, l'arborescence de ce paramétrage :

Image non disponible

Le paramétrage du partage (Partages) consiste en n dossiers de partages (Dossier) et n applications web (Application). Chaque Dossier comporte un nom et le chemin sur disque du dossier comme attributs. Chaque Application comporte un contexte correspondant au contexte de l'application dans ses paramètres de déploiement comme attribut, ainsi que n liens vers un dossier de partage (Partage). Chaque Partage comporte un nom qui correspond au nom d'un dossier de partage.

Cette configuration sera stockée dans un fichier xml contenu dans le dossier "conf" de Tomcat ("C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf").

III-E-2. Diagramme de classe du système

Image non disponible

III-E-2-a. Initialisation du système

Par défaut le fichier de configuration est "conf/partages.xml", il est possible de le setter grâce la méthode de classe "setConfigFile()" (surtout utile lors des tests).

Lorsque la méthode "init()" de "WebappLoaderAvecPartage" est appelée, elle demande le chargement de la configuration en appelant "chargerConfiguration()". Si la configuration n'a pas été déjà chargée ("configurationChargee à true"), "chargerConfiguration()" récupère le fichier de configuration grâce à "configFile()" et utilise le Digester obtenu par "createDigester()" afin de charger la config dans "partages".

Lorsque la méthode "start()" de "WebappLoaderAvecPartage" est appelée, elle fournit la liste des dossiers ("Dossier") à l'ensemble des "WebappClassLoaderAvecPartage" par la méthode de classe "setListeDossiers()". Puis, elle récupère la configuration de l'application ("Application") correspondant au contexte du "WebappLoaderAvecPartage" grâce à "getApplicationContexte()". Ensuite, elle fournit cette configuration d'application au "WebappLoaderAvecPartage" par la méthode "setApplication()".

Lorsque la méthode de classe "setListeDossiers()" est appelée, elle parcourt la liste des dossiers de partage afin de constituer une liste ("listeFichiersJar") commune à tous les "WebappClassLoaderAvecPartage" des différentes librairies ("FichierJar").

Lorsque la méthode "setApplication()" est appelée, elle constitue une liste ("listeFichiersJarApplication") grâce à la liste "listeFichiersJar" précédemment constituée, les informations de partages ("Partage") de l'application et les libraries contenues dans le "WEB-INF/lib" de l'application.

III-E-2-b. Comportement du système

Deux méthodes ont été ajoutées :

  • "getFileContainsResource()" retourne le fichier de "WEB-INF/lib" qui contient une ressource ou null si la ressource n'est contenue dans aucun fichier ou est également contenue dans "WEB-INF/classes".
  • "getFichierJarForResource()" retourne le "FichierJar" de "listeFichiersJarApplication" pouvant contenir la ressource grâce à "getFileContainsResource()".

D'après les informations collectées lors de l'étude du "Mécanisme de chargement", trois méthodes de "WebappClassLoader" doivent être substituées. Un comportement spécifique pour assurer le partage de librairies versionnées doit être effectué avant de passer la main à la classe mère si la ressource n'est pas concernée par le partage. Chacune des méthodes subtituées utilise "getFichierJarForResource()" afin de déterminer le "FichierJar" de "listeFichiersJarApplication" pouvant contenir la ressource.

  • "findLoadedClass0()" vérifie si le "FichierJar" pouvant contenir la ressource (fichier class correspondant à la classe) possède la classe dans son cache ("resourceEntries").
  • "findLoadedResource()" vérifie si le "FichierJar" pouvant contenir la ressource possède la ressource dans son cache ("resourceEntries").
  • "findResourceInternal()" charge la ressource depuis le "FichierJar" et la place dans le cache.

III-E-3. Implémentation du système

Partage.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;

/** Classe contenant les infomations d'un partage pour une application. */
public class Partage {

    private String nom;

    /** Constructeur sans paramètres */
    public Partage() {
    }

    /** Constructeur avec paramètres */
    public Partage(final String pNom) {
        nom = pNom;
    }

    /** Retourne le nom d'un dossier de partage. */
    public String getNom() {
        return nom;
    }

    /** Fixe le nom d'un dossier de partage. */
    public void setNom(final String pNom) {
        nom = pNom;
    }
}
Application.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;

import java.util.LinkedList;
import java.util.List;

/** Classe contenant les infomations d'une application. */
public class Application {

    private String contexte;
    private List<Partage> listePartages = new LinkedList<Partage>();

    /** Constructeur sans paramètres */
    public Application() {
    }

    /** Constructeur avec paramètres */
    public Application(final String contexte, final List<Partage> listePartages) {
        this.contexte = contexte;
        this.listePartages = listePartages;
    }

    /** Retourne le contexte de l'application. */
    public String getContexte() {
        return contexte;
    }

    /** Fixe le contexte de l'application. */
    public void setContexte(final String pContexte) {
        contexte = pContexte;
    }

    /** Retourne la liste des partages d'une application.*/
    public List<Partage> getListePartages() {
        return listePartages;
    }

    /** Ajoute un partage (correspondant à un dossier) à la liste des partages d'une application. */
    public void addPartage(final Partage pPartage) {
        listePartages.add(pPartage);
    }
}
Dossier.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;

/** Classe contenant les informations concernant un dossier de partage. */
public class Dossier {

    private String nom;
    private String chemin;

    /** Constructeur sans paramètres */
    public Dossier() {
    }

    /** Constructeur avec paramètres */
    public Dossier(final String pNom, final String pChemin) {
        nom = pNom;
        chemin = pChemin;
    }

    /** Retourne le nom du dossier de partage. */
    public String getNom() {
        return nom;
    }

    /** Fixe le nom du dossier de partage. */
    public void setNom(final String pNom) {
        nom = pNom;
    }

    /** Retourne le chemin du dossier de partage. */
    public String getChemin() {
        return chemin;
    }

    /** Fixe le chemin du dossier de partage. */
    public void setChemin(final String pChemin) {
        chemin = pChemin;
    }
}
Partages.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;

import java.util.LinkedList;
import java.util.List;

/** Classe contenant les infomations des partages. */
public class Partages {

    private List<Dossier> listeDossiers = new LinkedList<Dossier>();
    private List<Application> listeApplications = new LinkedList<Application>();

    /** Retourne la liste des dossiers de partage. */
    public List<Dossier> getListeDossiers() {
        return listeDossiers;
    }

    /** Ajoute un dossier à la liste des dossiers de partage. */
    public void addDossier(final Dossier pDossier) {
        listeDossiers.add(pDossier);
    }

    /** Retourne la liste des applications avec partage. */
    public List<Application> getListeApplications() {
        return listeApplications;
    }

    /** Ajoute une application à la liste des applications avec partage. */
    public void addApplication(final Application pApplication) {
        listeApplications.add(pApplication);
    }
}
FichierJar.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader;

import java.io.File;
import java.util.HashMap;

import org.apache.catalina.loader.ResourceEntry;

/** Classe représentant un fichier jar dans un dossier de partage */
public class FichierJar {

    private String nomDossier;
    private File file;
    private HashMap<String, ResourceEntry> resourceEntries = 
        new HashMap<String, ResourceEntry>();

    /** Constructeur sans paramètres */
    public FichierJar() {
    }

    /** Constructeur avec paramètres */
    public FichierJar(final String pNomDossier, final File pFile) {
        nomDossier = pNomDossier;
        file = pFile;
    }

    /** Retourne le nom du dossier (correspond à l'attribut nom de Dossier) */
    public String getNomDossier() {
        return nomDossier;
    }

    /** Fixe le nom du dossier */
    public void setNomDossier(String pNomDossier) {
        nomDossier = pNomDossier;
    }

    /** Retourne le File correspondant au fichier jar*/
    public File getFile() {
        return file;
    }

    /** Fixe le File correspondant au fichier jar */
    public void setFile(File pFile) {
        file = pFile;
    }

    /** Retourne les ResourceEntry du cache */
    public HashMap<String, ResourceEntry> getResourceEntries() {
        return resourceEntries;
    }

    /** Recherche une ResourceEntry dans le cache */
    public ResourceEntry getResourceEntry(final String pNomRessource) {
        return resourceEntries.get(pNomRessource);
    }

    /** Ajoute une ResourceEntry dans le cache */
    public void ajouteResourceEntry(final String pNomRessource, final ResourceEntry pResourceEntry) {
        resourceEntries.put(pNomRessource, pResourceEntry);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        FichierJar other = (FichierJar) obj;
        if (file == null) {
            if (other.file != null)
                return false;
        } else if (!file.equals(other.file))
            return false;
        if (nomDossier == null) {
            if (other.nomDossier != null)
                return false;
        } else if (!nomDossier.equals(other.nomDossier))
            return false;
        return true;
    }
}
WebappLoaderAvecPartage.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.loader.WebappLoader;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.digester.Digester;

import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Application;
import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Dossier;
import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partages;

/** Loader avec partage des librairies versionnées. */
public class WebappLoaderAvecPartage extends WebappLoader {

    private final static Log LOG = LogFactory.getLog(WebappLoaderAvecPartage.class);

    /** 
     * Nom du fichier de configuration du partage.
     * Le fichier "partages.xml" est dans le dossier "conf" de Tomcat.
     */
    protected static String configFile = "conf/partages.xml";

    /** Indique si le fichier de configuration a été chargée */
    private static boolean configurationChargee = false;

    /** Informations des partages. */
    private static Partages partages;

    /**
     * Retourne un objet File object correspondant au fichier de configuration.
     * @return L'objet File
     */
    protected static File configFile() {
        File file = new File(configFile);
        if (!file.isAbsolute())
            file = new File(System.getProperty("catalina.base"), configFile);
        return (file);
    }

    /**
     * Fixe le nom du fichier de configuration du partage.
     * @param pConfigFile Le nom.
     */
    protected static void setConfigFile(final String pConfigFile) {
        configFile = pConfigFile;
    }

    /**
     * Fixe les informations des partages.
     * @param pPartages Les informations des partages.
     */
    public void setPartages(final Partages pPartages) {
        partages = pPartages;
    }

    /**
     * Retourne l'objet "Application" associé au contexte du Loader.
     * @return L'objet "Application".
     */
    private Application getApplicationContexte() {
        final String lNomContexte = getContainer().getName();
        for(final Application lApplication : partages.getListeApplications()) {
            if(lNomContexte.equals(lApplication.getContexte())) {
                return lApplication;
            }
        }
        return null;
    }

    /**
     * Crée et configure le Digester pour le chargement de la configuration.
     */
    protected Digester createDigester() {
        final Digester lDigester = new Digester();
        lDigester.setValidating(false);
        lDigester.setRulesValidation(true);

        lDigester.addObjectCreate("Partages",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partages");
        lDigester.addSetNext("Partages", "setPartages",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partages");

        lDigester.addObjectCreate("Partages/Dossier",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Dossier");
        lDigester.addSetProperties("Partages/Dossier");
        lDigester.addSetNext("Partages/Dossier", "addDossier",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Dossier");

        lDigester.addObjectCreate("Partages/Application",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Application");
        lDigester.addSetProperties("Partages/Application");
        lDigester.addSetNext("Partages/Application", "addApplication",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Application");

        lDigester.addObjectCreate("Partages/Application/Partage",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partage");
        lDigester.addSetProperties("Partages/Application/Partage");
        lDigester.addSetNext("Partages/Application/Partage", "addPartage",
                "com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partage");

        return lDigester;
    }

    /**
     * Charge la configuration depuis le fichier CONFIG_FILE
     */
    protected synchronized Partages chargerConfiguration() {
        if(!configurationChargee) {
            configurationChargee = true;

            final Digester lDigester = createDigester();
            try {
                final InputStream lInputStream = new FileInputStream(configFile());
                lDigester.push(this);
                lDigester.parse(lInputStream);
                lInputStream.close();
            } catch (FileNotFoundException e) {
                LOG.warn("Chargement impossible du fichier de configuration de partage : " + configFile().getAbsolutePath());
            } catch (Exception e) {
                LOG.warn("Erreur lors du chargement de " + configFile() + " : ", e);
            }
        }

        return partages;
    }

    /**
     * Initialise le Loader.
     * Comme il s'agit d'une substitution de la méthode de WebappLoader,
     * on appelle dans un premier temps la méthode de WebappLoader.
     * Puis, on charge la configuration depuis le fichier.
     */
    public void init() {
        super.init();

        chargerConfiguration();
    }

    /**
     * Start this component, initializing our associated class loader.
     * Pareil que pour la méthode init(), dans un premier temps,
     * on appelle la méthode de WebappLoader.
     * @exception LifecycleException if a lifecycle error occurs
     */
    public void start() throws LifecycleException {
        super.start();

        if(partages != null) {
            final List<Dossier> lListeDossiers = partages.getListeDossiers();
            WebappClassLoaderAvecPartage.setListeDossiers(lListeDossiers);

            final Application lApplication = getApplicationContexte();
            ((WebappClassLoaderAvecPartage)getClassLoader()).setApplication(lApplication);
        }
    }
}
WebappClassLoaderAvecPartage.java
CacherSélectionnez
package com.developpez.rpouiller.partagelibrairiesversionnees.loader;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.naming.NamingException;

import org.apache.catalina.loader.ResourceEntry;
import org.apache.catalina.loader.WebappClassLoader;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.naming.resources.Resource;

import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Application;
import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Dossier;
import com.developpez.rpouiller.partagelibrairiesversionnees.loader.config.Partage;

/** ClassLoader avec partage des librairies versionnées. */
public class WebappClassLoaderAvecPartage extends WebappClassLoader {

    private final static Log LOG = LogFactory.getLog(WebappClassLoaderAvecPartage.class);

    protected static List<FichierJar> listeFichiersJar;

    /** Unique instance du filtre */
    private static FilenameFilterJar FILENAME_FILTER_JAR = new FilenameFilterJar();

    /**
     * Filtre de nom de fichier de jar
     */
    private static class FilenameFilterJar implements FilenameFilter {

        public boolean accept(File dir, String name) {
            return name.toUpperCase().endsWith(".JAR");
        }
        
    }

    private List<FichierJar> listeFichiersJarApplication;

    /**
     * Détermine les fichiers jar constituant les possibilités de partage 
     * à partir de la liste des dossiers de la configuration.
     * 
     * Parcours les différents dossiers contenus dans la configuration.
     * Stocke les informations liées aux fichiers jar de ces dossiers dans la liste.
     * 
     * @param pListeDossiers
     */
    public static synchronized void setListeDossiers(final List<Dossier> pListeDossiers) {
        if(listeFichiersJar == null) {
            listeFichiersJar = new LinkedList<FichierJar>();

            for(final Dossier lDossier : pListeDossiers) {
                final String lNomDossier = lDossier.getNom();
                final String lCheminDossier = lDossier.getChemin();
                final File lFileDossier = new File(lCheminDossier);
                if(lFileDossier.exists()) {
                    LOG.info("Détermination des librairies du dossier de partage '" + lNomDossier + "' (" + lCheminDossier + ")");
                    final File[] lFichiersJarDuDossier = lFileDossier.listFiles(FILENAME_FILTER_JAR);

                    for(final File lFichierJarDuDossier : lFichiersJarDuDossier) {
                        LOG.info("Fichier '" + lFichierJarDuDossier + "' trouvé");

                        final FichierJar lFichierJar = new FichierJar();

                        lFichierJar.setNomDossier(lNomDossier);
                        lFichierJar.setFile(lFichierJarDuDossier);

                        listeFichiersJar.add(lFichierJar);
                    }
                }
                else {
                    LOG.warn("Le dossier de partage '" + lNomDossier + "' n'a pas été trouvé au chemin '" + lCheminDossier + "'");
                }
            }
        }
    }

    /**
     * Retourne la liste des fichiers jar constituant les possibilités de partage.
     * @return La liste des fichiers jar.
     */
    protected static List<FichierJar> getListeFichiersJar() {
        return listeFichiersJar;
    }

    /**
     * Constructeur sans paramètres.
     */
    public WebappClassLoaderAvecPartage() {
        super();
    }

    /**
     * Constructeur avec ClassLoader parent
     * @param pParent
     */
    public WebappClassLoaderAvecPartage(final ClassLoader pParent) {
        super(pParent);
    }

    /**
     * Retourne la liste des fichiers jar pour l'application.
     * @return La liste des fichiers jar pour l'application
     */
    protected List<FichierJar> getListeFichiersJarApplication() {
        return listeFichiersJarApplication;
    }



    /**
     * Fixe la configuration du partage pour une application 
     * correspondant à ce ClassLoader.
     * 
     * Détermine à partir de ces informations les fichiers jar 
     * qui concernent ce ClassLoader.
     * 
     * Pour être retenu le fichier jar doit appartenir à un dossier
     * de partage de cette application et son nom doit correspondre
     * à un jar des URLs de ce ClassLoader.
     * 
     * Ces URLs ont été settées dans la méthode start() de WebappLoader.
     * 
     * @param pApplication
     */
    public void setApplication(final Application pApplication) {
        listeFichiersJarApplication = new LinkedList<FichierJar>();

        LOG.info("Détermination des librairies partagées pour l'application '" + pApplication.getContexte() + "'");

        final List<Partage> lListePartages = pApplication.getListePartages();

        boucleFichierJar:
        for(final FichierJar lFichierJar : listeFichiersJar) {

            for(final Partage lPartage : lListePartages) {

                // Si le fichier jar appartient à un dossier de partage 
                // de l'application
                if(lFichierJar.getNomDossier().equals(lPartage.getNom())) {

                    for(final File lJarRealFile : jarRealFiles) {

                        final String lNomJarRealFile = lJarRealFile.getName();
                        final String lNomFichierPartage = lFichierJar.getFile().getName();

                        // Si le fichier jar correspond à un un des fichiers du classpath
                        // alors on l'ajout à la liste des jar de l'application
                        // et on passe au fichier jar suivant
                        if(lNomJarRealFile.equals(lNomFichierPartage)) {
                            LOG.info("Librairie retenue : dossier : '" + lFichierJar.getNomDossier() + "' - fichier : '" + lFichierJar.getFile() + "'");
                            listeFichiersJarApplication.add(lFichierJar);
                            continue boucleFichierJar;
                        }
                    }
                }
            }
        }
    }

    @Override
    protected Class findLoadedClass0(final String name) {
        // Trouve le fichier jar concernée par la ressource
        final String lTempPath = name.replace('.', '/');
        final FichierJar lFichierJar = getFichierJarForResource(lTempPath + ".class");

        if(lFichierJar != null) {
            // Si la ressource est déjà chargée, on la retourne depuis le cache
            final ResourceEntry lLoadedResourceEntry = lFichierJar.getResourceEntry(name);
            if(lLoadedResourceEntry != null) {
                return lLoadedResourceEntry.loadedClass;
            }
        }

        // Passe la main à la classe mère si la ressource n'est pas concernée par le partage
        return super.findLoadedClass0(name);
    }

    @Override
    protected InputStream findLoadedResource(final String name) {

        // Trouve le fichier jar concernée par la ressource
        final FichierJar lFichierJar = getFichierJarForResource(name);

        if(lFichierJar != null) {
            // Si la ressource est déjà chargée, on la retourne depuis le cache
            final ResourceEntry lLoadedResourceEntry = lFichierJar.getResourceEntry(name);
            if(lLoadedResourceEntry != null) {
                if (lLoadedResourceEntry.binaryContent != null) {
                    return new ByteArrayInputStream(lLoadedResourceEntry.binaryContent);
                }
            }
        }

        // Passe la main à la classe mère si la ressource n'est pas concernée par le partage
        return super.findLoadedResource(name);
    }

    @Override
    public URL findResource(final String name) {
        // Trouve le fichier jar concernée par la ressource
        final FichierJar lFichierJar = getFichierJarForResource(name);

        if(lFichierJar != null) {
            // Si la ressource est déjà chargée, on la retourne depuis le cache
            ResourceEntry lLoadedResourceEntry = lFichierJar.getResourceEntry(name);
            // Si elle n'était pas dans le cache, on essaye de la charger
            if (lLoadedResourceEntry == null) {
                lLoadedResourceEntry = findResourceInternal(name, name);
            }
            if (lLoadedResourceEntry != null) {
                return lLoadedResourceEntry.source;
            }
        }

        // Passe la main à la classe mère si la ressource n'est pas concernée par le partage
        return super.findResource(name);
    }

    @Override
    protected ResourceEntry findResourceInternal(final String name, final String path) {

        if (!started) {
            LOG.info(sm.getString("WebappClassLoaderAvecPartage.stopped", name));
            return null;
        }

        // Trouve le fichier jar concernée par la ressource
        final FichierJar lFichierJar = getFichierJarForResource(path);

        if(lFichierJar != null) {
            synchronized(lFichierJar) {
                // Si la ressource est déjà chargée, on la retourne depuis le cache
                final ResourceEntry lLoadedResourceEntry = lFichierJar.getResourceEntry(name);
                if(lLoadedResourceEntry != null) {
                    return lLoadedResourceEntry;
                }

                // Si la ressource n'est pas dans le cache, on la charge depuis le Jar
                try {
                    final File lFile = lFichierJar.getFile();
                    final JarFile lJarFile = new JarFile(lFile);
                    final JarEntry lJarEntry = lJarFile.getJarEntry(path);
                    if(lJarEntry != null) {
                        final ResourceEntry lResourceEntry = new ResourceEntry();
                        lResourceEntry.codeBase = getURL(lFile, false);
                        lResourceEntry.source = new URL("jar:" + getURI(lFile) + "!/" + path);
                        lResourceEntry.lastModified = lFile.lastModified();
                        lResourceEntry.manifest = lJarFile.getManifest();
                        final InputStream lInputStream = lJarFile.getInputStream(lJarEntry);
                        final byte[] lBinaryContent = new byte[(int)lJarEntry.getSize()];
                        
                        int lPosition = 0;
                        while (true) {
                            int n = lInputStream.read(lBinaryContent, lPosition, lBinaryContent.length - lPosition);
                            if (n <= 0)
                                break;
                            lPosition += n;
                        }
                        lInputStream.close();

                        if(needConvert && path.endsWith(".properties")) {
                            final String lStringBynaryContent = new String(lBinaryContent, 0, lPosition);
                            lResourceEntry.binaryContent = lStringBynaryContent.getBytes("UTF-8");
                        }
                        else {
                            lResourceEntry.binaryContent = lBinaryContent;
                        }

                        if (lJarEntry != null) {
                            lResourceEntry.certificates = lJarEntry.getCertificates();
                        }

                        lFichierJar.ajouteResourceEntry(name, lResourceEntry);

                        return lResourceEntry;
                    }
                } catch (IOException e) {
                    return null;
                }
            }
        }

        // Passe la main à la classe mère si la ressource n'est pas concernée par le partage
        return super.findResourceInternal(name, path);
    }

    /**
     * Retourne le fichier jar (du WEB-INF/lib par exemple) qui contient la ressource en fonction de son chemin.
     * Si la ressource n'est contenue par aucun jar ou qu'elle est trouvée ailleurs 
     * que dans un jar (ex : WEB-INF/classes), la méthode retourne null.
     * @param pResourcePath Chemin de la ressource
     * @return Le fichier jar contenant la ressource
     */
    public File getFileContainsResource(final String pResourcePath) {
        for (int i=0;i<repositories.length;i++) {
            try {
                String fullPath = repositories[i] + pResourcePath;
                Object lookupResult = resources.lookup(fullPath);
                if (lookupResult instanceof Resource) {
                    return null;
                }
            } catch (NamingException e) {
            }
        }
        synchronized (jarFiles) {

            if (!openJARs()) {
                return null;
            }
            for (int i=0;i<jarFiles.length;i++) {

                final JarEntry lJarEntry = jarFiles[i].getJarEntry(pResourcePath);

                if (lJarEntry != null) {
                    return jarRealFiles[i];
                }
            }
        }
        return null;
    }

    /**
     * Retourne le fichier jar (FichierJar) de la liste des fichiers jar 
     * de l'application correspondant à la ressource passée en paramètre.
     * @param pResourcePath chemin de la ressource
     * @return Le fichier Jar; null si aucun ne correspond.
     */
    public FichierJar getFichierJarForResource(final String pResourcePath) {
        final File lFile = getFileContainsResource(pResourcePath);
        if(lFile != null) {
            final String lNomFichier = lFile.getName();
            for(final FichierJar lFichierJar : listeFichiersJarApplication) {
                final String lNomFichierJar = lFichierJar.getFile().getName();
                if(lNomFichierJar.equals(lNomFichier)) {
                    return lFichierJar;
                }
            }
        }
        return null;
    }
}
Téléchargement JAR Téléchargement Sources
ici (Miroir) ici (Miroir)

III-E-4. Installation du système de partage

Créer les dossiers "C:\dossierA", "C:\dossierB" et "C:\dossierC".

Copier les fichiers "versionneeLibA-1.0.jar" et "versionneeLibA-2.0.jar" dans "C:\dossierA".

Copier les fichiers "versionneeLibB-1.0.jar", "versionneeLibB-2.0.jar" et "versionneeLibB-3.0.jar" dans "C:\dossierB".

Copier les fichiers "versionneeLibC-1.0.jar" et "versionneeLibC-2.0.jar" dans "C:\dossierC".

Copier le jar "partagelibrairiesversionnees.jar" dans le dossier "lib" de Tomcat ("C:\Program Files\Apache Software Foundation\Tomcat 6.0\lib").

Créer le fichier de configuration "partages.xml" dans le dossier "conf" de Tomcat ("C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf"), avec le contenu suivant :

partages.xml
Sélectionnez
<?xml version='1.0' encoding='utf-8'?>
<Partages>

	<Dossier nom="A" chemin="c:\dossierA"/>
	<Dossier nom="B" chemin="c:\dossierB"/>
	<Dossier nom="C" chemin="c:\dossierC"/>

	<Application contexte="/application1">
		<Partage nom="A"/>
		<Partage nom="B"/>
		<Partage nom="C"/>
	</Application>
	<Application contexte="/application2">
		<Partage nom="A"/>
		<Partage nom="B"/>
	</Application>
	<Application contexte="/application3">
		<Partage nom="A"/>
		<Partage nom="C"/>
	</Application>
	<Application contexte="/application4">
		<Partage nom="A"/>
		<Partage nom="B"/>
		<Partage nom="C"/>
	</Application>

</Partages>

Dans "C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf\Catalina\localhost\" (créer les dossiers si nécessaire), créer un fichier "application1.xml", "application2.xml" et "application4.xml". Ces trois fichiers contiennent le code xml ci-dessous :

 
Sélectionnez
<?xml version='1.0' encoding='utf-8'?>
<Context>
	<Loader className="com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappLoaderAvecPartage" 
		loaderClass="com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage"
	/>
</Context>

Relancer Tomcat après le paramétrage.

Le fichier de log "catalina.[dateDuJour].log" de "C:\Program Files\Apache Software Foundation\Tomcat 6.0\logs" doit contenir les lignes (notamment 9 à 50) ci-dessous.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
19 mai 2009 23:35:29 org.apache.coyote.http11.Http11Protocol init
INFO: Initialisation de Coyote HTTP/1.1 sur http-8080
19 mai 2009 23:35:29 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 889 ms
19 mai 2009 23:35:29 org.apache.catalina.core.StandardService start
INFO: D?marrage du service Catalina
19 mai 2009 23:35:29 org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.18
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Détermination des librairies du dossier de partage 'A' (c:\dossierA)
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierA\versionneeLibA-1.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierA\versionneeLibA-2.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Détermination des librairies du dossier de partage 'B' (c:\dossierB)
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierB\versionneeLibB-1.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierB\versionneeLibB-2.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierB\versionneeLibB-3.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Détermination des librairies du dossier de partage 'C' (c:\dossierC)
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierC\versionneeLibC-1.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setListeDossiers
INFO: Fichier 'c:\dossierC\versionneeLibC-2.0.jar' trouvé
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Détermination des librairies partagées pour l'application '/application1'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'A' - fichier : 'c:\dossierA\versionneeLibA-1.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'B' - fichier : 'c:\dossierB\versionneeLibB-1.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'C' - fichier : 'c:\dossierC\versionneeLibC-1.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Détermination des librairies partagées pour l'application '/application2'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'A' - fichier : 'c:\dossierA\versionneeLibA-1.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'B' - fichier : 'c:\dossierB\versionneeLibB-2.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Détermination des librairies partagées pour l'application '/application4'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'A' - fichier : 'c:\dossierA\versionneeLibA-2.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'B' - fichier : 'c:\dossierB\versionneeLibB-1.0.jar'
19 mai 2009 23:35:29 com.developpez.rpouiller.partagelibrairiesversionnees.loader.WebappClassLoaderAvecPartage setApplication
INFO: Librairie retenue : dossier : 'C' - fichier : 'c:\dossierC\versionneeLibC-2.0.jar'
19 mai 2009 23:35:29 org.apache.catalina.startup.HostConfig deployWAR
INFO: D?ploiement de l'archive application3.war de l'application web
19 mai 2009 23:35:30 org.apache.coyote.http11.Http11Protocol start
INFO: D?marrage de Coyote HTTP/1.1 sur http-8080
19 mai 2009 23:35:30 org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
19 mai 2009 23:35:30 org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/47  config=null
19 mai 2009 23:35:30 org.apache.catalina.startup.Catalina start
INFO: Server startup in 1429 ms

Les lignes 9 à 28 indiquent la recherche des fichiers jar dans les différents dossiers de partage. Les lignes 29 à 36 indiquent les fichiers jar retenus pour l'application 1. Les lignes 37 à 42 indiquent les fichiers jar retenus pour l'application 2. Les lignes 43 à 50 indiquent les fichiers jar retenus pour l'application 4.

Selon la configuration fixée ici, l'application 1 et l'application 4 partagent les librairies A, B et C. L'application 2 partage les librairies A et B, mais ne partage pas la librairie C. L'application 3 ne profite pas du partage de librairies versionnées, car elle n'utilise pas le ClassLoader avec partage. Si l'application 3 avait utilisé le ClassLoader avec partage, elle aurait partagé les librairies A et C, mais pas la B.

III-G. Lancement des applications avec partage des librairies versionnées

III-G-1. Lancement de l'application 1

L'URL pour accéder à l'application est : http://localhost:8080/application1/

Le résultat est :

Image non disponible
Image non disponible

III-G-2. Lancement de l'application 2

L'URL pour accéder à l'application est : http://localhost:8080/application2/

Le résultat est :

Image non disponible
Image non disponible

III-G-3. Lancement de l'application 3

L'URL pour accéder à l'application est : http://localhost:8080/application3/

Le résultat est :

Image non disponible
Image non disponible

III-G-4. Lancement de l'application 4

L'URL pour accéder à l'application est : http://localhost:8080/application4/

Le résultat est :

Image non disponible
Image non disponible

III-G-5. Bilan du lancement des applications

Les résultats obtenus correspondent à ce qui est attendu en fonction du paramétrage mis en place.

Le comportement reste inchangé en ce qui concerne les classes et ressources issues de librairies communes ou de l'application.

Les classes partagées entre deux applications sont :

  • les classes de la version 1.0 de la librairie A entre les application 1 et 2
  • les classes de la version 1.0 de la librairie B entre les application 1 et 4

On constate que le partage des classes fonctionne. Les lignes VersionneClasse1 et VersionneeClasse2 du résultat de l'application 2, ainsi que les lignes VersionneClasse3 et VersionneeClasse4 du résultat de l'application 4, indiquent l'heure, la servlet et des urls de ClassLoader de première initialisation correspondant à celle de l'application 1.

Les ressources sont bien chargées depuis les librairies des dossiers de partage :

  • versionneA.txt, versionneB.txt et versionneC.txt pour l'application 1 et l'application 4
  • versionneA.txt et versionneB.txt pour l'application 2

Comme prévu dans la configuration, l'application 3 n'entre pas dans le partage de librairies versionnées.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2009 Régis POUILLER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.