JPDA Series:
1. JPDA (i): Write a debugger with JDI
2. JPDA (b): Structure of the source code analysis
Redefineclasses
JPDA provides a api,virtualmachine#redefineclasses that we can use to implement hot substitution of Java code.
Directly below the code, our target VM runs the following code, as mentioned earlier, the target VM needs to be added option when it is started,-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8787
public class main { public static void Main (string[] args) throws Exception {Random random = new Random (); while (true ) {int i = random.nextint (1000 ); if (I% 10 = = 0 ) {new Foo (). bar (); Thread.Sleep (5000 ); } } }}
publicclass Foo { publicvoidbar() { System.out.println("hello Foo."); }}
The code hot swap we're going to implement is to modify the method directly online Foo#bar
so that the output of the method hello HotSwapper.
is also equivalent to the thermal deployment feature. Here is the code for Hotswapper as debugger,
ImportCom.sun.jdi.Bootstrap;ImportCom.sun.jdi.ReferenceType;ImportCom.sun.jdi.VirtualMachine;ImportCom.sun.jdi.connect.Connector;ImportCom.sun.tools.jdi.SocketAttachingConnector;ImportJavassist. Classpool;ImportJavassist. Ctclass;ImportJavassist. Ctmethod; Public class hotswapper { Public Static void Main(string[] args)throwsexception{list<connector> connectors = Bootstrap.virtualmachinemanager (). Allconnectors (); Socketattachingconnector sac =NULL; for(Connector connector:connectors) {if(ConnectorinstanceofSocketattachingconnector) {sac = (socketattachingconnector) connector; } }if(SAC! =NULL) {map<string, connector.argument> defaultarguments = sac.defaultarguments (); Connector.argument Hostarg = Defaultarguments.get ("hostname"); Connector.argument Portarg = Defaultarguments.get ("Port"); Hostarg.setvalue ("localhost"); Portarg.setvalue ("8787"); Virtualmachine vm = Sac.attach (defaultarguments); list<referencetype> rtList = Vm.classesbyname ("Me.kisimple.just4fun.Foo"); Referencetype RT = Rtlist.get (0); Map<referencetype,byte[]> Newbytecodemap =NewHashmap<referencetype,byte[]> (1);byte[] Newbytecode = Gennewbytecode (); Newbytecodemap.put (RT, Newbytecode);if(Vm.canredefineclasses ()) {vm.redefineclasses (NEWBYTECODEMAP); } } }}
To use the VirtualMachine#redefineClasses
method, you need to get the bytecode of the Java class to be replaced, which is output by the pest genNewByteCode
method. Here are two ways to do this,
- Use Java Compiler API;
- Use javassist;
Javacompiler
The Java Compiler API is used in the following ways,
Private Static byte[]Gennewbytecodeusingjavacompiler()throwsException {Javacompiler compiler = Toolprovider.getsystemjavacompiler ();//Compiler.run (NULL, NULL, NULL, "E:\\projects\\just4fun\\src\\main\\java\\me\\kisimple\\just4fun\\foo.java") ;File Javafile =NewFile ("E:\\projects\\just4fun\\src\\main\\java\\me\\kisimple\\just4fun\\foo.java"); Standardjavafilemanager FileManager = Compiler.getstandardfilemanager (NULL,NULL,NULL); iterable<? Extends javafileobject> compilationunit = Filemanager.getjavafileobjectsfromfiles (Arrays.aslist (javaFile )); Compiler.gettask (NULL, FileManager,NULL,NULL,NULL, Compilationunit). Call (); File Classfile =NewFile ("E:\\projects\\just4fun\\src\\main\\java\\me\\kisimple\\just4fun\\foo.class"); InputStream in =NewFileInputStream (Classfile);byte[] buf =New byte[(int) Classfile.length ()]; while(In.read (BUF)! =-1) {}returnBuf }
In this way we need to change the source of Foo first,
publicclass Foo { publicvoidbar() { System.out.println("hello HotSwapper."); }}
Then run Hotswapper will use Javacompiler to recompile the modified source code, generate a new Foo.class file, and then use the file IO API to read into the class file to achieve our goal. Then we can see the output of the target VM as follows,
forat8787forat8787hello HotSwapper.hello HotSwapper.
The hot Swap, or thermal deployment, of the code is duly implemented.
When the class file is read into the byte array, there is a place to note that the byte[] buf = new byte[(int)classFile.length()];
size of the byte array can not be arbitrarily defined, or the following error will occur, the target VM will mistakenly assume that the entire byte array is the byte code of the class file,
in"main" java.lang.ClassFormatErrorin class file format com.sun.tools.jdi.VirtualMachineImpl.redefineClasses(VirtualMachineImpl.java:321)
Javassist
The Javassist API is much simpler to use.
privatestaticbytegenNewByteCodeUsingJavassistthrows Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("me.kisimple.just4fun.Foo"); CtMethod cm = cc.getDeclaredMethod("bar"); cm.setBody("{System.out.println(\"hello HotSwapper.\");}"); return cc.toBytecode(); }
In this way we do not need to modify the source files of Foo.
Hotswapper
In fact, in the javassist has achieved a hotswapper, through the source can also be seen, it is also the use of JPDA API to achieve hot swap.
Resources
- Http://openjdk.java.net/groups/compiler/guide/compilerAPI.html
- Http://docs.oracle.com/javase/7/docs/api/javax/tools/package-summary.html
JPDA (iii): implementation of code HOTSWAP