This article was originally written by Artem Karpenko, Java Developer, SMISS Ltd
We have a server application that saves data and receives requests for reading/modification from clients using API based on Java. Requests are objects of a class that implements definite interface (lets call this class “Request”). Upon request the object is serialized for network transmission. The request may be absent on the opposite side of class code, according to internal interaction protocol appropriate bytecode is requested from the client when necessary. After that the new class is uploaded on server, request is successfully deserialized and performed.
Problems may occur when we need to edit Request. As the class is uploaded on server already, it will perform old code, in spite of the fact that there is new version available on the client. Hot code swap is not realized on server.
In case of rare editing, the usual solution may be server rebooting. But if the class is in the process of development/testing, it may need too frequent server rebooting, that is inconvenient, especially if server is shared among developers.
Another possible variant – rename class before testing. For example, index may be added to the name (Request 1, Request 2, …). It is not convenient to do it manually, but I used this approach in the solution described in this article.
My first idea was to use some library for dynamic request proxying, for example, JDK proxy or cqlib. My aim was to get new name for proxy class every time and load it as new class of the type RequestProxy$1. Both libraries are appropriate for this task, so we tried both of them.
But only after approbation we found out that it won't work that way. As, in spite of the fact that proxy is new every time, base class Request, that includes edited code, stays with the same name. In such way server will load Request Proxy$1 for the first request and its ancestors: Request, and maybe others. At the second request server loads RequestProxy$2, but it doesn't load its ancestors, as their names didn't change. In such way every time initial code will be performed.
So we have only one very direct variant left – simply rename the class. Of course, we can't just rename it as a file. But we can try creating its copy with other name. Unfortunately, I didn't manage to find the way to do it simply with the help of cqlib. And I used a wonderful library Javassist.
So, next code created a class copy with new random name:
// cls — original class ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get(cls.getCanonicalName()); cc.setName(cls.getCanonicalName() + "Copy" + rnd.nextInt(Integer.MAX_VALUE)); Class queryClass = cc.toClass();
queryClass now points at the initial class copy with the name like RequestCopy123.
The peculiarity of the client part of this library is that for class bytecode transmission it finds class-file of the requested class on the disk and reads it by itself. That's why in our case it is necessary to generate new class-file and update class loader:
I'm sure that described solution may be useful not only in this specific situation, but also in other domains connected with testing or dynamic class loading.
You may download a file containing class-realization of described approach with some improvements like cleaning of folder with created classes after JVM process is finished and parameterized type to avoid manual adduction.
In fact, the described solution is an automation of process of manual renaming of class, when it needs to be tested after editing. That's why it also has disadvantages like manual approach. For instance, old class versions are left in memory of server part and in the case of long time testing may cause overflow of PermGen. That's why when testing is over and you have more or less stable class version, server should be rebooted. It follows that this approach should be used in production code very cautiously, only when the number of class request/copying is small.