Encodage des applications Java

Jérôme Lafosse

Cet article est un extrait gratuit de l'ouvrage Java EE - Guide de développement d'applications web en Java, Chapitre 1 : Objectifs et spécifications de Java EE, section "Encodage des applications Java".

Commentez

- Titre : Encodage des applications Java

- Auteur : Jérôme Lafosse

- Parution : 8 juillet 2009

- Mise à jour : 8 juillet 2009

- Licence : Copyright ® 2009 Jérôme Lafosse / ENI. 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.

Sommaire

- 1. Présentation

- 2. Les fichiers

- 3. Les pages JSP et JSPF

- 4. Les pages HTML/XHTML

- 5. Les feuilles de style XSL

- 6. Code Java

- 7. Encodage de la JVM

- 8. Gestion de l'encodage

1 - Présentation

Les ordinateurs travaillent avec des bits ou suites d'octets (un octet=8 bits). Les chaînes de caractères, entiers, réels sont donc codées sous forme d'octets. Les ordinateurs utilisent donc un procédé qui consiste à transformer les chaînes de caractères en octets, associé à une technique afin de relire les chaînes d'origine. C'est ce procédé qui est appelé encodage.

Il existe plusieurs encodages qui utilisent plus ou moins le même nombre d'octets, donc de caractères disponibles comme ISO-8859-1, ASCII, UTF-8...
L'encodage UTF-8 est le plus pratique pour échanger des textes constitués de caractères UNICODE (standard du consortium Unicode). Ce consortium a pour but de répertorier tous les caractères utilisés dans les différentes langues et d'associer à chacun un code noté sous forme hexadécimal. L'encodage UTF-8 est compatible avec l'encodage ASCII ce qui est très pratique lors des développements informatiques.

Lors des développements d'applications Java et/ou Java EE, il n'est pas rare de constater de nombreux problèmes d'encodage des applications tant au niveau des messages présents dans un navigateur, que des fichiers de propriétés d'une application ou encore de l'encodage des caractères saisis au clavier. La mise en oeuvre d'une application Java nécessite donc la gestion de plusieurs paramètres. La difficulté est qu'il ne faut pas en oublier un seul sous peine de constater l'affichage de "hiéroglyphes" à la place du caractère souhaité.

2 - Les fichiers

La première contrainte à vérifier est que tous les fichiers (HTML, JSP, JSPF, XML, XSLT...) de l'application développée soient dans l'encodage souhaité. Pour cela, la plupart des IDE peuvent se paramétrer afin de sélectionner l'encodage. Avec Eclipse, nous pouvons sélectionner l'encodage dans le menu Fenêtre - Préférences - Editeurs - Codage du fichier texte et Types de contenu.

3 - Les pages JSP et JSPF

Il est nécessaire de déclarer dans chaque page JSP ou JSPF d'entête l'encodage utilisé. Pour cela, il faut utiliser la directive JSP adaptée.

<jsp:directive.page contentType="text/html;charset=UTF-8" />

ou

<%@ page contentType="text/html;charset=UTF-8" %>

Il est possible également de centraliser l'encodage dans le fichier de configuration et de déploiement de l'application web.xml du serveur Tomcat.

<jsp-config>
<jsp-property-group>
<description>Config. de l'encodage des pages JSP</description>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
</jsp-property-group>
</jsp-config>

4 - Les pages HTML/XHTML

Il est également important de prévenir le navigateur client de l'encodage qu'il doit utiliser pour afficher la page HTML/XHTML. Cette directive est précisée avec la balise meta et le paramètre content.

<head>
<meta equiv="Content-Type" content="text/html;charset=UTF-8">
...
</head>

Il est également possible de préciser l'encodage d'une feuille de style externe à l'aide de la directive placée en tout début de fichier.

@charset "UTF-8";

5 - Les feuilles de style XSL

Si des transformations XSLT sont utilisées dans notre application, il est nécessaire de déclarer également explicitement l'encodage dans ces pages.

<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8"
indent="yes" />

6 - Code Java

Du point de vue du code Java, il est possible d'utiliser un filtre qui va forcer le serveur d'applications Java à lire les paramètres de la requête dans l'encodage souhaité et qui va renvoyer les réponses avec le même encodage.

package application.filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {
public static final String ENCODING = "encoding";
private String encoding;

public void init(FilterConfig filterConfig) throws ServletException
{
this.encoding = filterConfig.getInitParameter(ENCODING);
}

public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain filterChain)throws IOException, ServletException
{
req.setCharacterEncoding(encoding);
resp.setContentType("text/html;charset="+encoding);
filterChain.doFilter(req, resp);
}

public void destroy() {}
}

Il est alors possible de déclarer ce filtre au sein du fichier de configuration de l'application web.xml. Les filtres étant exécutés dans l'ordre de déclaration, ce mapping doit être le premier déclaré dans le fichier de configuration.

<filter>
<filter-name>encodingfilter</filter-name>
<filter-class>application.filters.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

L'encodage peut aussi être géré au sein d'une Servlet générique. Chaque Servlet du projet devra alors ensuite hériter de cette Servlet.

package application.servlets;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class EncodingServlet extends HttpServlet {

public static final String ENCODING = "encoding";
private String encoding;

public void init(ServletConfig servletConfig)throws ServletException
{
this.encoding = servletConfig.getInitParameter(ENCODING);
}

public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException
{
req.setCharacterEncoding(encoding);
resp.setContentType("text/html;charset="+encoding);
}

public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
}
}

7 - Encodage de la JVM

Il est important d'exécuter la JVM dans l'encodage voulu. Le traitement des chaînes de caractères doit être le même que le reste de l'application. C'est au lancement de la JVM, donc au lancement du serveur Java EE, que l'encodage est spécifié à l'aide de l'argument : -Dfile.encoding=UTF-8

Avec Tomcat, cet argument est spécifié dans le fichier de lancement du serveur, catalina.sh.

JAVA_OPTS="$JAVA_OPTS "-Dfile.encoding=utf-8"

Il est parfois également nécessaire de vérifier l'encodage des URL de l'application. Avec Tomcat, cet encodage est déclaré explicitement via l'attribut URIEncoding sur le connecteur Coyote. Voici la ligne du fichier server.xml concerné :

<Connector port="8080" maxHttpHeaderSize="8192"... URIEncoding="UTF-8" />

Le code suivant est très utile car il permet la transformation d'une chaîne de caractères dans un encodage précis.

public static String transformStringEncoding(String init, String encodingBefore,
String encodingAfter)
{
try
{
return new String(init.getBytes(encodingBefore), encodingAfter);
}
catch (UnsupportedEncodingException uee)
{
return null;
}
}

8 - Gestion de l'encodage

Il est tout à fait possible en Java de gérer l'encodage à utiliser. Pour les flots d'entrée par exemple, la connexion est réalisée à l'aide de la classe InputStreamReader. Le jeu de caractères à utiliser peut être alors précisé. Par défaut, le jeu de caractères est fonction du système d'exploitation utilisé et de la localisation (ex : fr_FR UTF-8).

Avec un flot, il est possible de préciser l'encodage utilisé :

InputStreamReader i=new InputStreamReader(is,"UTF-8");

Le nom du jeu de caractères utilisé par défaut est obtenu en programmation avec la méthode System.getProperty() et le paramètre file.encoding.

package com.betaboutique.classes;

public class Programmation {
public static void main(String[] args) {
System.out.println("Encodage : "+System.getProperty("file.encoding"));
}
}

Lors des développements Web, il est assez courant que les paramètres reçus par les méthodes HTTP GET et POST ne soient pas dans un format correct. Les problèmes portent alors sur les accents, les caractères spéciaux... Pour cela, la transformation d'un encodage peut être forcé en utilisant les octets. Le code ci-dessous permet de transformer un paramètre reçu en caractères UTF-8.

String parametre=(String)request.getParameter("parametre");
String parametreUTF8=new String(parametre.getBytes(),"UTF-8");

De même pour les envois d'informations en programmation Java à travers des flux, l'encodage et les transformations de jeux de caractères sont utilisés.

package com.betaboutique.classes;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;

public class Programmation {
public static void main(String[] args) {
try
{
//paramètre à envoyer en direction du flux
String parametreaenvoyer="mon paramètre";
String unautreparametreaenvoyer="un autre paramètre";
String unautreparametreeniso="un paramètre en iso";
URL url=new URL("http://www.essai.com"+"?parametre1="
+URLEncoder.encode (parametreaenvoyer,"UTF-8")
+"&parametre2="+URLEncoder.encode(new String
(unautreparametreaenvoyer.getBytes(), "UTF-8"),"UTF-8")
+"&parametre3="+URLEncoder.encode(unautreparametreeniso,"ISO-8859-1"));

//poster les informations dans le flux
InputStream fluxLecture=url.openStream();
//fermer le flux
fluxLecture.close();
}
catch(Exception e)
{
System.out.println("Erreur de connexion");
}
}
}