5 Replies Latest reply on Jun 5, 2009 8:05 AM by ad-rocha

    Using Javassist inside Mojo

      Hi all,

      I´m trying to use Javassist inside a Mojo. The code is very simple, but the program fails with a java.lang.ClassNotFoundException.

      First I collect all dependencies of Maven project:

      // maven dependencies
      List<String> classpath = new ArrayList<String>();
      classpath.addAll(project.getCompileClasspathElements());
      classpath.addAll(project.getTestClasspathElements());
      Set<Artifact> artifacts = project.getDependencyArtifacts();
      for (Artifact artifact : artifacts) {
       classpath.add(artifact.getFile().getAbsolutePath());
      }
      


      After that, I append all dependencies to ClassPool:

      ClassPool pool = ClassPool.getDefault();
      // add dependencies to classpath
      for (String element : classpath) {
       pool.appendClassPath(element);
      }
      // get class
      CtClass cc = pool.get(className);
      


      The error is caused by method.getAnnotations() call below:

      ...
      if (object instanceof CtMethod) {
       CtMethod method = (CtMethod) object;
       annotations = method.getAnnotations();
       ...
      }
      


      The error is: java.lang.ClassNotFoundException: org.testng.annotations.BeforeMethod

      But, org.testng.annotations.BeforeMethod is inside testng-5.8-jdk15.jar, wich was added to classpath. What am I doing wrong?

      Thanks in advance,

      Andre


        • 1. Re: Using Javassist inside Mojo
          chiba

          Have you checked the ClassPool object can access BeforeMethod?
          Please try:


          method.getDeclaringClass().getClassPool().get("org.testng...BeforeMethod")


          If this returns null, the ClassPool does not know how to obtain the class file. Maybe the appended class path is wrong. Otherwise, something I don't know is happening.

          • 2. Re: Using Javassist inside Mojo

            Thanks for your reply Chiba.

            I added this chek just before calling getAnnotations() and the class was found:

            try {
             CtClass c = method.getDeclaringClass().getClassPool().get("org.testng.annotations.BeforeMethod");
             System.out.println("FOUND: " + c);
            } catch (NotFoundException e) {
             System.out.println("NOT FOUND: " + e);
            }
            // error happens here
            annotations = method.getAnnotations();
            


            Console prints:
            FOUND: javassist.CtClassType@1a697a1[public abstract interface class org.testng.annotations.BeforeMethod implements java.lang.annotation.Annotation, fields= constructors= methods=javassist.CtMethod@da59c402[public abstract enabled ()Z], javassist.CtMethod@1e2eb9d5[public abstract groups ()[Ljava/lang/String;], javassist.CtMethod@87eeb3bb[public abstract dependsOnGroups ()[Ljava/lang/String;], javassist.CtMethod@9b4f6f0d[public abstract dependsOnMethods ()[Ljava/lang/String;], javassist.CtMethod@8b8eb7fd[public abstract alwaysRun ()Z], javassist.CtMethod@a79e83f0[public abstract inheritGroups ()Z], javassist.CtMethod@21e479fd[public abstract description ()Ljava/lang/String;], javassist.CtMethod@331b2daa[public abstract firstTimeOnly ()Z], ]
            


            Stack trace is:
            ...
            Caused by: java.lang.ClassNotFoundException: org.testng.annotations.BeforeMethod
             at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
             at java.security.AccessController.doPrivileged(Native Method)
             at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
             at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
             at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
             at org.codehaus.plexus.classworlds.realm.ClassRealm.loadRealmClass(ClassRealm.java:174)
             at org.codehaus.plexus.classworlds.strategy.DefaultStrategy.loadClass(DefaultStrategy.java:67)
             at org.codehaus.plexus.classworlds.strategy.ForeignStrategy.loadClass(ForeignStrategy.java:39)
             at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:201)
             at org.codehaus.plexus.classworlds.strategy.DefaultStrategy.loadClass(DefaultStrategy.java:73)
             at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:201)
             at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
             at java.lang.Class.forName0(Native Method)
             at java.lang.Class.forName(Class.java:247)
             at javassist.bytecode.annotation.MemberValue.loadClass(MemberValue.java:51)
             at javassist.bytecode.annotation.Annotation.toAnnotationType(Annotation.java:293)
             at javassist.CtClassType.toAnnoType(CtClassType.java:591)
             at javassist.CtClassType.toAnnotationType(CtClassType.java:492)
             at javassist.CtBehavior.getAnnotations(CtBehavior.java:185)
             at javassist.CtBehavior.getAnnotations(CtBehavior.java:156)
             at org.codecompany.jeha.mojo.Transformer.isAnnotated(Transformer.java:224)
             at org.codecompany.jeha.mojo.Transformer.transform(Transformer.java:94)
             ... 23 more
            


            • 3. Re: Using Javassist inside Mojo
              chiba

              OK, the problem is that the context class loader obtained by:

              Thread.currentThread().getContextClassLoader()


              could not find org.testng...BeforeMethod. So you must add testng-5.8-jdk15.jar to the class path of the context class loader.

              Otherwise, you can add that jar file to the class path of the class loader obtained by:

              classPool.getClass().getClassLoader()


              Javassist also tries this class loader to load the annotation class.

              If either option does not work in your case, let me know. Let's think about an extension to the Javassist API.


              • 4. Re: Using Javassist inside Mojo

                Chiba,

                It seems that testng-5.8-jdk15.jar is already in classpath. Look at this:

                Thread.currentThread().getContextClassLoader() and classPool.getClass().getClassLoader() are the same:

                System.out.println(Thread.currentThread().getContextClassLoader()):

                ClassRealm[/plugins/org.codecompany:jeha-plugin:1.0-SNAPSHOT@48/thread:main, parent: ClassRealm[plexus.core, parent: null]]
                


                System.out.println(pool.getClass().getClassLoader()):
                ClassRealm[/plugins/org.codecompany:jeha-plugin:1.0-SNAPSHOT@48/thread:main, parent: ClassRealm[plexus.core, parent: null]]
                


                System.out.println(pool):
                [class path: java.lang.Object.class;C:\Andre\workspace\Pessoal\jeha\jeha-test\target\classes;C:\Andre\workspace\Pessoal\jeha\jeha-test\target\test-classes;C:\Andre\workspace\Pessoal\jeha\jeha-test\target\classes;C:\Andre\workspace\Pessoal\jeha\jeha-core\target\classes;java.util.jar.JarFile@2585e;]
                


                I believe that java.util.jar.JarFile@2585e corresponds to testng-5.8-jdk15.jar

                Would it happen if I put the whole class code here?

                • 5. Re: Using Javassist inside Mojo [RESOLVED]

                  You´re righit Chiba. The code below did the trick. Thank you very much for your help!

                  ClassWorld world = new ClassWorld();
                  
                  ClassRealm realm = world.newRealm("org.codecompany.jeha.plugin",
                   Thread.currentThread().getContextClassLoader());
                  
                  ClassRealm runRealm = realm.createChildRealm("runenv");
                  
                  Set<Artifact> artifacts = project.getDependencyArtifacts();
                  for (Artifact artifact : artifacts) {
                   runRealm.addConstituent(artifact.getFile().toURI().toURL());
                  }
                  
                  Thread.currentThread().setContextClassLoader(
                   runRealm.getClassLoader());