Results 1 to 14 of 14
Like Tree1Likes
  • 1 Post By jim829

Thread: Javassist - what am I doing wrong?

  1. #1
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Javassist - what am I doing wrong?

    I'm playing with Javassist and it doesn't seem to be working. I don't get any errors, but the output is not what I expect.

    Java Code:
    package test;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    import javassist.expr.ExprEditor;
    import javassist.expr.FieldAccess;
    
    public class Test1 {
    	public static void main(String[] args) {
    		try {
    			// instrument the class
    			final CtClass ctClass = ClassPool.getDefault().get(Foo.class.getName());
    			final CtMethod ctMethod = ctClass.getDeclaredMethod("setValue");
    			ctMethod.instrument(new ExprEditor() {
    				@Override
    				public void edit(FieldAccess f) throws CannotCompileException {
    					//System.out.printf("instrumenting field access in %s, line %d\n", f.getFileName(), f.getLineNumber());
    					f.replace("System.out.println(\"fnord\");");
    				}
    			});
    
    			// test the instrumentation
    			Foo foo = new Foo();
    			foo.setValue(42);
    			System.out.println(foo.getValue());
    		}
    		catch (NotFoundException | CannotCompileException e) {
    			e.printStackTrace();
    		}
    	}
    }
    Java Code:
    package test;
    
    public class Foo {
    	private int value;
    
    	public int getValue() {
    		return value;
    	}
    
    	public void setValue(int value) {
    		this.value = value;
    	}
    }
    I expect the output to be:

    fnord
    0

    ...because the field access in the setter should be replaced by the statement that prints "fnord", and the value should remain uninitialized. But the output is actually:

    42

    The ExprEditor#edit(FieldAccess) method does run, as I can see if I uncomment the print statement on line 20. It correctly prints that it's operating on Foo.java line 11. But the following line seems to have no effect. What am I doing wrong?
    Last edited by kjkrum; 02-21-2014 at 09:48 AM.
    Get in the habit of using standard Java naming conventions!

  2. #2
    jim829 is offline Senior Member
    Join Date
    Jan 2013
    Location
    Northern Virginia, United States
    Posts
    3,786
    Rep Power
    5

    Default Re: Javassist - what am I doing wrong?

    Well, I have no idea really since I have never used Java Assist. My only guess for lack of any experience on this would be that your ctMethod should have been getValue instead of setValue. But again, I have no basis for this at all.

    Regards,
    Jim
    gimbal2 likes this.
    The JavaTM Tutorials | SSCCE | Java Naming Conventions
    Poor planning on your part does not constitute an emergency on my part

  3. #3
    jim829 is offline Senior Member
    Join Date
    Jan 2013
    Location
    Northern Virginia, United States
    Posts
    3,786
    Rep Power
    5

    Default Re: Javassist - what am I doing wrong?

    Well, that wasn't it.

    Regards,
    Jim
    The JavaTM Tutorials | SSCCE | Java Naming Conventions
    Poor planning on your part does not constitute an emergency on my part

  4. #4
    gimbal2 is offline Just a guy
    Join Date
    Jun 2013
    Location
    Netherlands
    Posts
    4,105
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    You tried it I guess? Too bad, I was cracking my brain over this and your suggestion seemed so blissfully logical.
    "Syntactic sugar causes cancer of the semicolon." -- Alan Perlis

  5. #5
    jim829 is offline Senior Member
    Join Date
    Jan 2013
    Location
    Northern Virginia, United States
    Posts
    3,786
    Rep Power
    5

    Default Re: Javassist - what am I doing wrong?

    Yes. I have downloaded the library. I find Java with all its generics, concurrency, etc hard enough. But to dynamically alter methods and edit byte code seems like one would be asking for trouble.

    Regards,
    Jim
    The JavaTM Tutorials | SSCCE | Java Naming Conventions
    Poor planning on your part does not constitute an emergency on my part

  6. #6
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    It is definitely asking for trouble. :) But this is how Hibernate does its magic, and I'm curious how it works.

    I had the idea that maybe my code modifies the bytecode in the CtClass, but not the live Class in the JVM. The Javassist tutorial says the JVM cannot reload a class (except under special circumstances) but I was under the impression that you can modify a loaded class. But maybe I need to write out the bytecode to the class file, and then run the program again to see my change. Or maybe I need to use a stub program to load my program with javassist.Loader instead of the system classloader. Or maybe both. The official tutorial is more like a list of weird things you can do with Javassist and not so much a step-by-step introduction.
    Get in the habit of using standard Java naming conventions!

  7. #7
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    Get in the habit of using standard Java naming conventions!

  8. #8
    jim829 is offline Senior Member
    Join Date
    Jan 2013
    Location
    Northern Virginia, United States
    Posts
    3,786
    Rep Power
    5

    Default Re: Javassist - what am I doing wrong?

    I read the tutorial, checked out the methods and the only thing I would have done differently was override edit using MethodCall as opposed to FieldAccess parameter. But doing that does not even get me as far as you got (i.e. the edit method is not even called). So after trying a variety of things I have not made any progress. But stuff like this is sort of fun to play around with (more so when it works as expected).

    Regards,
    Jim
    The JavaTM Tutorials | SSCCE | Java Naming Conventions
    Poor planning on your part does not constitute an emergency on my part

  9. #9
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    I might be onto something with writing out the file. When I add a call to

    Java Code:
    ctClass.writeFile()
    at line 24, and copy my class files out of the Eclipse workspace so it doesn't recompile them all the time, the first time I run the program I get my unexpected result, and the second time I get this:

    Java Code:
    javassist.CannotCompileException: the resulting value is not stored in $_
    	at javassist.expr.Expr.checkResultValue(Expr.java:219)
    	at javassist.expr.FieldAccess.replace(FieldAccess.java:181)
    	at test.Test1$1.edit(Test1.java:23)
    	at javassist.expr.ExprEditor.loopBody(ExprEditor.java:198)
    	at javassist.expr.ExprEditor.doit(ExprEditor.java:91)
    	at javassist.CtBehavior.instrument(CtBehavior.java:712)
    	at test.Test1.main(Test1.java:19)
    Get in the habit of using standard Java naming conventions!

  10. #10
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    I might be onto something with writing out the file. I still haven't figured out the syntax to not perform the assignment at all. But I can add a print statement before the assignment:

    Java Code:
    package test;
    
    import java.io.IOException;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.NotFoundException;
    import javassist.expr.ExprEditor;
    import javassist.expr.FieldAccess;
    
    public class Test1 {
    	public static void main(String[] args) {
    		try {
    			// instrument the class
    			final CtClass ctClass = ClassPool.getDefault().get(Foo.class.getName());
    			final CtMethod ctMethod = ctClass.getDeclaredMethod("setValue");
    			ctMethod.instrument(new ExprEditor() {
    				@Override
    				public void edit(FieldAccess f) throws CannotCompileException {
    					//System.out.printf("instrumenting field access in %s, line %d\n", f.getFileName(), f.getLineNumber());
    					f.replace("{ System.out.println(\"fnord\"); $_ = $proceed($$); }");
    				}
    			});
    			ctClass.writeFile();
    
    			// test the instrumentation
    			Foo foo = new Foo();
    			foo.setValue(42);
    			System.out.println(foo.getValue());
    		}
    		catch (NotFoundException | CannotCompileException | IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    The first time I run this, it prints "42". The second time, it prints "fnord" and then "42", as expected. The third time, it prints "fnord" three times, which I don't understand, and then prints "42". I had to copy my class files out of my Eclipse workspace to prevent them being recompiled every time I run the program.
    Get in the habit of using standard Java naming conventions!

  11. #11
    gimbal2 is offline Just a guy
    Join Date
    Jun 2013
    Location
    Netherlands
    Posts
    4,105
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    Quote Originally Posted by kjkrum View Post
    booooooo... oh wait.

    Considered checking out the Hibernate source code itself? Even though its bulky I find it surprisingly not-impossible to actually find stuff in it.
    "Syntactic sugar causes cancer of the semicolon." -- Alan Perlis

  12. #12
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    I think that once a class is returned by a ClassLoader, it cannot be modified in the running program. So I need to ensure that my class is loaded by the custom classloader provided with Javassist, which allows you to hook into the loading process and modify the classes before they're returned. To do that, I need to launch my program with a stub that creates the Javassist classloader, dynamically loads my real main class, and then calls its main method reflectively.

    Edit: not quite. See below.
    Last edited by kjkrum; 02-22-2014 at 09:51 PM.
    Get in the habit of using standard Java naming conventions!

  13. #13
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    Got it! Once a class is loaded, is is unmodifiable. But if a different version of the same class is loaded by a different classloader, the JVM treats it as a different class. Normally this wouldn't happen, as the other classloader would delegate to the parent and get the version of the class that has already been loaded. But javassist.Loader looks for a class definition in its ClassPool before it delegates to its parent. So within the class dynamically loaded by my javassist.Loader, any reference to Foo refers to my modified Foo.

    I skipped the reflective method call and just used a Runnable since I didn't need to pass any arguments.


    Java Code:
    package test;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.CtMethod;
    import javassist.Loader;
    import javassist.expr.ExprEditor;
    import javassist.expr.FieldAccess;
    
    public class Stub {
    	public static void main(String[] args) {
    		try {
    			// load the class into the ClassPool.  this causes the system
    			// classloader to load Foo, making it unmodifiable.  it makes no
    			// difference to use a literal string here instead of referencing
    			// the class; ClassPool loads it using the system classloader.
    			final CtClass ctClass = ClassPool.getDefault().get(Foo.class.getName());
    			final CtMethod ctMethod = ctClass.getDeclaredMethod("setValue");
    			ctMethod.instrument(new ExprEditor() {
    				@Override
    				public void edit(FieldAccess f) throws CannotCompileException {
    					// modify the class to print a message when the setter is called
    					f.replace("{ System.out.println(\"setter called\"); $_ = $proceed($$); }");
    				}
    			});
    			
    			// create a classloader that gets its class definitions from the ClassPool
    			Loader loader = new Loader(ClassPool.getDefault());
    			
    			// dynamically load a class in which test.Foo is the modified one
    			Class<?> klass = loader.loadClass("test.Test2");
    			Runnable r = (Runnable) klass.newInstance();
    			r.run();
    		}
    		catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    Java Code:
    package test;
    
    public class Test2 implements Runnable {
    	@Override
    	public void run() {
    		Foo foo = new Foo();
    		foo.setValue(42);
    		System.out.println(foo.getValue());
    	}
    }
    Get in the habit of using standard Java naming conventions!

  14. #14
    gimbal2 is offline Just a guy
    Join Date
    Jun 2013
    Location
    Netherlands
    Posts
    4,105
    Rep Power
    6

    Default Re: Javassist - what am I doing wrong?

    Yeah that makes sense that you can't modify an already loaded class. There may already be objects created based on it.
    "Syntactic sugar causes cancer of the semicolon." -- Alan Perlis

Similar Threads

  1. Javassist Enhancement failed
    By Gowramma in forum New To Java
    Replies: 3
    Last Post: 08-16-2013, 10:19 AM
  2. Replies: 4
    Last Post: 06-11-2013, 02:37 AM
  3. I can't find anything wrong with this but somehow it's wrong.
    By Biscuit Tickler in forum New To Java
    Replies: 2
    Last Post: 09-12-2012, 10:28 PM
  4. Javassist Usage, pros & Cons
    By vanisree in forum Advanced Java
    Replies: 2
    Last Post: 11-03-2011, 04:26 PM
  5. Javassist with Maven
    By pcman312 in forum Advanced Java
    Replies: 2
    Last Post: 02-01-2011, 10:42 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •