JNDI est une des technologies importante de JEE.
C'est une API qui permet d'accéder à des registres.
Ces registres peuvent contenir des objets de diverses natures.
Les serveurs d'applications y rangent les composants afin de les mettre à disposition des applications.
On y retrouve les services de base de données, de mail, des realms pour l'authentification...
C'est une implémentation du pattern Service Locator.
Comme avec spring, cela permet d'obtenir un service sans savoir comment il a été créé.
Pour récupérer une référence sur un service du jndi
Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("jdbc/mabase");
Le container fournit une DataSource qu'il a préalablement instanciée.
Sans changer notre code, on peut utiliser des bases de données différentes en dev et en prod.
La configuration est faite au niveau du serveur (conteneur d'application).
L'injection de dépendances comme le pratique Spring est une alternative au service locator.
En plus d'ignorer la construction des collaborateurs, c'est le conteneur qui injecte la dépendance.
Comme jndi reste souvent un point de passage obligé en jee, Spring offre un pont entre son modèle d'injection et jndi.
On crée la branche jndi à partir de la branche master.
$ git checkout master Switched to branch 'master' $ git checkout -b jndi Switched to a new branch 'jndi'
Actuellement, la définition de notre pool de connexion est la suivante :
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"/> <property name="url" value="jdbc:derby:target/taskdb;create=true"/> <property name="username" value=""/> <property name="password" value=""/> </bean>
Nous utilisons une base derby en mémoire.
Le but est de passer cette configuration sous jndi.
Au démarrage de l'application, tomcat remplit le registre jndi en lisant le fichier context.xml.
Par défaut, il va chercher le fichier context.xml dans le répertoire META-INF de l'application web.
Pour notre besoin, il faut donc définir le fircher src/main/webapp/META-INF/context.xml suivant.
<Context> <Resource name="jdbc/taskdb" auth="Container" type="javax.sql.DataSource" username="" password="" driverClassName="org.apache.derby.jdbc.EmbeddedDriver" url="jdbc:derby:target/taskdb;create=true" maxActive="10" maxIdle="2"/> </Context>
Et d'informer le plugin tomcat de sa présence
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <path>/</path> <contextFile>src/main/webapp/META-INF/context.xml</contextFile> </configuration> </plugin>
La configuration de derby dans le context est la même que celle du fichier spring.
Maintenant on modifie notre context spring afin d'utiliser jdbc/taskdb depuis le jndi.
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/taskdb" /> </bean>
La configuration spring devient plus simple et indépendante de l'environnement.
Et encore une fois, nos composants n'ont pas été affectés par cette modification.
On peut simplifier un peu la configuration spring.
Spring dispose d'un namespace jee afin de faciliter la récupération de références dans le jndi.
<beans ... xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation=" ... http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
On peut ensuite écrire.
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/taskdb" />
Avoir un fichier context.xml dans notre projet est pratique pour des tests.
Dans une installation tomcat existante, il est possible de mettre ces fichiers dans le $CATALINA_BASE.
Extrait de la documentation tomcat :
Context elements may be explicitly defined: In the $CATALINA_BASE/conf/context.xml file: the Context element information will be loaded by all webapps. In the $CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default file: the Context element information will be loaded by all webapps of that host. In individual files (with a ".xml" extension) in the $CATALINA_BASE/conf/[enginename]/[hostname]/ directory. The name of the file (less the .xml extension) will be used as the context path. Multi-level context paths may be defined using #, e.g. foo#bar.xml for a context path of /foo/bar. The default web application may be defined by using a file called ROOT.xml. Only if a context file does not exist for the application in the $CATALINA_BASE/conf/[enginename]/[hostname]/, in an individual file at /META-INF/context.xml inside the application files. If the web application is packaged as a WAR then /META-INF/context.xml will be copied to $CATALINA_BASE/conf/[enginename]/[hostname]/ and renamed to match the application's context path. Once this file exists, it will not be replaced if a new WAR with a newer /META-INF/context.xml is placed in the host's appBase. Inside a Host element in the main conf/server.xml.
La solution préférée est un fichier context dans $CATALINA_BASE/conf/[enginename]/[hostname]/.
Par défaut, enginename est "Catalina" et hostname est "localhost".
Par exemple, si notre application ROOT.war est déposée dans $CATALINA_BASE/webapps, tomcat va aller chercher un fichier $CATALINA_BASE/conf/Catalina/localhost/ROOT.xml.
Il est donc possible de configurer l'application au travers de ce fichier context.xml
Tomcat ne prendra pas en compte notre META-INF/context.xml si un fichier contexte est déjà présent dans $CATALINA_BASE.
Cependant, ce n'est pas très propre d'envoyer un fichier servant aux tests en production.
Nous allons donc utiliser maven afin de filter ce fichier au moment du build.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <packagingExcludes>META-INF/context.xml</packagingExcludes> </configuration> </plugin>
Avec un mvn package, le fichier context.xml est donc exlu de notre war.
$ mvn clean ... $ mvn package ... $ jar tf target/todooz-1.0-SNAPSHOT.war META-INF/ META-INF/MANIFEST.MF ... META-INF/maven/fr.todooz/todooz/pom.xml META-INF/maven/fr.todooz/todooz/pom.properties
Nous application utilise jndi et notre war est davantage portable.
Chaque serveur d'application ou conteneur de servlet dispose de ses propres moyens de configuration.
Il faut donc regarder les moyens offerts par chaque serveur afin de configurer au mieux l'application.