DevoxxPL: JRebel Under The Covers

Preview:

Citation preview

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

Recommended