62
Having fun with Javassist

JavaOne 2015 - Having fun with Javassist

Embed Size (px)

Citation preview

Page 1: JavaOne 2015 - Having fun with Javassist

Having fun with Javassist

Page 2: JavaOne 2015 - Having fun with Javassist

Me

Anton Arhipov @antonarhipov

We Javassist a lot!

Page 3: JavaOne 2015 - Having fun with Javassist

You?

Are you interested in Javassist?

Want to become a better programmer?

Bytecode instrumentation, anyone?

Page 4: JavaOne 2015 - Having fun with Javassist
Page 5: JavaOne 2015 - Having fun with Javassist

@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;  

Page 6: JavaOne 2015 - Having fun with Javassist

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;  

Page 7: JavaOne 2015 - Having fun with Javassist

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!

Page 8: JavaOne 2015 - Having fun with Javassist

The main use case for bytecode generation in Java framewoks

is to generate proxies

Page 9: JavaOne 2015 - Having fun with Javassist
Page 10: JavaOne 2015 - Having fun with Javassist

Agenda

Javassist basics -javaagent

HacksApplications

… and a little bit on the use of Javassist in JRebel

Page 11: JavaOne 2015 - Having fun with Javassist

Javassist 101www.javassist.org

Page 12: JavaOne 2015 - Having fun with Javassist

ClassPool

CtClassCtClassCtClass

CtClass

CtFieldCtMethodCtConst

CtMethod

insertBeforeinsertAfterinstrument

It feels almost like Java Reflection API :)

Page 13: JavaOne 2015 - Having fun with Javassist

   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");  

 }

Page 14: JavaOne 2015 - Having fun with Javassist

   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();  

Page 15: JavaOne 2015 - Having fun with Javassist

   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()  {        }    }  

Page 16: JavaOne 2015 - Having fun with Javassist

   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");  

 }

Page 17: JavaOne 2015 - Having fun with Javassist

   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  Compiled  from  "A.java"    public  class  com.zt.A  extends  com.zt.Clazz  {        public  com.zt.A();            Code:                0:  aload_0                1:  invokespecial  #10                  4:  return  

Page 18: JavaOne 2015 - Having fun with Javassist

   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

Page 19: JavaOne 2015 - Having fun with Javassist

   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

Page 20: JavaOne 2015 - Having fun with Javassist

   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");

 }

Page 21: JavaOne 2015 - Having fun with Javassist

   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");

 }

Page 22: JavaOne 2015 - Having fun with Javassist

   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()  {      }  

Page 23: JavaOne 2015 - Having fun with Javassist

   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)  {      }  

Page 24: JavaOne 2015 - Having fun with Javassist

   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;Ljava/lang/String;)V");  

       foo.insertBefore("System.out.println();");  

       Class  c  =  ctClass.toClass();        A  a  =  (A)  c.newInstance();        a.foo("Hello");

 }

 Descriptors  might  get  quite  long  ;)  

Page 25: JavaOne 2015 - Having fun with Javassist

   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  

Page 26: JavaOne 2015 - Having fun with Javassist

   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)  

Page 27: JavaOne 2015 - Having fun with Javassist

   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");

 }

Page 28: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

       foo.insertBefore(…);  

       foo.insertAfter(…);  

Can implement tracing

Page 29: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

       foo.insertBefore(…);  

       foo.insertAfter(…);  

… or add logging

Page 30: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

       foo.insertBefore(…);  

       foo.insertAfter(…);  

… or implement AOP

Page 31: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

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

       });

Page 32: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

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

       });

Page 33: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

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

       }); Intercept new instances

Page 34: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

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

       }); Intercept new instances

Page 35: JavaOne 2015 - Having fun with Javassist

       CtMethod  foo  =  …  

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

       }); Remove unwanted invocations

Page 36: JavaOne 2015 - Having fun with Javassist

       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

Page 37: JavaOne 2015 - Having fun with Javassist

This slide is intentionally left blank

Page 38: JavaOne 2015 - Having fun with Javassist

Java Agent

Page 39: JavaOne 2015 - Having fun with Javassist

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

Page 40: JavaOne 2015 - Having fun with Javassist

ClassFileTransformernew 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(); }  }

Page 41: JavaOne 2015 - Having fun with Javassist

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

Page 42: JavaOne 2015 - Having fun with Javassist

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

Page 43: JavaOne 2015 - Having fun with Javassist

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

Page 44: JavaOne 2015 - Having fun with Javassist
Page 45: JavaOne 2015 - Having fun with Javassist
Page 46: JavaOne 2015 - Having fun with Javassist
Page 47: JavaOne 2015 - Having fun with Javassist
Page 48: JavaOne 2015 - Having fun with Javassist
Page 49: JavaOne 2015 - Having fun with Javassist

https://github.com/zeroturnaround/callspy

Page 50: JavaOne 2015 - Having fun with Javassist

Javassist inhttp://0t.ee/javaone-jr

Page 51: JavaOne 2015 - Having fun with Javassist

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Page 52: JavaOne 2015 - Having fun with Javassist

JRebel core

Spring plugin

Hibernate plugin

EJB pluginjrebel.jar

Page 53: JavaOne 2015 - Having fun with Javassist

Spring plugin

Hibernate plugin

EJB pluginReloads

classes

JRebel core

Page 54: JavaOne 2015 - Having fun with Javassist

JRebel core

Spring plugin

Hibernate plugin

EJB pluginNotifies

plugins

Page 55: JavaOne 2015 - Having fun with Javassist

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Refresh configurations

Page 56: JavaOne 2015 - Having fun with Javassist

JRebel core

Javassist lives here

Spring plugin

Hibernate plugin

EJB plugin

Page 57: JavaOne 2015 - Having fun with Javassist

Spring

Hibernate

OpenEJB

JRebel core

Spring plugin

Hibernate plugin

EJB plugin

Page 58: JavaOne 2015 - Having fun with Javassist

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  ));

Page 59: JavaOne 2015 - Having fun with Javassist

https://github.com/antonarhipov/jpoint

HowItWorks

Page 60: JavaOne 2015 - Having fun with Javassist
Page 61: JavaOne 2015 - Having fun with Javassist

Your task

Javassist

Page 62: JavaOne 2015 - Having fun with Javassist

https://speakerdeck.com/antonarhipov

http://www.slideshare.net/arhan

@antonarhipov [email protected]

http://0t.ee/javaone-jr