Slide 1

Slide 1 text

Having fun with Javassist @antonarhipov

Slide 2

Slide 2 text

Me Anton Arhipov @antonarhipov

Slide 3

Slide 3 text

Me Anton Arhipov @antonarhipov Javassist inside!

Slide 4

Slide 4 text

You?

Slide 5

Slide 5 text

You? Are you interested in Javassist?

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

You? Are you interested in Javassist? Want to become a better programmer? Bytecode instrumentation, anyone?

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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;  

Slide 11

Slide 11 text

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!

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Agenda Javassist basics -javaagent HacksApplications

Slide 16

Slide 16 text

Agenda Javassist basics -javaagent HacksApplications … and a little bit on the use of Javassist in JRebel

Slide 17

Slide 17 text

Javassist 101 www.javassist.org

Slide 18

Slide 18 text

ClassPool CtClass CtClass CtClass CtClass CtField CtMethod CtConst CtMethod insertBefore insertAfter instrument It feels almost like Java Reflection API :)

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

   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  

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

   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

Slide 29

Slide 29 text

   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

Slide 30

Slide 30 text

   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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

   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  

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

   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)  

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

       CtMethod  foo  =  …          foo.insertBefore(…);          foo.insertAfter(…);   Can implement tracing

Slide 44

Slide 44 text

… or add logging        CtMethod  foo  =  …          foo.insertBefore(…);          foo.insertAfter(…);  

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

       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  +  "($$);"  +  "}");
                }                            }          });

Slide 55

Slide 55 text

       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

Slide 56

Slide 56 text

java.lang.ClassFormatError:  LVTT  entry   for  'callbackTypes'  in  class  file     com/google/inject/internal/ProxyFactory   does  not  match  any  LVT  entry

Slide 57

Slide 57 text

This slide is intentionally left blank

Slide 58

Slide 58 text

Java Agent

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

https://github.com/zeroturnaround/callspy

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

JRebel core Spring plugin Hibernate plugin EJB plugin

Slide 74

Slide 74 text

JRebel core Spring plugin Hibernate plugin EJB plugin jrebel.jar

Slide 75

Slide 75 text

Spring plugin Hibernate plugin EJB plugin JRebel core

Slide 76

Slide 76 text

Spring plugin Hibernate plugin EJB plugin Reloads classes JRebel core

Slide 77

Slide 77 text

JRebel core Spring plugin Hibernate plugin EJB plugin Notifies plugins

Slide 78

Slide 78 text

JRebel core Spring plugin Hibernate plugin EJB plugin Refresh configurations

Slide 79

Slide 79 text

JRebel core Javassist lives here Spring plugin Hibernate plugin EJB plugin

Slide 80

Slide 80 text

Spring Hibernate OpenEJB JRebel core Spring plugin Hibernate plugin EJB plugin

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

https://github.com/antonarhipov/jpoint HowItWorks

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

Your task Javassist

Slide 92

Slide 92 text

https://speakerdeck.com/antonarhipov http://www.slideshare.net/arhan @antonarhipov [email protected] http://0t.ee/rigadevday2016