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 :
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 :
Selon mon temps disponibles, je vais tenter de découper les billets de la manière suivante :
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.
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.
A mon sens, il existe 3 types d'éditeur qui permettent de mettre à jour un contenu XML :
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 :
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.
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].
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.
Voici une copie d'écran de la page NavigationRule de FacesConfigEditor :

Cette copie d'écran est une représentation graphique du fragment XML suivant :
<navigation-rule>
<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 :
<?xml version="1.0" encoding="UTF-8"?>
<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.
La vue Properties permet d'éditer les propriétés selon la nature de l'éditeur. N'importe quel éditeur peut utiliser cette vue :
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();
}
...
}
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 :
Pour étudier comment se comporte FacesConfigEditor, nous allons ouvrir le fichier faces-config.xml dans 2 éditeurs différents:
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.
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 :

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>
...
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.
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é.
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 :
Dans les 2 cas, le modèle XML doit être capable de notifier que sa structure change.
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...).
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 :
FacesConfigType emfFacesConfig = ...
EList<ManagedBeanType> emfManagedBeans = emfFacesConfig.getManagedBean();
Avec DOM, on devrait faire :
Document domFacesConfig = ...
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).
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 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).
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();
}
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) :
public void doSave(IProgressMonitor monitor_) {
...
pageflowPage.doSave(file, monitor_);
...
sourcePage.doSave(monitor_);
...
}
Vous devez être identifié pour poster un commentaire.
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 |
Copyright © 2000-2012 - www.developpez.com