Tutoriel plugins Eclipse

publicité
Tutoriel Plugins Eclipse (partie 1)
© 2007­2008, Frédéric Peschanski
Dans ce tutoriel de niveau débutant, nous réalisons une petite étude de cas de création de plugins Eclipse. Nous allons créer en pas à pas deux plugins:
–
–
plugin timer : un plugin inspiré de l'outil Cron d'Unix, ici en version très simplifiée, et qui permet d'enregistrer des tâches exécutées à intervalles réguliers. Ce plugin montre l'extension du Workbench eclipse avec des Action Sets (menus) et illustre également la définition de points d'extensions
plugin clock : exemple d'extension pour le timer. Montre également les bases de la création de vues en JWT/JFace.
Dans un premier temps, on lancera Eclipse dans sa version 3.1 ou 3.2.
Partie I. Le Plugin timer
Le développement de plugins Eclipse est pris en charge dans l'environnement par l'environnement de développement de plugins PDE (Plugin Development Environment) qui est fourni avec Eclipse en standard. Ce dernier est basé sur la boîte à outils de développement Java JDT (Java Development Toolkit). Les plugins Eclipse sont donc en général implémenté en Java. Ce tutoriel suppose donc un niveau intermédiaire de programmation orientée­objet en Java, ainsi que des connaissances minimales de l'environnement Eclipse du point de vue utilisateur/développeur Java.
1) Création du projet
Pour démarrer le développement d'un plugin, on passe par un assistant (Wizard) de création de projet en choisissant le menu Fichier (File) et Nouveau Projet (New Project) puis en sélectionnant le type de projet Projet de Plugin (Plug­in Project). Les étapes 1 et 2 sont représentées ci­dessous :
Etape 1 : Nouveau projet
Etape 2 : Projet de Plugin
On doit ensuite choisir le nom du plugin TimerPlugin en séparant de préférence les répertoires src/ et bin/ et en sélectionnant un plugin Eclipse et non Equinoxe1 (cf. étape 3). A l'étape suivante, on choisira l'identificateur (Plugin ID) du plugin upmc.cps.timer.TimerPlugin ainsi que son numéro de version (1.0.0 par défaut) et son nom descriptif (Plugin Name) TimerPlugin Plug­in. On générera un activateur donc le nom de classe sera upmc.cps.timer.Activator et ce plugin contribuera à l'interface utilisateur d'Eclipse. En revanche, ce n'est pas une application final RCP2 (Rich Client Platform). Les entrées nécessaires sont figurée à l'étape 4 ci­dessous :
1 Les plugin Equinoxe sont de bas niveaux et se placent au­dessus du framework OSGI et non d'Eclipse
2 Les plugins RCP sont des applications finales qui se placent au dessus­d'Eclipse mais sans les outils de développement.
Etape 3 : Informations générales
Etape 4 : Contenu du plugin
La page suivante (Next) de l'assistant propose, de façon optionnelle, de suivre des modèles de Plugins (templates) qui permettent de générer un code d'exemple pour différents types de contribution à l'environnement Eclipse (cf. étape 5). Ici, nous choisirons uniquement le modèle Action set « Bonjour le monde » ("Hello world" Action set) qui génère le code minimal nécessaire pour contribuer un menu global d'Eclipse3 (cf. étape 6).
3 Il est déconseillé de contribuer directement un menu global à Eclipse, et nous ne le faisons ici que pour illustrer l'extension de menus. En pratique, on préférera ajouter soit un menu contextuel soit, au pire, un sous­menu de menu global.
Etape 5 : les modèles de plugin
Etape 6 : le modèle "Hello World" Action Set pour contribuer un menu
Finalement, on choisira les options du menu que nous souhaitons ajouter sur la page suivante de l'assistant. On choisira le nom de paquetage Java (Java Package Name) upmc.cps.timer.actions (cf. étape 7 mais le nom de paquetage Java doit être corrigé). On trouvera dans ce paquetage toutes les classes générées par Eclipse pour réagir aux entrées du menu contribué. Le nom de classe d'action (Action Class Name) est TimerAction et le message d'information sera Timer Activated.
Etape 7 : les options de l'action set
2) Découverte de l'architecture du Plugin
L'assistant se termine après l'étape 7 en cliquant sur Terminer (Finish). Dans ce tutoriel, on choisit en fait de revenir en arrière et de n'utiliser aucun modèle. On se retrouve donc avec l'arborescence décrite à l'étape 8 ci­dessous :
Etape 8 : l'arboresence générée pour le plugin TimerPlugin
Cette vue de l'explorateur de paquetage (Package explorer) est composée des éléments suivants:
–
Le répertoire racine du projet TimerPlugin/
–
Le répertoire des sources src/
–
dans le paquetage upmc.cps.timer, la classe d'activation OSGI Activator (cf. ci­dessous)
–
Les dépendances standards Java JRE (Java Runtime Environment)
–
Les dépendances de Plug­in
–
ici nous dépendons de tous les plugins de base d'Eclipse:
–
framework OSGI/Equinoxe: org.eclipse.osgi.* et org.eclipse.equinoxe.*
–
les plugins de base: org.eclipse.core.*
–
les plugins d'interface graphique: org.eclipse.swt.*, org.eclipse.jface.*, org.eclipse.ui.*
–
–
–
Le fichier manifeste MANIFEST.MF (géré par l'assistant de plugin cf. ci­dessous)
Le fichier de configuration du plugin plugin.xml (ici non encore généré car nous n'effectuons encore aucune contribution. Ce fichier est également géré par l'assistant de plugin)
Les propriétés de construction build.properties (géré par l'assistant de plugin)
En double­cliquant dans l'explorateur de paquetage sur le fichier manifeste, le fichier de configuration ou les propriétés de construction, on obtient la vue de l'assistant de configuration de plugin (cf. Etape 9)
Etape 9 : Vue de l'assistant PDE de configuration de Plugin (onglet aperçu Overview)
C'est dans cette interface de configuration que nous éditerons la configuration du plugin. L'édition dans un éditeur de texte des fichiers de configuration du plugin est rarement nécessaire, mais la synchronisation entre l'interface graphique et l'édition textuelle est en général bien gérée par Eclipse.
Dans cette interface, on peut gérer:
–
les informations générales du plugin
–
l'environnement d'exécution (si besoin de spécifier une version spécifique de Java par exemple)
–
les dépendances (plugins requis)
–
l'environnement d'exécution (par exemple, pour utiliser des bibliothèques tierces)
–
les extensions et points d'extension pour contribuer à la plateforme Eclipse
–
le test du plugin: exécution et déboguage
–
l'export du plugin sous forme binaire
Parmi les fichiers les plus intéressants générés par le PDE, il y a par exemple le manifeste MANIFEST.MF qui décrit le plugin Eclipse en tant que Bundle OSGI4 dont le contenu est décrit ci­
dessous:
Manifest­Version: 1.0
Bundle­ManifestVersion: 2
Bundle­Name: TimerPlugin Plug­in
Bundle­SymbolicName: upmc.cps.timer.TimerPlugin
Bundle­Version: 1.0.0
Bundle­Activator: upmc.cps.timer.Activator
Bundle­Localization: plugin
Require­Bundle: org.eclipse.ui,
org.eclipse.core.runtime
Eclipse­LazyStart: true
Ce fichier contient les informations importantes du plugin ainsi que les dépendances en termes de bundles OSGI. Nous n'entrons pas dans les détails de ces dépendances dans ce tutoriel débutant. La dernière ligne indique à Eclipse que le plugin n'est pas un plugin de base et doit donc être chargé « à la demande » (ou de façon paresseuse lazy) pour ne pas occuper inutilement de la mémoire.
Lorsque chargé, le framework Equinoxe/OSGI déclenche l'activation du plugin dont le code est donné par la méthode start de l'activateur upmc.cps.timer.Activator généré par le PDE. La méthode stop effectue le travail inverse mais, en version 3.2 en tout cas, Eclipse ne décharge pas automatiquement les plugins non utilisés, il faut les désactiver à la main.
Le code source de l'activateur OSGI est le suivant:
package upmc.cps.timer;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug­in life cycle
*/
public class Activator extends AbstractUIPlugin {
// The plug­in ID
public static final String PLUGIN_ID = "upmc.cps.timer.TimerPlugin";
// The shared instance
4 Nous rappelons qu'Eclipse est construit au dessus du modèle de composants binaires OSGI.
private static Activator plugin;
public Activator() {
plugin = this;
}
public void start(BundleContext context) throws Exception {
super.start(context);
}
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
public static Activator getDefault() {
return plugin;
}
}
En règle générale, on ne touche qu'au code des méthodes start et stop de l'activateur.
3) Contribution de menus : extension Action Set
Nous allons maintenant contribuer à l'environnement Eclipse en ajoutant un nouveau menu global permettant d'activer ou de désactiver le plugin Timer.
Dans l'assistant de configuration de plugin, sélectionner l'onglet Extensions puis cliquer sur Ajout (Add) pour ajouter une extension (cf. étape 10). On choisira d'ajouter une extension Action Set (ensemble d'actions) en sélectionnant org.eclipse.ui.actionSets dans la liste des points d'extension (extension points) disponibles (cliquer sur Finish) (cf. étape 11).
Etape 10 : Ajout d'extension
Etape 11 : sélection du point d'extension
On saisit ensuite les informations générales de l'extension. L'identificateur de l'extension sera upmc.cps.timer.TimerActions et le nom d'affichage de l'action sera Time Actions (cf. Etape 12)
Etape 12 : informations générales de l'extension Action Set
Le fichier de configuration plugin.xml est spécifique à Eclipse et concentre la plupart des informations sur le plugin en termes d'architecture. Une fois l'extension demandée (étape 12 terminée) le fichier plugin.xml généré apparaît avec le contenu XML suivant :
<?xml version="1.0" encoding="UTF­8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
id="upmc.cps.timer.TimerActions"
name="Timer Actions"
point="org.eclipse.ui.actionSets">
</extension>
</plugin>
On retrouve ici les informations de l'interface utilisateur. Pour ajouter nos menus, ce fichier doit être modifié avec le détails des actions à ajouter. Le contenu du plugin.xml devient :
<?xml version="1.0" encoding="UTF­8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
id="upmc.cps.timer.TimerActions"
name="Timer Actions"
point="org.eclipse.ui.actionSets">
<actionSet
label="Timer action set"
visible="true"
id="upmc.cps.timer.actionSet">
<menu
label="Timer menu"
id="timerMenu">
<separator
name="timerGroup">
</separator>
</menu>
<action
label="Stop timer"
icon="icons/sample.gif"
class="upmc.cps.timer.actions.StopAction"
tooltip="Stop timer"
menubarPath="timerMenu/timerGroup"
toolbarPath="timerGroup"
id="upmc.cps.timer.actions.StopAction">
</action>
<action
label="Start timer"
icon="icons/sample.gif"
class="upmc.cps.timer.actions.StartAction"
tooltip="Start timer"
menubarPath="timerMenu/timerGroup"
toolbarPath="timerGroup"
id="upmc.cps.timer.actions.StartAction">
</action>
</actionSet>
</extension>
</plugin>
Ici nous ajoutons deux éléments de menu pour respectivement démarrer et stopper le fonctionnement du plugin Timer. Attention : dans cette version prototype, ceci n'influe pas sur le chargement/déchargement en mémoire du plugin. Chaque action est identifiée à un élément XML action associé à un certain nombre d'attributs. L'attribut le plus important est l'attribut class dont la valeur « point » vers une classe dite « déléguée » (delegate) qui sera instanciée lorsque l'élément de menu est sélectionné. Toutes les actions se trouvent dans un ensemble d'action actionSet correspondant à un menu complet. Par défaut un action set se place au niveau global, et il faut reconstruire le menu à modifier si l'on veut modifier un sous­menu (Eclipse 4.x devrait « simplifier » cette étape).
4) Code d'interface et code métier
Le code source des deux classes de déléguées se trouve dans le paquetage upmc.cps.timer.actions. On donne ci­dessous le code source de l'action de démarrage (classe StartAction), nous laissons en exercice le code de l'action inverse StopAction.
package upmc.cps.timer.actions;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import upmc.cps.timer.TimerManager;
public class StartAction implements IWorkbenchWindowActionDelegate {
private IWorkbenchWindow window;
public StartAction() {
window = null;
}
public void init(IWorkbenchWindow window) {
this.window = window;
}
public void run(IAction action) {
if(TimerManager.fetchGlobalTimer().isActivated()) {
MessageDialog.openInformation(
window.getShell(),
"Timer",
"Timer already started");
} else {
TimerManager.fetchGlobalTimer().activate();
MessageDialog.openInformation(
window.getShell(),
"Timer",
"Timer started");
}
}
public void selectionChanged(IAction action, ISelection selection) {
// réagir en fonction de la sélection, si besoin est
}
public void dispose() {
// ici, relâcher les ressources si besoin est
}
}
La partie intéressante correspond à la méthode run qui indique le code à exécuter lorsque l'action est déclenchée par l'utilisteur. Ici, on affiche un message d'information (classe standard d'Eclipse MessageDialog, cf. documentation) puis si le timer n'est pas encore activé alors on l'active en invoquant la méthode de classe fetchGlobalTimer() pour récupérer le timer global, puis en invoquant activate() sur ce dernier. Remarque : Comme les dépendances entre plugins sont très souvent statiques (dépendances via plugin.xml par exemple), il est fréquent d'utiliser des singletons (cf. design pattern Singleton) pour communiquer au sein de la plateforme.
La classe TimerManager correspond au code métier de notre plugin, son code est le suivant:
package upmc.cps.timer;
import java.util.Timer;
import java.util.TimerTask;
public class TimerManager {
private Timer timer;
private boolean activated;
private static TimerManager globalTimer = null;
public TimerManager() {
timer = new Timer("upmc.cps.timer.TimerPlugin");
activated = false;
}
public void activate() {
if(!activated) {
activated = true;
}
}
public void desactivate() {
if(activated) {
timer.cancel();
activated=false;
}
}
public boolean isActivated() {
return activated;
}
/* package */ static void startGlobalTimerManager() {
if(globalTimer==null)
globalTimer = new TimerManager();
}
public static TimerManager fetchGlobalTimer() {
return globalTimer;
}
public static void stopGlobalTimerManager() {
globalTimer.desactivate();
}
public void scheduleTask(TimerTask task, int delay, int period) {
System.out.println("Task to schedule");
if(task!=null) {
System.out.println("Starting task");
timer.schedule(task,delay*1000,period*1000);
} else
System.out.println("Task is null");
}
}
Nous ne détaillons pas ce code qui est du code standard Java (les println sont pour le déboguage « manuel » et doivent bien sûr être enlevés en « production »). Notons qu'il s'agit plus d'un prototype très naïf de timer plutôt qu'un code réellement utilisable. Important: ne pas utiliser ce plugin !
Nous devons également modifier l'activateur pour créer le singleton TimerManager global. Dans la classe upmc.cps.timer.Activator on modifie donc les méthodes start et stop de la façon suivante:
public void start(BundleContext context) throws Exception {
super.start(context);
TimerManager.startGlobalTimerManager();
}
public void stop(BundleContext context) throws Exception {
plugin = null;
TimerManager.stopGlobalTimerManager();
super.stop(context);
}
5) Test du plugin
Après résolution des erreurs éventuelles de compilation, il ne nous reste plus qu'à tester notre plugin. Pour cela il suffit de sélectionner le répertoire principal du projet TimerPlugin dans l'explorateur de paquetages et d'activer le menu contextuel avec le bouton droit de la souris. Dans ce menu on choisira Démarrer (Run as) en tant qu'application eclipse (Eclipse Application) pour lancer un eclipse « fils » dans lequel notre plugin est chargé (cf. étapes 13 et 14).
Etape 13 : lancement du Plugin en tant qu'application Eclipse
Etape 14 : lancement du processus Eclipse « fils » dans lequel le plugin timer est actif (cf. le menu Timer)
Et finalement nous pouvons activer notre plugin par menu (cf. étape 15)
Etape 15 : activation du timer par menu
Remarque : les affichages sur la sortie standard ou d'erreur effectués par le timer (pour le déboguage) sont en fait visibles dans l'Eclipse de développement et non l'Eclipse « fils » de test.
Important : ne jamais utiliser l'Eclipse « fils » de test pour autre chose ... du test ! (exemple: développer un plugin dans le « fils » que l'on testera dans le « fils du fils », etc. ! A éviter !).
6) La suite du tutoriel
Dans la deuxième partie du tutoriel, nous verrons comment:
1. effectuer une contribution de vue à l'environnement Eclipse, dans le cadre d'un plugin Clock qui affiche une horloge
2. définir un point d'extension, au niveau du plugin Timer pour permettre à d'autres plugins d'enregistrer des tâches répétitives
3. connecter le plugin Clock au plugin Timer pour renouveler l'affichage de l'heure toutes les secondes.
© 2007­2008 Frédéric Peschanski, licence Art Libre / Free Art License
Téléchargement