Ociensa
Gestion de l'encodage UTF-8 dans les applications Web Java avec Tomcat et Mysql


Version 1.0 24/07/2008
Le couple Tomcat - Mysql est très populaire pour le développement et l'hébergement d'applications Web Java. Il est relativement aisé de construire une application Web Java avec ces 2 outils car leur administration reste généralement simple en comparaison d'autres produits. Néanmoins l'utilisation d'un autre encodage que l'ISO Latin-1 (ISO 8859-1) dans l'application nécessite des réglages plus fins. Plus généralement, cet articles fait le point sur la gestion de l'encodage dans un projet Web en Java en analysant également les impacts au niveau des sources du projet et la migration des données d'une base déjà existante.
Dans cet articles, nous détaillerons les étapes à suivre pour configurer Mysql, Tomcat et l'application Web afin d'utiliser l'encodage UTF-8.
I   Adaptation de Mysql
I.1   Configuration du serveur
Mysql doit être configurée pour l'encodage UTF-8. On peut fixer l'encodage par défaut au niveau de la base et/ou fixer l'encodage pour chaque table. Le fichier my.ini est modifié pour fixer l’encodage par défaut du serveur.
[mysql]
default-character-set=utf8
[mysqld]
default-character-set=utf8
La modification dans la section [mysqld] est indispensable pour que les caractères ne soient pas convertis en ISO Latin-1.
Le script de création des tables peut indiquer l'encodage de chaque table :
create table nom_table (
. . .
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I.2   Migration d'une base en UTF-8
La procédure ci-dessous permet de migrer une base utilisant l'encodage ISO Latin-1 vers une base utilisant l'encodage UTF-8.
  • 1.  Exporter la base dans un fichier texte contenant les requêtes sql de création de la base et d'insertion des données à l'aide de l'outil standard mysqldump :
    mysqldump -p --default_character-set=latin1 -u root --skip-set-charset nom_base > nom_base.sql
  • 2.  Par sécurité, recopier le fichier origine cp nom_base.sql en nom_base_utf8.sql.
  • 3.  Remplacer dans le fichier précédent toutes les occurences de 'latin1' par 'utf8' dans la gestion de l'encodage
  • 4.  Encoder le fichier en UTF-8 (utiliser un éditeur qui réalise la transformation) ou utiliser, sous Linux, l'utilitaire recode (http://directory.fsf.org/recode.php) :
    recode latin1..utf8 nom_base_utf8.sql
    les .. appartiennent à la syntaxe.
Dans Mysql :
  • 1.  Supprimer la base originale encodée en ISO Latin-1
    DROP DATABASE nom_base;
  • 2.  Recréer la base encodée en UTF-8
    CREATE DATABASE nom_base CHARACTER SET utf8 COLLATE utf8_general_ci;
  • 3.  Importer les données dans la base
    mysql -uroot -p --default-character-set=utf8 --max_allowed_packet=64M nom_base < nom_base_utf8.sql
    Le fichier d'import contient généralement une seule requête SQL d'insertion par table. L'option --max_allowed_packet=64M permet de fixer la taille maximale d'une requête SQL (valeur par défaut 16M).
II   Configuration de Tomcat
II.1   Options de lancement de tomcat
On agit sur le lancement de Tomcat en ajoutant l’option -Dfile.encoding=UTF-8 au niveau de la JVM. Deux cas se présentent selon le mode de lancement.
  • - Dans le cas d'un lancement de Tomcat par script, on modifie soit le script catalina.bat pour Windows, soit catalina.sh pour Unix :
    set JAVA_OPTS=-Dfile.encoding=UTF-8 autres options éventuelles.
  • - Dans le cas d'un lancement de Tomcat via un moniteur, on configure la JVM au niveau du moniteur de Tomcat.
Cette option est indispensable car le driver Mysql s’appuie sur cet encodage pour la conversion lorsque l’encodage de mysql est UTF-8 (en fait lorsque l’encodage de mysql utilise plus d’un octet, ce qui est le cas de l'UTF-8).
II.2   Traitement des requêtes HTTP
Modifier le fichier de configuration de Tomcat conf/server.xml pour indiquer au connecteur HTTP d'accepter les url encodées en UTF-8. Cela permet, par exemple, d’ouvrir un fichier contenant des accents à partir d’un mail contenant le lien sur ce fichier.
<Connector port="8085" ... URIEncoding="UTF-8">
. . .
</Connector>
III   Configuration de l'application
On souhaite que l'application soit totalement gérée en UTF-8 : les sources et les données échangées avec le navigateur.
III.1   Encodage des fichiers
Modifier l’encodage des fichiers de configuration tels que le descripteur de l'application WEB-INF/web.xml. les descripteurs TLD des bibliothèques de tag WEB-INF/tlds/biblio1.tld Modifier toutes les pages JSP pour y inclure l’encodage UTF-8. Pour les pages utilisant la syntaxe standard JSP, ajouter la ligne suivante au début de chaque fichier.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
L'attribut pageEncoding indique au serveur d'application (ici Tomcat) l'encodage du fichier source de la page JSP pour sa bonne lecture lors de la traduction en source Java. L'indication charset=UTF-8 indique l'encodage utilisée dans la réponse HTTP faite au navigateur. Attention, la gestion de l'attribut pageEncoding est différente selon les versions de la spécifique des JSP (notamment la gestion des fragment JSP inclus). S'il est absent, la valeur du charset de l'attribut contentType sera prise en compte.

On peut simplifier l'adaptation des sources en déplaçant la déclaration d'encodage dans le descripteur web.xml.
<web-app ...>
. . .
<jsp-property-group>
<url-pattern>/jsp/*</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</web>
Pour les pages JSP écrites avec la syntaxe XML, l'attribut pageEncoding doit reprendre la même valeur que la déclaration XML initiale au début de la page.
<?xml version="1.0" encoding="UTF-8" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
contenu de la page
</jsp:root>
Dans les fichiers tagfile, situés conventionnellement dans WEB-INF/tags/, ajouter la déclaration d’encodage du fichier.
<%@ tag pageEncoding="UTF-8" %>
Le contenu html des pages et fragments JSP est adapté pour utiliser l'encodage UTF-8.
  • - Modifier les balises meta dans les pages et fragments JSP pour inclure l'encodage UTF-8.
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  • - Si les fichiers javascripts sont encodés en UTF-8, dans la balise d’inclusion de code Javascript, ajouter l’encodage :
    <script . . . charset="UTF-8"></script>
  • - La ligne suivante est ajoutée au début de chaque fichier CSS pour préciser l'encodage.
    @charset "UTF-8";
    suite de la feuilles de styles
  • - Dans la page JSP, préciser dans la balise d’inclusion link, l’encodage :
    <link rel="stylesheet" type="text/css" href="..." charset="UTF-8" />
  • - Les pages JSP possédant un formulaire peuvent être modifiées de la manière suivante : Ajouter l’attribut accept-charset="UTF-8" dans la balise form.
    <form ... accept-charset="UTF-8">
    . . .
    </form>
    En pratique, cela semble superflu car l'encodage est déjà spécifié avec le content-type fixé à UTF-8 dans la balise meta.
Si on utilise un EDI comme eclipse, on peut fixer l'encodage des éditeurs dans les préférences. Ci-dessous sont indiqués les paramètres pour les version 3.3 et 3.4 d'eclipse.
  • -Sources Java : General > Workspace.
  • -Pages HTML statiques : Web > HTML Files.
  • -Feuilles de styles CSS : Web > CSS Files.
  • -Pages JSP : Web > JSP Files
  • -Sources Javascript : General > Workspace (option dépend des plugins installés).
III.2   Traitement des requêtes HTTP
Dans le cas où l'application réalise l'upload de fichiers dont le nom peut être encodé en UTF-8, il faut lire le nom de ces fichiers en UTF-8. La bibliothèque commons-fileupload d'Apache permet de fixer l'encodage via la méthode DiskFileUpload.setHeaderEncoding(String encodage). Cette méthode doit être invoquée avant analyse de la requête.
Ajouter un filtre pour fixer l’encodage de lecture de la requête à UTF-8. Ce filtre est obligatoire pour lire correctement les données soumises par des formulaires HTML sans attribut enctype="multipart/form-data". Apache fournit un tel filtre qui doit être déclaré comme suit dans le descripteur web.xml.
<web-app ... >
. . .
<filter>
<filter-name>CharacterEncoderFilter</filter-name>
<filter-class>org.apache.webapp.admin.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<filter-mapping>
<filter-name>CharacterEncoderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Le paramètre obligatoire encoding (aucune valeur par défaut) indique l'encodage de lecture du flux. Le paramètre optionnel ignore (valeur par défaut true) permet d'ignorer (true) une information d'encodage envoyée par le client HTTP.
III.3   Fichiers de données
Si l'application utilise des fichiers de configuration ou de données, ces fichier pourront utiliser un encodage arbitraire (UTF-8 ou ISO Latin-1). Cependant dans le code de l'application, il sera nécessaire de préciser l'encodage lors de la lecture (typiquement avec une instance de Reader s'appuyant sur une instance de la classe InputStreamReader pour indiquer l'encodage utilisé en lecture).

Copyright Ociensa Technologies - 1.0 24/07/2008