avril
2009
Il y a quelques mois j’ai décidé de me lancer dans le développement d’un éditeur Eclipse de workflow XML. Cet éditeur permet de mettre à jour un fichier XML de workflow (basé sur un schéma XML propriétaire) à partir d’un éditeur multi-pages comme celui que l’on peut trouver dans l’éditeur de JSF webtools qui permet de gérer le workflow des pages d’un projet JSF (navigation-rules) du fichier XML de configuration faces-config.xml à l’aide de plusieurs pages (éditeur graphique, UI classiques) :
Pour développer ce type d’editeur Eclipse, je me suis inspiré de divers projets dont :
- PDE Incubator
- JSF webtools, plugin eclipse qui permet de gérer le fichier XML de configuration faces-config.xml.
- Spring Webflow Editor plugin Eclipse qui permet de gérer le fichier de configuration Spring webflow.
Lors de la conception et réalisation de mon editeur de workflow, je me suis heurté à de nombreuses problématiques car je n’avais aucune expérience dans ce domaine. Le but des billet à venir sont de de faire partager mon expérience sur la conception d’un éditeur Eclipse de workflow XML :
- Les questions que je me suis posées au cours du développement et que j’aurais dû me poser dès le départ fautes d’expériences.
- Les différentes solutions techniques (que j’ai pu découvrir dans les projets sur lesquels je me suis inspiré) qui existent pour créér un éditeur de workflow XML.
Selon mon temps disponibles, je vais tenter de découper les billets de la manière suivante :
- Etudes de plusieurs projets d’éditor XML existant. Cette étude permettra de mettre en avant les différents composants (EditorPart, Property View, Outline…) d’un editeur XML, les problématiques d’un éditeur XML. L’étude portera sur les projets suivants :
- Le billet step1 fera un état de l’art des études et listera toutes les questions (autant fonctionnelles que techniques) qui me semblent importantes à se poser avant de démarrer un projet d’éditeur de workflow XML.
- Les billets suivants décriront pas à pas comment j’ai implémenté mon éditeur de workflow XML et je tenterai d’expliquer mes choix fonctionnels et techniques.
Avant de rentrer dans le vif du sujet, je remercie DaveShot qui lui aussi est en train de concevoir un tel editeur Eclipse et qui m’a aidé à rédiger ces billets.
Etude de JSF Webtools
JSF webtools, est un plugin eclipse qui permet entre autre de gérer le fichier XML de configuration faces-config.xml à l’aide de l’éditeur FacesConfigEditor. Dans cette section nous décrirons le comportement de cet éditeur et de ses composants (page source…). Des Note technique seront accompagnées tout au long dans ce billet pour vous donner des point d’entrées dans le code de JSF webtools si jamais vous souhaitez aussi étudier le code de JSF webtools.
Editor – FacesConfigEditor
A mon sens, il existe 3 types d’éditeur qui permettent de mettre à jour un contenu XML :
- Editeur textuel. Cet type d’éditeur permet de saisir directement le contenu XML du workflow XML. Il peut être enrichit avec des fonctionnalités comme la complétion ou la validation (pour mettre des marqueurs dans le cas d’erreur de cohérence).
- Editeur UI avec Interface Utilisateur (UI/SWT). Ce type d’éditeur permet de mettre à jour le contenu XML à l’aide d’une widgets (Text, Combo, Table….).
- Editeur graphique (GEF). Ce type d’éditeur permet de mettre à jour le contenu XML à partir d’une représentation graphique.
Il est possible de combiner ces 3 types d’editor dans un même editors (appelé éditeur multi-pages). JSF webtools utilise cette approche ou l’editor JSF FacesConfigEditor est constitué de plusieurs pages dont :
- Page Source : éditeur textuel qui affiche le source XML du fichier faces-config.xml.
- Page ManagedBean : éditeur UI qui affiche les managed bean JSF sous forme de tableau. SWT
- Page NavigationRule qui affiche sous forme de graphique le « flow » des pages configurées dans le fichier faces-config.xml.
Note technique:
- Les sources de JSF FacesConfigEditor se retrouvent dans le projet org.eclipse.jst.jsf.facesconfig.ui.
- L’editor JSF FacesConfigEditor est géré par la classe org.eclipse.jst.jsf.facesconfig.ui.FacesConfigEditor.
- La classe org.eclipse.jst.jsf.facesconfig.ui.FacesConfigEditor editor multi-page, étend la classe org.eclipse.ui.forms.editor.FormEditor.
- FormEditor étent la classe org.eclipse.ui.part.MultiPageEditorPart qui est la classe de base pour les éditor multi-page. FormEditor permet d’avoir un rendu des pages de type formulaire (Eclipse Forms) à l’aide org.eclipse.ui.forms.widgets.FormToolkit. Pour plus d’informations sur Eclipse Forms, veuillez consultez l’article Eclipse Forms: New in 3.3 et Eclipse Forms: Rich UI for the Rich Client
- Un éditor multi-page est constitué de plusieurs pages qui étendent généralement org.eclipse.ui.part.EditorPart. Les pages de type UI FacesConfigEditor étendent la classe org.eclipse.ui.forms.editor.FormPage qui est un EditorPart capable de gérer un rendu Eclipse Forms.
Page Source
Voici une copie d’écran de la page Source de FacesConfigEditor :
Note technique:
- La page Source est gérée à l’aide d’un éditeur texte WST org.eclipse.wst.sse.ui.StructuredTextEditor.
public class FacesConfigEditor extends FormEditor implements IEditingDomainProvider, ISelectionProvider {
...
/** The source text editor. */
private StructuredTextEditor sourcePage;
...
}- Dans le cas d’un contenu XML, StructuredTextEditor est capable de gérer en interne un Document W3c DOM synchronisé avec le contenu
saisi dans l’éditor text.- Pour plus d’informations sur StructuredTextEditor , vous pouvez lire le billet Tutorial WST- DOM SSE [Step 2].
Page ManagedBean
Voici une copie d’écran de la page ManagedBean de FacesConfigEditor :
Note technique:
- La page ManagedBean qui est un éditeur de type UI, est gérée par la classe org.eclipse.jst.jsf.facesconfig.ui.page.ManagedBeanPage.
- Cette page utilise le principe Master/Détail.
Page NavigationRule
Voici une copie d’écran de la page NavigationRule de FacesConfigEditor :
Cette copie d’écran est une représentation graphique du fragment XML suivant :
<display-name>page1.html</display-name>
<from-view-id>/page1.html</from-view-id>
<navigation-case>
<to-view-id>/page2.html</to-view-id>
</navigation-case>
</navigation-rule>
Les figures graphiques représentant les pages (page1.html et page2.html), et les liens peuvent peuvent être positionnés à l’endroit ou on le souhaite. Si vous fermer l’editeur FacesConfigEditor et que vous le ré-ouvrez vous pouver remarquer que les positions des figures graphiques ont été conservées, autrement dit les positions x, y de chacune des figures ont été stockées quelque part.
le fichier faces-config.xml ne contient pas les informations x,y de figures. JSF Web Tools gère un autre fichier (qui n’est pas visible) qui se trouve dans YOUR_PROJECT/.metadata/WebContent/WEB-INF/faces-config.pageflow. Voici le contenu du fichier faces-config.pageflow correspondant aux graphiques NatvigationRule ci-dessus :
<pageflow:Pageflow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:pageflow="http://www.sybase.com/suade/pageflow"
id="pf12399546824680"
configfile="/JSFProject/WebContent/WEB-INF/faces-config.xml">
<nodes xsi:type="pageflow:PFPage" name="page1.html" x="88" y="104" id="pf12399547449371" outlinks="pf12399547449372" path="/page1.html"/>
<nodes xsi:type="pageflow:PFPage" name="page2.html" x="284" y="192" id="pf12399547449373" inlinks="pf12399547449372" path="/page2.html"/>
<links id="pf12399547449372" target="pf12399547449373" source="pf12399547449371"/>
</pageflow:Pageflow>
Ce fichier contient toutes les informations (positions, liens) du graphique NatvigationRule. FacesConfigEditor s’occupe de synchroniser le fichier faces-config.pageflow et le fichier XML faces-config.xml.
Note technique:
- La page NavigationRule qui est un éditeur de type graphique, est gérée par la classe org.eclipse.jst.jsf.facesconfig.ui.pageflow.PageflowEditor.
- PageflowEditor étent la classe de GEF org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette qui est un org.eclipse.ui.part.EditorPart.
- Le fichier faces-config.pageflow est le résultat de la sérialisation d’une instance EMF org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow qui est synchronisée avec l’instance EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType.
- GEF(Graphical Editor Framework) est une API qui permet de gérer des graphes en deux dimensions. Elle est basée sur le pattern MVC ou le Modèle dans le cas de JSF Webtools correspond à l’instance EMF Pageflow.
Properties View
La vue Properties permet d’éditer les propriétés selon la nature de l’éditeur. N’importe quel éditeur peut utiliser cette vue :
- pour éditer des propriétés simple (clé/valeur). Pour plus d’information, veuillez consulter l’article Take control of your properties.
- pour personnaliser la vue Properties pour afficher des propriétés sous un autre format. Pour plus d’information, veuillez consulter l’article The Eclipse Tabbed Properties View.
Par exemple la Page ManagedBean permet d’éditer les propriétés du managed bean sélectionné :
Note technique:
- La personnalisation de la vue Properties s’effectue dans le cas de FacesConfigEditor par une implémentation de org.eclipse.ui.views.properties.IPropertySheetPage qui est appelé via la méthode FacesConfigEditor#getAdapter(Class adapter) :
...
public Object getAdapter(Class adapter) {
...
if (adapter == IPropertySheetPage.class) {
return getPropertySheetPage();
}
...
}
Synchronisation XML<->UI
Le contenu XML de la page source doit être synchrone avec l’UI (widgets ou graphique) des pages constituant l’éditor FacesConfigEditor et iversement. Autrement dit si l’utilisateur modifie le contenu XML de la page source, l’UI des différentes pages doivent se synchronisés et prendre la valueur du XML. Si l’utilisateur modifie une widget ou le graphique (page NavigationRule), le contenu XML doit se synchroniser et doit se mettre à jour avec le contenu de la widget modifiée.
Cette synchronisation XML<->UI peut s’effectuer de plusieurs manières :
- synchrone : l’utilisateur tape le contenu XML dans la page Source et toutes les pages UI se synchronisent automatiquement.
- a-synchrone : l’utilisateur tape le contenu XML dans la page Source, aucune page se synchronisent. Lorsque une page UI est activée (l’utilisateur clique sur la page), celle-ci se rafraîchit avec le contenu du XML.
Pour étudier comment se comporte FacesConfigEditor, nous allons ouvrir le fichier faces-config.xml dans 2 éditeurs différents:
- Editeur FacesConfigEditor.
- Editeur XML en faisant Open With/XML Editor.
Mettez côte à côte les 2 éditeurs (à gauche celui de FacesConfigEditor et à droite XML Editor) pour vvoir comment se comporte la synchronisation XML<->UI.
Synchronisation XML<->UI Widgets
Dans cette section nous allons étudier comment se comporte la synchronisation entre le modèle XML et les widgets UI. Pour cela accédez à la page ManagedBean :
UI 2 XML
Pour tester la synchronisation de l’UI widgets vers le modèle XML, il suffit de tapper du texte dans le champs Managed Bean Name. Dans notre cas Managed Bean Name à la valeur « string ». Tappez « A » à la fin de ce champs. Le champs contient maintenant « stringA » . Vous pourrez constatez que l’éditeur XML (celui de droite) n’a pas été rafraichit. Pour qu’il soit rafraichit, il faut activer une autre page de FacesConfigEditor. Cliquez par exemple sur la page Introduction, et vous constaterez que l’éditeur XML s’est rafraichit avec la valeur stringA.
<managed-bean>
<managed-bean-name>stringA</managed-bean-name>
...
XML 2 UI
Avant de tester, revenez sur la page ManagedBean. Pour tester la synchronisation du modèle XML vers l’UI widgets, il suffit de tapper du contenu XML dans l’éditor XML. Tappez « B » à la fin du node Text de l’élement managed-bean-name :
<managed-bean>
<managed-bean-name>stringAB</managed-bean-name>
...
Pous pourrez constater que l’UI ne s’est pas rafraichit. Quitter la page (comme précedemment) et revenez sur la page ManagedBean. L’UI s’est rafraichit correctement.
Synchronisation XML<->UI Graphiques
Dans cette section nous allons étudier comment se comporte la synchronisation entre le modèle XML et le graphique GEF. Pour cela accédez à la page NavigationRule :
Vous pouvez effectuer les mêmes tests de synchronisation que précédemment, et vous pourrez constater que le XML est totalement synchronisé avec l’éditeur GEF graphique. Il n’y a pas besoin de changer de page pour que le XML ou l’éditeur GEF soit synchronisé.
Modèle XML – DOM vs EMF
La plupart des éditeurs Eclipse permettent de gérer le contenu de divers fichiers. Dans le cas de JSF webtools, FacesConfigEditor permet de gérer le contenu du fichier faces-config.xml à tarvers plusieurs pages (Page Source, Page ManagedBean….). Lorsq’une widget est modifée et qu’elle doit mettre à jour le contenu XML, cette widget travaille avec une structure Java qui représente le XML. On parle alors de modèle XML. De ce que j’ai pu étudier, il existe 2 manière d’implémenter ce modèle XML :
- Approche par DOM : le contenu XML est chargé dans un Document W3c DOM.
- Approche par EMF : le contenu XML est chargé dans une instance EMF.
Dans les 2 cas, le modèle XML doit être capable de notifier que sa structure change.
Modèle XML – DOM
DOM est l’approche utilisée dans Spring Webflow Editor. Ce dernier utilise aussi un éditeur text WST org.eclipse.wst.sse.ui.StructuredTextEditor qui est capable de gérer en interne un DOM Document. Ce DOM Document (appelé DOM-SSE) est capable de notifier des evénements lorsque sa structure change (attribut XML change, elements XML insérés, supprimés…).
Modèle XML – EMF
EMF est l’approche utilisé dans JSF webtools. Je ne vais pas ici expliquer EMF car il existe de nombreux tutoriaux traitant le sujet. Dans le cas de JSF Webtools, le modèle XML est representé par une instance EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType qui se trouve dans le projet org.eclipse.jst.jsf.facesconfig.emf. Avec EMF on travaille sur une structure Java et non DOM. Par exemple pour récupérer la liste des manged-bean de faces-config, avec EMF FacesConfigType on fait :
EList<ManagedBeanType> emfManagedBeans = emfFacesConfig.getManagedBean();
Avec DOM, on devrait faire :
NodeList domManagedBeans = domFacesConfig.getDocumentElement().getElementsByTagName("managed-bean");
On peut déja voir à travers ce code que l’utilisation de l’API DOM est bien plus verbeuse que celle de EMF. De plus si la structure XML (validé par un schéma XML) change, il sera très difficile de voir l’impact avec DOM alors qu’avec EMF des erreurs de compilations apparaitront dans le projet (si par exemple on modifie dans le schéma XML, l’élement XML managed-bean par managed-beans par exemple).
EMF – Chargement
Dans JSF webtools, l’instance EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType est totalement synchronisée avec le contenu XML de faces-config.xml. Autrement dit, si l’utilisateur saisi du XML dans la page source, ceci met à jour l’instance EMF, et si l’utilisateur modifie une widget UI, ceci modifie l’instance EMF qui modifie le contenu XML.
En EMF classique, pour charger une instance EMF à partir d’un contenu XML, on utilise une implémentation de org.eclipse.emf.ecore.resource.Resource comme par exemple org.eclipse.emf.ecore.xmi.impl.XMLResourceImp. Cette implémentation de Resource ne gère pas de DOM en interne, et elle est utilisée uniquement pour le chargement.
Comme nous avons vu précédemment, WST propose la possibilité de récupérer un DOM-SSE à partir d’un fichier XML. Ce DOM-SSE est capable de notifier des événements lorsqu’il est modifié (l’utilisateur tappe du contenu XML). WST propose la possibilité de synchroniser une instance EMF avec un DOM-SSE à l’aide des org.eclipse.wst.common.internal.emf.resource.Translator WST qui permettent de définir un mapping entre un nom d’élement XML et un feature EMF.
Pour charger une instance EMF à partir d’un contenu XML et la synchroniser avec un DOM-SSE à l’aide des Translator, WST founit divers classes à implémenter dont org.eclipse.wst.common.internal.emf.resource.RootTranslator et org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl qui est une classe abstraite qui implémente org.eclipse.emf.ecore.resource.Resource et qui utilise les Translator.
JSF Web Tools utilise l’approche WST Translator pour synchroniser le contenu XML avec l’instance EMF.
Note technique:
- org.eclipse.wst.common.internal.emf.resource.RootTranslator (qui permet d’effectuer le mapping entre un nom d’élement, attribut XML et un feature EMF) de FacesConfigType est implémentée par la classe org.eclipse.jst.jsf.facesconfig.internal.translator.FacesConfigTranslator du projet org.eclipse.jst.jsf.facesconfig.
- org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl(qui est une Resource EMF qui fonctionne avec les Translator WST) est implémenté par la classe org.eclipse.jst.jsf.facesconfig.util.FacesConfigResourceImpl du projet org.eclipse.jst.jsf.facesconfig. C’est l’implémentation EMF Resource qui utilise les Translator FacesConfigTranslator.
- org.eclipse.wst.common.internal.emf.resource.TranslatorResourceFactory (qui est une factory de Resource EMF avec Translator) est implémenté par la classe org.eclipse.jst.jsf.facesconfig.util.FacesConfigResourceFactory du projet org.eclipse.jst.jsf.facesconfig.
EMF.Edit
EMF.Edit est aussi utilisé dans JSF Webtools et implémente l’interface EMF.Edit org.eclipse.emf.edit.domain.EditingDomain qui permet de modifier l’instance EMF FacesConfigType à partir de commandes EMF org.eclipse.emf.common.command.Command.
Autrement dit, dans JSF Webtools l’instance EMF n’est jamais modifié directement (lorsque par exemple l’UI change) mais elle est modifiée à travers l’instance EditingDomain qui permet de gérer en interne une stack de command eclipse.emf.common.command.CommandStack. Cette stack est très utile pour gérer le undo/redo de l’editeur (undo/redo des modifications effectuées sur l’instance EMF).
Etat dirty editor
Tout éditeur Eclipse gère un état appelé dirty, qui permet d’indiquer si le modèle géré par l’éditeur doit être sauvegardé ou non. L’éditeur devient dirty si le modèle à changé (le contenu du fichier a été modifié) à travers l’éditeur et qu’il doit être sauvegardé. L’état dirty se visualise généralement par le caractère * qui s’affiche à gauche de l’onglet de l’editeur. Dans le cas de JSF Webtools, l’éditeur FacesConfigEditor gère le modèle XML faces-config.xml, lorsqu’une modification du modèle XML est effectué à travers n’importe quelle page (Page Source, Page ManagedBean….), l’éditeur devient dirty :
Note technique:
- L’état dirty est géré en implémentant la métode org.eclipse.ui.ISaveablePart#isDirty() de l’éditor part.
- JSF webtools implémente l’état dirty dans la classe org.eclipse.jst.jsf.facesconfig.ui.FacesConfigEditor comme suit :
public boolean isDirty() {
return ((BasicCommandStack) editingDomain.getCommandStack()).isSaveNeeded() || super.isDirty();
}
Sauvegarde
La sauvegarde dans un editeur Eclipse s’effectue en implémentatnt les méthodes org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor monitor) et org.eclipse.ui.ISaveablePart#doSaveAs().
Voici le code de FacesConfigEditor#doSave(IProgressMonitor monitor) :
...
pageflowPage.doSave(file, monitor_);
...
sourcePage.doSave(monitor_);
...
}
- pageflowPage page qui gère la Page NavigationRule permet de sauver l’instance EMF org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow (qui contient les coordonnées de chacune des figures du diagrame GEF).
- sourcePage est l’editeur texte WST org.eclipse.wst.sse.ui.StructuredTextEditor. La sauvegarde du contenu XML s’effectue via cet éditeur (et pas via l’instance EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType).
6 Commentaires + Ajouter un commentaire
Articles récents
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step5]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step4]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step3]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step2]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step1]
Commentaires récents
- Conception d’un Editeur Eclipse de workflow XML [step 0] dans
- Conception d’un Editeur Eclipse de workflow XML [step 19] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
ok,je vais essayer cette solution.
Merci pour votre disponibilité.
Bonjour snatiz87 ,
Merci de tes encouragements. Pour debugger JSF Webtools , je n’ai pas repris les sources de CVS mais importer le Plug-In JSF WebTools à l’aide de la vue Plu-Ins (Window->Show View->Other… Puis PDE->Plug-ins) puis selection d’un plugin JSF WebTools, SSE.. bouton droit puis Import As-> Source Project.
Le fait de récuperer les sources de CVS ne peut pas bien marcher car les sources ne doivent pas etre en phase avec ceux des Plug-ins installe dans ton Eclipse.
Bon courage.
Angelo
Bonjour,
Est ce que vous pourriez m’aider concernant l’échange de données dans un workflow.
Je dois créer 2 agents avec JADE en utilisant Eclipse biensur, et les 2 agents en question doivent s’échanger des fichiers xml par exemple.
Slimfakhfakh@hotmail.fr (pour me contacter)
Merci d’avance.
Bonjour Angelo,
Merci encore pour ce très bon tuto,je vous demande si c’est possible de m’envoyer le code source du plugins JSF Webtools pour le debbuger, car j’ai essayé de le télécharger depuis le CVS, mais il contient bcp d’erreurs que je n’arrive pas à les corriger.
Merci d’avance
Bonsoir Mickael,
Merci pour tes encouragements, j’espère que ce billet et les prochains pourront t’aider.
Angelo
Merci beaucoup pour cet article très riche. Je vais le garder précieusement de côté pour mes prochains développement.
Mickael