devoxxpl: jrebel under the covers
TRANSCRIPT
JRebel Under the Covers How is it even possible?
Simon Maple -‐ @sjmaple @sjmapleSimon Maple
Make a change Compile
Build
Deploy
Observe the result
Take some coffee
Avg: 3 min
state external
serializable
temporary
derivative
How can I reload a single class preserving the state?
ClassLoader API
How to reload web application? (and preserve state)
Classes
Libraries
OldClassLoader NewClassLoader
Sevlet New Classes New
Libraries
Sevlet
Session Session init()
App State
App State
Serialize/deserialize
OldClassLoader NewClassLoader
Sevlet New Classes New
Libraries
Sevlet
Session Session
App State
App State
Class loaders are good for isolation, but suck at reloading the code … (and perfect for memory leaks)
Leaking ClassLoaders
Hello, HotSwap! Hotswapping methods since 2002
M. Dmitriev. Safe class and data evolution in large and long-lived java[tm] applications. Technical report, Mountain View, CA, 2001.
The Heap & Garbage CollecBon
Hello, JRebel! Improving HotSwap since 2007
Demo Time!
HotSwap JRebel Changing method bodies Adding/removing methods Adding/removing constructors Adding/removing fields Adding/removing annotaBons Changing superclass Adding/removing interfaces
How does it work???
The engine
ClassA
+field1 +field2 +field3
+method1(n:int) +method2(s:String) +method3(z:double)
ClassA
+field1 +field2 +field3 +proxy methods
ClassA0
+method1(n:int) +method2(s:String) +method3(z:double)
Transformer
public class C extends X { int y = 5; int method1(int x) { return x + y; } void method2(String s) { System.out.println(s); }}
public class C extends X { int y = 5; int method1(int x) { Object[] o = new Object[1]; o[0] = x; return Runtime.redirect(this, o, "C", "method1", "(I)I"); } void method2(String s) { Object[] o = new Object[1]; o[0] = s; return Runtime.redirect( this, o, "C", "method2", "(Ljava/lang/String;)V"); }}
public abstract class C0 { public static int method1(C c, int x) { int tmp1 = Runtime.getFieldValue(c, "C", "y", "I"); return x + tmp1; } public static void method2(C c, String s) { PrintStream tmp1 = Runtime.getFieldValue( null, "java/lang/System", "out", "Ljava/io/PrintStream;"); Object[] o = new Object[1]; o[0] = s; Runtime.redirect( tmp1, o, "java/io/PrintStream;", "println", "(Ljava/lang/String;)V"); }}
public class C extends X { int y = 5; int method1(int x) { return x + y; } //...}
public class C extends X { int y = 5; int z() { return 10; } int method1(int x) { return x + y + z(); } //...}
public class C1 { public static int z(C c) { return 10; } public static int method1(C c, int x) { int tmp1 = Runtime.getFieldValue(c, "C", "y", "I"); int tmp2 = Runtime.redirect(c, null, "C", "z", "(V)I"); return x + tmp1 + tmp2; } //...}
JRebel Reloading
static { }
Adding new static fields Removing static fields Changing values of static fields Factories etc
package a;public class A { int say() {return 1;}}
package a;class Test { public static void main(String args[]) { a.A a = new b.B(); System.out.println(a.say()); }}
package b;public class B extends a.A { int say() {return 2;}}
An Exercise
“package” visibility
package -> public -> package
concurrency
synchronized volatile
Object.wait() Object.notify()
serialization
Serializable Externalizable
transient
reflection
Class, Method, Field,
Constructor, Proxy
The Ecosystem
Javassist: binary patching for fun and profit
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 { }); }}
java –javaagent:agent.jar …
META-INF/MANIFEST.MF Premain-Class: Agent
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));
transformClass(ct, cp);
return ct.toBytecode(); }}
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));
transformClass(ct, cp);
return ct.toBytecode(); }}
JRebel Plugins
@Path(“/”) public String foo() { return “Foo”; }
@Path(“/foobar”) public String foo() { return “FooBar”; }
JRebel core
JRebel Spring plugin
Remote?
THE IMPACT
http://zeroturnaround.com/company/case-studies/
“Team velocity was increased by 40.6%, and according to t-test it was a statistically significant difference.”
“Netty and Spring together with JRebel is real pleasure to work with. The value of time saved is over 50% of my development time.”
Questions? @sjmaple
Credits https://www.flickr.com/photos/smemon/4556099850/ https://www.flickr.com/photos/gforsythe/10173857405 https://www.flickr.com/photos/dkshots/6967173295 https://www.flickr.com/photos/reckless_sniper/4545673070 https://www.flickr.com/photos/omcoc/8350510425 https://www.flickr.com/photos/bufivla/3619927485 https://www.flickr.com/photos/39747297@N05/5229733647 http://leadershipelements.files.wordpress.com/2014/01/square-peg-round-hole.jpg