WTF Spoonlet?

Ca fait longtemps que je n’ai pas parlé de code. Qu’a cela ne tienne, dans la série « Le bon gros bug de m**** », je vous présente Spoon… Dans un élan de refactoring, je me suis mis à extraire une vrai API du kernel de Petals DSB. L’idée est simple, le faire aurait du l’être: Extraire toutes les interfaces d’un projet A pour créer un projet A-api et en dépendre dans le projet A. Trois clics de souris et deux drag&drop plus loin, testons la compilation:

[INFO] ------------------------------------------------------------------------
[INFO] Building dsb-kernel
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources {execution: default-resources}]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Nothing to compile - all classes are up to date
[INFO] [spoon:recompile {execution: default}]
[INFO] org.objectweb.fractal.fraclet.annotation.processor.FractalComponentProcessor
[WARNING] FractalComponentProcessor >> No value found for property 'generatorClass' in processor org.objectweb.fractal.fraclet.annotation.processor.FractalComponentProcessor
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] fail to execute</code>

[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 23 seconds
[INFO] Finished at: Mon Jun 20 23:27:21 CEST 2011
[INFO] Final Memory: 50M/123M
[INFO] ------------------------------------------------------------------------

Ahah, qu’a cela ne tienne, le même en mode debug donc…

[DEBUG] Trace
org.apache.maven.lifecycle.LifecycleExecutionException: fail to execute
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:719)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.plugin.MojoExecutionException: fail to execute
at net.sf.alchim.spoon.contrib.maven.AbstractSpoonMojo.execute(AbstractSpoonMojo.java:96)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
... 17 more
Caused by: java.lang.NullPointerException
at spoon.support.reflect.declaration.CtAnnotationImpl.convertValue(CtAnnotationImpl.java:160)
at spoon.support.reflect.declaration.CtAnnotationImpl.getElementValue(CtAnnotationImpl.java:253)
at spoon.support.reflect.declaration.CtAnnotationImpl$AnnotationInvocationHandler.invoke(CtAnnotationImpl.java:69)
at $Proxy31.name(Unknown Source)

Yeah cool, une NPE. Ah dommage, sur $Proxy31.name… Genre le proxy généré par Spoon. Et donc bon, on fait quoi la? On revert tout et on tente de bouger les interfaces une par une pour voir qui fout son bordel? Mouais… Trève de blahblah, après avoir passé quelques dizaine de minutes à bouger, compiler, reverter, voici la conclusion: Définir des constantes dans des interfaces qui sont dans des dépendances et les utiliser dans les annotations des implémentations n’est pas possible, par exemple, dans une implémentation du style :

@FractalComponent
@Provides(interfaces = { @Interface(name = "service", signature = ServiceBinderRegistry.class) })
public class ServiceBinderRegistryImpl implements ServiceBinderRegistry {

@Requires(name = BINDER_PREFIX, signature = ServiceBinder.class, cardinality = Cardinality.COLLECTION, contingency = Contingency.OPTIONAL)
private final Map<String, Object> binders = new Hashtable<String, Object>();

On ne peut pas avoir BINDER_PREFIX dans l’interface ServiceBinderRegistry qui est dans une dépendance, par contre ca marche bien quand l’interface est dans le même projet… Quand on tombe la dessus à minuit, c’est rude…

4 réflexions sur “WTF Spoonlet?

  1. Sympa…
    Ça m’intéresse parce que j’étais en train de m’interroger sur l’opportunité de découper le macro-kernel et donc de passer par un refactoring identique. En gros, je vais bientôt développer un nouveau composant et je vais avoir besoin d’écrire plein de tests et d’automatiser leur exécution.

    Je me prends la tête sur ces frameworks de tests. Ça fait un an et demi d’ailleurs…
    Et la seule solution potable, c’est de passer par un conteneur en mémoire, qu’on pourrait directement appeler depuis des tests JUnit (et donc dans Maven). Ce serait super pratique, déjà pour envoyer directement des messages à un composant (provides). Et avec une autre implémentation du transporteur, on pourrait aussi tester les composants qui consomment des services.

    Petite réflexion que je me faisais, alors que je me voyais déjà victorieux du refactoring du conteneur… Tant qu’à refactorer, pourquoi ne pas dégager Fractal par la même occasion ? Dans mon cas, il me faudrait faire un effort pour ne pas bundliser les modules du noyau. Mais même en Java pur, ça doit se faire. Il suffit que l’assemblage fasse le disptach entre les composants et qu’il ait un système de class loader efficace (hypothèse forte, je sais…). Au moins, on pourrait débugguer simplement….

    • POur le refactoring je pense qu’il faut que l’on en parle tous ensemble, toi, moi et la prod. J’ai deja fait pas mal de choses qui visent à simplifier l’utilisation du bus au niveau développeur et utilisateur final.
      C’est aussi le cas pour le transport ou je suis parti de ce qui existait pour fournir un framework de transporteur et pouvoir facilement en développer d’autres. J’ai dans les cartons un transporteur SOAP/HTTP, un XMPP et un maison pour un projet recherche.
      Quand tu dis que tu veux envoyer directement un message à un provide, c’est pareil, j’ai ce qu’il faut qui permet de s’abstenir de tout ce qui est JBI, donc plus besoin de déployer quelque chose pour pouvoir invoquer, c’est maintenant possible depuis n’importe quel composant du kernel.
      Enfin pour fractal, je suis d’accord avec toi, on peut s’en passer. On peut soit utiliser de l’injection à la main mais qui est un peu lourde, soit utiliser les annotations d’injection avec le framework qui va bien, soit se la faire a la CXF avec un contexte accessible depuis partout qui permet de recuperer tout ce que l’on veut. J’ai fait une implementation de la dernière proposition et ca ne pose pas de problème majeur puisque Fractal ne sert a rien sauf a injecter. Ensuite, il y a la solution OSGI et Spring DM qui est assez puissante et qui permet d’avoir des features sympas de classloading, de bufferisation des appels, etc…
      Bref, si tu veux te lancer dans quelque chose, on peut en discuter plus profondément et je peux t’expliquer ce que j’ai déjà pour ne pas que tu refasses ce que j’ai déjà, en plus je voudrait aussi faire la même chose que toi😉

  2. C’est un truc à discuter collectivement, je suis d’accord.

    En l’état, je ne vais pas me laisser seul, notamment parce que mon planning pour les 2 prochains mois est déjà saturé. Et la roadmap du prochain (prochain) studio va être costaud. Je ne pense pas non plus que ce refactoring puisse être intégré pour la v4 de Petals (celle dont les RC vont commencer à sortir en septembre).

    Par contre, pour la version suivante, je pense que ce débat va être nécessaire.
    Ce sera l’occasion de confronter les idées, même si je m’attends à pas mal de convergences. Quant à mon histoire de tests, je vais passer sur des projets de tests ad-hoc. Mais on devrait pouvoir les migrer ensuite facilement, vu que ce sera du pur Java avec du JUnit.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s