10 Replies Latest reply on Feb 7, 2010 9:34 AM by Arvind K

    cannot access added Field in some cases. Please help.

    Arvind K Newbie

      Hello,

       

      Some background regarding my issue:

      I have obtained the byteArray of the class using a ClassFileTransformer added to an Instrumentation object. I am then using the

      ClassPool.makeClassIfNew() 

      function to create the corresponding CtClass object.

       

      Issue:

      I am creating a simple int field using

           myCtField = CtField.new(CtClass.intType, "myInt", myCtClassObj) 

       

      and then adding theCtField to the CtClass using

      myCtClassObj.addField(myCtField);

       

      Now I am adding access to the field, within a method in the class represented by the CtMethod object myCtM, using a simple statement like,

      myCtM.insertBefore("myInt = 10;");               

       

      I am then using CtClass.toBytecode() to get the instrumented bytecode which I am sending back to the ClassFileTransformer to be loaded. Now, When the class gets instantiated and 'executed', I would get a CannotCompileException as follows:

      javassist.CannotCompileException: [source error] no such field:  myInt
      

       

      HOWEVER,

      when I perform the same instrumentations such as above in a simpler Javassist example and then use CtClass.toClass(), create a new class and a new instance of it using the newInstance() function and execute the modified method, I am able to access the newly added field just fine!

       

      What am I missing here? Please provide your thoughts on this. Thanks!

       

      -Arvind

        • 1. Re: cannot access added Field in some cases. Please help.
          jaikiran pai Master

          Can you please post the entire exception stacktrace and more of your relevant code? And which version of Javassist are you using?

          • 2. Re: cannot access added Field in some cases. Please help.
            Arvind K Newbie

            Hello Jaikiran,

             

            Thanks for your prompt response. Here are the first 3 lines of the stack trace (after which the part of my code where I access the myInt variable forms the next stack trace element):

             

             javassist.CannotCompileException: [source error] no such field: myInt
             javassist.CtBehavior.insertBefore(CtBehavior.java:721)
             javassist.CtBehavior.insertBefore(CtBehavior.java:681)
            


            Javassist version is 3.11.GA

             

            One more thing I'd like to add is that I am getting all the fields in the CtClass using CtClass.getFields() and displaying them sometime before adding the new field and soon after adding the new field and I do see the new integer field 'myInt' added after CtClass.addField() call. However, the next statement is an 'CtMethod.insertBefore()' which throws the above exception. Thanks.

             

            -Arvind

            • 3. Re: cannot access added Field in some cases. Please help.
              Arvind K Newbie

              I just want to add that I tried rebuilding the class file soon after I add the new CtField to the CtClass object and before I call CtClass.toBytecode() but that did not help.

               

              This post has a similar issue  http://spiritandopportunity.com/list/44/41185.html  but it wasn't very helpful to me.

              I saw a similar discussion with Mr. Chiba in 2004 here -> http://www.mail-archive.com/search?q=abount&l=jboss-development@lists.sourceforge.net

              Don't know if its been resolved. I don't see it in the bug tracker.

              any help is greatly appreciated. thanks

              • 4. Re: cannot access added Field in some cases. Please help.
                jaikiran pai Master

                You haven't posted your complete code nor the complete exception stacktrace (i reason i keep insisting on these is because they can contain vital information). Anyway, i just gave a quick try against 3.11.0.GA version javassist:

                 

                public class NewFieldTestCase extends TestCase
                {
                
                   public  void testNewFieldAddition() throws Exception 
                   {
                      
                      
                      CtClass dummyClass = ClassPool.getDefault().get("org.myapp.test.javassist.Dummy");
                      CtField newIntField = new CtField(CtClass.intType,"myInt",dummyClass);
                      dummyClass.addField(newIntField);
                      CtMethod dummyMethod = dummyClass.getDeclaredMethod("dummyMethod");
                      dummyMethod.insertBefore("myInt = 10;");
                      
                      byte[] modifiedDummyClassBytes = dummyClass.toBytecode();
                      assertTrue("Empty bytes", modifiedDummyClassBytes.length > 0);
                      
                   }
                
                
                }
                

                 

                Works fine. Test case passes, no exceptions thrown.

                1 of 1 people found this helpful
                • 5. Re: cannot access added Field in some cases. Please help.
                  Arvind K Newbie

                  Ok..Jaikiran. Thanks for checking it out. Here is the complete stacktrace for two such exceptions (separated by the line). Another weird thing is that, it works for some classes but throws an exception for others.

                   

                  javassist.CannotCompileException: [source error] no such field: abc

                  javassist.CtBehavior.insertBefore(CtBehavior.java:721)

                  javassist.CtBehavior.insertBefore(CtBehavior.java:681)

                  mypkg1.SimpleInstrumenter.prefixMethod(SimpleInstrumenter.java:288)

                  mypkg1.SimpleInstrumenter.instrument(SimpleInstrumenter.java:195)

                  mypkg1.Instrument$Logger.transform(Instrument.java:77)

                  sun.instrument.TransformerManager.transform(TransformerManager.java:122)

                  sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:155)

                  java.lang.ClassLoader.defineClass1(Native Method)

                  java.lang.ClassLoader.defineClass(ClassLoader.java:620)

                  java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

                  java.net.URLClassLoader.defineClass(URLClassLoader.java:260)

                  java.net.URLClassLoader.access$100(URLClassLoader.java:56)

                  java.net.URLClassLoader$1.run(URLClassLoader.java:195)

                  java.security.AccessController.doPrivileged(Native Method)

                  java.net.URLClassLoader.findClass(URLClassLoader.java:188)

                  java.lang.ClassLoader.loadClass(ClassLoader.java:306)

                  sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)

                  java.lang.ClassLoader.loadClass(ClassLoader.java:251)

                  org.apache.catalina.loader.StandardClassLoader.loadClass(StandardClassLoader.java:941)

                  org.apache.catalina.loader.StandardClassLoader.loadClass(StandardClassLoader.java:857)

                  org.apache.catalina.loader.StandardClassLoader.loadClass(StandardClassLoader.java:941)

                  org.apache.catalina.loader.StandardClassLoader.loadClass(StandardClassLoader.java:857)

                  org.apache.catalina.core.StandardWrapper.isContainerProvidedServlet(StandardWrapper.java:1274)

                  org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:875)

                  org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:668)

                  org.apache.catalina.servlets.InvokerServlet.serveRequest(InvokerServlet.java:416)

                  org.apache.catalina.servlets.InvokerServlet.doPost(InvokerServlet.java:216)

                  javax.servlet.http.HttpServlet.service(HttpServlet.java:760)

                  javax.servlet.http.HttpServlet.service(HttpServlet.java:853)

                  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)

                  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)

                  com.mypkg2.AccessFilter.doFilter(AccessFilter.java:43)

                  org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:213)

                  org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)

                  org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

                  org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

                  org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

                  org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

                  org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

                  org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

                  org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)

                  org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

                  org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

                  org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)

                  org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

                  org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

                  org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)

                  org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)

                  org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)

                  org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)

                  org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)

                  org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)

                  org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)

                  org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)

                  org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)

                  java.lang.Thread.run(Thread.java:595)

                  Caused by: compile error: no such field: abc
                  at javassist.compiler.MemberResolver.lookupField(MemberResolver.java:322)
                  at javassist.compiler.MemberResolver.lookupFieldByJvmName(MemberResolver.java:308)
                  at javassist.compiler.TypeChecker.fieldAccess(TypeChecker.java:843)
                  at javassist.compiler.TypeChecker.atFieldRead(TypeChecker.java:770)
                  at javassist.compiler.TypeChecker.atExpr(TypeChecker.java:571)
                  at javassist.compiler.ast.Expr.accept(Expr.java:67)
                  at javassist.compiler.TypeChecker.atPlusExpr(TypeChecker.java:370)
                  at javassist.compiler.TypeChecker.atBinExpr(TypeChecker.java:311)
                  at javassist.compiler.ast.BinExpr.accept(BinExpr.java:40)
                  at javassist.compiler.JvstTypeChecker.atMethodArgs(JvstTypeChecker.java:220)
                  at javassist.compiler.TypeChecker.atMethodCallCore(TypeChecker.java:702)
                  at javassist.compiler.TypeChecker.atCallExpr(TypeChecker.java:681)
                  at javassist.compiler.JvstTypeChecker.atCallExpr(JvstTypeChecker.java:156)
                  at javassist.compiler.ast.CallExpr.accept(CallExpr.java:45)
                  at javassist.compiler.CodeGen.doTypeCheck(CodeGen.java:241)
                  at javassist.compiler.CodeGen.atStmnt(CodeGen.java:329)
                  at javassist.compiler.ast.Stmnt.accept(Stmnt.java:49)
                  at javassist.compiler.Javac.compileStmnt(Javac.java:568)
                  at javassist.CtBehavior.insertBefore(CtBehavior.java:701)
                  ... 84 more

                  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                  javassist.CannotCompileException: [source error] no such field: abc

                  javassist.CtBehavior.insertBefore(CtBehavior.java:721)

                  javassist.CtBehavior.insertBefore(CtBehavior.java:681)

                  mypkg1.SimpleInstrumenter.prefixMethod(SimpleInstrumenter.java:288)

                  mypkg1.SimpleInstrumenter.instrument(SimpleInstrumenter.java:195)

                  mypkg1.Instrument$Logger.transform(Instrument.java:77)

                  sun.instrument.TransformerManager.transform(TransformerManager.java:122)

                  sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:155)

                  java.lang.ClassLoader.defineClass1(Native Method)

                  java.lang.ClassLoader.defineClass(ClassLoader.java:620)

                  java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)

                  java.net.URLClassLoader.defineClass(URLClassLoader.java:260)

                  java.net.URLClassLoader.access$100(URLClassLoader.java:56)

                  java.net.URLClassLoader$1.run(URLClassLoader.java:195)

                  java.security.AccessController.doPrivileged(Native Method)

                  java.net.URLClassLoader.findClass(URLClassLoader.java:188)

                  java.lang.ClassLoader.loadClass(ClassLoader.java:306)

                  sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)

                  java.lang.ClassLoader.loadClass(ClassLoader.java:251)

                  java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)

                  com.mypkg2.TimeoutThread.run(TimeoutThread.java:106)

                   

                  Message was edited by: Arvind K

                  • 6. Re: cannot access added Field in some cases. Please help.
                    jaikiran pai Master

                    I have a suspicion that for the class that fails, the class has already been loaded by the classloader, before you do any of this changes through Javassist. But i can't say for sure, because i don't know the details about your runtime environment. For that matter, i don't even know the complete code which you are using to do this. I know that it's not always possible to post the code for various reasons. If that's the case why you haven't yet posted your code, then just speak up

                     

                    Anyway, here's what i think would be relevant about classloaders http://www.csg.is.titech.ac.jp/~chiba/javassist/tutorial/tutorial.html#load

                    • 7. Re: cannot access added Field in some cases. Please help.
                      Arvind K Newbie

                      Hello Jaikiran,

                       

                      I do not think that the class with the issue has already been loaded. In my first post, I mentioned that I am using java.lang.instrument API to do the loading and transformation initiation for me. The ClassFileTransformer hands me the bytecode of the class and I convert that into a CtClass object using CtClass.makeClassIfNew() and perform my transformations. I then hand over the modified bytecode back to the classfiletransformer to complete the loading process.

                       

                      You are right that I am not in a position to post all of my code as-is mainly because I do not know where the issue(s) lies and that it might be voluminous if I post everything and of course I do not own it to post it but I would be happy to provice snippets of some main parts for clarity. Here is the method in question where I do my simple modifications. I call this method for every method of the class I am instrumenting but add the int field 'abc' to the class while instrumenting the first method only.

                       

                      protected void prefixMethod(CtMethod myCtM, String methodName) {
                           try {
                           if (fieldsAdded == false)
                           {
                               CtField tempCtF = new CtField(CtClass.intType, "abc",this.CtC); //myCtM is a method in this.CtC
                                tempCtF.setModifiers(Modifier.PUBLIC);
                                this.CtC.addField(tempCtF,"10");
                                displayFields(false); // displays all the fields in this.CtC. Field 'abc' shows up here always.
                                Log2File.write("Added Fields");
                                // this.CtC.rebuildClassFile(); // i thought this function might solve the issue but it did not
                                tempCtF = this.CtC.getField("abc"); // just a precaution
                           }
                           myCtM.insertBefore("System.out.println(\"In " + this.className + "." + methodName + ". abc is \" + this.abc);");
                           } catch (CannotCompileException cce) {
                                System.out.println("prefixMethod for " + className + " method name " + methodName + ". Exception: " + cce);
                                cce.printStackTrace();
                                Log2File.writeErr(cce,"prefixMethod for " + className);
                           }
                           catch (NotFoundException ntfnde) {
                                System.out.println("prefixMethod for " + className + " method name " + methodName + ". Exception: " + ntfnde);
                                ntfnde.printStackTrace();
                                Log2File.writeErr(ntfnde,"prefixMethod for " + className);
                           }
                      }
                      

                       

                      Another twist is that, it works for a few methods in a class but not others in the same class. I do not know what made the difference there. I have a suspision that it works for non-empty methods and not empty ones but I am not sure. I tried skipping the empty ones with CtMethod.isEmpty() but I couldn't seem to skip them.

                       

                      Appreciate your help so far.

                       

                      -Arvind

                      • 8. Re: cannot access added Field in some cases. Please help.
                        Arvind K Newbie
                        part of the issue was, I was futilely trying to access an instance variable from an inherited protected method. Now, I think I am getting less exceptions than before but the issue is not solved.
                        • 9. Re: cannot access added Field in some cases. Please help.
                          jaikiran pai Master

                          This is  very specific to your application. You will have to attach a debugger and step through your code and the Javassist code to see what's going wrong.

                          • 10. Re: cannot access added Field in some cases. Please help.
                            Arvind K Newbie

                            Is it true that one cannot access fields added to a class using Javassist from methods inherited by the class?