Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Riga Dev Day 2016 - Having fun with Javassist

Riga Dev Day 2016 - Having fun with Javassist

1bc80e2eee2adeaa8bb577798d92e9d0?s=128

Anton Arhipov

March 01, 2016
Tweet

Transcript

  1. Having fun with Javassist @antonarhipov

  2. Me Anton Arhipov @antonarhipov

  3. Me Anton Arhipov @antonarhipov Javassist inside!

  4. You?

  5. You? Are you interested in Javassist?

  6. You? Are you interested in Javassist? Want to become a

    better programmer?
  7. You? Are you interested in Javassist? Want to become a

    better programmer? Bytecode instrumentation, anyone?
  8. None
  9. @Entity
 @Table(name  =  "owners")
 public  class  Owner  extends  Person  {


           @Column(name  =  "address")
        @NotEmpty
        private  String  address;
 
        @Column(name  =  "city")
        @NotEmpty
        private  String  city;
 
        @Column(name  =  "telephone")
        @NotEmpty
        @Digits(fraction  =  0,  integer  =  10)
        private  String  telephone;
 
        @OneToMany(cascade  =  CascadeType.ALL,                                  mappedBy  =  "owner")
        private  Set<Pet>  pets;  
  10. public  class  JavassistLazyInitializer              

       extends  BasicLazyInitializer                  implements  MethodHandler  {   final  JavassistLazyInitializer  instance              =  new  JavassistLazyInitializer(…);   
 ProxyFactory  factory  =  new  ProxyFactory();
 factory.setSuperclass(interfaces.length  ==  1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER);   
 Class  cl  =  factory.createClass();
 final  HibernateProxy  proxy  =  (HibernateProxy)  cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed  =  true;
 return  proxy;  
  11. public  class  JavassistLazyInitializer              

       extends  BasicLazyInitializer                  implements  MethodHandler  {   final  JavassistLazyInitializer  instance              =  new  JavassistLazyInitializer(…);   
 ProxyFactory  factory  =  new  ProxyFactory();
 factory.setSuperclass(interfaces.length  ==  1?persistentClass:null);
 factory.setInterfaces(interfaces);
 factory.setFilter(FINALIZE_FILTER);   
 Class  cl  =  factory.createClass();
 final  HibernateProxy  proxy  =  (HibernateProxy)  cl.newInstance();
 ((ProxyObject)proxy).setHandler(instance);
 instance.constructed  =  true;
 return  proxy;   Generates  proxy!
  12. The main use case for bytecode generation in Java frameworks

    is to generate proxies
  13. The main use case for bytecode generation in Java frameworks

    is to generate proxies
  14. None
  15. Agenda Javassist basics -javaagent HacksApplications

  16. Agenda Javassist basics -javaagent HacksApplications … and a little bit

    on the use of Javassist in JRebel
  17. Javassist 101 www.javassist.org

  18. ClassPool CtClass CtClass CtClass CtClass CtField CtMethod CtConst CtMethod insertBefore

    insertAfter instrument It feels almost like Java Reflection API :)
  19.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  20.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  21.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }    ClassPool  cp  =  new  ClassPool(null);      cp.appendSystemPath();  
  22.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  23.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }    public  class  A  extends  Clazz  {  
        public  A()  {
        }
    }  
  24.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  25.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  26.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }  mars:output  anton$  javap  -­‐c  com/zt/A.class      public  class  com.zt.A  extends  com.zt.Clazz  {        public  com.zt.A();            Code:                0:  aload_0                1:  invokespecial  #10                  4:  return  
  27.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    }
  28.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ct  =  cp.makeClass("com.zt.A",                    cp.get("com.zt.Clazz"));          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    } Can generate classes from metadata at build time
  29.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          cp.appendClassPath(new  ClassPath(){  …  });          CtClass  ct  =  cp.get("com.zt.A");          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    } … or you can post process the compiled classes
  30.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          cp.appendClassPath(new  ClassPath(){  …  });          CtClass  ct  =  cp.get("com.zt.A");          CtMethod[]  methods  =  ct.getMethods();
        for  (CtMethod  method  :  methods)  {
              //…          }          ct.writeFile("/output");    } … or you can post process the compiled classes
  31.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "()V");          foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  32.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "()V");          foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  33.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "()V");          foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  34.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "()V");          foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }    public  void  foo()  {  
    }  
  35.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }    public  void  foo(String  s)  {  
    }  
  36.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lja        foo.insertBefore("System.out.println();");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }  Descriptors  might  get  quite  long  ;)  
  37.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println($1)");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }  $1,  $2,  $3  —  local  variables    $0  —  this  for  non-­‐static  methods  
  38.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println($1)");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  39.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println($1)");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }        Exception  in  thread  "main"  javassist.CannotCompileException:  [source  error]  ;  is  missing            at  javassist.CtBehavior.insertBefore(CtBehavior.java:774)            at  javassist.CtBehavior.insertBefore(CtBehavior.java:734)            at  com.zt.basics.Ex.main(Ex.java:35)  
  40.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println($1);");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  41.    public  static  void  main(String[]  args)  throws  Exception  {  

           ClassPool  cp  =  ClassPool.getDefault();          CtClass  ctClass  =  cp.get("com.zt.A");          CtMethod  foo  =  ctClass.getMethod("foo",                "(Ljava/lang/String;)V");          foo.insertBefore("System.out.println($1);");          Class  c  =  ctClass.toClass();
        A  a  =  (A)  c.newInstance();
        a.foo("Hello");
  }
  42.        CtMethod  foo  =  …      

       foo.insertBefore(…);          foo.insertAfter(…);  
  43.        CtMethod  foo  =  …      

       foo.insertBefore(…);          foo.insertAfter(…);   Can implement tracing
  44. … or add logging        CtMethod  foo  =

     …          foo.insertBefore(…);          foo.insertAfter(…);  
  45. … or implement AOP        CtMethod  foo  =

     …          foo.insertBefore(…);          foo.insertAfter(…);  
  46.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          });
  47.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          });
  48.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          });
  49.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          });
  50.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          }); Intercept new instances
  51.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(NewExpr  e)                        throws  CannotCompileException  {
                e.replace("{"  +
                        "$_  =  $proceed($$);"  +
                        "System.out.println($_);"  +
                        "}");
            }          }); Intercept new instances
  52.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(MethodCall  m)                        throws  CannotCompileException  {
                if(m.getMethodName().contains("println"))  {
                    m.replace("{}");
                }                            }          });
  53.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(MethodCall  m)                        throws  CannotCompileException  {
                if(m.getMethodName().contains("println"))  {
                    m.replace("{}");
                }                            }          }); Remove unwanted invocations
  54.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(FieldAccess  m)                        throws  CannotCompileException  {
                if  (f.isWriter())  {
                    CtField  field  =  f.getField();
                    String  setterName  =  findSetter(field);
                    f.replace("{"  +  "$0."  +  setterName  +  "($$);"  +  "}");
                }                            }          });
  55.        CtMethod  foo  =  …      

       foo.instrument(new  ExprEditor()  {              @Override
            public  void  edit(FieldAccess  m)                        throws  CannotCompileException  {
                if  (f.isWriter())  {
                    CtField  field  =  f.getField();
                    String  setterName  =  findSetter(field);
                    f.replace("{"  +  "$0."  +  setterName  +  "($$);"  +  "}");
                }                            }          }); Replace direct field access with setter calls
  56. java.lang.ClassFormatError:  LVTT  entry   for  'callbackTypes'  in  class  file  

      com/google/inject/internal/ProxyFactory   does  not  match  any  LVT  entry
  57. This slide is intentionally left blank

  58. Java Agent

  59. Java Agent import java.lang.instrument.ClassFileTransformer;   import java.lang.instrument.Instrumentation;   public class

    Agent {   public static void premain(String args, Instrumentation inst)   throws Exception {   inst.addTransformer(new ClassFileTransformer {   // here be code });   }   }
  60. Java Agent import java.lang.instrument.ClassFileTransformer;   import java.lang.instrument.Instrumentation;   public class

    Agent {   public static void premain(String args, Instrumentation inst)   throws Exception {   inst.addTransformer(new ClassFileTransformer {   // here be code });   }   } META-­‐INF/MANIFEST.MF   Premain-­‐Class:  Agent
  61. Java Agent import java.lang.instrument.ClassFileTransformer;   import java.lang.instrument.Instrumentation;   public class

    Agent {   public static void premain(String args, Instrumentation inst)   throws Exception {   inst.addTransformer(new ClassFileTransformer {   // here be code });   }   } META-­‐INF/MANIFEST.MF   Premain-­‐Class:  Agent $>  java  –javaagent:agent.jar  application.Main
  62. ClassFileTransformer new ClassFileTransformer() { public byte[] transform(ClassLoader loader,   String

    className,   Class<?> classBeingRedefined,   ProtectionDomain protectionDomain,   byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault();   CtClass ct = cp.makeClass(new   ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); }   }
  63. new ClassFileTransformer() { public byte[] transform(ClassLoader loader,   String className,

      Class<?> classBeingRedefined,   ProtectionDomain protectionDomain,   byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault();   CtClass ct = cp.makeClass(new   ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); }   } ClassFileTransformer
  64. new ClassFileTransformer() { public byte[] transform(ClassLoader loader,   String className,

      Class<?> classBeingRedefined,   ProtectionDomain protectionDomain,   byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault();   CtClass ct = cp.makeClass(new   ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); }   } ClassFileTransformer
  65. new ClassFileTransformer() { public byte[] transform(ClassLoader loader,   String className,

      Class<?> classBeingRedefined,   ProtectionDomain protectionDomain,   byte[] classfileBuffer){ ClassPool cp = ClassPool.getDefault();   CtClass ct = cp.makeClass(new   ByteArrayInputStream(classfileBuffer)); // here we can do all the things to ‘ct’ return ct.toBytecode(); }   } ClassFileTransformer
  66. None
  67. None
  68. None
  69. None
  70. None
  71. https://github.com/zeroturnaround/callspy

  72. Javassist in http://0t.ee/rigadevday2016

  73. JRebel core Spring plugin Hibernate plugin EJB plugin

  74. JRebel core Spring plugin Hibernate plugin EJB plugin jrebel.jar

  75. Spring plugin Hibernate plugin EJB plugin JRebel core

  76. Spring plugin Hibernate plugin EJB plugin Reloads classes JRebel core

  77. JRebel core Spring plugin Hibernate plugin EJB plugin Notifies plugins

  78. JRebel core Spring plugin Hibernate plugin EJB plugin Refresh configurations

  79. JRebel core Javassist lives here Spring plugin Hibernate plugin EJB

    plugin
  80. Spring Hibernate OpenEJB JRebel core Spring plugin Hibernate plugin EJB

    plugin
  81. None
  82. class  Framework  {      public  void  configure(){    

           //…
    }     }
  83. class  Framework  {      public  void  configure(){    

           //…
    }     } CtClass  framework        =  cp.get("com.zt.Framework");
  84. class  Framework  {      public  void  configure(){    

           //…
    }     } CtClass  framework        =  cp.get("com.zt.Framework"); class  Framework      implements  Listener  {      public  void  configure(){
    }     }
  85. class  Framework  {      public  void  configure(){    

           //…
    }     } CtClass  framework        =  cp.get("com.zt.Framework"); framework.addInterface(    cp.get("com.zt.jrebel.Listener")); class  Framework      implements  Listener  {      public  void  configure(){
    }     }
  86. class  Framework  {      public  void  configure(){    

           //…
    }     } CtClass  framework        =  cp.get("com.zt.Framework"); framework.addInterface(    cp.get("com.zt.jrebel.Listener")); class  Framework      implements  Listener  {      public  void  configure(){
    }     } framework.addMethod(      CtNewMethod.make(          "public  void  onEvent(){"  +          "    configure();"  +          "}",          framework   ));
  87. https://github.com/antonarhipov/jpoint HowItWorks

  88. None
  89. None
  90. Why would you use Javassist? > Implement you own custom

    framework (like if there’s not enough) > Tools > Profilers > Post-processing of Java classes > Integrate with 3rd party solution that doesn’t provide an API > JRebel plug-ins :)
  91. Your task Javassist

  92. https://speakerdeck.com/antonarhipov http://www.slideshare.net/arhan @antonarhipov anton@zeroturnaround.com http://0t.ee/rigadevday2016