MANGUE Impression de données 0. Préambule 1. Principe de Fonctionnement 2. Description des fichiers XML générés 2.1 DescriptionImpression.XML 2.2 Fichier XML généré 2.3 Exemples complets de balise et résultat des fichiers XML 2.3.1 Balises de DescriptionImpression.XML 2.3.2 Fichier XML dans le cas où on demande de générer les données pour l'individu 2.3.3 Fichier XML dans le cas où l'individu est issu d'une relation 3. Réalisation des fichiers Jasper avec ireport 3.1 Variables à définir dans le rapport 3.2 Paramètres à définir dans le rapport 3.2.1 Paramètres obligatoires 3.2 Sous- rapports 3.3 Tests d'un rapport ou d'un sous-rapport avec ireport 4. Implémenter les méthodes d'impression 4.1 Sans utiliser le multithreading 4.1.1 Côté Client 4.1.2 Côté Serveur 4.2 En utilisant le multi-threading 4.2.1 Côté Client 4.2.2 Côté Serveur 4.2.3 Impression des arrêtés 4.3 Phases de développement 0. Préambule : Configuration de Mangue 1/ Installer si nécessaire tous les jars nécessaires à Jasper dans le directory WebObjects/Extensions. Personnellement, j'ai mis tous les jars de ireport/lib 2/ Configurer dans les propriétés de l'application Mangue (fichier Properties dans Mangue.Woa ou dans XCode Resources:Properties) les deux arguments DIRECTORY_IMPRESSION, DIRECTORY_JASPER pour indiquer où on doit imprimer les fichiers et où se trouvent les fichiers jasper 3/ Configurer l'exécutable de Mangue pour ajouter l'option -Djava.awt.headless=true. Cette option empêche qu'une mini-appli WebObjects soit lancée sur le serveur quand on fait du swing 3/ Modifier le composant JavaClient.wod pour indiquer le réglage par défaut du directory dans lequel le pdf est rappatrié sur le client : prefsrepertoireimpression 4/ Installer sur le serveur les fichiers Jasper générés par ireport dans le directory désigné par la propriété DIRECTORY_JASPER. Ils doivent tous être dans ce directory. 5/ Lancer Mangue et côté client régler, dans les préférences utilisateur, le directory dans lequel on souhaite que l'impression soit faite (sinon elle sera faite dans le directory indiqué par prefsrepertoireimpression) 6/ Pour tester avec ireport, modifier FicheIdentite.jrxml et FicheSynthese.jrxml pour modifier la valeur par défaut des variables DIRECTORY_IMPRESSION et DIRECTORY_JASPER et linstaller dans le directory bin de irepord ces fichiers 1. Principe de Fonctionnement L'impression est basée sur Jaspers. Elle utilise comme source de données des fichiers XML. Les fichiers XML (et image pour l'etat Civil) sont générés sur le serveur à partir des données de l'application suite à une demande du poste client. Jasper génère le fichier pdf sur le serveur. Celui-ci est ensuite retourné sur le client puis affiché. Les fichiers jasper sont créés avec ireport et utilisent des sources de données de type XML. Principe de Fonctionnement 2. Description des fichiers XML générés Les données dans les fichiers sont générées à partir de la description des données à imprimer dans le fichier DescriptionImpression.XML qui se trouve dans les ressources de l'application. Le but de DescriptionImpression.XML est de limiter le nombre d'attributs générés dans les fichiers XML pour que Jasper soit performant. Génération des fichiers XML
2.1 DescriptionImpression.XML Ce fichier contient pour chaque entité imprimable la liste des attributs/méthodes/relations qui doivent être générés dans le fichier XML. La balise <entite> correspond à une entité du modèle (Mangue.eomodeld) et son nom est le nom de cette entité dans le modèle. Exemple : <entite nom=individuulr> La balise <attribut> correspond à un attribut, une méthode ou une relation du modèle et son nom correspond au nom dans le modèle ou la classe java associée à cette entité (pour les méthodes) : valeur_simple="oui" attribut optionnel, il indique que l'objet retourné n'est pas une entité attribut_obligatoire ="oui" attribut optionnel qui indique que l'attribut doit toujours être généré. Un attribut qui n'est pas obligatoire ne sera pas généré lorsque l'entité est issue d'une relation. Exemples : <attribut nom="nomusuel" valeur_simple="oui" obligatoire="oui"/> : attribut de l'entité, toujours généré, valeur simple <attribut nom="personnel"/> : relation de l'entité (telle qu'elle est nommée dans le modèle), on attend comme valeur une autre entité <attribut nom="situationprofessionnelle" valeur_simple="oui"/> : méthode de l'entité, valeur simple Le fichier DescriptionImpression.XML peut être modifié sans recompilation (en mode déploiement) : - si on ajoute des attributs/relations définis dans le modèle et accessibles côté serveur (présence du diamant) - si on modifie le statut obligatoire ou non des attributs. 2.2 Fichier XML généré Ce fichier contient les données pour l'entité ou le tableau d'entités (de même type) que l'on veut imprimer. Dans le cas de l'impression d'une entité unique la balise d'entête est le nom de l'entité. Dans le cas de l'impression d'un tableau, la balise est le nom de l'entité au pluriel. Les attributs générés sont les valeurs des attributs correspondant à la description de l'entité dans DescriptionImpression.XML Exemples : - Génération pour un individu - Tableau d'individus <IndividuUlrs> <prenom>pascal</prenom> <nomusuel>mars</nomusuel> </IndividuUlrs> 2.3 Exemples complets de balise et résultat des fichiers XML 2.3.1 Balises de DescriptionImpression.XML <entite nom="individuulr"> <attribut nom="civilite" valeur_simple="oui" obligatoire="oui"/> <attribut nom="prenom" valeur_simple="oui" obligatoire="oui"/> <attribut nom="nomusuel" valeur_simple="oui" obligatoire="oui"/> <attribut nom="nompatronymique" valeur_simple="oui"/> <attribut nom="prenom2" valeur_simple="oui"/> <attribut nom="dnaissance" valeur_simple="oui"/> <attribut nom="villedenaissance" valeur_simple="oui"/> <attribut nom="indnoinsee" valeur_simple="oui"/> <attribut nom="indcleinsee" valeur_simple="oui"/> <attribut nom="situationprofessionnelle" valeur_simple="oui"/> <attribut nom="personnel"/> <attribut nom="tosituationfamiliale"/> <entite nom="personnel"> <attribut nom="numen" valeur_simple="oui" obligatoire="oui"/> <attribut nom="nbenfants" valeur_simple="oui" obligatoire="oui"/> <entite nom="situationfamiliale"> <attribut nom="lsituationfamille" valeur_simple="oui" obligatoire="oui"/> 2.3.2 Fichier XML dans le cas où on demande de générer les données pour l'individu <civilite>madame</civilite> <nompatronymique>durand</nompatronymique> <prenom2>sebastienne</prenom2> <dnaissance>12/05/1963</dnaissance> <villedenaissance>tanger</villedenaissance> <indnoinsee>2630599350042</indnoinsee> <indcleinsee>48</indcleinsee> <situationprofessionnelle>titulaire : INGENIEUR D'ETUDES RF 2E CLASSE Echelon : 03</situationProfessionnelle> <personnel> <numen>48a9843484aqo</numen> <nbenfants>4</nbenfants> </personnel> <tosituationfamiliale> <lsituationfamille>marie</lsituationfamille> </tosituationfamiliale> 2.3.3 Fichier XML dans le cas où l'individu est issu d'une relation <civilite>madame</civilite> <nompatronymique>durand</nompatronymique> 3. Réalisation des fichiers Jasper avec ireport Avec ireport, on génère des fichiers Jasper en définissant un fichier de rapport principal et éventuellement des sous-rapports. 3.1 Variables à définir dans le rapport Pour chaque attribut à imprimer, on définit une variable de la manière suivante : On donne un nom au champ, on définit son type et dans la zone de description, on indique le nom de la balise à rechercher dans le fichier XML à partir du nœud racine. Exemples : voir l'exemple de fichier XML ci-dessus Variable NOM_USUEL, type String, description : nomusuel Variable NUMEN, type String, description : personnel/numen
Définition des variables 3.2 Paramètres à définir dans le rapport 3.2.1 Paramètres obligatoires DIRECTORY_JASPER : chemin d'accès sur le serveur du directory qui contient les fichiers Jasper. Une valeur par défaut est fournie, elle sera modifiée lors de l'exécution dans Mangue par la propriété DIRECTORY_JASPER définie dans les propriétés de l'application ou les arguments de lancement. DIRECTORY_IMPRESSION : chemin d'accès sur le serveur du directory qui contiendra les fichiers imprimés. Une valeur par défaut est fournie, elle sera modifiée lors de l'exécution dans Mangue par la propriété DIRECTORY_IMPRESSION définie dans les propriétés de l'application ou les arguments de lancement. NUMERO_FICHIER : indique le numéro de fichier qui sera accolé au nom de fichiers XML/PDF pour éviter des conflits entre utilisateur. Ce numéro est incrémenté à chaque impression. Pour la valeur par défaut, mettre une chaîne vide (""). Il sera utilisé dans les sous-rapports. 3.2 Sous- rapports Les sous rapports récupèrent les données dans des fichiers XML qui ne sont pas nécessairement les mêmes que le fichier XML du rapport principal pour avoir des fichiers légers. On définit les sous-rapports dans le rapport principal. Qu'on utilise ou non un fichier XML différent, on règle le paramétrage du sous-rapport en indiquant : - dans l'onglet Subreport : - utilisation d'une "datasource expression" - valeur de l'expression : on saisit une expression sous la forme new net.sf.jasperreports.engine.data.jrxmldatasource(nomfichier, chemin dans le fichier) - dans l'onglet Subreport (Other) : - subreport expression : $P{DIRECTORY_JASPER + Nom fichierjasper du sous-rapport Exemple : on veut avoir un sous-rapport qui affiche les diplômes. 1/ Créer les paramètres définis en 1 2/ Créer le sous-rapport et le paramétrer de la manière suivante : new net.sf.jasperreports.engine.data.jrxmldatasource($p{directory_impression + "Diplomes" + $P{NUMERO_FICHIER + ".XML","/IndividuDiplomess/IndividuDiplomes") Paramétrage du sous rapport 3.3 Tests d'un rapport ou d'un sous-rapport avec ireport 1/ Les fichiers XML doivent être copiés dans le directory bin de ireport 2/ Définir avec le menu Data:Connections /DataSources la source de données : - nom de la source de données - type de connection - nom du fichier XML - Chemin d'accès dans le fichier XML (voir 3.2.1 pour les chemins d'accès)
3/ Sélectionner la source définie comme source de données par défaut Définition d'une source de données pour tester avec ireport 4. Implémenter les méthodes d'impression L'impression sans ou avec thread utilise côté client et côté serveur un bon nombre de méthodes identiques. Il est donc recommandé de lire le paragraphe 4.1 même lorsqu'on veut utiliser uniquement le multithreading. 4.1 Sans utiliser le multithreading 4.1.1 Côté Client La classe univ.mangue.client.impression.utilitairesimpression contient une méthode pour déclencher l'impression et afficher le fichier pdf à l'utilisateur (voir figure en 1.) : afficherpdfavecmethode(eoeditingcontext editingcontext,string nommethode,class[] classeparametres,object[] parametres,string nomfichierpdf) Le nom de la méthode correspond au nom de la méthode implémentée côté serveur dans la classe Session (elle commence obligatoirement par "clientsiderequest") Le tableau de classes correspond à la classe des paramètres de la méthode et le tableau de nom à la valeur des paramètres Le nom du fichier pdf est le nom du fichier qui sera généré sur le client dans le directory réglé dans les préférences utilisateur (PrefsPersonnel) Rappel : lorsqu'on a besoin de communiquer des objets métier entre le client et le serveur, il faut passer leur GlobalID. Exemple : génération et affichage de la fiche identité Class[] classeparametres = new Class[] {EOGlobalID.class; Object[] parametres = new Object[]{editingContext().globalIDForObject(currentIndividu()); UtilitairesImpression.afficherPdfAvecMethode(editingContext(),"clientSideRequestImprimerFicheIdentite", classeparametres,parametres,"ficheidentite_" + currentindividu().nomusuel()); 4.1.2 Côté Serveur 1/ Implémenter dans la session une méthode correspondant à celle appelée côté client. Elle doit retourner un NSDictionary contenant les données du fichier pdf (clé data) et un message de réalisation ou d'erreur (clé message) 2/ Implémenter dans la classe univ.mangue.serveur.impression.imprimeur la méthode réelle : - la classe univ.mangue.serveur.impression.generateurxml contient des méthodes pour générer les fichiers XML - la classe univ.mangue.serveur.impression.serveurimpression contient une méthode pour générer le fichier pdf avec Jasper univ.mangue.serveur.impression.generateurxml : public static boolean genererxmlpourrecord(string nomfichierxml,nskeyvaluecoding record,boolean imprimerrelations); public static boolean genererxmlpourrecords(string nomfichierxml,nsarray records,boolean imprimerrelations); public static boolean genererxmlpourrecords(string nomfichierxml,nsarray records,nsdictionary autresvaleurs,boolean imprimerrelations); Le nom du fichier xml est le nom du fichier à générer sans son extension. Il sera généré dans le directory "XML" du directory DIRECTORY_IMPRESSION défini dans les propriétés de l'application. imprimerrelations indique si on souhaite ou non imprimer les relations du record. Ces trois méthodes acceptent des EOGenericRecord mais aussi des objets qui implémentent l'interface NSKeyValueCoding (voir l'impression de la fiche de synthèse dans le code). On ajoute alors dans DescriptionImpression.XML la description de l'objet comme si c'était une entité normale (voir <FicheSynthese> dans DescriptionImpression.XML) La dernière méthode accepte qu'on lui fournisse des valeurs supplémentaires dans un dictionnaire. Ces valeurs sont générées dans le noeud racine du fichier XML avec comme tags les clés du dictionnaire (ce doit être des chaînes de caractères) et comme valeurs des objets qui répondent à la méthode tostring(). univ.mangue.serveur.impression.serveurimpression : public static NSDictionary imprimerfichier(string nomfichierxml,string nomfichierjasper,string cheminaccesrecord) Le nom du fichier xml est le nom du fichier xml contenant les données, le nom du fichier jasper est le nom du fichier Jasper utilisé pour générer le rapport, le chemin d'accès est le chemin d'accès du record dans le fichier XML. Voir la définition des sources de données en 3.2.1 Le fichier pdf est généré dans le directory "PDF" du directory DIRECTORY_IMPRESSION défini dans les propriétés de l'application. Tous les noms de fichier sont à fournir sans leur extension. Exemple : génération de la fiche identité Dans la classe "Session" : public NSDictionary clientsiderequestimprimerficheidentitesansthread(eoglobalid individuid) { EOIndividu individu = (EOIndividu)defaultEditingContext().faultForGlobalID(individuID,defaultEditingContext()); return Imprimeur.sharedInstance().imprimerFicheIdentite(individu); Dans la classe "Imprimeur" : public NSDictionary imprimerficheidentite(eoindividu individu) { GenerateurXML.genererXMLPourRecord("FicheIdentite.XML",individu,true); GenerateurXML.genererXMLPourRecords("Enfants.XML",individu.enfants(),false);... return ServeurImpression.imprimerFichier("FicheIdentite","FicheIdentite","/IndividuUlr"); catch (Exception e) { return new NSDictionary(e.getMessage(),"message"); On utilise la méthode genererxmlpourrecord pour l'individu car on veut imprimer un seul record, on utilise la méthode genererxmlpourrecords pour les enfants car on veut générer autant de records que d'enfants (voir 2.2 Fichier XML) 4.2 En utilisant le multi-threading 4.2.1 Côté Client Un gestionnaire d'impression a été ajouté côté client pour gérer le multithreading (GestionImpressions). Il prend en charge toute la gestion du multi-threading et affiche les messages émis par le serveur au cours de l'impression dans un dialogue. La classe univ.mangue.client.impression.utilitairesimpression contient une méthode pour mettre en œuvre ce dialogue, déclencher l'impression et afficher le fichier pdf à l'utilisateur (voir figure en 1.) : imprimeravecdialogue(eoeditingcontext editingcontext,string nommethode,class[] classeparametres,object[] parametres,string nomfichierpdf,string titrefenetre) Le nom de la méthode correspond au nom de la méthode implémentée côté serveur dans la classe Session (elle commence obligatoirement par "clientsiderequest") Le tableau de classes correspond à la classe des paramètres de la méthode et le tableau de nom à la valeur des paramètres Le nom du fichier pdf est le nom du fichier qui sera généré sur le client dans le directory réglé dans les préférences utilisateur (PrefsPersonnel) Rappel : lorsqu'on a besoin de communiquer des objets métier entre le client et le serveur, il faut passer leur GlobalID. Exemple : génération et affichage de la fiche identité Class[] classeparametres = new Class[] {EOGlobalID.class; Object[] parametres = new Object[]{editingContext().globalIDForObject(currentIndividu()); UtilitairesImpression.imprimerAvecDialogue(editingContext(),"clientSideRequestImprimerFicheIdentite",
classeparametres,parametres,"ficheidentite_" + currentindividu().nomusuel(),"fiche d'identité"); 4.2.2 Côté Serveur 1/ Implémenter dans la session une méthode correspondant à celle appelée côté client. Elle doit retourner un NSDictionary contenant les données du fichier pdf (clé data) et un message de réalisation ou d'erreur (clé message). Cette méthode doit instancier un serveur thread manager. Le constructeur de la classe ServeurThreadManager est : ServerThreadManager(Object object,string nommethode,class[] classeparametres,objet[] parametres) où : - objet est l'objet dans lequel sera déclenché la méthode - nommethode est le nom de la méthode à déclencher dans cet objet - classeparametres et parametres sont le tableau de classes correspond à la classe des paramètres de la méthode et le tableau de nom à la valeur des paramètres Puis déclencher le thread par l'invocation de la méthode start(). 2/ Implémenter dans la classe univ.mangue.serveur.impression.imprimeur la méthode réelle. Cette méthode doit comporter les arguments décrits dans la méthode de la session plus un paramètre de classe ServeurThreadManager : ce paramètre peut être utilisé pour signaler un message qui sera transmis au client. Il existe dans la classe Imprimeur une méthode signaler signaler(serverthreadmanager manager,string message) pour le faire. -Tous les noms de fichier sont à fournir sans leur extension. Exemple : génération de la fiche identité en multi-thread Dans la classe "Session" : public Boolean clientsiderequestimprimerficheidentite(eoglobalid individuid) { EOIndividu individu = (EOIndividu)SuperFinder.objetForGlobalIDDansEditingContext(individuID,defaultEditingContext()); Class[] classeparametres = new Class[] {EOIndividu.class; Object[] parametres = new Object[]{individu; threader = new ServerThreadManager(Imprimeur.sharedInstance(),"imprimerFicheIdentite",classeParametres,parametres); threader.start(); return new Boolean(true); catch (Exception e) { e.printstacktrace(); return new Boolean(false); Dans la classe "Imprimeur" : public NSDictionary imprimerficheidentite(eoindividu individu,serverthreadmanager manager) { signaler(manager,"préparation de l'impression"); GenerateurXML.genererXMLPourRecord("FicheIdentite.XML",individu,true); GenerateurXML.genererXMLPourRecords("Enfants.XML",individu.enfants(),false);... signaler(manager,"génération du fichier pdf"); return ServeurImpression.imprimerFichier("FicheIdentite","FicheIdentite","/IndividuUlr"); catch (Exception e) { return new NSDictionary(e.getMessage(),"message"); 4.2.3 Impression des arrêtés Plusieurs contraintes sont imposées pour pouvoir imprimer les arrêtés : 1.Classes métier : elles doivent comporter une méthode public NSArray visas() qui retourne les références légales à afficher dans l'arrêté. La classe EOVisa comporte plusieurs méthodes statiques si retrouvent les visas en fonction de critères différents (type population, type congé, ) 2.Classes d'interface : la méthode typegestionarrete() doit retourner autre chose que null pour que le bouton "imprimante" s'active. En effet seuls certains types d'arrêté sont gérés par les établissements. Dans les autres cas, on ne peut saisir que les numéros et dates d'arrêté pour mémoire. 3.Classes d'impression : elles doivent à la fois générer le XML pour les visas ainsi que celui des données du record à publier dans l'arrêté voir la méthode imprimerarretepourcarriere de la classe Imprimeur. 4. Fichier Jasper : il doit comporter un paramètre supplémentaire VISA_DATA_SOURCE qui indique la source de données pour les visas Lorsque les classes d'interface héritent des classes GestionCongeAvecArrete ou GestionEvenementAvecArrete une contrainte supplémentaire est imposée car les méthodes d'impression sont génériques et implémentées dans les superclasses. Le nom des fichiers Jasper doit respecter la nomenclature suivante : "Arrete" + nom de l'entité (EOModel)pour lequel on imprime l'arrêté. Exemples : ArreteCongeMaladie, MiTpsTherap. Exemple : supposons qu'il faille pouvoir imprimer des arrêtés pour les mises à disposition. 1. Ajout de la méthode public NSArray visas() à la classe EOMad 2. Dans la classe GestionMad, modification de la méthode typegestionarrete() 3. Rien à faire car GestionMad est une sous-classe de GestionEvenementAvecArrete 4. Création d'un fichier Jasper en dupliquant par exemple ArreteMiTpsTherap (pour bénéficier du paramètre VISA_DATA_SOURCE). Ce fichier se nomme ArreteMad (Mad = nom entité) et adaptation du fichier Jasper aux besoins 4.3 Phases de développement 1/ Développer les méthodes côté client et côté serveur sans faire apppel à la méthode ServeurImpression.imprimerFichier pour n'avoir que la génération des fichiers XML 2/ Tester sous Mangue 3/ Récupérer les fichiers XML sur le serveur : - vérifier qu'ils sont conformes aux attentes : - structure du XML correcte - tous les attributs nécessaires sont présents Dans le cas où il manque des attributs, modifier DescriptionImpression.XML et les objets métier si nécessaire pour ajouter des méthodes retournant les valeurs nécessaires. Recommencer les phases 2 et 3. 4/ Installer les fichiers XML dans le directory "bin" de ireport 5/ Créer les fichiers jasper avec ireport et tester le pdf généré sous ireport 5/ Intégrer à Mangue en rajoutant la méthode imprimerfichier dans la méthode d'impression implémentée