Dynamic program updating, runtime compilation, and class loaders - java

I have an application that needs the ability to update parts of itself (one class at a time) without stopping and restarting. With the JavaCompiler API, it is straightforward to generate modified class source code, recompile, load, and instantiate a class. I can do this all in memory (no files read from disk or net).
The application will never instantiate more than one object of such a class. There will only be two or three references to that object. When the modified class is loaded and instantiated, all those references will be changed to the new object. I can also probably guarantee that no method in the affected class is running in another thread while loading the modified class.
My question is this: will my class loader have problems loading a modified class with the same name as a class it previously loaded?
If I do not explicitly implement a cache of loaded classes in the class loader, would that avoid problems? Or could delegation to a parent class loader still cause a problem?
I hope to use a single instance of my class loader, but if necessary, I could instantiate a new one each time I update a class.
Note: I looked at OSGI and it seems to be quite a bit more than I need.

There's a useful example on this at http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html
We do quite a bit of dynamic class reloading ourselves (using Groovy for compilations). Note if you've got class dependencies then you may need recompile these dependencies on reload. In dev stacks we keep a track of these dependencies and then recompile whenever dependencies become stale. In production stacks we opted for a non-reloading ClassLoader and create a new ClassLoader when ever anything changes. So you can do it either way.
BTW - you might find the GroovyScriptEngine at http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.groovy/groovy-all/1.8.5/groovy/util/GroovyScriptEngine.java#GroovyScriptEngine very interesting if you want to dig around how they do it.

Okay, it should work: when you load the new class, it will replace the class name in the appropriate tables, and the memory should be GC'd. That said, I'd give it a strenuous test with a real short program that compiles a nontrivial class and replaces it, say 10,000 times.

Related

Eclipse refactor to add superclass to type hierarchy

I've read the different options that are listed in the manual for refactoring here but I don't think what I want is there.
I have a project with many batch jobs which are classes that extend org.quartz.Job to which I of course I can't make changes. As I find myself writing a bunch of the same code in each job to do things specific to my system, it becomes apparent that I should have an abstract class that is the parent of all my batch jobs and does those system-specific tasks, let's call that org.mycompany.MySystemJob which will extend org.quartz.Job
I would've imagined that this refactoring would be readily available, where all the classes that are directly extending from class A are made to extend from a new class B which is created by the refactor and made to extend A.
I've considered "extract superclass" but this is actually a lot simpler, since the methods would be from a single source and the "extract superclass" refactor asks you to manually add all the "sibling" classes which in this case are all real siblings already.
I know I can find all such classes in the type hierarchy and then change a single line on each. It is not that hard, sure, but it seems to me that saving this kind of manual work is exactly what refactoring is supposed to be.
Anyway, maybe I am just missing what is there. I hope so. Thanks.
The trick is to create a class/interface with the same qualified name as the library class/interface which can not be changed. If this class/interface is moved or renamed, all references that originally related to the class/interface of the library are automatically updated to this newly created class/interface.
Consider also to use composition over inheritance instead.

Calling Class.forName() twice

I am currently reading Class loader and their hierarchy functionality.
If I call below code -
ClassA a= Class.forName("com.test.ClassA")
As per my understanding,now it will be initialized and loaded in memory with help of Application Class loader.
I have below questions:
What will happen if I call again call the above code? Will there be new instance created in memory for 'ClassA' or will it return the same loaded class reference?
If yes, as per this post of javarevisited,"By using separate ClassLoader you can also loads same class from multiple sources and they will be treated as different class in JVM"
What will be use of it?
You will get the same class. Just test it. Load it a second time and check if a1 == a2.
The most frequent usage of this feature is probably in app servers: you can deploy several web applications in a single server, and all can use the same classes. But they shouldn't share static variables. And if one uses class Foo from library 1.0, and the other one uses class Foo from library 2.0, there should be no problem. Hence the need to load the same class with different class loaders.
New instance is created only if you call:
ClassA inst = new ClassA();
If you call Class.forName, the class definition (metadata and bytecode) is loaded into JVM, into special section of managed memory called HEAP. Usually, the applications use this function to preload class into JVM, so that later on there is no latency when application needs it.
The use is hotdeploy for example. You're debugging java web server. You find a mistake, and just want to change (reload) one class, not the whole application. Important point here is also "Different Class Loaders". That means that libraries in java can be loaded from different sources: jar, war (archives), from database, from network. Compare this to COM model of Windows, where library must be in same folder or /system32/ folder.

How to reset loaded classes fast in java?

I'm using a custom classloader to load some java classes. I need to execute some methods from these loaded classes in a loop. For each loop iteration I need a fresh initialization of all the classes (all static fields). I have measured that the execution time is three times slower if I use a new classloader for each iteration than the execution time when not using a fresh classloader in each iteration.
Can I reset the loaded classes to their initial state without loading them with a new classloader?
Or is there a way to speed up recurring loading of the same classes in different classloaders?
When you load the class with a new classloader, the JMV will almost certainly have to re-jit the byte code. Until it does, the first uses of the newly loaded classes will be slower.
I assume these classes are library code which you cannot modify? Because the fact that you're having to use the classes in this way suggests flawed design to me.
Just from the top of my head: can you take a snapshot of the initial state of the class using reflection and then restoring it?

Getting class cast exception where both classes are exactly the same

I am doing a JBoss SEAM project and when I view a form I get this error.
java.lang.ClassCastException:
it.cogitoweb.csi.entity.csiorelav.CsiTipoLav cannot be cast to
it.cogitoweb.csi.entity.csiorelav.CsiTipoLav
Its alway the same JPA class which is related to the form which is shown on the screen, it doesn't make sense to me why is it the same class, it seems impossible.
This happens when two different ClassLoader objects load classes with the same name. The equality of two classes in Java depends on the fully qualified name and the class loader that loaded it.
So if two independent class loaders load classes from the same location, then objects of those types will not be able to be cast to each others type, even if their classes are called the same.
As Joachim explained earlier, java.lang.ClassCastException typically occurs when two classloaders load the classes with the same name. However, i have come across another situation when this could occur.
This could occur with some IDE's that automatically reloads classes that have been modified. In such cases there might be older versions of the class retained in memory causing ClassCastException.
Here are a few ways you could resolve this issue :
If you are writing a custom class loader, while loading a class make sure that the base/default class loader does not already have an instance of that class loaded.
Make the class being loaded a sub-class of the class that is already loaded by the default class loader.
Make the class being loaded implement an interface that is already loaded by the default class loader.
More info here - http://www.jspwiki.org/wiki/A2AClassCastException
This is because the class has been loaded by two different classloaders. You cannot cast between them.
You've likely got a duplicate copy of CsiTipoLav in your application, and the two different copies are being loaded at different times from different classloaders. JBoss has a plethora of different classloaders in a hierarchy, and it's easy to get things in a twist.
Make sure you only have one copy of the class.
The object you are trying to cast, is loaded by a different classloader than the one which has loaded the class you are trying to cast into.
In my case i had two different *.ear and wanted to load a class from the other.
So i had to isolate the classloader. I used this description:
http://www.thorgull.be/wiki/index.php?title=ClassLoader_isolation_in_JBOSS
It worked for me.

Problem with static attributes

My problem is that I'm working on a project that requires me to run multiple instances of someone elses code which has many static attributes/variables, which causes all the instances to share those resources and, well, crash. I can run multiple instances of this other person's program if I create a .jar file off of it and open it multiple times by running the .jar in windows, but running calling the "main" method multiple times in my code (which is what I need to do) won't work.
I thought about creating a .jar and using Runtime.getRuntime().exec( "myprog.jar" ); to call the program multiple times, but that won't work for me since I have to pass an instance of my object to this new program and I don't think this solution would allow for that.
PS: This is also posted in the Sun forums, so I`ll post the answer I get there here or the answer I get here there naturally giving proper credit once I this is solved =P.
Remember that a static element in Java is unique only in the context of a classloader (hierarchy); a class is uniquely identified in a JVM by the tuple {classloader, classname}.
You need to instantiate isolated classloaders and load the jar using that class loader. Each loaded class (and thus statis elements) are unique in their classloader and will not interfere with one another.
I'd say you have three alternatives:
Refactor the legacy application so that it doesn't use static attributes. If you can do this, this may be the best solution in the long term.
Continue with your approach of launching the legacy application in a separate JVM. There are a number of ways that you can pass (copies of) objects to another JVM. For example, you could serialize them and pass them via the child processes input stream. Or you could stringify them and pass them as arguments. In either case, you'll need to create your own 'main' class/method that deals with the object passing before calling the legacy app.
I think you should be able to use classloader magic to dynamically load a fresh copy of the legacy application each time you run it. If you create a new classloader each time, you should get a fresh copy of the legacy application classes with a separate set of statics. But, you have to make sure that the legacy app is not on your main classpath. The problem with this approach is that it is expensive, and you are likely to create memory leaks.
The description is a little confusing.
If you are running the code multiple times, you are running multiple independent processes, each running in its own JVM. There is no way that they are actually sharing the values of their static fields. Java doesn't let you directly share memory between multiple VMs.
Can you elaborate more (ideally with examples and code) what the attributes are defined as and what kind of failures you are getting? This may be completely unrelated to them being static.
In particular, what exactly do you mean by shared resources? What resources are your programs sharing?
The proper approach was already suggested - using custom ClassLoaders. Another thing comes to my mind, which might seem ugly, but will probably do, and is a bit more object-oriented approach.
The legacy code is used for its operations, and it incorrectly uses static instead of instance variables. You can fix that using inheritance and reflection:
create (or reuse) an utility class that copies instance variables to static ones
extend the classes in question and provide the same instance variables as the static ones
override all methods. In the overriding methods use the utility to copy the state of the current object to the static variables, and then delegate to (call) the super methods.
Then start using instance of your class, instead of the legacy ones. That way you will simulate the proper behaviour.
Have in mind this is NOT thread-safe.

Categories