Proper hot deployment, a proposal
swd847 Apr 8, 2009 12:45 PMThere have been a lot of requests on this forum and in the jira for hot deployment of ejb's. I have a bit of a theory on why this has not been done yet, and why it would not be much of a time saver even if it was done. Hot deployment requires that the classloader containing the old classes be discareded, and new classes loaded into a different classloader, and then the deployment process run on these new classes. This means that we would not be hot deploying one ejb at a time but rather all of them in the archive (for a lot of people this would be all their classes), and it would probably take almost as long as a full redeploy by the time the ejb and seam deployers have finished doing their thing. I think the only way to solve this problem is to allow classes to be reloaded in the VM the same way javarebel does. I think I know how this can be acomplished and I have attached an outline below. If no one can see any show stopping technical reasons as to why this won't work I will try and implement it.
Even though it is easy to replace a class inside the JVM using the java instrumentation api, it does not let you change the signature after the class has been loaded. My idea of how to get around this limitation is to change classes before they are loaded in such a way as we can fake adding methods etc.
The use the instrumentation API our class must be compiled as a java agent and installed in the JVM at startup using the –javagent option. Transformation and class redefinition must be enabled in the agent’s manifest file.
This agent will provide an method call to allow classes to be replaced, the actual job of deciding which classes to replace and supplying the bytecode can be done by code outside the agent, and is not very difficult to implement.
This agent will supply a re-implementation of the reflection api that is aware of the structure of our instrumented classes. As classes are loaded they are transformed so all calls to the reflection api are redirected through this wrapper. The java instrumentation api provides a methods to inspect and transform clasess as they are loaded.
As classes that might be replace are loaded they have a method with the signature ‘Object myNewMethodImpl(int,Object[]) added to them. It is through this method that we will fake adding new methods to classes. The int parameter refers to the order that the new method was added (the first method we add is index 0), and the object array contains the parameters for the method call. Calls to a method that we have added are transparently redirected to this method, either through our reflection wrapper api or by transforming new classes as they are loaded (existing classes will not have a reference to this method, otherwise it would have been there at compile time and we would not need all this mucking around).
If the new class definition is missing a method definition that was present on the original class then a definition that throws a MethodNotFoundError is added to the class at load time.
If a method has been added the agent creates the bytecode for a whole new class, gives it a random name, and loads it into the JVM. This class has a single static method with the same signature and body as the new method on the class, except that it is static and the first argument is a reference to an instance of the class that is being replaced. The new method definition is removed from the original class and the myNewMethodImpl that was added at class load time has code added to it to call the newly created static method if the correct index is passed. Any classes that are loaded by the JVM from this point on will have calls to the newly added method routed through myNewMethodImpl instead.
Seam and ejb3 hot deploy will have to be layered on to of this through the use of call backs when a class is replaced, when this happens the framework will be responsible for generating new proxies, scanning for observer methods etc.
Fields, annotations, constructors etc can be added / removed / replaced in a similar manner to the way methods are added.
Fields would have an array of objects installed in the class definition to hold values of newly added fields, access to this field is turned into array access. To add constructors we would have to add a constructor MyConstructor(int,Object[]) and proceed in a similar manner to the way methods are added. Annotation support is added purely in the reflection wrapper library.
The state is stored in static variables inside the agent. This state is used by the reflection wrapper and the instrumentation process. System classes would not be instrumented, there is no need. Some classes (e.g. application server classes) would only need to be instrumented to rewrite reflection calls to the wrapper.
Can anyone see any technical reason why this would not work? It is going to involve a lot of bytecode instrumentation and may slow things down a little bit, but if it works it would give seam and ejb3 the same sort of turn around time php developers enjoy.
Even though it is easy to replace a class inside the JVM using the java instrumentation api, it does not let you change the signature after the class has been loaded. My idea of how to get around this limitation is to change classes before they are loaded in such a way as we can fake adding methods etc.
The use the instrumentation API our class must be compiled as a java agent and installed in the JVM at startup using the –javagent option. Transformation and class redefinition must be enabled in the agent’s manifest file.
This agent will provide an method call to allow classes to be replaced, the actual job of deciding which classes to replace and supplying the bytecode can be done by code outside the agent, and is not very difficult to implement.
This agent will supply a re-implementation of the reflection api that is aware of the structure of our instrumented classes. As classes are loaded they are transformed so all calls to the reflection api are redirected through this wrapper. The java instrumentation api provides a methods to inspect and transform clasess as they are loaded.
As classes that might be replace are loaded they have a method with the signature ‘Object myNewMethodImpl(int,Object[]) added to them. It is through this method that we will fake adding new methods to classes. The int parameter refers to the order that the new method was added (the first method we add is index 0), and the object array contains the parameters for the method call. Calls to a method that we have added are transparently redirected to this method, either through our reflection wrapper api or by transforming new classes as they are loaded (existing classes will not have a reference to this method, otherwise it would have been there at compile time and we would not need all this mucking around).
If the new class definition is missing a method definition that was present on the original class then a definition that throws a MethodNotFoundError is added to the class at load time.
If a method has been added the agent creates the bytecode for a whole new class, gives it a random name, and loads it into the JVM. This class has a single static method with the same signature and body as the new method on the class, except that it is static and the first argument is a reference to an instance of the class that is being replaced. The new method definition is removed from the original class and the myNewMethodImpl that was added at class load time has code added to it to call the newly created static method if the correct index is passed. Any classes that are loaded by the JVM from this point on will have calls to the newly added method routed through myNewMethodImpl instead.
Seam and ejb3 hot deploy will have to be layered on to of this through the use of call backs when a class is replaced, when this happens the framework will be responsible for generating new proxies, scanning for observer methods etc.
Fields, annotations, constructors etc can be added / removed / replaced in a similar manner to the way methods are added.
Fields would have an array of objects installed in the class definition to hold values of newly added fields, access to this field is turned into array access. To add constructors we would have to add a constructor MyConstructor(int,Object[]) and proceed in a similar manner to the way methods are added. Annotation support is added purely in the reflection wrapper library.
The state is stored in static variables inside the agent. This state is used by the reflection wrapper and the instrumentation process. System classes would not be instrumented, there is no need. Some classes (e.g. application server classes) would only need to be instrumented to rewrite reflection calls to the wrapper.
Can anyone see any technical reason why this would not work? It is going to involve a lot of bytecode instrumentation and may slow things down a little bit, but if it works it would give seam and ejb3 the same sort of turn around time php developers enjoy.