2010-11-09 8 views
0

J'ai une application en cours de construction par Maven qui est un projet mixte Groovy et Java. En utilisant le plugin GMaven (v1.3), je peux facilement exécuter des tests Groovy sur les classes Java et Groovy. Et pendant les versions de l'application, mes classes java sont liées aux fichiers stub augmentés qui déclarent les méthodes de GroovyObject.Les classes d'application Groovy et les tests d'unité Java

Cependant, si j'écris un test en Java avec le code Groovy de l'application et que j'essaie d'appeler des méthodes de GroovyObject, j'obtiens des échecs de compilation.

Y a-t-il une solution de contournement pour cela? Existe-t-il des paramètres de configuration pour GMaven qui rendent cela possible?

merci.

est ici les choses de mon pom de build.plugins:

   <plugins> 
       <plugin> 
        <groupId>org.apache.maven.plugins</groupId> 
        <artifactId>maven-compiler-plugin</artifactId> 
        <configuration> 
         <includes> 
          <include>target/generated-sources/groovy-stubs/main</include> 
         </includes> 
        </configuration> 
       </plugin> 
       <plugin> 
        <groupId>org.codehaus.gmaven</groupId> 
        <artifactId>gmaven-plugin</artifactId> 
        <version>1.3</version> 
        <executions> 
         <execution> 
          <goals> 
           <goal>generateStubs</goal> 
           <goal>generateTestStubs</goal> 

           <goal>compile</goal> 
           <goal>generateTestStubs</goal> 
           <goal>testCompile</goal> 
          </goals> 
          <configuration> 
           <!-- providerSelection probably defaults to 1.7 now --> 
           <providerSelection>1.7</providerSelection> 
          </configuration> 
         </execution> 
        </executions> 
       </plugin> 
      </plugins> 

Voici la classe de test java:

public class JavaGroovyTest extends TestCase { 
     @Test 
     public void testGroovyClasses(){ 
     Model m = new Model(); //Model is an application class written in Groovy 
     assertNotNull(m); 
     assertEquals(4,m.getMetaClass().getProperties().size()); 
     } 
} 

Et voici la sortie du compilateur:

[ERROR] BUILD FAILURE 
[INFO] ------------------------------------------------------------------------ 
[INFO] Compilation failure 
/Users/mkohout/Documents/trunk/src/test/java/JavaGroovyTest.java:[17,24] cannot find symbol 
symbol : method getMetaClass() 
location: class com.q.Model 
+0

Quels échecs de compilation voyez-vous? – ataylor

+0

Après tout, en pratique, on ne peut pas accéder à la plupart des "méta-méthodes d'exécution" de Groovy à partir de Java compilé statiquement. - Au mieux, les quatre méthodes (!) 'GroovyObject' sont accessibles. - Il existe des solutions de contournement pour d'autres méthodes Groovy d'exécution, mais c'est un mauvais hacking. – robbbert

Répondre

2

Déclarez le groovy objet en tant que GroovyObject. Exemple:

import groovy.lang.GroovyObject 

public class JavaGroovyTest extends TestCase { 
    @Test 
    public void testGroovyClasses(){ 
     GroovyObject m = new Model(); //Model is an application class written in Groovy 
     assertNotNull(m); 
     assertEquals(4,m.getMetaClass().getProperties().size()); 
    } 
} 

Edit: une explication plus

Le compilateur groovy ajoute une méthode getMetaClass aux classes, mais il marque comme synthetic. Il s'agit d'un indicateur JVM interne pour les méthodes et champs générés en tant que "détails d'implémentation" et qui ne doivent pas être visibles par le code. Vous pouvez vérifier cela avec javap:

$ javap -verbose Model | grep -A18 getMetaClass\(\) 
public groovy.lang.MetaClass getMetaClass(); 
    Code: 
    Stack=2, Locals=1, Args_size=1 
    0: aload_0 
    1: getfield  #42; //Field metaClass:Lgroovy/lang/MetaClass; 
    4: dup 
    5: ifnull 9 
    8: areturn 
    9: pop 
    10: aload_0 
    11: dup 
    12: invokevirtual #28; //Method $getStaticMetaClass:()Lgroovy/lang/MetaClass; 
    15: putfield  #42; //Field metaClass:Lgroovy/lang/MetaClass; 
    18: aload_0 
    19: getfield  #42; //Field metaClass:Lgroovy/lang/MetaClass; 
    22: areturn 
    23: nop 
    Synthetic: true 

Vous pouvez contourner ce si, en jetant à l'interface groovy.lang.GroovyObject, qui déclare la méthode getMetaClass (cette fois non synthétique).

Cela peut ne pas être une excellente solution, mais cela dit, piquer autour de la metaClass groovy dans Java est probablement mal avisé en premier lieu. S'il n'est pas possible d'utiliser simplement groovy dans vos tests, je chercherais à exposer les informations metaClass dont vous avez besoin dans les classes groovy avec des méthodes java accessibles normales.

+0

Hmm .. Cela fonctionnera pour ce test particulier, mais pour d'autres tests (réalistes), j'utiliserais des champs hors du Modèle et autres joyeusetés. Je suppose que pourrait lancer à GroovyObject lors de l'accès à ces méthodes, mais il semble que ce soit un hack. L'intellisense d'IntelliJ semble résoudre les appels sur les méthodes groovy fournies par Model ... comment puis-je faire pour que maven fasse la même chose? –

0

Réponse courte: Vous ne pouvez pas accéder aux "méta-méthodes" Groovy à partir de Java.

Réponse longue:

DefaultGroovyMethods.getMetaClass(..) est pas une méthode qui peut être compilé statiquement en bytecode Java.

[Correction: En attendant, Don a posté une réponse, ce qui suggère de jeter à GroovyObject. Ceci est correct, et de cette façon, vous devriez être en mesure d'appeler des méthodes de méta Groovy comme ça:

List<Book> books = (List<Book>) 
    ((GroovyObject) Book).invokeMethod(
    "findAllByAuthorAndTitle", new String[] {"author", "title"}) 

(ou similaire). - Néanmoins, c'est impraticable pour la programmation quotidienne.]

Regardez le DefaultGroovyMethods ApiDocs. Le premier paramètre de chaque méthode est le type d'exécution de l'objet. Le reste de la méthode correspondante constitue la signature de méthode à utiliser dans le code Groovy. Vous serez déjà familier avec beaucoup de ceux-là.

Toutes ces « runtime méthodes méta » ne sont pas compilé statiquement dans le GroovyObject ((presque) tout objet Groovy dérive de ce type), mais, lorsqu'il est appelé, dynamiquement expédié lors de l'exécution - généralement, en utilisant le GroovyObject.invokeMethod(String, Object) method. Ainsi, votre code Java appelle une méthode qui n'existe tout simplement pas au moment de la compilation. - Mais, comment le code de Groovy peut-il se référer à cette méthode si c'est pas compilé au bytecode de Java? - Le bytecode Groovy (Java) ne fait pas directement référence aux méthodes (constructeurs, propriétés, etc.), mais construit à la place des structures appelées à l'exécution pour l'envoi dynamique.

Votre classe de test, par exemple, écrit en Groovy compilerait à ce (raccourci pour plus de clarté):

public class JavaGroovyTest extends TestCase 
    implements GroovyObject 
{ 
    public JavaGroovyTest() 
    { 
    JavaGroovyTest this; 
    CallSite[] arrayOfCallSite = $getCallSiteArray(); 
    MetaClass tmp12_9 = $getStaticMetaClass(); this.metaClass = ((MetaClass)ScriptBytecodeAdapter.castToType(tmp12_9, $get$$class$groovy$lang$MetaClass())); tmp12_9; 
    } 
    public void testGroovyClasses() { CallSite[] arrayOfCallSite = $getCallSiteArray(); Model m = arrayOfCallSite[0].callConstructor($get$$class$Model()); 
    arrayOfCallSite[1].callStatic($get$$class$JavaGroovyTest(), m); 
    arrayOfCallSite[2].callStatic($get$$class$JavaGroovyTest(), $const$0, arrayOfCallSite[3].call(arrayOfCallSite[4].call(arrayOfCallSite[5].call(m)))); return; 
    } 
} 

Lorsque ce code est exécuté, le moteur d'exécution Groovy effectuera des centaines, milliers et parfois même des milliards de recherche ;-) afin, finalement, de ne pas appeler la méthode, directement, mais par un appel de type reflet invokeMethod(..).

La plupart des parties du langage de programmation Groovy (y compris les constructeurs et autres bibliothèques) s'appuient fortement sur ce concept de meta programming, qui peut être implémenté à compile-time ou runtime.

Malheureusement, Groovy préfère ce dernier, bien que toutes les fonctionnalités sont ajoutées dynamiquement pas compilé bytecode Java et peuvent pas accessibles par le code Java, directement.

+0

Merci pour la réponse en profondeur. Je suis assez à l'aise avec Groovy et le développement dynamique en général (les 3 dernières années de ma carrière ont été Groovy/JRuby), mais maintenant je suis de retour (principalement) dans le monde de Java. Peut-être que je ne comprends pas comment GMaven fonctionne, mais à partir de leur site (http://gmaven.codehaus.org/Building+Groovy+Projects), je lis "Les fichiers de classe Stub sont utilisés pour résoudre les classes à la compilation, à l'exécution du les classes Groovy compilées seront utilisées à la place "comme signifiant que I * peut * appeler ces méthodes depuis Java. En fait, dans le code de non-test de l'application, j'ai fait ça et ça marche très bien. –

+0

Et quand j'ai mis un point d'arrêt dans le test (en commentant l'appel à getMetaClass()), je peux évaluer "m.getMetaClass()" sans aucune difficulté ... ce qui me porte à croire que getMetaClass ne nécessite pas de groovy- tapez les callsites à générer. –

+0

Si vous regardez le fichier de classe, vous verrez que getMetaClass est défini comme une méthode régulière, accessible depuis java: 'javap Model | grep getMetaClass'. Cependant, groovy le marque avec la propriété synthétique, donc javac prétend qu'il n'existe pas. Casting à l'interface GroovyObject contourne cela. – ataylor

Questions connexes