Publié par
Il y a 7 années · 18 minutes · Java / JEE

Utiliser Guice et Peaberry pour développer un plugin Eclipse

Sans travailler spécifiquement sur la plateforme Eclipse et sans être committeur sur un des projets liés, il n’est pas rare de devoir écrire un plugin pour l’IDE Eclipse. Les raisons peuvent être variées : intégrer le gestionnaire de tâches de votre entreprise, supporter le DSL que vous venez de créer, templater des parties récurrentes de votre développement, etc…

Quand on découvre la plateforme, on peut être surpris par deux différences majeures par rapport au développement d’un projet Java « entreprise » :

  • La manière de builder le projet : Eclipse propose ses propres conventions de structure de projet et fournit ses outils pour le builder. Certains points sont paramétrables mais on ne retrouve pas la richesse offerte par Ant, Maven ou encore Gradle. Cela fera éventuellement l’objet d’un autre article ;
  • Dans le code lui même : le passage des dépendances entre classes ou entre bundles est particulier, et c’est ce dernier point qui nous intéresse dans cet article. En effet le code d’un plugin Eclipse ressemble souvent à ça : SomePlugin.getDefault().getSomeComponent().getSomeChild().doSomething().

Les singletons sont omniprésents, principalement pour fournir un point d’accès aux différents bundles. Par ailleurs beaucoup d’actions ne sont accessibles que via des méthodes statiques.

Évidemment, le principal problème qui se pose est celui des tests unitaires. Eclipse propose de lancer votre plugin et d’exécuter des tests JUnit dessus, mais il s’agit là de tests d’intégration qui s’exécutent lentement et ne permettront pas de faire du TDD. Il est toujours possible de faire de l’injection de dépendances (DI) « à la main », mais on aurait tort de se priver de l’utilisation d’un framework dédié à cela, d’autant plus qu’on pourrait y gagner d’autres fonctionnalités : binding interface/implémentation, gestion de scopes (singleton, session…), AOP, etc…

En attendant Eclipse 4 – qui proposera nativement des fonctionnalités de DI – je vous propose donc de mettre en place Guice et son extension Peaberry sur un projet-type de plugin pour Eclipse.

Note : pour ce qui est de l’appel de méthodes statiques, aucun framework de DI ne pourra changer la donne puisqu’on est alors dépendant d’une méthode, et non d’une classe. Il faudra donc déléguer ces appels à des classes « tampons » ou mocker ces appels lors des tests avec un framework spécifique tel que JMockit.

Créer le projet de démonstration

Tout le code nécessaire à la démonstration est dans cet article, mais il est parfois abrégé pour des questions de lisibilité. Si vous ne désirez pas créer le projet de démonstration mais que vous voulez malgré tout avoir accès au code dans son intégralité, vous pouvez le trouver sur GitHub.

Nul besoin de quelque chose de compliqué pour cet article, on se contentera du HelloWorld d’Eclipse :

  • Créer un nouveau projet : « New » > « Project » > « Plug-in Development » > « Plug-in Project » ;
  • Lui donner un nom, pour notre exemple : fr.xebia.eclipse.guiceexample ;
  • Changer le nom par défaut de l’Activator afin qu’il représente mieux le fait que ce sera un point d’entrée au plugin, par exemple : GuiceExamplePlugin ;
  • Choisir le template « Hello, World » ;
  • Valider.

A ce stade, vous pouvez lancer le plugin (« Run As » > « Eclipse Application ») et constater qu’il vous dit bonjour lorsque vous cliquez sur « Sample Menu » > « Sample Action ».

Afin d’avoir un peu de matière pour la suite, je vous propose deux ajouts :

  1. dans le message, afficher le thème graphique utilisé par la plateforme ;
  2. dans la console, afficher, lors du démarrage, des statistiques sur les points d’extension utilisés ou fournis par le plugin.

Le premier ajout se situe donc dans SampleAction.run() :

public void run(IAction action) {
	String currentTheme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getLabel();
	String message = "Hello, Xebia world. " + NL + "The current theme is: " + currentTheme;
	MessageDialog.openInformation(window.getShell(), "Guice Example", message);
}

Pour le deuxième point, ajoutons le code suivant dans GuiceExamplePlugin.start() :

public void start(BundleContext context) throws Exception {
	super.start(context);
	plugin = this;

	ExtensionStatistics stats = new ExtensionStatistics();
	String message = "This plugin declares " + stats.countDeclaredExtensions() + " extensions and provides "
			+ stats.countDeclaredExtensionPoints() + " extension points";
	getLog().log(new Status(IStatus.INFO, PLUGIN_ID, message));
}

Le code de la classe ExtensionStatistics étant le suivant :

public class ExtensionStatistics {

	public int countDeclaredExtensions() {
		return Platform.getExtensionRegistry().getExtensions(GuiceExamplePlugin.PLUGIN_ID).length;
	}

	public int countDeclaredExtensionPoints() {
		return Platform.getExtensionRegistry().getExtensionPoints(GuiceExamplePlugin.PLUGIN_ID).length;
	}
}

Enfin, afin que le plugin démarre en même temps qu’Eclipse, ajoutons cette ligne dans le fichier plugin.xml :

<extension point="org.eclipse.ui.startup" />

A présent, lors du lancement de l’application, on peut lire dans la console de l’instance Eclipse de développement :

!ENTRY fr.xebia.eclipse.guiceexample 1 0 2010-12-18 16:47:33.848
!MESSAGE This plugin declares 2 extensions and provides 0 extension points

ou encore dans la vue « Error Log » de la nouvelle instance d’Eclipse :

Et en cliquant sur « Sample Action » on obtient :

Voilà, la suite de cet article va consister à réécrire ces deux bouts de code pour qu’ils utilisent Guice, afin de s’affranchir des couplages forts entre nos classes et leurs dépendances. Mais commençons tout d’abord par l’importation de Guice dans la plateforme.

Importer Guice

Vous trouverez la dernière version de Guice ici. Deux JARs seulement nous intéressent dans l’archive ZIP : guice-2.0.jar et aopalliance.jar.

Eclipse propose deux manières d’importer des JARs dans la plateforme pour les utiliser avec votre plugin :

  1. inclure les JARs dans votre plugin ;
  2. exposer les JARs en question comme des plugins, desquels dépendra votre plugin.

Dans le premier cas, il vous faut :

  • Placer les JARs dans votre projet (disons dans un répertoire « lib ») ;
  • Puis, au sein de l’éditeur du manifest de votre plugin, les ajouter dans la section « Classpath » de l’onglet « Runtime » ;
  • Créer aussi la library « . » pour que le plugin voit encore ses propres classes ;
  • Enfin, sauvegarder.

La deuxième solution est à préférer si vous comptez utiliser vos dépendances au sein de plusieurs plugins. Les étapes sont les suivantes :

  • Créer un nouveau projet : « New » > « Project » > « Plug-in Development » > « Plug-in from Existing JAR Archives » ;
  • Donner l’accès aux JARs à importer ;
  • Donner un nom au plugin : pour éviter toute collision, une bonne méthode consiste à utiliser le nom de votre organisation ou de votre plugin (selon la portée de votre dépendance), suivi du nom de la dépendance (ou de l’ensemble de dépendances) – dans notre exemple : fr.xebia.eclipse.guiceexample.guice ;
  • Décocher « Unzip the JAR archives into the project » (ça ne sert à rien) ;
  • Enfin, déclarer ce nouveau plugin comme dépendance de celui qui nous intéresse : là encore, utiliser l’éditeur du manifest – onglet « Dependencies » – et ajouter fr.xebia.eclipse.guiceexample.guice.

On notera que ce nouveau plugin expose les JARs de la même manière qu’avec la première solution, mais au moins on n’a pas à le faire à la main.

Créer un module Guice et un injecteur

En bref, Guice requiert un ou plusieurs modules définissant les bindings entre des types Java et leur(s) implémentation(s), ainsi qu’un ou plusieurs injecteurs permettant de construire le graphe d’objets à partir de ces définitions. Pour démarrer nous créerons donc un module principal pour notre plugin :

package fr.xebia.eclipse.guiceexample;

import com.google.inject.AbstractModule;

public class CoreModule extends AbstractModule {

	@Override
	protected void configure() {
	}
}

ainsi qu’un injecteur, dans GuiceExamplePlugin :

private Injector injector;

public void start(BundleContext context) throws Exception {
	super.start(context);
	plugin = this;

	injector = Guice.createInjector(new CoreModule());

	ExtensionStatistics stats = injector.getInstance(ExtensionStatistics.class);
	// ...
}

// méthode d'accès qui nous servira plus tard
public Injector getInjector() {
	return injector;
}

Dans le cas qui nous intéresse, nous voudrions nous passer de l’appel à Platform.getExtensionRegistry(). Il suffit de déclarer une méthode @Provides dans CoreModule pour que Guice sache comment nous le fournir :

public class CoreModule extends AbstractModule {
	// configure(){} ...

	@Provides
	protected IExtensionRegistry provideExtensionRegistry() {
		return Platform.getExtensionRegistry();
	}
}

public class ExtensionStatistics {

	private final IExtensionRegistry extensionRegistry;

	@Inject
	public ExtensionStatistics(IExtensionRegistry extensionRegistry) {
		this.extensionRegistry = extensionRegistry;
	}

	public int countDeclaredExtensions() {
		return extensionRegistry.getExtensions(GuiceExamplePlugin.PLUGIN_ID).length;
	}
	// ...
}

Cela nous évite ainsi de créer notre propre factory. Vous l’aurez compris la suite ne relève que l’utilisation basique de Guice, déjà discutée sur ce blog ici et .

Seulement voilà : lors d’un développement pour Eclipse IDE, la plupart de nos classes est déclarée comme étant une extension à la plateforme (au sein du fichier plugin.xml) et est instanciée par Eclipse, comme c’est le cas pour SampleAction. Dans ce cas-là, comment faire pour s’éviter de devoir à nouveau récupérer par nous-même nos dépendances ?

Injecter des dépendances dans une extension Eclipse

Peaberry’s GuiceExtensionFactory

Une première solution à notre problème est apportée par Peaberry. Il s’agit d’une extension pour Guice qui a été développée alors qu’il ne fonctionnait pas encore en milieu OSGi. Cela est réparé depuis la version 2 de Guice, mais Peaberry n’en fournit pas moins un support beaucoup plus avancé d’OSGi en général, et d’Eclipse en particulier.

Avant toute chose, il faut importer Peaberry et Peaberry-Eclipse. La dernière version se récupère ici, il s’agit alors de procéder comme précédemment pour inclure : aopalliance-1.0.jar, guice-customloader-yyyymmdd.jar, peaberry-1.1.1.jar et peaberry.eclipse-1.1.1.jar. C’est ce dernier JAR qui va nous apporter le support spécifique pour les extensions Eclipse.

Note : il semblerait que l’équipe de Peaberry ait eu un problème de packaging sur la version 1.1.1, perdant ainsi la déclaration du point d’extension décrit ci-après. La procédure pour corriger cela est fournie en fin d’article.

Dans notre plugin (fichier plugin.xml), il est à présent possible de demander à Peaberry d’instancier nos extensions via sa factory :

<extension point="org.eclipse.ui.actionSets">
   <!-- ... -->
            class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory:fr.xebia.eclipse.guiceexample.actions.SampleAction"
   <!-- ... -->
</extension>

Il faut alors lui préciser le module Guice à utiliser pour injecter les dépendances :

<extension point="org.ops4j.peaberry.eclipse.modules">
   <module class="fr.xebia.eclipse.guiceexample.CoreModule" />
</extension>

Une autre syntaxe est possible, plus lisible, où le nom de la classe à instancier est en fait l’ID de l’extension (ce qui était déjà le cas dans notre exemple) :

<!-- ... -->
         class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory"
         id="fr.xebia.eclipse.guiceexample.actions.SampleAction"
<!-- ... -->

On peut à présent modifier CoreModule et SampleAction comme suit :

public class CoreModule extends AbstractModule {
	// ...

	@Provides
	protected IThemeManager provideThemeManager() {
		return PlatformUI.getWorkbench().getThemeManager();
	}
}

public class SampleAction implements IWorkbenchWindowActionDelegate {
	// ...
	private final IThemeManager themeManager;

	@Inject
	public SampleAction(IThemeManager themeManager) {
		this.themeManager = themeManager;
	}

	public void run(IAction action) {
		String currentTheme = themeManager.getCurrentTheme().getLabel();
		String message = "Hello, Xebia world. " + NL + "The current theme is: " + currentTheme;
		MessageDialog.openInformation(window.getShell(), "Guice Example", message);
	}
}

On le voit, une fois le problème de l’import de Peaberry-Eclipse réglé, c’est assez simple à utiliser. Il reste malheureusement un problème potentiel : il est impossible de demander à la GuiceExtensionFactory d’utiliser un injecteur existant, elle va donc en créer un pour notre plugin. Si le contenu de plugin.xml est notre seul point d’entrée, tout va bien ; mais si on désire également mettre de la logique dans GuiceExamplePlugin.start() sur la base du même module Guice, une classe déclarée @Singleton pourra alors être instanciée deux fois…

Heureusement la solution est assez simple : écrire notre propre extension factory.

Ecrire sa propre extension factory

GuiceExtensionFactory implémente l’interface IExecutableExtensionFactory, permettant de contrôler l’instantiation des extensions. L’idée est de créer une factory abstraite faisant le même travail à l’exception près que l’injecteur sera fourni par une sous-classe concrète. Voici le code de la factory abstraite (voir le code complet ici) :

public abstract class AbstractGuiceInjectorExtensionFactory implements IExecutableExtension, IExecutableExtensionFactory {
	// définition des champs ...

	public void setInitializationData(final IConfigurationElement config, final String name, final Object data) {
		configuration = config;
		contributor = config.getContributor();
		// si la classe n'est pas fournie dans le même attribut que la factory (i.e: class="factory:classe"), on la récupère dans l'attribut "id"
		className = data instanceof String ? (String) data : config.getAttribute("id");
	}

	public Object create() throws CoreException {
		if (null == className) {
			throw newCoreException("Configuration is missing class information");
		}

		final Class<?> clazz;
		try {
			clazz = resolve(contributor).loadClass(className);
		} catch (final InvalidRegistryObjectException e) {
			throw newCoreException(e);
		} catch (final ClassNotFoundException e) {
			throw newCoreException(e);
		}

		final Object o = getInjector().getInstance(clazz);
		if (o instanceof IExecutableExtension) {
			((IExecutableExtension) o).setInitializationData(configuration, null, null);
		}
		return o;
	}

	abstract protected Injector getInjector();

	// newCoreException(){}...
}

On peut à présent créer une factory différente pour chaque besoin que l’on pourrait avoir. Dans le cas de cet exemple, le but était de partager l’injecteur défini dans GuiceExamplePlugin :

public class GuiceExampleExtensionFactory extends AbstractGuiceInjectorExtensionFactory {

	@Override
	protected Injector getInjector() {
		return GuiceExamplePlugin.getDefault().getInjector();
	}
}

et voici le contenu de plugin.xml associé :

<!-- on n'utilise plus le point d'extension déclarant CoreModule -->

<extension point="org.eclipse.ui.actionSets">
   <!-- ... -->
            class="fr.xebia.eclipse.guiceexample.GuiceExampleExtensionFactory"
            id="fr.xebia.eclipse.guiceexample.actions.SampleAction"
   <!-- ... -->
</extension>

Note : on pourrait tout à fait utiliser cette factory pour créer des contextes d’injection enfants, dédiés par exemple à un certain type d’extensions définies dans plugin.xml :

public class ActionHandlerFactory extends AbstractGuiceInjectorExtensionFactory {

    private static Injector injector;

    @Override
    protected synchronized Injector getInjector() {
        if (injector == null) {
            injector = GuiceExamplePlugin.getDefault().getInjector().createChildInjector(new ActionHandlingModule());
        }
        return injector;
    }

    public static void cleanup() {
        injector = null;
    }
}

A ce stade, la plupart des besoins en injection de dépendances apparaissant lors du développement d’un plugin basique pour Eclipse sont couverts. On pourra même fournir notre contexte d’injection à des plugins autorisés en exposant une méthode createChildInjector() au niveau de notre plugin. Pourtant, quand le besoin arrive d’aller plus loin dans les intéractions entre bundles, il convient de s’intéresser aux services OSGi et aux points d’extension Eclipse.

Aller plus loin avec Peaberry

Eclipse est avant tout une plateforme OSGi, il est donc possible de publier et consommer des services. Là encore il serait intéressant de pouvoir les injecter automatiquement, voici donc deux grandes fonctionnalités proposées par Peaberry et Peaberry-Eclipse.

Injecter des services OSGi

Voici un exemple montrant l’injection du service EnvironmentInfo d’Eclipse, afin d’afficher l’OS sur lequel tourne la plate-forme :

public class CoreModule extends AbstractModule {

	@Override
	protected void configure() {
		// lors d'une injection, toute instance de EnvironmentInfo sera créée par un service déclarant cette interface
		bind(EnvironmentInfo.class).toProvider(Peaberry.service(EnvironmentInfo.class).single());
	}
}

public class GuiceExamplePlugin extends AbstractUIPlugin {
	// ...

	public void start(BundleContext context) throws Exception {
		// ...
		// ajout du support des services OSGi avec Peaberry.osgiModule()
		injector = Guice.createInjector(Peaberry.osgiModule(context), new CoreModule());

		// récupération du service
		EnvironmentInfo info = injector.getInstance(EnvironmentInfo.class);
		getLog().log(new Status(IStatus.INFO, PLUGIN_ID, "This platform runs on " + info.getOS()));
	}
	// ...
}

Note : il existe une solution plus simple pour récupérer cette information, mais là encore avec une méthode statique : org.eclipse.core.runtime.Platform.getOS().

Injecter des extensions clientes

Le système d’extensions d’Eclipse repose en fait aussi sur des services OSGi, Peaberry-Eclipse va donc nous permettre d’injecter de telles extensions.

Voici un dernier exemple dans lequel notre plugin propose un point d’extension à d’autres plugins pour qu’ils puissent aussi nous dire bonjour :

  • Créer l’interface définissant le contrat à remplir par l’extension :
package fr.xebia.eclipse.guiceexample.extensionpoint;

public interface HelloWorldParticipant {
	String sayHello();
}
  • Déclarer le point d’extension dans plugin.xml :
<extension-point id="helloWorldParticipant" name=""Hello World" Participant" schema="schema/helloWorldParticipant.exsd"/>
  • Le définir dans schema/helloWorldParticipant.exsd de la manière suivante :
<?xml version='1.0' encoding='UTF-8'?>
<schema targetNamespace="fr.xebia.eclipse.guiceexample" xmlns="http://www.w3.org/2001/XMLSchema">

   <element name="extension">
      <complexType>
         <choice minOccurs="1" maxOccurs="unbounded">
            <element ref="client"/>
         </choice>
         <attribute name="point" type="string" use="required" />
      </complexType>
   </element>

   <element name="client">
      <complexType>
         <attribute name="class" type="string" use="required">
            <annotation>
               <appInfo>
                  <meta.attribute kind="java" basedOn=":fr.xebia.eclipse.guiceexample.extension.HelloWorldParticipant"/>
               </appInfo>
            </annotation>
         </attribute>
      </complexType>
   </element>

</schema>

Enfin, pour les besoins de l’exemple, déclarons une extension sur ce point d’extension (dans le même plugin pour faire simple) :

  • Créer l’extension :
package fr.xebia.eclipse.guiceexample.extension;

import fr.xebia.eclipse.guiceexample.extensionpoint.HelloWorldParticipant;

public class SampleHelloWorldParticipant implements HelloWorldParticipant {

	@Override
	public String sayHello() {
		return "Sample Hello";
	}
}
  • La déclarer dans plugin.xml :
<extension point="fr.xebia.eclipse.guiceexample.helloWorldParticipant">
   <client class="fr.xebia.eclipse.guiceexample.extension.SampleHelloWorldParticipant" />
</extension>

Voici alors la manière « standard » de récupérer de telles extensions :

public class SampleAction implements IWorkbenchWindowActionDelegate {
	// ...

	public void run(IAction action) {
		String currentTheme = themeManager.getCurrentTheme().getLabel();
		String message = "Hello, Xebia world. " + NL + "The current theme is: " + currentTheme;

		message += NL + getHellosFromParticipants();

		MessageDialog.openInformation(window.getShell(), "Guice Example", message);
	}

	private String getHellosFromParticipants() {
		String extensionPoint = "fr.xebia.eclipse.guiceexample.helloWorldParticipant";
		IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(extensionPoint);

		final StringBuilder buffer = new StringBuilder();
		for (IConfigurationElement element : elements) {
			try {
				final Object extension = element.createExecutableExtension("class");

				if (!(extension instanceof HelloWorldParticipant))
					continue;

				appendParticipantHello(buffer, (HelloWorldParticipant) extension);

			} catch (CoreException e) {
				// ...
			}
		}

		return buffer.toString();
	}

	private void appendParticipantHello(final StringBuilder buffer, final HelloWorldParticipant participant) {
		SafeRunner.run(new ISafeRunnable() {

			public void run() throws Exception {
				if (buffer.length() == 0)
					buffer.append(NL).append("Other hellos:");
				buffer.append(NL).append(participant.sayHello());
			}

			public void handleException(Throwable throwable) {
				// ...
			}
		});
	}
}

On le voit, c’est plutôt indigeste. Peaberry-Eclipse nous permet de d’injecter ces extensions de la manière suivante :

// annotation de l'interface de l'extension pour fournir l'ID du point d'extension
@ExtensionBean("fr.xebia.eclipse.guiceexample.helloWorldParticipant")
public interface HelloWorldParticipant {
	// ...
}

public class CoreModule extends AbstractModule {

	@Override
	protected void configure() {
		// toute instance de HelloWorldParticipant déclarée par un service (une extension) sera injectée
		// si un Iterable<HelloWorldParticipant> est demandé
		bind(TypeLiterals.iterable(HelloWorldParticipant.class)).toProvider(Peaberry.service(HelloWorldParticipant.class).multiple());
	}
}

public class GuiceExamplePlugin extends AbstractUIPlugin {
	// ...

	public void start(BundleContext context) throws Exception {
		// ...
		// ajout du support du registre des services-extensions d'Eclipse avec EclipseRegistry.eclipseRegistry()
		injector = Guice.createInjector(Peaberry.osgiModule(context, EclipseRegistry.eclipseRegistry()), new CoreModule());
	}
}

public class SampleAction implements IWorkbenchWindowActionDelegate {
	// ...
	private final Iterable<HelloWorldParticipant> participants;

	@Inject
	public SampleAction(IThemeManager themeManager, Iterable<HelloWorldParticipant> participants) {
		this.themeManager = themeManager;
		this.participants = participants;
	}

	// run(){}...

	private String getHellosFromParticipants() {
		final StringBuilder buffer = new StringBuilder();

		for (final HelloWorldParticipant participant : participants) {
			appendParticipantHello(buffer, participant);
		}

		return buffer.toString();
	}
}

Et voilà le résultat :

Dans le cas d’une extension « non-exécutable » (ne fournissant pas une classe à instancier mais des propriétés à récupérer) il est également possible de demander à Peaberry de mapper les propriétés exposées à des méthodes de l’interface annotée avec @ExtensionBean.

Note : n’oubliez pas que les services OSGi sont par nature dynamiques et ne sont donc jamais une ressource acquise. Peaberry est assez sympathique dans le sens où il injecte des proxies dynamiques pour gérer cela automatiquement, mais pour toute utilisation avancée, je vous invite à vous renseigner en détail sur les autres fonctionnalités de Peaberry en rapport avec OSGi.

Conclusion :

Cet article nous a permis de voir qu’il est tout à fait possible d’utiliser un framework d’injection de dépendances lors d’un développement pour Eclipse. Pour les cas les plus basiques Guice seul suffira, de même que d’autres frameworks s’ils supportent le mécanisme de class-loading de OSGi (par exemple Spring). Mais il est possible d’aller plus loin dans le support d’OSGi grâce à Peaberry qui permet d’injecter des services (Spring Dynamic Modules fournit un support équivalent) mais surtout dans le support de la plate-forme Eclipse avec Peaberry-Eclipse qui simplifie grandement la récupération d’extensions clientes.

Les techniques décrites dans cet article me permettent actuellement d’améliorer grandement la lisibilité et la testabilité d’un plugin Eclipse au développement duquel je participe (MoreUnit). En attendant Eclipse 4, je ne peux donc que vous encourager à utiliser ces solutions.

Ressources

Annexe : Procédure pour utiliser le point d’extension org.ops4j.peaberry.eclipse.modules

  • Récupérer le fichier modules.exsd, il s’agit de la description d’un point d’extension ;
  • Le placer dans le plugin qui expose Peaberry, dans un dossier « schema » ;
  • Ajouter un fichier « plugin.xml » à la racine de ce même plugin (s’il n’existe pas déjà) ;
  • Dans l’éditeur de manifest, cocher « This plug-in is a singleton » (onglet « Overview ») ;
  • Dans ce même éditeur, ajouter une dépendance à « org.eclipse.core.runtime » (onglet « Dependencies », section « Required plug-ins ») ;
  • Toujours dans l’éditeur, inclure « plugin.xml » au binard built (onglet « Build », section « Binary Build ») ;
  • Enfin, à l’intérieur de plugin.xml, déclarer le point d’extension :
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension-point id="org.ops4j.peaberry.eclipse.modules" name="Guice binding modules" schema="schema/modules.exsd"/>
</plugin>
Nicolas Demengel
Nicolas est arrivé chez Xebia en 2010 et a 5 ans d'expérience sur le développement d'applications Java/Web.

Nicolas est un développeur passionné et pragmatique. Il aime la technologie mais n'oublie pas que la finalité d'un produit, c'est le service rendu à l'utilisateur.
Son credo : un produit doit se construire et prouver sa qualité avec du feedback et des tests !

Pour cette raison, Nicolas travaille également sur le plug-in MoreUnit pour Eclipse.

2 réflexions au sujet de « Utiliser Guice et Peaberry pour développer un plugin Eclipse »

  1. Publié par Epo, Il y a 5 années

    Bonjour et merci pour cet article fort instructif
    bien que cela fasse 3 ans ^^’ .

    Une question, cette fàçon de procéder est elle toujours d’actualité avec la eclipse4 ? il y a t il des changement à prévoir ? merci à vous .

    Epo

  2. Publié par Nicolas Demengel, Il y a 5 années

    Bonjour Epo,

    De rien ;-)

    Eclipse 4 propose son propre container pour gérer l’injection de dépendances, il est donc probablement préférable de l’utiliser ! Vous trouverez je pense un bon tutoriel ici : http://www.vogella.com/articles/EclipseRCP/article.html notamment au niveau des chapitres 17, 18, 30, et surtout 31 pour ce qui est de gérer les dépendances à vos propres objets (par opposition à ceux fournis par la plateforme).

    À noter que je n’ai pas encore eu le loisir de mettre tout cela en pratique sur MoreUnit, devant encore être compatible avec Eclipse 3 pour quelques temps. Mais cela parait prometteur, notamment pour ce qui est des contextes d’injection spécifiques aux différentes parties de l’UI, ou de la mise à jour automatique des valeurs injectées, telles que la sélection de l’utilisateur, ou encore les services OSGI.

    PS : j’ai fini par retirer Guice de MoreUnit, afin de réduire le poids du plugin au téléchargement. Le passage de dépendances à l’ancienne, ça marche aussi très bien, à la construction ou via un service locator (voir cet article pour plus de détails http://martinfowler.com/articles/injection.html) !

    Bonne découverte !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *