Zi Youltimayte Castor gaïde !
DevFrançaisTutorials
1 Introduction
Ce document permet de savoir si finalement oui ou non, le castor Lapon est hermaphrodite.
URL : http://www.castor.org
Licence : Exolab License : http://www.castor.org/license.html (BSD-like)
1.1 A quoi ça sert ?
Castor permet de générer à partir de fichiers XML des classes Java et vice-versa…
En gros ça peut être très pratique dans les cas suivants (non exhaustif) :
Web services :
– Générer le flux XML à envoyer depuis nos objets Java,
– Transformer le flux XML reçu en Objets Java,
Stockage de données (objet) :
– Sauvegarder des objets Java en fichier XML pour relecture ultérieure
– Lire des fichiers XML pour les récupérer en objet Java
– La partie sérialisation et lecture d’une base de donnée objet par exemple.
Pleins d’autres truc encore comme :
– Ecrire un roman en XML et générer des objets Chapitres qui possèdent des List de Mots, des mots clefs en Java ! (Comment ça à quoi ça sert ? C’est pourtant évident : à rien ! )
– Graver sur des milliers de CD-ROM le castor.jar afin d’atteindre enfin l’étage tant convoité de votre dulciné(e) !
– Proposition de DUS : Couper des arbres pour faire une échelle. (NDR cette méthode semble couteuse car il faut tenir le castor par la queue et l’utiliser comme une hache pour que les dents de devant viennent frapper l’arbre ! Ça use beaucoup de castor, il faut compter 18 castors pour un arbre de 54cm de diamètre soit 6750 castors pour 250 arbres de 81 cm de diamètre).
Alors c’est indispensable non ?
1.2 Comment ça marche ?
Avec ses jambes (ok je sors…).
Eh bien en fait c’est super simple :
Objets Java (-> fichier XML de mapping) -> castor -> XML
XML -> castor (-> fichier XML de mapping) -> Objets Java
Pas très compliqué ! En plus le fichier XML de mapping est facultatif, il permet juste d’obtenir un XML personnalisé.
Edit :
Pour automatiser la création du fichier de mapping, Castor fourni des exécutables pour faire tout ça :
Allez voir : Zi Youltimayte Castor gaïde 2, Zi automatizaïcheun !
1.3 Est-ce-que ça marche ?
Oui ça marche sur plein de projets super connu comme Crésus chez AXA ou bien encore GAÏA et le DMP chez Accenture et même à la DGI (si je ne me trompe pas) !! Castor évolue en ce moment même et est très actif. Il y a des Wiki, des listes de diffusion, des forums bref tout ce qu’il faut pour ne pas être perdu seul au milieu des bois entouré de castors sauvages qui mangent les petit développeurs.
Pour les perfs y à pas de soucis même si je n’ai jamais lancé de bench pour le comparer à je sais pas quoi encore.
2 La démo
2.1 Intro
Bon donc ici on va faire une petite application toute simple qui va nous servir de base de données. On va avoir une application qui d’une part va être capable d’ajouter des bouteilles dans une cave (Java vers XML) et qui sera capable de récupérer ces bouteilles depuis la cave (XML vers Java). Très simple donc.
Alors je n’ai pas mis de package, pas de système de log, pas de gestion des Exceptions etc., c’est juste du code prêt à compiler et à tester.
Si vous voulez ajouter tout ça vous êtes grands maintenant.
2.2 Notre coeur de métier
Nous avons 2 choses : Une cave et des bouteilles. La cave possède un index courant des bouteilles stockées (id unique pour les bouteilles) et une liste de bouteilles. La bouteille à un id, un nom, une année, une quantité, un type et un format.
2.2.1 Le XML à générer
Notre souhait est de stocker les bouteilles dans un XML comme celui-ci : Fichier : maCave.xml (quand elle sera créée…)
<?xml version="1.0" encoding="UTF-16"?> <Cave monIndexCourantDesBouteilles="2"> <bouteille id="1" nom="Ma bouteille" annee="1990" quantite="12" type="Rhône Rouge" format="75 cl"/> <bouteille id="2" nom="..." annee="2000" .../> <bouteille .../> <bouteille .../> </Cave>
Voilà le but. Donc nous allons commencer par créer notre Objet Bouteille.
2.2.2 L’objet Bouteille
Fichier : Bouteille.java
public class Bouteille { private String id; private String nom; private String annee; private String quantite; private String type; private String format; public Bouteille() { } public String getId () { return id; } public void setId (String id) { this.id = id; } public String getNom () { return nom; } public void setNom (String nom) { this.nom = nom; } public String getAnnee () { return annee; } public void setAnnee (String annee) { this.annee = annee; } public String getQuantite () { return quantite; } public void setQuantite (String quantite) { this.quantite = quantite; } public String getType () { return type; } public void setType (String type) { this.type = type; } public String getFormat () { return format; } public void setFormat (String format) { this.format = format; } public String toString() { StringBuffer sb = new StringBuffer("Bouteille {\n"); sb.append("\tid = '").append(id).append("'\n"); sb.append("\tnom = '").append(nom).append("'\n"); sb.append("\tannee = '").append(annee).append("'\n"); sb.append("\tquantite = '").append(quantite).append("'\n"); sb.append("\ttype = '").append(type).append("'\n"); sb.append("\tformat = '").append(format).append("'\n"); sb.append("}\n"); return sb.toString(); } }
Rien de particulier, tout est plus que très normal.
2.2.3 L’objet Cave
Cet objet est le coeur métier de l’application. La cave possède une List de Bouteille et un index qui représente le dernier id des bouteilles enregistrées, c’est juste pour avoir une valeur simple en plus d’une liste pour la démo, ne pas y chercher la grosse bébête.
Fichier : Cave.java
import java.util.List; import java.util.Iterator; import java.util.ArrayList; public class Cave { private List bouteillesListe = new ArrayList(); private int bouteillesIndex; public List getBouteillesListe() { return bouteillesListe; } public void setBouteillesListe(final List liste) { bouteillesListe = liste; } public String getBouteillesIndex() { return String.valueOf(bouteillesIndex); } public void setBouteillesIndex(final String aBouteillesIndex) { bouteillesIndex = Integer.parseInt(aBouteillesIndex); } public String getNextBouteillesIndex() { return String.valueOf(bouteillesIndex + 1); } public void ajouterBouteille(Bouteille boutanche) { bouteillesListe.add(boutanche); bouteillesIndex ++; } public void ajouterBouteilles(List boutanches) { bouteillesListe.add(boutanches); bouteillesIndex += boutanches.size(); } public String toString() { StringBuffer sb = new StringBuffer("Cave {\n"); sb.append("bouteillesIndex = '").append(bouteillesIndex).append("'\n"); Iterator ite = bouteillesListe.iterator(); while (ite.hasNext()) { Bouteille bout = (Bouteille) ite.next(); sb.append(bout.toString()); } sb.append("}\n"); return sb.toString(); } }
Rien de compliqué ici aussi, ce sont de purs Objets métier qui correspondent parfaitement à notre besoin.
2.2.4 Le CastorHelper
C’est une classe toute bête : elle s’occupe de tout faire XML vers Java et Java vers XML. On lui passe un Objet, le chemin vers le fichier de mapping, et le chemin vers le XML à générer ou existant et il fait tout 😉
C’est parti :
Fichier : CastorHelper.java
import org.xml.sax.InputSource; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.ValidationException; import org.exolab.castor.mapping.MappingException; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.InputStream; import java.io.IOException; import java.io.FileReader; public class CastorHelper { private static CastorHelper ourInstance = new CastorHelper(); public static CastorHelper getInstance() { return ourInstance; } private CastorHelper() { } private final String ENCODING = "UTF-16"; /** * Permet de générer un fichier contenant le XML généré. * * @param monObjet la classe contenant les données à 'castoriser' * @param mapping le fichier XML de mapping pour savoir quoi faire avec l'Objet * @param xml l'endroit ou se trouve le fichier XML à générer * @throws Exception si une erreur survient. */ public void saveXMLFile(<strong>Object monObjet</strong>, String mapping, String xml) throws Exception { File original = new File(xml); if (original == null) { throw new Exception("Le fichier '" + xml + "' ne peut être créé !"); } try { // On va juste charger le fichier XML de mapping InputStream lIs = ourInstance.getClass().getResourceAsStream(mapping); // Chargement du mapping Mapping lMapping = new Mapping(); lMapping.loadMapping(new InputSource(lIs)); FileOutputStream lFos = new FileOutputStream(original); OutputStreamWriter lOsw = new OutputStreamWriter(lFos); // Marshal l'object -> <strong>La transformation de Java en XML !</strong> final Marshaller lMarshaller = new Marshaller(lOsw); lMarshaller.setValidation(false); lMarshaller.setMapping(lMapping); lMarshaller.setEncoding(ENCODING); lMarshaller.<strong>marshal(monObjet)</strong>; } catch (IOException e) { throw e; } catch (ValidationException e) { throw e; } catch (MarshalException e) { throw e; } catch (MappingException e) { throw e; } } /** * Permet de lire un fichier XML contenant les données. * * @param aClasse la classe d'implémentation des données à 'décastoriser' * @throws Exception si une erreur survient. */ public void loadXMLFile(<strong>Object monObjet</strong>, String mapping, String xml) throws Exception { File original = new File(xml); if (original == null || original.exists() == false) { throw new Exception("Le fichier '" + xml + "' n'existe pas !"); } try { FileReader lFichier = new FileReader(original); InputStream lIs = ourInstance.getClass().getResourceAsStream(mapping); // Chargement du mapping Mapping lMapping = new Mapping(); lMapping.loadMapping(new InputSource(lIs)); // UnMarshal l'object -> <strong>La transformation XML vers Java !</strong> Unmarshaller lUnmarshaller = new Unmarshaller(<strong>monObjet</strong>); lUnmarshaller.setMapping(lMapping); monObjet = lUnmarshaller.<strong>unmarshal(lFichier)</strong>; } catch (IOException e) { throw e; } catch (ValidationException e) { throw e; } catch (MarshalException e) { throw e; } catch (MappingException e) { throw e; } } }
Et voilà il ne reste plus qu’à faire le XML de mapping et à tester tout ça avec une petite classe de test.
2.2.5 Le fichier XML de mapping
Ce fichier est là pour dire que l’Object Cave est composé d’un index et d’une liste de Bouteille.
Il est également là pour dire que Bouteille est composé de tel et tel attribut et qu’il faut les positionner comme ça dans le fichier XML à générer ou à lire.
Voici notre mapping :
Fichier : mappingCave.xml
<?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN" "http://castor.exolab.org/mapping.dtd"> <mapping> <description>The ultimate Castor Guide</description> <!-- name= le nom Java de notre Object ex : mon.package.MaClasse Ici nous n'avons pas de package donc Cave directement --> <class name="Cave"> <!-- la ligne suivante c'est le nom de la balise xml qui va avec notre Object Cave --> <map-to xml="Cave" /> <!-- la c'est l'attribut java de notre index de type String qui est mappe en attribut de la balise cave --> <field name="bouteillesIndex" type="string" > <bind-xml name="bouteillesIndex" node="attribute" /> </field> <!-- la name=bouteilleListe est le nom Java de notre List de Bouteille mappe en tant qu'element dans une balise pour chaque bouteille --> <field name="bouteillesListe" collection="arraylist" type="Bouteille" > <bind-xml name="bouteille" node="element" /> </field> </class> <!-- la c'est notre element bouteille pour mapper l'Object Bouteille --> <class name="Bouteille" > <!-- la c'est le nom de la balise de l'element de notre Bouteille --> <map-to xml="bouteille" /> <!-- la c'est l'attribut id de notre bouteille --> <field name="id" type="string" > <bind-xml name="id" node="attribute"/> </field> <!-- la c'est l'attribut nom de notre bouteille --> <field name="nom" type="string" > <bind-xml name="nom" node="attribute"/> </field> <!-- la c'est l'attribut annee de notre bouteille --> <field name="annee" type="string" > <bind-xml name="annee" node="attribute"/> </field> <!-- la c'est l'attribut quantite de notre bouteille --> <field name="quantite" type="string" > <bind-xml name="quantite" node="attribute"/> </field> <!-- la c'est l'attribut type de notre bouteille --> <field name="type" type="string" > <bind-xml name="type" node="attribute"/> </field> <!-- la c'est l'attribut format de notre bouteille --> <field name="format" type="string" > <bind-xml name="format" node="attribute"/> </field> </class> </mapping>
Voilà qui est fait on passe à notre classe de test.
2.2.6 La classe de test
Toute simple permet au choix de castoriser ou de décastoriser 😉
Fichier : TestCastor.java
import java.io.File; import java.io.InputStream; import java.io.FileInputStream; public class TestCastor { public static void main(String[] args) { try { if (args.length != 3) { TestCastor.usage(); System.exit(0); } if ("I".equalsIgnoreCase(args[2])) { TestCastor.importerDepuisXml(args[0], args[1]); } else if ("E".equalsIgnoreCase(args[2])) { TestCastor.exporterVersXml(args[0], args[1]); } else { TestCastor.usage(); } } catch (Exception e) { e.printStackTrace(); } } public static void usage() { StringBuffer sb = new StringBuffer(1000); sb.append("\nUsage :"); sb.append("\n\tjava TestCastor /chemin/vers/mappingCastor.xml /chemin/vers/xmlGenere.xml [I/E]"); sb.append("\n\t\tI = Importer un objet java depuis un fichier XML"); sb.append("\n\t\tE = Exporter un objet java vers un fichier XML"); sb.append("\nExemples :"); sb.append("\njava TestCastor /home/julien/dev/sf/mapping.xml /home/julien/dev/sf/test/bouteille.xml I"); sb.append("\nExemple : java TestCastor /home/julien/dev/sf/mapping.xml /home/julien/dev/sf/test/bouteille.xml E"); sb.append("\n\n- Euh...je veux pas dire mais tes exemples ils servent a rien et ce sont les meme presque !!"); sb.append("\n- Et alors ? T'as un truc a redire ? C'est moi le developpeur ! Je fais ce que je veux tiens je vais te mettre un autre exemple (dans ta gueule)..."); sb.append("\n\nExemple : java TestCastor C:\windows\System32\oxWmr32.dll c:\boutanche.ocx I"); sb.append("\n- Alors ? c'est pas un bel exemple ca ? Et j'en ai plein d'autre ! Hahahahahahaha ..........."); sb.append("\n\n[root@neptune] # shutdown -h now"); sb.append("\nShutdown in progress..."); System.out.println(sb.toString()); } public static void importerDepuisXml(String mapping, String xml) throws Exception { System.out.println("Importation(" + mapping + ", " + xml + ")"); Cave maCave = new Cave(); CastorHelper.getInstance().loadXMLFile(maCave, mapping, xml); System.out.println("Voici le contenu de mon objet JAVA :\n" + maCave.toString()); System.out.println("\nAlors ? Il est pas beau mon Castor ?"); } public static void exporterVersXml(String mapping, String xml) throws Exception { System.out.println("Exportation(" + mapping + ", " + xml + ")"); // On va commencer par ajouter des bouteilles dans la cave Cave maCave = new Cave(); // 12 boutanches pour commencer Bouteille boutanche = new Bouteille(); boutanche.setId(maCave.getNextBouteillesIndex()); boutanche.setNom("Cote Rotie La Mouline"); boutanche.setAnnee("1990"); boutanche.setQuantite("12"); boutanche.setType("Rhone Rouge"); boutanche.setFormat("75 cl"); maCave.ajouterBouteille(boutanche); // 6 autres pour continuer boutanche = new Bouteille(); boutanche.setId(maCave.getNextBouteillesIndex()); boutanche.setNom("Fixin"); boutanche.setAnnee("2000"); boutanche.setQuantite("6"); boutanche.setType("Bourgogne Rouge"); boutanche.setFormat("75 cl"); maCave.ajouterBouteille(boutanche); // et 4 de champagne pour terminer boutanche = new Bouteille(); boutanche.setId(maCave.getNextBouteillesIndex()); boutanche.setNom("Ruinart"); boutanche.setAnnee("2004"); boutanche.setQuantite("4"); boutanche.setType("Champagne blanc"); boutanche.setFormat("75 cl"); maCave.ajouterBouteille(boutanche); // Et il ne reste plus qu'à sauver en base (XMl) // On va écraser le fichier existant notre but n'est pas de se prendre la tête. CastorHelper.getInstance().saveXMLFile(maCave, mapping, xml); System.out.println("\n\tTu peut aller voir le contenu du fichier XML genere : " + xml); } }
Voilà il ne reste plus qu’à exécuter.
2.3 Envoyer le bouzin
Avant d’éxécuter il vous faut bien sur les librairies castor et xerces (castor-xml.jar et xerces.jar).
Pour compiler (sisi je tiens à vous aider 😉 ):
[root@neptune]# javac -classpath .:castor-xml.jar:xerces.jar *.java
Puis on exécute:
[root@neptune]# java -classpath .:castor-xml.jar:xerces.jar TestCastor
Et là il faut suivre les instructions 😉 Juste : si tu es en JDK 1.5 et plus si affinités, essaye de trouver un commons-logging.jar dans un coin pour pouvoir exécuter, sinon pour les jdk < pas de soucis.
Par exemple on commence par l’export :
[root@neptune]# java -cp .:castor-xml.xml.jar:xerces.jar TestCastor mappingCave.xml maCave.xml E
Puis on importe :
[root@neptune]# java -cp .:castor-xml.jar:xerces.jar TestCastor mappingCave.xml maCave.xml I
3. Conclusion
Castor c’est cool et c’est pratique. Toi aussi utilise Castor et transmet-le. Castor (tout comme Torque) fait partie d’une chaîne de l’espoir, il ne faut pas la briser ! Si dans les 37.5 secondes tu n’arrives pas à convaincre 42 personnes d’utiliser Castor tu seras maudit à jamais et des Castors lapons hermaphrodites viendront te sod… te voir la nuit tous les jours !!! Par contre si tu y arrives, sache que dès le lendemain la SPA viendra te voir et te dira :”Merci de nous avoir aidé à la sauvegarde des Castor qui eux aussi ont des grandes dents pointues tel le monstre de Caerbannog ! (rien que d’y penser j’ai peur).
Alors, pour toutes ces raisons, toi aussi devient un Castor User !
Castor rulez…
@musez-vous bien !
Leave a comment