UGE Paint

ServiceLoader: des plugins en Java ?!

Toutes vos classes pour cet exercice doivent être dans le package fr.uge.poo.ducks.

Attention à ne pas modifier les packages des classes fournies. Java se base sur le nom complet des classes (package+nom de la classe).

Cet exercice est une introduction très rapide à la classe ServiceLoader qui permet d'aller charger dans des jars des classes qui implémentent une interface donnée.

Pour illustrer, nous allons considérer un exemple simple.

Des canards en plugin

Les jars ont été construits de manière à signaler à Java qu'ils fournissent des implémentations de l'interface fr.uge.poo.ducks.Duck. La classe ServiceLoader permet alors de récupérer les classes fournies par les jars (si ils sont dans le classpath) comme suit:

ServiceLoader<Duck> loader = ServiceLoader.load(fr.uge.poo.ducks.Duck.class);
for(Duck duck : loader) {
	...
}

Ecrire une classe DuckFarm dont le main va charger les différents canards et afficher leur cri. Il faudra rajouter les jars dans le classpath de votre projet.

Sous Eclipse, il faut aller dans Project>Properties>Java Build Path puis sélectionner classpath et ajouter "External jar". Sous IntelliJ, il faut aller dans File>Project Structure>Librairies et cliquer sur + pour ajouter le jar.

Une fois les jars ajoutés au classpath, vous devriez avoir un affichage du type:

RubberDuck[Anonymous] refuses to quack.
RegularDuck[Anonymous] says quack.	

L'ordre n'est pas garanti.

L'approche précédente présente deux problèmes:

Des usines à canards

Nous voudrions pouvoir créer plusieurs canards et les nommer. L'idée est de ne plus fournir un canard mais une usine à canard c'est à dire un objet capable de créer des canards à volonté à partir de leur nom.

Pour cela, on introduit l'interface fr.uge.poo.ducks.DuckFactory.java.

package fr.uge.poo.ducks;

public interface DuckFactory {
    Duck withName(String name);
}

On vous fournit trois jars RegularDuckFactory.jar,RubberDuckFactory.jar et Surprise.jar qui fournissent des classes pour l'interface fr.uge.poo.ducks.DuckFactory.java.

Ecrire une classe DuckFarmBetter qui charge toutes les implémentations disponibles de DuckFactory et pour chacune crée 3 canards nommés Fifi, Riri et Loulou puis affiche leur cri.

Comment créer des jars qui fournissent une implémentation pour une interface ?

Un jar est juste une archive comme un fichier ZIP. Il contient des fichiers compilés .class et des fichiers textes de configuration.

On peut décompresser le jar RegularDuckFactory.jar avec la commande suivante:

$ jar -xf RegularDuckFactory.jar

Le contenu du jar est le suivant:

|-META-INF/
          |-services/
                    |-fr.uge.poo.ducks.DuckFactory
|-fr/
    |-uge/
         |-poo/
              |-ducks/
       		     |-RegularDuck.class
                     |-Duck.class
                     |-RegularDuckFactory.class
                     |-DuckFactory.class

Le jar contient les fichiers compilés des classes RegularDuck et RegularDuckFactory et des interfaces Duck et DuckFactory. En plus des classes compilées, le jar contient dans le répertoire META-INF/services un fichier nommé fr.uge.poo.ducks.DuckFactory. Ce fichier fr.uge.poo.ducks.DuckFactory n'a qu'une ligne qui est le nom (package+classe) de la classe qui implémente l'interface fr.uge.poo.ducks.DuckFactory. Dans notre cas, c'est fr.uge.poo.ducks.RegularDuckFactory.

Pour créer le jar, on peut rassembler tous les fichiers nécessaires dans un répertoire tmp avec la bonne structure et créer le jar avec la commande:

$ jar cvf RegularDuckFactory.jar -C tmp/ .

Comme ce procédé est un peu long et pénible, nous avons utilisé un script makeSPIJar.sh.

Librairies graphiques comme des plugins

Toutes vos classes pour cet exercice doivent être dans le package fr.uge.poo.paint.ex8. Recopiez vos classes de l'exercice 7 dans ce nouveau package.

Comme la librairie CoolGraphics est un plagiat de la librairie SimpleGraphics, la société EvilCorp a des problèmes juridiques. Il vous faut trouver une solution pour pouvoir distribuer votre application sans la librairie CoolGraphics. Cependant on voudrait que l'utilisateur puisse rajouter cette librairie en téléchargeant un jar sur le site d'EvilCorp mais sans toucher au code de votre application.

L'idée est de fournir le code de la libraire CoolGraphics, de votre adaptateur pour cette librairie, ... dans un jar. L'application Paint utilisera un ServiceLoader dans votre application pour charger les classes de ce jar si il est présent.

Quelle interface doit fournir votre jar ?

Construisez un jar pour la librairie CoolGraphics et modifier votre application Paint pour qu'elle charge CoolGraphics si disponible, et utilise SimpleGraphics sinon.

Pour construire votre jar, vous pouvez adapter le script makeSPIJar.sh que nous avons utilisé pour créer le RegularDuckFactory.jar à l'exercice précédent. Il faut adapter le script.