Intéressé par des cours d'informatique en ligne ?
Visitez mon nouveau site https://www.yesik.it !

Cet article est une reprise de l'article Accéder à un pilote de base de données via JNDI que j'avais précédemment publié sur http://wiki.esicom-st-malo.fr

JNDI [1] est un service d'annuaire qui permet à un composant d'accéder à des objets gérés par son serveur d'application. Dans ce tutoriel, nous allons voir comment enregistrer un pilote de base de données auprès de JNDI pour le rendre accessible aux différentes applications hébergées par JBoss AS.

Objectifs A la fin de ce tutoriel, vous saurez:
  • Lier un pilote de base de données à un nom JNDI dans le serveur d'application JBoss
  • Accéder à une base de données via JNDI à partir d'une Servlet
Prérequis
Moyens


Préparation

Avant de commencer, nous allons créer une base de données "de test" dans MySQL:

CREATE DATABASE JNDITest;
USE JNDITest;
CREATE TABLE Dance (name VARCHAR(30) PRIMARY KEY NOT NULL);
INSERT INTO Dance VALUES
           ("Hip-Hop"), 
           ("Krump"), 
           ("Jazz"), 
           ("Street"), 
           ("Belly Dancing"), 
           ("Tribal Fusion")
;

Il est fortement déconseillé de laisser des applications accéder à un SGBD avec les autorisations super-utilisateur. Par conséquent nous allons ajouter un nouvel utilisateur, et lui donner toutes les permissions sur les tables de la base que nous venons de créer:

GRANT ALL PRIVILEGES 
     ON JNDITest.* 
     TO jbosstest@localhost 
     IDENTIFIED BY 'secret-password'
;

Vous pouvez tester que la création de la base de données et de l'utilisateur associé ont bien fonctionné en utilisant la commande suivante à partir du shell:

sh$ echo 'SELECT * FROM Dance' | mysql -u jbosstest -psecret-password -D JNDITest
name
Belly Dancing
Hip-Hop
Jazz
Krump
Street
Tribal Fusion

Enregistrer le driver

Tout d'abord, il faut rendre le driver pour votre SGBD accessible à JBoss. Pour ce faire, copier simplement le pilote JDBC dans le répertoire server/default/lib de votre installation de JBoss AS:

sh$ cp /path/to/mysql-connector-java-5.0.7-bin.jar $JBOSS_HOME/server/default/lib

Maintenant que JBoss peut accéder au driver, il faut l'enregistrer dans l'annuaire JNDI. A nouveau dans JBoss la procédure et assez simple, puisqu'il suffit de créer le fichier mysql-test-ds.xml puis de le copier dans le répertoire $JBOSS_HOME/server/default/deploy.

Remarque:

Le nom du fichier doit impérativement se terminer par -ds.xml pour être détecté par le système de déploiement automatique de JBoss.

Soyez bon avec JBoss

Procédez bien en deux temps:

  1. d'abord éditer le fichier,
  2. puis le recopier dans le répertoire de déploiement.

Si vous travaillez directement dans ce dernier, à chaque enregistrement vous forcez JBoss à redéployer le pilote de base de données!

<datasources>
    <local-tx-datasource>
        <jndi-name>MySQLTestDS</jndi-name>
        <connection-url>jdbc:mysql://localhost/JNDITest</connection-url>                 
        <driver-class>com.mysql.jdbc.Driver</driver-class>
        <user-name>jbosstest</user-name>
        <password>secret-password</password>
    </local-tx-datasource>
</datasources>

Le contenu du fichier mysql-test-ds est assez facile à interpréter. C'est un fichier XML qui enregistre une nouvelle source de données (datasource) avec les caractéristiques suivantes:

Une fois votre fichier édité, vous pouvez le copier dans le répertoire de déploiement de JBoss:

sh$ cp /path/to/mysql-test-ds.xml $JBOSS_HOME/server/default/deploy

Si tout se passe bien, vous devriez voir apparaître la ligne suivante dans le journal de JBoss:

10:53:55,627 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager
'jboss.jca:service=DataSourceBinding,name=MySQLTestDS' to JNDI name 'java:MySQLTestDS'

Maintenant, tout ce qu'à besoin de connaître une application hébergée par JBoss AS pour accéder à votre base de données, c'est son nom JNDI java:MySQLTestDS.

Tester

Pour tester, nous allons écrire une petite servlet. Celle-ci va se connecter à la base de données et renvoyer le contenu de la table Dance.

package fr.esicom.demo;
 
import java.io.*;
 
import javax.sql.*;
import java.sql.*;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/**
 * Servlet implementation class for Servlet: JNDITestServlet
 *
 */
public class JNDITestServlet 
             extends javax.servlet.http.HttpServlet {
 
    /* (non-Java-doc)
     * @see javax.servlet.http.HttpServlet#HttpServlet()
     */
    public JNDITestServlet() {
	super();
    }   	
 
    /* (non-Java-doc)
     * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                     throws ServletException, IOException {
	response.setContentType("text/plain");
	PrintWriter  os = new PrintWriter(response.getOutputStream());
 
	Connection conn = null;
	Statement stmt = null;
	ResultSet rs = null;
 
	try {
	    /*
	     * Recherche de la source de données
	     * dans l'annuaire JNDI
	     */
	    InitialContext ctx = new InitialContext();
	    DataSource ds = (DataSource) ctx.lookup("java:MySQLTestDS");
 
	    /*
	     * Requête JDBC
	     */
	    conn = ds.getConnection();
	    stmt = conn.createStatement();
	    rs = stmt.executeQuery("SELECT name FROM Dance");
 
	    /*
	     * Génère le réponse
	     * (HTTP/200 OK)
	     */
	    response.setStatus(200);
	    os.println("Esicom Dance Skool");
	    while(rs.next()) {
		String danceName = rs.getString("name");
		os.println("- "+danceName);
	    }
	}
	catch (Exception e) {
	    /*
	     * Rapporte l'erreur détectée
	     * (HTTP/500 Internal Server Error)
	     */
	    response.setStatus(500);
	    os.println(e.toString());
	}
	finally {
	    /*
	     * Dans tous les cas, libérer les ressources
	     */
	    os.close();
	    try { rs.close(); } catch(Exception ignored) {}
	    try { stmt.close(); } catch(Exception ignored) {}
	    try { conn.close(); } catch(Exception ignored) {}
	}
    }  	   	  	    
}

La seule différence entre le code ci-dessus et celui d'une servlet qui accède directement au driver de la base de données se trouve dans les lignes:

/* ... */
	    InitialContext ctx = new InitialContext();
	    DataSource ds = (DataSource) ctx.lookup("java:MySQLTestDS");
/* ... */

L'objet ctx représente le contexte initial dans lequel s'exécute la servlet. C'est au travers de cet objet qu'il est possible d'interroger JNDI. L'appel à la méthode ctx.lookup fait effectivement l'interrogation de l'annuaire, et recherche l'objet enregistré sous le nom java:MySQLTestDS.

Remarque:

Comme l'annuaire peut stocker n'importe quel type d'objet, la méthode lookup renvoie une référence sur un Object.

Avant de pouvoir manipuler la référence renvoyée, il est donc nécessaire de convertir celle-ci en une référence vers une instance du type réel de l'objet (ici DataSource). C'est à ça que sert la coercission (DataSource).

Une fois la Servlet compilée et déployée, vous pouvez la tester à l'aide d'un client http (navigateur ou autre). Ici, nous testerons avec curl, afin de pouvoir visualiser également l'en-tête http renvoyé:

sh$  curl -D - 'http://localhost:8080/JNDITest/JNDITestServlet'
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: Servlet 2.4; JBoss-4.2.1.GA (build: SVNTag=JBoss_4_2_1_GA  date=200707131605)/Tomcat-5.5
Content-Type: text/plain
Content-Length: 90
Date: Tue, 08 Jan 2008 10:49:37 GMT

Esicom Dance Skool
- Belly Dancing
- Hip-Hop
- Jazz
- Krump
- Street
- Tribal Fusion

A vous de jouer

Vous pouvez essayer de manipuler un peu cette Servlet pour tester comment elle réagit:

Normalement, dans tous les cas, vous devriez obtenir une réponse http 500 Internal Server Error.