III. PARTAGE DE LIBRAIRIES VERSIONNEES▲
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 partie des librairies Common, System ou Bootstrap.
Le diagramme ci-dessous représente les dépendances possibles entre catégories de classes.
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 classes 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▲
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.
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.
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 ».
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 |
---|---|---|---|---|---|---|
1re lib. du Common |
commonLibA |
commonA.txt |
commonA |
|||
2e lib. du Common |
commonLibB |
commonB.txt |
commonB |
|||
Lib. propre à l'Appli |
applicationLib |
application.txt |
application |
|||
1re lib. versionnée |
versionneeLibA-[NumeroVersion] |
1.0 |
versionneeA.txt |
versionneeA-1.0 |
||
2e lib. versionnée |
versionneeLibB-[NumeroVersion] |
1.0 |
versionneeB.txt |
versionneeB-1.0 |
||
3e lib. versionnée |
versionneeLibC-[NumeroVersion] |
1.0 |
versionneeC.txt |
versionneeC-1.0 |
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] ».
<?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>
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
" "
;
}
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"
, " "
);
}
catch
(
ex.ception 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
?" "
: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
(
" "
);
}
}
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 |
||
Application2 |
commonLibA, commonLibB, applicationLib, versionneeLibA-1.0, versionneeLibB-2.0 et versionneeLibC-2.0 |
||
Application3 |
commonLibA, commonLibB, applicationLib, versionneeLibA-2.0, versionneeLibB-3.0 et versionneeLibC-1.0 |
||
Application4 |
commonLibA, commonLibB, applicationLib, versionneeLibA-2.0, versionneeLibB-1.0 et versionneeLibC-2.0 |
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 :
III-D-2. Lancement de l'application 2▲
L'URL pour accéder à l'application est : http://localhost:8080/application2/
Le résultat est :
III-D-3. Lancement de l'application 3▲
L'URL pour accéder à l'application est : http://localhost:8080/application3/
Le résultat est :
III-D-4. Lancement de l'application 4▲
L'URL pour accéder à l'application est : http://localhost:8080/application4/
Le résultat est :
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 :
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▲
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 substitué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▲
package
com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;
/** Classe contenant les informations 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;
}
}
package
com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;
import
java.util.LinkedList;
import
java.util.List;
/** Classe contenant les informations 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);
}
}
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;
}
}
package
com.developpez.rpouiller.partagelibrairiesversionnees.loader.config;
import
java.util.LinkedList;
import
java.util.List;
/** Classe contenant les informations 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);
}
}
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
;
}
}
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é */
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
(
ex.ception 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);
}
}
}
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 URL de ce ClassLoader.
*
* Ces URL 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é 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é 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
;
}
}
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 :
<?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 :
<?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.
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-F. Lancement des applications avec partage des librairies versionnées▲
III-F-1. Lancement de l'application 1▲
L'URL pour accéder à l'application est : http://localhost:8080/application1/
Le résultat est :
III-F-2. Lancement de l'application 2▲
L'URL pour accéder à l'application est : http://localhost:8080/application2/
Le résultat est :
III-F-3. Lancement de l'application 3▲
L'URL pour accéder à l'application est : http://localhost:8080/application3/
Le résultat est :
III-F-4. Lancement de l'application 4▲
L'URL pour accéder à l'application est : http://localhost:8080/application4/
Le résultat est :
III-F-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 applications 1 et 2
- les classes de la version 1.0 de la librairie B entre les applications 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 URL 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.