La configuration de Tomcat passe par l'édition du fichier server.xml
qui
se trouve dans le répertoire conf
de l'installation. Ce fichier XML se découpe
en différents éléments, qui correspondent à des notions propres à Tomcat, que nous allons
voir une par une.
Ces éléments XML se divisent en deux groupes. Le premier est constiué des éléments
conteneur : Engine
: Host
et Context
. Ces éléments
s'imbriquent les uns dans les autres suivant des règles très précises, et sont obligatoires.
Le second groupe est constitué d'éléments facultatifs et flottants, au sens où ils
peuvent être placés dans les différents conteneurs : GlobalNamingResources
,
Resources
, Realm
et Valve
.
L'unique élément racine Server
modèlise un serveur Catalina dans sa totalité.
Catalina est le nom du serveur proprement dit, alors que Tomcat est le nom du projet complet.
Tout ce qui est défini dans cet élément est global au serveur, et sera donc appliqué, entre
autres, à toutes les applications web qu'il supporte. Techniquement, un server
est un élément XML, et une interface Java : org.apache.catalina.Server
.
Cet élément supporte trois attributs :
className
: la classe d'implémentation de l'interface
org.apache.catalina.Server
. En principe on n'en change pas, et l'on peut
omettre cet attribut, qui prendra alors sa valeur par défaut.
port
: le port d'arrêt (shutdown) de Tomcat.
Ce port ne correspond pas au port HTTP écouté par Tomcat, mais à un port sur lequel Tomcat
reçoit la commande de s'arrêter. Lorsque l'on tape la commande shutdown
sur
l'invite de commande, cet ordre est émis, et Tomcat s'éteint alors.
shutdown
: la commande envoyée sur ce port.
Seul l'attribut port
est réellement susceptible d'être modifié. Cette modification
est même nécessaire si l'on installe plusieurs Tomcat en même temps sur une même machine.
L'élément server
accepte trois type d'enfants : des Listener
, un
GlobalNamingResources
et des Service
Un service est un container dans lequel on peut trouver autant de sous-éléments
Connector
que l'on veut, et un unique sous-élément Engine
.
On peut définir autant de services que l'on veut au sein d'un serveur donné, à condition
qu'ils diffèrent tous par leurs noms.
Cet élément possède deux attributs :
className
: nom de la classe Java qui implémente l'interface
org.apache.catalina.Service
. Peut être omis, dans ce cas l'implémentation
par défaut est choisie.
name
: nom logique de ce service, doit être unique au sein de tous
les services définis dans un serveur.
Un connecteur est un objet Java capable d'écouter un port précis et comprenant un protocole précis. À chaque protocole supporté par Tomcat est associé une classe Java de connecteur. Chaque connecteur dirige ensuite les requêtes qu'il reçoit au moteur de servlets défini dans ce service. Donc un moteur de servlet donné peut répondre à des requêtes en provenance de plusieurs ports, et suivant des protocoles différents.
Quel que soit le connecteur choisi, un certain nombre d'attributs sont toujours disponibles :
port
: le port que ce connecteur écoute.
enableLookups
: autorise ou non les requêtes DNS lorsqu'une
servlet invoque la méthode request.getRemoteHost()
. Une requête DNS est un
processus éventuellement coûteux. Si enableLookups
est à false
,
alors request.getRemoteHost()
retourne l'adresse IP plutôt que le nom de
domaine associé.
maxPostSize
: la taille maximale des requêtes POST
supportée
par ce serveur. La valeur par défaut est fixée à 2Mo. Une valeur de -1 signifie qu'il
n'y a pas de limite.
Le connecteur HTTP supporte le standard HTTP/1.1, et permet à Tomcat de fonctionner comme un serveur web à part entière.
maxThreads
: indique le nombre maximal de requêtes que ce connecteur peut
traiter à la fois (une requête est traitée dans son propre thread par le connecteur HTTP).
Au-delà de cette limite, les requêtes sont placées dans une file d'attente.
acceptCount
: taille maximale de la file d'attente des requêtes. Si des
requêtes supplémentaires arrivent, alors elles sont rejetées.
protocol
: cet attribut peut prendre deux types de valeur, un nom de protocole,
tel que HTTP/1.1
, ou un nom de classe. Les trois classes supportées par
défaut sont :
org.apache.coyote.http11.Http11Protocol
: correspond au protocole HTTP/1.1
standard.
org.apache.coyote.http11.Http11NioProtocol
: correspond au protocole HTTP
construit sur l'API NIO.
org.apache.coyote.http11.Http11AprProtocol
: correspond au protocole
construit sur l'APR (Apache Portable Runtime, qui est la librairie runtime utilisée
par Apache 2.2).
Le connecteur AJP communique avec un autre serveur web, en utilisant le protocole AJP. Il existe deux connecteurs de ce type :
JK 1.2.* : permet de communiquer avec tous les serveurs web qui supportent le protocole JK ;
mod_proxy
: permet de communiquer avec Apache 2.2.*.
Exemple 2.4. Exemple d'éléments Connector
<!-- Port standard d'écoute de requêtes HTTP sur le port 8080 --> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" maxThread="100" maxCount="100" redirectPort="8443" /> <!-- Port standard d'écoute de requête Apache AJP/1.3 sur le port 8009 --> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
L'élément Engine
modélise le moteur de servlet proprement dit. À chaque
serveur Catalina est associé un unique moteur de servlet, auquel on accède via des connecteurs.
Un moteur peut comporter plusieurs sous-éléments Host
, chacune représentant un
hôte virtuel.
Toutes les implémentations par défaut de cet élément supportent les attributs suivants :
name
: le nom de ce moteur, notamment utilisé dans les fichiers journal ;
defaultHost
: l'hôte virtuel par défaut vers lequel diriger cette
requête.
Cet élément modélise un hôte virtuel. Un hôte virtuel doit être associé à l'adresse IP
de ce serveur, via un DNS ou un fichier hosts
. Si l'attribut
defaultHost
du moteur dans lequel se trouve cet hôte est défini, alors
au moins un des hôtes doit obligatoirement posséder ce nom.
Toutes les implémentations de Host
doivent supporter les attributs suivants :
name
: le nom de cet hôte virtuel ;
appBase
: l'application base pour cet hôte. Il s'agit
du répertoire contenant les applications web pour cet hôte. Il peut s'agir d'un chemin
absolu, ou d'un chemin relation au répertoire $CATALINA_BASE
. La valeur
par défaut de cet attribut est true
.
autoDeploy
: indique à Tomcat s'il doit charger automatiquement
les applications web qui sont copiées dans le répertoire appBase
. Le cas
échéant, Tomcat scrute le contenu de ce répertoire à intervalles de temps réguliers,
afin de détecter de nouveaux répertoires, ou de nouveaux fichiers .war
.
deployOnStartup
: indique à Tomcat s'il doit charger les applications web
se trouvant dans appBase
lors de son démarrage. La valeur par défaut de cet
attribut est true
.
L'implémentation par défaut supporte en plus les attributs suivants.
workDir
: répertoire de travail propre à cet hôte. Par défaut, Catalina
utilise un sous-répertoire de $CATALINA_BASE/work
pour chaque hôte, mais
il est possible d'en choisir un autre. On peut accéder à ce répertoire d'une servlet,
via l'attribut d'application javax.servlet.context.tempdir
.
unpackWARs
: indique à Catalina qu'il doit exécuter les applications web de cet
hôte dans un répertoire de travail avant de les exécuter. La valeur par défaut de cet
attribut est true
.
deployXML
: indique à Catalina s'il peut ou non prendre en compte les fichiers
/META-INF/context.xml
des applications web. La valeur par défaut de cet
attribut est true
.
Enfin, l'élément Context
modélise une application web. Cet élément est un peu
particulier, car il peut se trouver en plusieurs endroits.
Historiquement (Tomcat v4), cet élément se trouvait nécessairement dans le fichier
server.xml
de Tomcat. Cette façon de faire posait deux problèmes :
toute modification d'un contexte pour une unique application web imposait de redémarrer l'ensemble du serveur Tomcat, puisque ce fichier est pris en compte au démarrage de Tomcat ;
ce fichier étant la propriété de l'administrateur de Tomcat, il y avait un problème de partage de droits, puisque ce sont les administrateurs des applications web qui connaissent le contenu de ces éléments.
Ce problème a été levé, et il est maintenant possible de placer cet élément dans un fichier
context.xml
rangé dans le répertoire META-INF
de l'application.
La prise en compte ou non de ce fichier à cet endroit peut être réglé par l'administrateur
de Tomcat (attribut deployXML
de l'élément Host
). S'il choisit
de ne pas prendre en compte ces fichiers, alors Tomcat ira chercher ce fichier ailleurs,
dans un endroit contrôlé par l'administrateur.
On peut définir autant d'éléments Context
que l'on veut dans un hôte virtuel,
avec une restriction :
chaque élément Context
doit être associé à un unique attribut path
,
qui représente le chemin vers cette application web.
au moins un Context
doit être associé à un chemin vide. C'est cette application
qui servira toutes les requêtes non servies par les autres applications.
Cet élément permet de configurer des ressources et des variables d'environnement, qui seront ensuite disponibles pour toutes les applications web gérées par cette instance de Tomcat. La distribution par défaut nous donne l'exemple de la base d'utilisateurs reconnue par cet instance de Tomcat.
Voyons tout d'abord un exemple de définition d'une variable d'environnement.
Exemple 2.7. Exemple de variable d'environnement
<Environment name="maxRetry" type="java.lang.Integer" value="10" override="false"/>
Notons que l'attribut name
, qui représente le nom de la variable créée,
est relatif au context java:comp/env
. Dans notre exemple, le nom JNDI
complet de la variable sera donc java:comp/env/maxRetry
.
Examinons maintenant la ressource définie dans le server.xml
par défaut.
Exemple 2.8. Exemple de Resources
: UserDatabase
<Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
Toute ressource doit posséder un nom (attribut name
) et un type
,
sous forme d'une classe Java. Cette ressource UserDatabase
sera donc
représentée par un objet de type UserDatabase
, que Tomcat peut construire
grâce à la classe factory MemoryUserDatabaseFactory
.
Cet objet a besoin du paramètre suivant pour s'initialiser : pathname
, qui
lui indique dans quel fichier les utilisateurs sont définis.
On peut définir des ressources très diverses, les plus fréquentes sont les connexions
aux bases de données, disponibles sous forme de DataSource
, ou aux
serveurs de mail.
Exemple 2.9. Exemple de Resources
: serveur de mail
<Resource name="mail/Session" auth="Container" type="javax.mail.Session" mail.smtp.host="localhost"/>
Nous verrons dans la suite comment il est possible d'accéder à ces ressources d'une application web.
Un Realm
est une notion qui appartient à l'environnement de sécurité de Java,
défini par JAAS (Java Authentification and Authroization Service)
pour la partie JSE et JCCA pour la partie JEE. Par définition, un Realm
est un ensemble d'utilisateurs qui partagent la même méthode d'authentification. Techniquement
pour Tomcat, un Realm
est défini par cette méthode d'authentification.
Comme il a été dit, un élément Realm
est un élément qui peut être inclus dans
plusieurs éléments conteneur du fichier conf/server.xml
de Tomcat :
Engine
, Host
ou Context
. Un élément Realm
inclus dans un élément parent est automatiquement hérité dans les éléments enfants. Ainsi,
un Realm
défini dans un Host
(donc au niveau d'un hôte virtuel)
sera automatiquement pris en compte par tous les Context
(donc les applications
web) de ce Host
.
Un élément Realm
permet d'associer des droits d'utilisation aux application web.
Un tel élément modélise une base d'utilisateurs et rôles, qu'il est ensuite possible d'utiliser
pour restreindre l'utilisation de certaines applications.
Tomcat propose plusieurs implémentations de Realm
. Nous en avons déjà vu une :
org.apache.catalina.realm.MemoryRealm
, qui utilise le fichier
tomcat-users.xml
. Les autres implémentations permettent notamment de lire la liste
des utilisateurs et des rôles dans des bases de données, qui elles-mêmes peuvent être administrées
par ailleurs.
Le choix d'une implémentation particulière se fait en fixant une valeur à l'attribut
className
de l'élément Realm
.
Examinons une de ces implémentations : org.apache.catalina.realm.JDBCRealm
. Dans
cette implémentation, les utilisateurs sont enregistrés dans une table. Tomcat a besoin de
plusieurs informations pour y accéder :
la chaîne de connexion à la base de données, un nom d'utilisateur et un mot de passe ;
le nom de la table, et des différentes colonnes où se trouvent les informations dont il a besoin.
Voici un exemple d'un tel élément.
Exemple 2.10. Configuration d'un Realm
JDBC
<Realm className="org.apache.catalina.realm.JDBCRealm" driverName="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/db_user" connectionName="admin_db" connectionPassword="passwd" userTable="t_user" userNameCol="user_name" userCredCol="passwd" userRoleTable="t_role" roleNameCol="role_name" />
On reconnaît sans problème les paramètres de connexion à la base parmi les attributs de cet
élément. Viennent ensuite les paramètres de la table contenant les utilisateurs. Tomcat
utilise deux colonnes : userNameCol
porte les noms des utilisateurs, et
userCredCol
leurs mots de passe. Notons que par défaut les mots de passe
sont stockés en clair, mais qu'il est possible de configurer ce Realm
pour qu'ils
soient cryptés. Enfin, viennent les paramètres de la table des rôles : userRoleTable
porte le nom de cette table et userRoleTable
le nom de la colonne qui contient
les noms des rôles. Cette table doit aussi comporter une colonne userNameCol
,
qui réalise la jointure entre la table des utilisateurs et celle des rôles.
Il existe également une implémentation de Realm
capable de s'adresser à une
DataSource
directement : org.apache.catalina.realm.DataSourceRealm
.
La DataSource
est obtenue par une requête JNDI dont le nom est donné en paramètre.
Voyons un exemple.
Exemple 2.11. Configuration d'un Realm
sur une DataSource
<Realm className="org.apache.catalina.realm.DataSourceRealm" dataSourceName="jdbc/UserDataSource" userTable="t_user" userNameCol="user_name" userCredCol="passwd" userRoleTable="t_role" roleNameCol="role_name" />
Les attributs permettant de lire les tables d'utilisateurs et de rôles sont les mêmes
que pour le Realm
JDBC.
De même que l'élément Realm
, l'élément Valve
peut se placer dans les
conteneurs Engine
, Host
et Context
. De même que pour
le Realm
, une valve définie au niveau d'un conteneur s'applique à ses
sous-conteneurs.
Une valve est un élément utilitaire, qui peut avoir différentes fonctions, qui s'insère dans le
processus de traitement d'une requête, et realise un certain traitement. À chaque
type de valve est associé un traitement particulier.
Un élément Valve
comporte
toujours un attribut className
, qui indique quelle implémentation de Valve
est utilisée pour cet élément. Suivent ensuite une liste d'attributs propres à chaque
implémentation.
Voyons les principales implémentations de Valve
:
La valve de journalisation permet d'enregistrer des informations sur chaque requête, au même format que les serveurs web classiques (Apache notamment).
Exemple 2.12. Valve de journalisation
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="fichier_log" resolveHosts="false" rotatable="true" fileDateFormat="yyyy-MM-dd.HH"/>
On crée ici une valve de journalisation, qui enregistre ses informations dans des fichiers
dont le préfixe est fichier_log
, dans le répertoire
$CATALINA_BASE/logs
. Ce fichier subira une rotation toutes les heures, ce qui
est fixé par la valeur de l'attribut yyyy-MM-dd.HH
. L'attribut
resolveHost
décide si une requête DNS est faite afin d'enregistrer le nom
d'hôte du client, ou juste son adresse IP.
Ces valves permettent de refuser l'accès aux appplications web en fonction de l'adresse IP du client, ou en fonction de son nom d'hôte.
Exemple 2.13. Valve de filtrage par adresse IP
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="192.168.1.*" deny="192.168.0.*"/> <Valve className="org.apache.catalina.valves.RemoteHostValve" allow="*.fr" deny="*.com"/>
Dans les deux cas, la stratégie d'accès est la même. Si l'attribut allow
est présent,
alors la requête doit correspondre à sa valeur. S'il n'est pas présent,
alors la requête est acceptée, sauf si elle correspond à la valeur
de l'attribut deny
. Dans tous les cas, une requête qui correspond à la valeur
de l'attribut deny
est rejetée.
Notons enfin que l'attribut deny
de RemoteHostValve
est décodé
en utilisant la expressions régulières Java (package java.util.regex
).
Tomcat propose d'autres valves que celles présentées ici :
Une valve de débugage : la RequestDumperValve
permet d'enregistrer dans un
fichier l'intégralité des paramètres d'une requête HTTP. Elle ne doit être utilisée
qu'en phase de mise au point d'une application, sous peine de saturer rapidement
le disque dur.
Des valves de sécurité et d'authentification :
SingleSignOn
: permet de s'authentifier sur plusieurs applications
web d'un même hôte virtuel ;
BasicAuthenticator
et DigestAuthenticator
: permettent
de forcer l'authentification HTTP basic ou digest
pour l'accès à une application web.
FormAuthenticator
et SSLAuthenticator
: activent
l'authentification par formulaire, en mode normal ou SSL.