Ce document est un guide d'utilisation simple permettant de prendre en main le Framework LDAP. Il explique les points suivants :
WEB-INF
, gestion du classpath)<var>
qui déclarent et définissent des
variables. Ces variables peuvent être utilisées dans n'importe
quelle partie du descripteur XML sous deux formes différentes :
${nomvar}
, la valeur utilisée est celle de la
variable correspondante, dans le contexte d'exécution, au moment
où la variable est lue ;#{nom_variable}
, la substitution est effectuée
à la volée au moment de la lecture du fichier XML : dans ce
cas, le configurateur fonctionne comme des macros dans un fichier C.WEB-INF/classes
peut
être utilisé pour stocker le descripteur qui sera dans ce cas
accessible par l'intermédiaire du class loader du composant. Le
fragment de code ci-dessous charge le fichier de configuration depuis
le répertoire WEB-INF/classes/fr/oqube/
et en effectue l'analyse
syntaxique :
XMLConfigurator xc = new XMLConfigurator(); xc.parse(Thread.currentThread() .getContextClassLoader() .getResourceAsStream("fr/oqube/configuration.xml"));
WEB-INF
. Dans ce cas, il est
possible d'y accéder à partir du contexte de servlet :
XMLConfigurator xc = new XMLConfigurator(); xc.parse(getServletContext().getResourceAsStream("/WEB-INF/configuration.xml"));
properties
externe.value
. Dans le cas du descripteur exemple (cf. annexes), on aurait
directement dans le fichier XML les modifications suivantes :
... <var name="user" value="cn=cdmadmin,ou=users,o=services" /> <var name="pass" value="cdmadmin" /> <var name="url" value="ldap://130.81.0.6:389" /> <!-- basic sample XML configuration file for LDAP --> <!-- this file is used to test source configuration and --> <!-- search operations on a genuine LDAP source --> <j:jndi-mapper
&connexion;
dont le contenu est stockée dans la ressource
resources/test/connexion.xml
. La ressource doit être accessible
dans le classpath.
<!DOCTYPE mapper-config [ <!ENTITY connexion SYSTEM "file://resources/test/connexion.xml" > ]>
resources/test/connexion.xml
sera inséré dans le fichier de
configuration.
&connexion;
properties
.
Ce mécanisme permet d'isoler certaines variables et donc favorise la modularité de la configuration2.
connexion.xml
, quant à lui, peut être par
exemple :
<var name="user" value="cn=cdmadmin,ou=users,o=services" /> <var name="pass" value="cdmadmin" /> <var name="url" value="ldap://130.81.0.6:389" />
properties
standard de Java. Pour
mémoire, les fichier de propriétés de Java sont de simples
fichiers textes contenant des couples clés-valeurs : un couple
clé-valeur séparé par le signe égal par
ligne1.
Par exemple, voici l'équivalent de la définition des variables
ci-dessus sous la forme de properties
:
user=cn=cdmadmin,ou=users,o=services pass=cdmadmin url=ldap://130.81.0.6:389
connexion
de la
manière suivante :
<!DOCTYPE mapper-config [ <!ENTITY connexion SYSTEM "property://resources/test/connexion.properties" > ]>
properties
,
resources/test/connexion.properties
est résolu relativement au
classpath.
framework-ldap.jar
. Le fragment suivant décrit
cette configuration pour la version 1.0.2 de Maven.
<dependency> <groupId>norsys</groupId> <artifactId>framework-ldap</artifactId> <version>1.0</version> <properties> <eclipse.dependency>true</eclipse.dependency> <war.bundle>true</war.bundle> </properties> </dependency>
On supposera qu'un référentiel contenant l'archive est accessible depuis le poste de développement.
La configuration dans eclipse se fait soit à partir d'un projet Maven par la commande :
$> maven eclipse __ __ | \/ |__ _Apache__ ___ | |\/| / _` \ V / -_) ' \ ~ intelligent projects ~ |_| |_\__,_|\_/\___|_||_| v. 1.0.2 build:start: eclipse:generate-project: [echo] Creating /home/nono/norsys/oqube/framework-ldap/workspace/demonstrateur/.project ... eclipse:generate-classpath: [echo] Creating /home/nono/norsys/oqube/framework-ldap/workspace/demonstrateur/.classpath ... [echo] Contains JUnit tests [echo] Setting compile of src/test to target/test-classes Plugin 'cactus-maven' in project 'Demonstrateur-Framework-LDAP' is not available [echo] Setting default output directory to target/classes eclipse: [echo] Now refresh your project in Eclipse (right click on the project and select "Refresh") BUILD SUCCESSFUL Total time: 2 seconds
Dans un environnement eclipse 3.1 autonome, il suffit de rajouter le
fichier framework-ldap.jar
aux propriétés du projet. Dans la
fenêtre de navigation Java
sélectionner le projet et le menu
contextuel properties
sélectionner Build path
Selon les cas, choisir Add jar
(si le fichier
d'archive se trouve dans le projet), Add external jar
si le
fichier se trouve ailleurs, Add variable
si le chemin d'accès
au fichier d'archive est accessible à partir d'une variable.
Ce chapitre décrit les principes de base de l'utilisation de l'API du framework LDAP et des mappers métiers définis dans un fichier de configuration. Nous prendrons comme exemple la configuration du démonstrateur livré avec le framework LDAP version 1.0.
Nous supposons que le contexte de développement est une application web standard. En particulier, nous supposerons que le framework Spring ou tout autre framework de composants n'est pas utilisé bien que le framework LDAP soit prévu pour s'intégrer harmonieusement avec de tels systèmes. Ce choix nous permettra d'illustrer l'ensemble du cycle de vie d'un composant de mapping métier.
Le détail de la conception d'un fichier de configuration XML est précisé dans le chapitre suivant. Ces informations ne sont normalement pas nécessaires au développeur d'application intégrant un composant de mapping.
Pour pouvoir être utilisable dans le code, le composant de mapping
métier doit être initialisé, configuré et mis en
marche. L'exemple de la classe MapperLoader
ci-dessous représente
un code typique pour réaliser ces opérations.
public class MapperLoader { private MapperManager mapperManager; private XMLConfigurator configurator; private String mapFile; public void init() throws MapperException, XMLConfiguratorException { configurator.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream(mapFile)); configurator.configure(mapperManager); mapperManager.setConfigured(); mapperManager.start(); } }
Les opérations suivantes sont réalisées :
mapFile
est un nom de chemin dans le classpath le référencant ;MapperManager
est configurée par le configurateur
;MapperManager
et de XMLConfigurator
peuvent être
créées naturellement par appel du constructeur.
Dans le cas d'une application web, le code du chargeur sera invoqué
soit dans une méthode init()
soit dans une servlet
d'initialisation.
UserManager
déclare des méthodes
métiers permettant de rechercher un utilisateur, de le sauvegarder, ...
public interface UserManager { List searchUsersByCriteria(User criteria) throws MapperException; void saveUser(User user) throws MapperException; User getUser(String id) throws MapperException; void deleteUser(String id) throws MapperException; }
A chaque méthode de l'interface Facade correspondront une ou
plusieurs invocations de l'API du framework et des maps définis
dans le fichier de configuration. Nous détaillons ci-après les
différents méthodes représentant un ensemble d'opérations
CRUD standards sur un objet métier spécifique, en l'occurence un
utilisateur représenté par un objet User
.
Le fragment Java ci-dessous est une implantation de
searchUsersByCriteria
. On remarquera que l'encapsulation dans une
méthode métier permet de s'assurer de la libération des
ressources par appel à la méthode release()
.
Les différentes étapes sont toujours les mêmes. On commence par récupérer une instance du mapper à partir de son nom symbolique :
public List searchUsersByCriteria(User criteria) throws MapperException { SearchMapper searchMapper = (SearchMapper) mapperManager .getMapper("searchUsersByCriteria");
L'objet SearchMapper
récupéré est construit par le framework
à partir de la description XML ci-dessous, qui définit le
filtre, utilisé les paramètres d'entrée, la structure de
sortie, la configuration de la recherche.
<!-- searchUsersByCriteria --> <j:search input="userSearchCriteria" output="userSearchResult" name="searchUsersByCriteria"> <j:filter><![CDATA[(&(objectclass=person)(sn=${lastName})(givenName=${firstName})(oqubeattsite=${site}))]]> </j:filter> <j:root>ou=Personnes,o=oqube</j:root> <j:searchScope>1</j:searchScope> <j:derefLinkFlag>true</j:derefLinkFlag> <j:returningObjFlag>true</j:returningObjFlag> <j:countLimit>0</j:countLimit> </j:search>
Ensuite, on construit la map en input
contenant un ensemble de couples
clés-valeurs qui seront utilisés pour la construction de la
requête. Dans le cas présents, on effectue une recherche à
partir du nom et du prénom, sur un certain site.
Map map = new HashMap(); String firstName = user.getFirstName(); String site = user.getSite(); String lastName = user.getLastName(); if(firstName == null || firstName.equals("")) firstName = "*"; if(lastName == null || lastName.equals("")) lastName = "*"; if(site == null || site.equals("")) site = "*"; map.put("firstName",firstName); map.put("lastName",lastName); map.put("site",site);
Enfin, on invoque le mapper qui retourne une liste d'objets Map
contenant les attributs définis dans le paramètre output
de la
définition de la map.
List results; try { results = searchMapper.search(map, null); } finally { mapperManager.release(searchMapper); } return results; }
Rappelons que l'appel à la méthode release()
est très
important, d'où sa présence dans un bloc finally
car c'est lui
qui permet au système de libérer les ressources nécessaires
à l'exécution de la requête.
L'ajout d'une entité suit le même schéma en utilisant un
objet AddMapper
à la place d'un objet SearchMapper
. Nous
détaillons le code d'une méthode d'ajout d'un utilisateur.
On commence par récupérer l'objet mapper utilisé.
public void addUser(User user) throws MapperException { AddMapper addMapper = (AddMapper) mapperManager.getMapper("addUser");
Celui-ci correspond à la définition suivante dans le fichier de description XML. On notera que celle-ci est beaucoup plus simple que dans le cas d'une recherche :
<!-- addUser --> <j:add input="input-user" name="addUser" output="output-user"> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> </j:add>
On crée ensuite la map en entrée en utilisant une méthode
générique de transformation d'un objet en map fournie par la
classe fr.norsys.mapper.util.Convert
. Cette méthode effectue par
réflexion la transformation des propriétés de l'objet en une
instance de Map
:
Map input = Convert.toMap(user);
La map est complété par des informations propres à
l'application concernée, en l'occurence un identifiant unique
construit à partir de certaines valeurs stockées dans
l'annuaire. Enfin, on invoque la méthode add
et l'on oublie pas de
libérer les ressources.
input.put("objectClass","oqubeobjperson"); user.setId(getNextId.next()); input.put("id",user.getId()); try { addMapper.add(input,null); } finally { mapperManager.release(addMapper); } }
La modification suit un schéma très proche de celui de
l'ajout. Dans le code du démonstrateur, c'est la même méthode
façade qui est invoquée dans les deux cas, le tri se faisant en
fonction de l'existence ou nom d'un identifiant unique dans l'objet
User
.
Récupération du mapper :
ModifyMapper modifyMapper = (ModifyMapper) mapperManager.getMapper("modifyUser");
Celui-ci correspond à la définition XML suivante. Par défaut, tous les attributs de la map en input remplaceront les attributs correspondant définis dans la map en output. Le concepteur a aussi la possibilité de préciser les opérations de modification qu'il souhaite réaliser.
<!-- modifyUser --> <j:modify input="input-user" name="modifyUser" output="output-user"> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> </j:modify>
Enfin la map en entrée est construite et la méthode modify
invoquée sur l'objet mapper, sans oublier bien sûr de libérer
le mapper.
Map input = Convert.toMap(user); input.put("id", user.getId()); input.put("objectClass","oqubeobjperson"); try { modifyMapper.modify(input,null); } finally { mapperManager.release(modifyMapper); } }
La suppression est là aussi très simple.
public void deleteUser(String id) throws MapperException { DeleteMapper deleteMapper = (DeleteMapper) mapperManager.getMapper("deleteUser"); Map input = new HashMap(); input.put("id",id); try { deleteMapper.delete(input,null); } finally { mapperManager.release(deleteMapper); } }
La map correspondante est définie comme :
<j:delete input="" name="deleteUser" output=""> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> </j:delete>
Certains attributs dans un annuaire peuvent-être multivalués :
à un même nom d'attribut correspondent plusieurs valeurs. Dans
l'espace applicatif des objets, ces valeurs sont stockés dans une
instance de la classe java.util.List
. Une telle liste peut être
utilisée soit dans une opération d'ajout d'un noeud, soit dans
une opération de recherche.
Le cas de l'ajout est le plus simple : il suffit de stocker dans la
map en input
une instance de List
contenant les valeurs d'attributs
à stocker et le framework se chargera de convertir cette liste en
un ensemble de valeurs pour un même attribut.
Attention : si le schéma de stockage "physique" de l'annuaire n'autorise pas l'attribut à être multivalué, une erreur surviendra.
Le cas de la recherche exige plus de travail de la part du
développeur : si un attribut multivalué est récupéré
dans une recherche, ses valeurs sont automatiquement stockées dans
la map de retour sous la forme d'une List
. Il appartient au client de
vérifier si le type de la valeur stockée dans la map est une
liste ou non.
User mapToUser(Map m ) { User u = new User(); u.setId(m.get("id")); u.setNom(m.get("nom")); ... // il peut y avoir zero ou plusieurs sites Object o = m.get("sites"); if(o != null && (o instanceof java.util.List)) { u.addSites((List)o); } else if(o != null) { // un seul site u.addSite((String)o); }
L'encapsulation de ces traitements dans une méthode générique sera dans la plupart des cas souhaitable.
Lorsque l'application s'arrête, il est préférable, si le contexte d'exécution est maintenue, d'arrêter explicitement le manager, ce qui permet de propager l'information et éventuellement de laisser la possibilité aux composants techniques internes de libérer des ressources systèmes.
public class MapperLoader { ... public void destroy() { mapperManager.stop(); } }
Ce code pourra par exemple être invoqué au moment de la destruction d'une servlet.
Ce chapitre décrit comment on peut construire manuellement un fichier de configuration XML d'un composant de mapping métier. Ce fichier de configuration XML comprend deux parties imbriquées l'une dans l'autre :
base-configuration.dtd
;jndi-configuration.dtd
pour le système de mapping JNDI/LDAP).L'utilisation d'un parseur non validant peut-être utile pour faire en sorte que des éléments soient totalement ignorés lors de l'analyse sans provoquer de problèmes.
La DTD principale est framework-ldap.dtd
, à utiliser comme
identifiant SYSTEM
dans un fichier de configuration :
<!DOCTYPE mapper-config SYSTEM "framework-ldap.dtd" >
Cette DTD principale inclus la DTD de base base-configuration.dtd
et
les autres, pour l'instant uniquement jndi-configuration.dtd
avec des
directives conditionnelles basées sur des entités. Si par
exemple on a une configuration sql et pas de configuration jndi, on
pourrait imaginer une déclaration de DOCTYPE suivante :
<!DOCTYPE mapper-config SYSTEM "framework-ldap.dtd" [ <!ENTITY % mapper.jndi "IGNORE" > <!ENTITY % mapper.sql "INCLUDE" > >
L'identifiant SYSTEM
est utilisé pour résoudre la
référence donnée en un flot de données remplacant
l'entité. Cet identifiant est normalement une URI absolue mais peut
être, comme ci-dessus, une URI relative. Dans le premier cas,
l'identifiant est préfixé par un mode d'accés, p.ex. http://
ou file://
qui indiquent respectivement une ressource accessible par
HTTP ou une ressource sur le système de fichier courant. En
l'absence de protocole, le parseur XML SAX produit des URI absolues
en référence au répertoire courant : si le répertoire
courant est /home/nono/framework-ldap
, l'identifiant SYSTEM
"framework-ldap.dtd"
est transformé en
file:///home/nono/framework-ldap/framework-ldap.dtd
avant passage aucd
résolveur d'entités.
Il est donc nécessaire de faire attention à l'emplacement des
fichiers de configuration DTD
lors de l'utilisation d'un configurateur
XML validant. Le Framework LDAP inclut un résolveur d'entités
qui reconnaît le préfixe classpath://
ce qui permet de
définir des identifiants systèmes relativement au classpath de
l'application et non relativement à un système de fichiers. Par
défaut, les DTD sont incluses dans l'archive contenant le
framework, dans un répertoire dtd/
.
On peut utiliser la déclaration suivante :
<!DOCTYPE mapper-config SYSTEM "classpath://dtd/framework-ldap.dtd" >
mapper-config
qui ne possède aucun
attribut mais définit un espace de nommage qui par convention se
trouve associé à l'URI
http://norsys.fr/framework-ldap/configuration.dtd
.
Cette balise peut contenir deux autres balises, dans un ordre
quelconque :
<var>
de déclaration de variables ;<sub-config>
déclarant un sous-configurateur et un
espace de nommage associé.<var>
permet de définir des variables dans le fichier
XML. Ces variables sont toujours déclarées dans la portée du
tag englobant :
addParameter()
) si la variable est
définie hors de toute source;<mapper-subconfig>
. Dans ce cas, le configurateur racine XML
maintient un environnement accumulant les variables déclarées
dans cette portée et les ajoute lorsque la fin d'une balise
source est détectée. Les variables déclarées hors d'une
source mais dans une sous-configuration sont simplement ajoutées
toutes les sources déclarées dans cet environnement.#{nom_variable}
: dans ce cas, le configurateur fonctionne comme des
macros dans un fichier C.
Par exemple, soit les déclarations suivantes :
<var name="user" value="cn=cdmadmin,ou=users,o=services" /> <var name="pass" value="cdmadmin" /> <var name="url" value="ldap://130.81.0.6:389" /> <var name="regval" value="[0-9]{10}"/> <var name="regemail" value="[a-zA-Z0-9._%-]+\@[a-zA-Z0-9._%-]+\.[a-zA-Z]{2,4}"/>
[0-9]{10}
sera substitué à la chaîne
#{regval}
dans la déclaration suivante et de
même pour email
.
<j:regex key="mobileNumber" value="#{regval}" ignoreNull="true"/> <j:regex key="email" value="#{regemail}" ignoreNull="true"/>
A contrario, les valeurs des variables url
, pass
et user
ne seront pas
instanciées au moment de leur déclaration mais au moment de
l'utilisation des propriétés les contenant dans le fragment
suivant :
<j:property key="java.naming.provider.url" val="${url}" /> <j:property key="java.naming.security.credentials" val="${pass}" /> <j:property key="java.naming.security.principal" val="${user}" />
Un sous-configurateur est associé à un certain espace de
nommage dans le configurateur général. Cette association va
permettre au configurateur racine d'identifier les balises XML dont
il peut déléguer le traitement à un
sous-configurateur, elle se fait en utilisant la
balise <sub-config>
qui a deux attributs, namespace
et class
associant
une URI d'espace de nommage à un nom qualifié de classe. La
classe sera instanciée dynamiquement par le configurateur racine
pour traiter les différentes balises correspondantes.
<sub-config name="http://norsys.fr/framework-ldap/jndi-configuration.dtd" class="fr.norsys.mapper.jndi.JNDIXMLConfigurator" />
Lors de l'analyse de chaque balise, le configurateur racine effectue la substitution du contenu des attributs avant de passer la main au sous-configurateur associé à l'espace de nommage de la balise (s'il existe). Pour permettre un paramétrage incrémental en développement, une balise à laquelle n'est associée aucun espace de nommage est simplement ignorée.
Le configurateur racine a aussi pour fonction de réaliser les substitutions de variables
dans les feuilles (contenu des balises). La méthode String getBuffer()
dans
la classe XMLConfigurator
permet de récupérer leur contenu après substitution.
Nous décrivons dans cette section le détail de la configuration pour
le composant de fourniture de service JNDI/LDAP. L'ensemble des classes
d'implantation de ce composant se trouve dans le paquetage fr.norsys.mapper.jndi
et
ses descendants. La DTD pour les configurations JNDI se trouve reprise
en annexe B du présent document.
Dans la présente version, le mapper JNDI/LDAP réalise une association
bidirectionnelle entre des objets de type java.util.Map
et des noeuds
d'un annuaire de type X.500 5. Un annuaire est une structure arborescente
à chaque noeud de laquelle sont attachés un ensemble d'attributs, c'est
à dire de couples clés-valeurs. La structure de l'annuaire respecte
un certain schéma qu'il appartient au concepteur et au développeur de
respecter. Il n'entre pas dans le cadre de ce document de définir
plus précisément le fonctionnement d'un annuaire X.500.
Le composant de service de mapping JNDI/LDAP s'appuie sur la spécification Java Naming and Directory Interface6. Cette spécification est intégrée par défaut dans le kit de développement Java depuis la version 1.4 et est disponible sur toutes les plateformes Java : J2SE, J2EE, J2ME, ... Elle généralise la notion d'annuaire pour permettre de stocker dans un référentiel de nommage commun des couples noms-objets, ceci afin de faciliter le paramétrage des applications J2EE par exemple. Le CSM JNDI utilise donc l'infrastructure et les appels JNDI standard, un annuaire LDAP assurant éventuellement la communication avec un mode de stockage persistant.
Une instance configurée de CSM JNDI offre ses services sous la forme d'un ensemble de mappers rattachés à des sources, chaque source représentant une "base de donnée" de type annuaire, ie. une connexion pour accéder à un certain serveur LDAP.
Un mapper réalise *une et une seule* une unique requête sur la source de donnée en
tenant compte de données fournies en entrée dans une map et en
produisant des données dans une map de sortie. Chaque objet map
représente une vue "métier" sur les paramètres et le résultat de
la requête. Un mapper peut-être réutilisé pour exécuter
une même requête avec différents paramètres d'entrée,
par exemple en association avec un autre mapper dans une requête de
type jointure (voir JoinMapper).
Les différentes requêtes possibles sont :
root
) présente dans
toutes les requêtes..
Nous donnons dans les sections suivantes un exemple commenté de configuration.
jndi-mapper
elle même contenant des déclarations de configuration config
,
des déclarations de map
, de règles de validations valid
et
de sources
de données. Un espace de nommage avec préfixe est déclaré
à partir de la balise jndi-mapper
ce qui permet de diriger correctement
les traitements dans le configurateur global (voir ci-dessus).
<j:jndi-mapper xmlns:j="http://norsys.fr/framework-ldap/jndi-configuration.dtd" > <j:map name="personne"> .... <j:map name="outmap1"> ... <j:valid key="email" val=".*@.*\\..*" <j:source name="simplesource"> ... </j:jndi-mapper>
<j:map name="personne"> <j:attr key="nom" val="" /> <j:attr key="prenom" val="" /> <j:attr key="email" val="" /> </j:map>
Une map possède un nom unique dans l'attribut name
. Une map peut hériter
de une ou plusieurs autres maps :
<j:map name="employe" inherits="personne"> <j:attr key="boss" val="" /> </j:map>
Dans ce cas, la map finale est construite par aggrégation de la
hiérarchie de map en partant des ancêtres et en suivant l'ordre
de déclaration dans l'attribut inherits
de la balise. Cet ordre
a son importance lorsqu'une valeur est présente dans l'attribut
val
de la balise attr
.
Une map peut être utilisée en entrée comme pour les maps ci-dessus ou en sortie :
<j:map name="outmap1"> <j:attr key="CN" val="nom" /> <j:attr key="personEmail" val="email" /> </j:map>
Dans ce dernier cas, l'attribut key
correspond au nom d'un attribut de
la source de données qui sera mappé sur l'attribut val
correspondant
dans une map. Ces map présentent la particularité d'être bijectives.
Une règle de validation restreint les valeurs admises dans les entrées des maps. Ces règles de validation peuvent être attachées à différents éléments d'un composant de mapping métier : à un mapper spécifique, à une source, à un composant de service ou au niveau global.
Les règles définies dans un certain contexte s'appliquent dans tous les contextes englobés : une règle définie dans une source sera présente dans tous les mappers associés à cette source. Lorsqu'elles sont définies dans une balise englobante, elles sont héritées par toutes les balises descendantes :
<jndi-mapper>
s'appliquera
toutes les sources ;<source>
s'appliquera à
tous les mappers de la source ;<search>
, <add>
, ... ne
s'appliquera que sur ce mapper.map()
et chaque mapper possède sa propre structure de
règles de validation dépendant des règles définies au
moment de sa configuration et de sa création. L'ensemble des règles s'appliquant à un mapper constitue
une expression logique et la méthode map()
ne sera in
fine exécutée que si l'évaluation de l'expression logique avec en paramètres
la map input
et l'environnement env
retourne True.
Ces expressions sont décrites dans un fichier de configuration par une syntaxe XML. Les règles décrites ci-après sont illustrées par des exemples de configuration XML.
<regex key="email" val=".*@.*\\..*" />
Le contenu d'un champ étiqueté par l'attribut key
est
transformé en chaîne par la méthode toString()
ou une autre
méthode appropriée et cette chaîne doit être conforme à
l'expression rationnelle val
.
La syntaxe des expressions rationnelles qu'il
est possible de définir est décrite dans la classe
java.util.regex.Pattern.
L'attribut ignoreNull
permet de définir le comportement de la
règle lorsque l'attribut sur lequel elle s'applique n'a pas de
valeur. Si l'attribut est true
alors les valeurs nulles sont
ignorées et la règle sera considérée comme vraie, sinon
elle est fausse.
<defaults map="personne" />
Cette régle de validation est particulière dans la mesure où elle renvoie toujours vrai mais où elle peut modifier la map input.
L'attribut map
doit être un nom de map présent dans
le contexte de configuration de la règle et les valeurs
de cette map sont utilisées comme valeurs par défaut pour toutes
les map en input du contexte dans lequel s'applique la règle de
validation.
Lorsque la règle est évaluée, si la valeur dans la map en input
d'un champ de la map defaults est null
(c'est à dire si le champ a vraiment pour valeur null ou s'il
n'existe pas), alors la valeur par défaut lui est attribuée. Lors
de la configuration de la règle, les valeurs par défauts peuvent
contenir des références de variables de substitution à
l'exécution, auquel cas ces variables seront évaluées et
substituées en utilisant la map en input comme
environnement d'évaluation. Ce mécanisme permet de construire des
valeurs par défauts en fonction de la valeur de certains champs
d'une map en input.
<mandatory map="personne" />
Cette règle permet d'imposer la présence de certains champs dans
une map en input. L'attribut map
doit être un nom de map présent dans
le contexte de configuration de la règle.
Lorsque la règle est évaluée, si la valeur dans la map en input
d'un champ de la map mandatory est null
(c'est à dire si le champ a vraiment pour valeur null ou s'il
n'existe pas), alors la règle est false.
<and> <valid key="email" val=".*@.*\\..*" /> <valid key="nom complet" val=".* .*" /> </and>
Une règle and
est valide si et seulement si toutes les règles qui
la composent sont valides. Notons que les éléments de la règle situés
après un élément évalué false ne sont pas évalués.
<or> <valid key="email" val=".*\\..*@toto.fr" /> <valid key="email" val=".*\\..*@tutu.fr" /> </or>
Une règle or
est valide si et seulement si l'une des règles qui
la composent sont valides. Notons que les éléments de la règle situés
après un élément évalué true ne sont pas évalués.
<not> <valid key="email" val=".*@spam.fr" /> </not>
Une règle not
est valide si et seulement si toutes les
règles qui la composent ne sont pas valides.
Les règles de validation peuvent être arbitrairement composées pour décrire des règles complexes :
<and> <not> <valid key="email" val=".*@spam.fr" /> </not> <or> <valid key="email" val=".*\\..*@toto.fr" /> <valid key="email" val=".*\\..*@tutu.fr" /> </or> </and>
Une source représente schématiquement une configuration de connexion, c'est à dire les informations nécessaire à une opération de liaison (bind) et les mappers associés.
Une source est identifiée par un nom unique et une balise config
contient
un ensemble de valeurs de configuration, sous la forme de propriétés
de type clés-valeurs. Les propriétés disponibles sont celles
prévues par la spécification JNDI dans la description de l'interface
javax.naming.DirContext
, plus des propriétés propres au composant
qui permettent de faciliter la définition de politiques d'accès.
<j:source name="simplesource"> <j:config> <j:property key="java.naming.factory.initial" val="com.sun.jndi.ldap.LdapCtxFactory" /> <j:property key="java.naming.provider.host" val="${host}" /> <j:property key="java.naming.provider.protocol" val="ldap" /> <j:property key="java.naming.provider.port" val="389" /> <j:property key="java.naming.provider.baseDN" val="${basedn}" /> <j:property key="java.naming.security.principal" val="${user}" /> <j:property key="java.naming.security.credentials" val="${passwd}" /> </j:config> </j:source>
Ces propriétés sont :
search
, modify
, add
,
rename
, delete
, compare
) paramétrables. Chaque requête est identifiée
par un nom unique dans l'attribut name
et éventuellement d'autres
attributs.
search
possède les attributs suivants :
input
qui est une référence vers une map dont les entrées
sont utilisées dans la requête. Cette map est utilisée comme règle de
validation mandatory ;output
est une référence vers une map définissant la structure
de sortie de la requête. Notons que les clés de cette map sont toujours
utilisées pour contraindre la requête (et éviter les requêtes génériques
grosses consommatrices de ressources).<j:search input="employe" output="outmap1" name="searchEmail"> <j:filter>(&(CN=#{nom} #{prenom})(job=${jobs}))</j:filter> <j:root>OU=NPIF, O=Norsys</j:root> <j:count-limit>20</j:count-limit> <j:search-scope value="Children" /> </j:search>
filter
est le texte de la requête 7 conforme à un filtre
LDAP standard 8. Les valeurs des entrées de la map input
sont
utilisées comme variables et substituées dans la requêtes (variables
#{xxxx}
ainsi que les variables globales de la configuration ${zzz}
)
avant son exécution. Cette balise n'est pas obligatoire, si elle
est absente, l'ensemble des paires clés-valeurs de la map input
est
utilisé comme filtre de la requête lié par une condition
and
.
La balise root
est le nom distingué (DN) du noeud à partir duquel
sera effectuée la requête. Les variables locales (map en input) et
globales (configuration) sont aussi utilisables dans la définition
de la balise.
La balise search-controls
contient des paramètres de contrôles tels
que décrits dans javax.naming.directory.SearchControls permettant
de contrôler plus finement la requête.
Une requêtes search
retourne une liste de maps contenant des couples
clés-valeurs. Les clés sont fonction de la map output
et les
valeurs sont la représentation sous forme de chaîne des valeurs
stockés dans l'annuaire. Si un attribut en retour est
multivalué, la valeur est une instance de l'interface List
, sinon
il s'agit d'un Object
tel que retourné par le système LDAP. Le
DN de chaque objet est stocké dans l'entrée __DN__
de la map, ce
nom est relatif à la source à laquelle appartient le mapper.
Une requête modify
décrit un ensemble de modifications des attributs
d'un noeud. --Les attributs à modifier sont définis par l--La
map input
définit les variables d'entrée de la méthode, la
correspondance entre le métier et l'annuaire est donné par la map output
,
le noeud cible par la balise root
et les changements par une
suite de balises operation
. Chaque balise permet de réaliser une
opération sur un attribut :
Un même attribut peut être modifié par plusieurs opérations successives identiques ou différentes : si les opérations sont identiques, les valeurs sont concaténées pour former une liste de valeurs.
Important : La valeur de l'attribut name
d'une operation
doit correspondre
à une clé dans la map input et ou à une valeur dans la map output
.
<j:modify input="inmap1" output="outmap" name="updateUser"> <j:root>cn=#{nom} #{prenom},cn=Users</j:root> <j:operation name="boss" op="modify" /></j:operation> </j:modify>
Enfin, si aucune opération n'est précisée, tous les champs en entrée possédant une valeur dans la map en sortie seront utilisés pour construire une requête de remplacement de valeur.
Les requêtes d'ajout add
et suppression delete
de noeuds sont très simples:
<j:add input="employe" output="empout" name="newUser"> <j:root>cn=#{nom} #{prenom},cn=Users</j:root> </j:add> <j:delete input="employe" output="empout" name="delUser"> <j:root>cn=#{nom} #{prenom},cn=Users</j:root> </j:delete>
La map en input de add
permet de définir les attributs du nouvel objet
créé au noeud root
. Dans le cas de delete
, la map en input peut être null
.
Les maps output
permettent de contrôler la correspondance entre
les noms dans l'annuaire et les noms "métier". Par défaut, les mêmes noms
sont utilisés.
Si une valeur stockée dans la map en input est une instance de
Collection
, alors on considérera qu'il s'agit d'un attribut
multivalué qui sera traité en conséquence.
La requête rename
modifie la position d'un noeud dans la hiérarchie
de l'annuaire.
<j:rename input="employe" name="fire"> <j:root>cn=#{nom},cn=Users</j:root> <j:newRDN>cn=#{nom}</j:newRDN> </j:rename>
Le nouveau DN du noeud est fonction des paramètres d'attributs =newRdn= et
=newSuperior= : ce dernier est un nom relatif qui va être le parent du
noeud déplacé, et du paramètre newRDN
qui indique la
nouvelle position du noeud.
La requête compare
enfin, vérifie que les attributs d'un noeud correspondent
aux attributs demandés. Ce type de requête peut-être utilisé par exemple pour
lire des mots de passe ou vérifier des certificats.
<j:compare input="employe" output="outmap1" name="chkPass"> <j:root>cn=#{nom},cn=Users</j:root> </j:compare>
La requête compare les valeurs de tous les attributs de la map
input
après renommage par la map output
.
<?xml version="1.0" encoding="ISO-8859-1" ?> <!-- This DTD can be used as a general DTD --> <!-- for the framework ldap system. Various --> <!-- subsystems can be configured by defining --> <!-- the value mapper.xxxx entities to either --> <!-- INCLUDE or IGNORE --> <!-- Base declarations, always included --> <![INCLUDE[ <!ENTITY % base SYSTEM "classpath://dtd/base-configuration.dtd" > %base; ]]> <!-- DTD for JNDI mapper subsystem --> <!ENTITY % mapper.jndi "INCLUDE"> <![%mapper.jndi;[ <!ENTITY % jndi SYSTEM "classpath://dtd/jndi-configuration.dtd" > %jndi; ]]>
<!-- DTD for mapper configuration --> <!-- Norsys.2005 --> <!-- root element is mapper-config --> <!ELEMENT mapper-config ANY > <!-- default namespace declaration for root tag --> <!ATTLIST mapper-config xmlns CDATA #FIXED "http://norsys.fr/framework-ldap/configuration.dtd" name CDATA #IMPLIED > <!-- subconfigurator reference : name and class --> <!ELEMENT sub-config EMPTY > <!ATTLIST sub-config name CDATA #REQUIRED class CDATA #REQUIRED > <!-- variable declaration : name and value (optional) --> <!ELEMENT var EMPTY > <!ATTLIST var name CDATA #REQUIRED value CDATA #IMPLIED >
<!-- DTD for jndi mapper configuration --> <!-- Norsys - 2005 --> <!-- parameter denoting key/value pairs --> <!ENTITY % key-val "key CDATA #REQUIRED val CDATA #IMPLIED" > <!-- list of validation nodes --> <!ENTITY % valid "(j:regex | j:not | j:or | j:and)" > <!-- root element : config --> <!ELEMENT j:jndi-mapper ((j:source | j:map | %valid; )*) > <!-- default namespace declaration for j:jndi-mapper --> <!ATTLIST j:jndi-mapper xmlns:j CDATA #REQUIRED > <!-- a j:source defines parameters and env --> <!-- to access a JNDI server --> <!ELEMENT j:source (j:config?,(j:search | j:modify | j:add | j:delete | j:rename | j:compare)*) > <!ATTLIST j:source name ID #REQUIRED pooled ( true | false ) "false" > <!-- configuration for accessing server --> <!-- these are various properties controling --> <!-- the behavior of the JNDI subsystem. It usually --> <!-- includes at least the following elements : --> <!-- java.naming.factory.initial = class name of initial context factory --> <!-- java.naming.provider.host = host name of jndi server --> <!-- java.naming.provider.protocol = protocol of jndi server access (eg. ldap) --> <!-- java.naming.provider.port = port where jndi server is listening --> <!-- java.naming.provider.baseDN = base DN from where queries are issued --> <!-- java.naming.provider.url = alternative to the preceding elements --> <!-- java.naming.security.principal = principal identification for authenticating request --> <!-- java.naming.security.credentials = credentials for authenticating request --> <!-- All the properties listed in the javax.naming.Context interfaces and --> <!-- javax.naming.ldap.LdapContext interfaces may be specified here --> <!-- @see http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/Context.html#field_detail --> <!-- @see http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/ldap/LdapContext.html#field_detail --> <!-- POOLING --> <!-- when source is pooled, the following properties may be useful, --> <!-- although they all have sensible defaults --> <!-- jndi.connection.pool.maxPoolSize = maximum size of pool, must be superior at min size (default= 30) --> <!-- jndi.connection.pool.minPoolSize = minimum and starting size of pool, must be greater than 0 (default = 10) --> <!-- jndi.connection.pool.timeout = timeout for blocking on unavailable connections (default = 300) --> <!-- jndi.connection.pool.factory = fqcn of ConnectionFactory instance to use --> <!ELEMENT j:config (j:property*) > <!-- properties are key/values pairs --> <!ELEMENT j:property EMPTY > <!ATTLIST j:property %key-val; > <!-- regex validation rules : a name/re pair --> <!-- rules are enforced for a configuration file --> <!ELEMENT j:regex EMPTY > <!ATTLIST j:regex %key-val; ignoreNull ( true | false) "false" > <!-- a binary validatation rule --> <!ELEMENT j:or (%valid;,%valid;) > <!ELEMENT j:and (%valid;,%valid;) > <!ELEMENT j:not (%valid;) > <!-- mapping definition --> <!-- defines a list of names/default values that may be used as --> <!-- input/output maps in queries --> <!ELEMENT j:map (j:attr*) > <!-- a map as a unique identifier --> <!-- a map may inherit from zero or more other --> <!ATTLIST j:map name ID #REQUIRED inherits IDREFS #IMPLIED > <!ELEMENT j:attr EMPTY > <!ATTLIST j:attr %key-val; > <!-- SEARCH --> <!-- defines a search mapper query --> <!ELEMENT j:search (j:filter?,j:root,j:countLimit?,j:derefLinkFlag?,j:returningObjFlag?,j:searchScope?,j:timeLimit?,j:sort*,j:group*) > <!-- attribute input references a map (see above) and --> <!-- is the input map for the search --> <!-- output is of course the output map for the query --> <!-- the output map is a string to string map that converts --> <!-- jndi attributes names to business names --> <!-- the output map also controls which attributes are effectively --> <!-- retrieved in a query --> <!-- attributed name is the unique name of this query --> <!ATTLIST j:search input IDREF #REQUIRED output IDREF #REQUIRED name ID #REQUIRED > <!-- filter contains the JNDI/LDAP query to run --> <!-- the query text may contains variables of the form #{xxx} --> <!-- these variables will be substituted with the corresponding --> <!-- input field name before query execution --> <!ELEMENT j:filter (#PCDATA) > <!-- this element contains the DN of the root context --> <!-- from which the query must be run --> <!ELEMENT j:root (#PCDATA) > <!-- maximum number of nodes retrieved inthe query --> <!ELEMENT j:countLimit (#PCDATA) > <!-- maximum amount of seconds to wait for answer --> <!ELEMENT j:timeLimit (#PCDATA) > <!-- flag indicating wether or not links are dereferenced --> <!-- automatically --> <!ELEMENT j:derefLinkFlag (#PCDATA) > <!-- flag indicating wether or not objects will be returned in --> <!-- requests. only relevant for general JNDI operations, not LDAP (??) --> <!ELEMENT j:returningObjFlag (#PCDATA) > <!-- search scope --> <!-- maybe level zero (search only base DN), one level or subtree --> <!-- default is one level --> <!ELEMENT j:searchScope (#PCDATA) > <!-- sort order of search results --> <!-- each sort attribute defines a key/value pair indicating --> <!-- how sorting should be performed --> <!ELEMENT j:sort EMPTY > <!-- name is the attributes name to be sorted --> <!-- order is ascending ordescending order, default to ascending --> <!-- filter is not implemented --> <!ATTLIST j:sort name CDATA #REQUIRED order (asc | desc) "asc" filter CDATA #IMPLIED > <!-- MODIFY --> <!-- defines a modify query --> <!-- this kind of query modifies the attributes of an entry --> <!-- result is a map with the single entry 'result' which --> <!-- is either the string "OK" or an exception indicating --> <!-- reason of failure --> <!ELEMENT j:modify (j:root,j:operation*) > <!-- attribute input references a map (see above) and --> <!-- is the input map for the search --> <!-- attribute output gives correspondance between attributes --> <!-- name in input map and attributes names in directory --> <!-- attributed name is the unique name of this query --> <!ATTLIST j:modify input IDREF #REQUIRED output IDREF #REQUIRED name ID #REQUIRED > <!-- defines a single operation on an input attribute --> <!-- each operation defines a name we operate on and --> <!-- an operation which may be either 'modify', 'add' or 'delete' --> <!-- the content of the operation tag defines the (string representaiton of) --> <!-- attribute value to use --> <!-- an attribute may appear multiple times with the same or --> <!-- different operation, in which case values are concatenated to form --> <!-- a multivalued attribute --> <!ELEMENT j:operation (#PCDATA) > <!ATTLIST j:operation name CDATA #REQUIRED op (modify | delete | add) "modify" > <!-- ADD --> <!-- defines an add query --> <!-- attributes are the input map containing attributes --> <!-- to be added to the new entry --> <!-- this kind of query only contains the root (DN) to where --> <!-- the entry is created --> <!-- result is a map with the single entry 'result' which --> <!-- is either the string "OK" or an exception indicating --> <!-- reason of failure --> <!-- attributed name is the unique name of this query --> <!-- attribute output gives correspondance between attributes --> <!-- name in input map and attributes names in directory (for deletion) --> <!ELEMENT j:add (j:root) > <!ATTLIST j:add input IDREF #REQUIRED output IDREF #IMPLIED name ID #REQUIRED > <!-- DELETE --> <!-- defines a delete query --> <!-- attributes are : --> <!-- the input map (if present) containing which must be match by the --> <!-- entry to be deleted --> <!-- this kind of query only contains the root (DN) to where --> <!-- the entry is created --> <!-- result is a map with the single entry 'result' which --> <!-- is either the string "OK" or an exception indicating --> <!-- reason of failure --> <!-- attributed name is the unique name of this query --> <!ELEMENT j:delete (j:root) > <!ATTLIST j:delete name ID #REQUIRED output IDREF #IMPLIED input IDREF #IMPLIED > <!-- MODIFY --> <!-- defines a modify DN query --> <!-- attributes are : --> <!-- - name is the unique name of this query --> <!-- result is a map with the single entry 'result' which --> <!-- is either the string "OK" or an exception indicating --> <!-- reason of failure --> <!ELEMENT j:rename (j:root,j:newRDN,j:deleteOldRDN?,j:newSuperior?) > <!ATTLIST j:rename name ID #REQUIRED > <!-- the new relative DN of the modified entry --> <!ELEMENT j:newRDN (#PCDATA) > <!-- flag indicating whether or not the old entry is deleted --> <!ELEMENT j:deleteOldRDN EMPTY > <!ATTLIST j:deleteOldRDN value (true | false) "false" > <!-- the name of the new parent of the modified entry --> <!ELEMENT j:newSuperior (#PCDATA) > <!-- COMPARE --> <!-- defines a compare query --> <!-- attributes are : --> <!-- input map is the key/value pairs to be compared --> <!-- name is the unique ID of this query --> <!ELEMENT j:compare (j:root) > <!ATTLIST j:compare name ID #REQUIRED input IDREF #REQUIRED output IDREF #IMPLIED>
Cette annexe contient le fichier de configuration complet pour l'application de démonstration de la version 1.0 du framework.
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE mapper-config SYSTEM "classpath://dtd/framework-ldap.dtd" > <mapper-config xmlns="http://norsys.fr/framework-ldap/configuration.dtd"> <sub-config name="http://norsys.fr/framework-ldap/jndi-configuration.dtd" class="fr.norsys.mapper.jndi.JNDIXMLConfigurator" /> <var name="user" value="cn=admin,ou=users,o=services" /> <var name="pass" value="12345678" /> <var name="url" value="ldap://130.81.9.192:389" /> <var name="regval" value="[0-9]{10}"/> <var name="regemail" value="[a-zA-Z0-9._%-]+\@[a-zA-Z0-9._%-]+\.[a-zA-Z]{2,4}"/> <!-- basic sample XML configuration file for LDAP --> <!-- this file is used to test source configuration and --> <!-- search operations on a genuine LDAP source --> <j:jndi-mapper xmlns:j="http://norsys.fr/framework-ldap/jndi-configuration.dtd"> <j:regex key="mobileNumber" value="#{regval}" ignoreNull="true"/> <j:regex key="email" value="#{regemail}" ignoreNull="true"/> <!-- an input map definition --> <j:map name="input-user-search"> <j:attr key="firstName" val="" /> <j:attr key="lastName" val="" /> <j:attr key="site" val="" /> </j:map> <j:map name="input-id"> <j:attr key="id" val="" /> </j:map> <j:map name="input-user"> <j:attr key="id" val="" /> <j:attr key="lastName" val="" /> <j:attr key="firstName" val="" /> <j:attr key="site" val="" /> <j:attr key="fullName" val="" /> <j:attr key="employeeType" val="" /> <j:attr key="internetProfile" val="" /> <j:attr key="civility" val="" /> <j:attr key="email" val="" /> <j:attr key="company" val="" /> <j:attr key="function" val="" /> <j:attr key="telephoneNumber" val="" /> <j:attr key="mobileNumber" val="" /> <j:attr key="faxNumber" val="" /> <j:attr key="floor" val="" /> <j:attr key="office" val="" /> <j:attr key="ip" val="" /> <j:attr key="service" val="" /> <j:attr key="login" val="" /> <j:attr key="objectClass" val="" /> </j:map> <j:map name="input-nextid"> <j:attr key="nextId" val="" /> </j:map> <!-- an output map definition --> <j:map name="output-user-search"> <j:attr key="cn" val="id" /> <j:attr key="fullName" val="fullName" /> <j:attr key="oqubeattsite" val="site" /> </j:map> <j:map name="output-user"> <j:attr key="cn" val="id" /> <j:attr key="fullName" val="fullName" /> <j:attr key="oqubeattsite" val="site" /> <j:attr key="oqubeattcivilite" val="civility" /> <j:attr key="givenName" val="firstName" /> <j:attr key="sn" val="lastName" /> <j:attr key="mail" val="email" /> <j:attr key="employeeType" val="employeeType" /> <j:attr key="oqubeattsociete" val="company" /> <j:attr key="oqubeattfonction" val="function" /> <j:attr key="oqubeattprofilmail" val="internetProfile" /> <j:attr key="telephoneNumber" val="telephoneNumber" /> <j:attr key="mobile" val="mobileNumber" /> <j:attr key="facsimileTelephoneNumber" val="faxNumber" /> <j:attr key="siteLocation" val="floor" /> <j:attr key="roomNumber" val="office" /> <j:attr key="oqubeattiplan" val="ip" /> <j:attr key="oqubeattservice" val="service" /> <j:attr key="uid" val="login" /> <j:attr key="objectclass" val="objectClass" /> </j:map> <j:map name="output-yearnextid"> <j:attr key="oqubeattnum" val="year"/> <j:attr key="oqubeattnextnb" val="nextId"/> </j:map> <j:map name="output-nextid"> <j:attr key="oqubeattnextnb" val="nextId"/> </j:map> <!-- non emptiness of pid validation rule --> <!-- a first source --> <j:source name="ldifsource" pooled="true" > <!-- configuration for this source (ie. connection) --> <j:config> <j:property key="java.naming.factory.initial" val="com.sun.jndi.ldap.LdapCtxFactory" /> <j:property key="java.naming.provider.url" val="${url}" /> <j:property key="java.naming.security.credentials" val="${pass}" /> <j:property key="java.naming.security.principal" val="${user}" /> <j:property key="jndi.connection.pool.minPoolSize" val="10" /> <j:property key="jndi.connection.pool.maxPoolSize" val="30" /> <j:property key="jndi.connection.pool.timeout" val="1000" /> </j:config> <!-- searchUsersByCriteria --> <j:search input="input-user-search" output="output-user-search" name="searchUsersByCriteria"> <j:filter><![CDATA[(&(objectclass=person)(sn=${lastName})(givenName=${firstName})(oqubeattsite=${site}))]]> </j:filter> <j:root>ou=Personnes,o=oqube</j:root> <j:searchScope>1</j:searchScope> <j:derefLinkFlag>true</j:derefLinkFlag> <j:returningObjFlag>true</j:returningObjFlag> <j:countLimit>0</j:countLimit> </j:search> <!-- getUser --> <j:search input="input-id" output="output-user" name="getUser"> <j:filter><![CDATA[(&(objectclass=oqubeobjperson)(cn=${id}))]]></j:filter> <j:root>ou=Personnes,o=oqube</j:root> <j:searchScope>1</j:searchScope> <j:derefLinkFlag>true</j:derefLinkFlag> <j:returningObjFlag>true</j:returningObjFlag> <j:countLimit>0</j:countLimit> </j:search> <!-- getNextId --> <j:search input="" output="output-yearnextid" name="getNextId"> <j:filter>(objectclass=*)</j:filter> <j:root>ou=Personnes,o=oqube</j:root> <j:searchScope>0</j:searchScope> <j:derefLinkFlag>true</j:derefLinkFlag> <j:returningObjFlag>true</j:returningObjFlag> <j:countLimit>0</j:countLimit> </j:search> <!-- addUser --> <j:add input="input-user" name="addUser" output="output-user"> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> </j:add> <!-- modifyUser --> <j:modify input="input-user" name="modifyUser" output="output-user"> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> <j:operation op="modify" name="fullName">${fullName}</j:operation> <j:operation op="modify" name="site">${site}</j:operation> <j:operation op="modify" name="civility">${civility}</j:operation> <j:operation op="modify" name="firstName">${firstName}</j:operation> <j:operation op="modify" name="lastName">${lastName}</j:operation> <j:operation op="modify" name="fullName">${fullName}</j:operation> <j:operation op="modify" name="email">${email}</j:operation> <j:operation op="modify" name="employeeType">${employeeType}</j:operation> <j:operation op="modify" name="company">${company}</j:operation> <j:operation op="modify" name="function">${function}</j:operation> <j:operation op="modify" name="telephoneNumber">${telephoneNumber}</j:operation> <j:operation op="modify" name="mobileNumber">${mobileNumber}</j:operation> <j:operation op="modify" name="faxNumber">${faxNumber}</j:operation> <j:operation op="modify" name="floor">${floor}</j:operation> <j:operation op="modify" name="office">${office}</j:operation> <j:operation op="modify" name="ip">${ip}</j:operation> <j:operation op="modify" name="service">${service}</j:operation> <j:operation op="modify" name="login">${login}</j:operation> </j:modify> <!-- modifyNextId --> <j:modify input="input-nextid" name="modifyNextId" output="output-nextid"> <j:root>ou=Personnes,o=oqube</j:root> <j:operation op="modify" name="nextId">${nextId}</j:operation> </j:modify> <!-- deleteUser --> <j:delete input="" name="deleteUser" output=""> <j:root>cn=${id},ou=Personnes,o=oqube</j:root> </j:delete> </j:source> </j:jndi-mapper> </mapper-config>
1. voir Spécifications SPI pour le détail et conception/config/jndi-configuration.dtd
2. on notera que ce mécanisme peut être utilisé pour autre chose que des variables, par exemple pour modulariser la structure des sources et des mappers.
3. voir la description de la classe Properties dans la Javadoc
5. voir les spécifications relatives à la norme X.500 pour plus de détails : http://archive.dante.net/np/ds/osi/9594-1-X.500.A4.ps
6. voir http://java.sun.com/products/jndi/ pour plus de détails concernant cette spécification.
7. n'y-a-t'il pas des possibilités de problème avec l'encodage des caractères ?