I have one jar file in my application's class path. At run time, I add new classes to the jar file and sometimes also modify the fields/methods of the existing classes. Currently I am using URLClassLoader to load the classes dynamically. The new classes added dynamically are loaded correctly and I am able to use them at runtime. But it fails to reload the existing classes that are modified at runtime. I read many articles which states we need to explicitly handle reloading because class once loaded will not be reloaded until all the references to the class are destroyed. Also I tried out sample code that I found but none of them worked.
Can anyone suggest me a proper approach for reloading ? Any sample code for the same will be highly appreciated.
Normally to reload a class you need to unload the entire class loader. i.e. remove all references to all classes loaded for that class loader.
Another option is to use instrumentation to change the byte code of an existing class. This usually comes with limitations and changing fields is something you cannot do. i.e. the objects of that type would have to be translated somehow.
What I normally do is have services which are very quick to start/restart. This way to you easily restart a process which needs updated code ideally by pressing the Run in my IDE. This minimises deployment time as well.
In principle, a class that has already been loaded cannot be reloaded with the same classloader.
For a new load, it is necessary to create a new classloader and thus load the class.
Using URLClassLoader has one problem and that is that the jar file remains open.
If you have multiple classes loaded from one jar file by different instances of URLClassLoader and you change the jar file at runtime, you will usually get this error: java.util.zip.ZipException: ZipFile invalid LOC header (bad signature). The error may be different.
In order for the above errors not to occur, it is necessary to use the close method on all URLClassLoaders using the given jar file. But this is a solution that actually leads to a restart of the entire application.
A better solution is to modify the URLClassLoader so that the contents of the jar file are loaded into the RAM cache. This no longer affects other URLClassloaders that read data from the same jar file. The jar file can then be freely changed while the application is running. For example, you can use this modification of URLClassLoader for this purpose: in-memory URLClassLoader
Related
I have a use case where I need to dynamically load and share a predefined set of packages/classes between multiple classloaders. The goal is to increase overall performance every time we load a jar file.
We are trying to load and execute "apps". Each app contains our SDK libraries. Each app that is loaded is done so in it's own URLClassLoader to keep it isolated from other code as well as to prevent library version conflicts. Since our SDKs libraries are in each and every app, we would like to cache these specific sets of packages/classes so that the next time we load another app, all of the SDK classes do not need to be loaded again and thus removing this overhead.
Since we have several versions of our SDK out there, we are attempting to do this dynamically. Such that, when an app is loaded, we already know what version of the SDK it is. So we would like to have some implementation of a parent Classloader that is specific to this sdk version (we will call it the SDKCacheClassLoader). When the URLClassLoader (or some custom subclass of it) loads and recognizes a package/class from the sdk, we would like to forward that class to the shared parent SDKCacheClassLoader. Then, the next time we load another jar file with the URLClassLoader (setting SDKCacheClassLoader as the shared parent classloader), the parent should already have that class found and loaded from the last execution and the new jar file should not need to be scanned looking for the SDK classes again.
Anyone have any ideas on how to accomplish this goal?
Also, to be clear, I do not need to share instantiated objects, just simply the class definition since I understand that the JVM considers the same classes loaded by 2 different class loaders as completely separate classes.
I am developing a smartcard tester. The test case script files are written in java. In this tester, I have editors for editing the test case files. After editing, these files should be able to be compiled, loaded and instantiated.
I have finished the compilation work by using the JavaCompiler. The problem is about the loading. I used the URLClassLoader.newInstance to get a URLCLassLoader object and load my .class dynamically on the fly. It works well except that it wouldn't reload the class file even if the case file has been edited and compiled. It use the old version of the class file and give the old result. It loads the new class file only if i restart the tester.
Is there a way for me to control the reloading of class files by using the URLClassLoader?
Thanks a lot.
Once a class has been loaded, you generally can't modify that class instance. The only options are:
Create a new class loader, load a new copy of the class, and update all references to the old version of the class and any instances of the class. This can be difficult, which is why class loader memory leaks occur in application server environments.
Use JVMTI or Instrumentation to redefine the class bytes of an already loaded class. I'm not as familiar with JVMTI, but the Instrumentation.redefineClasses method has many restrictions:
The redefinition may change method bodies, the constant pool and
attributes. The redefinition must not add, remove or rename fields or
methods, change the signatures of methods, or change inheritance.
These restrictions maybe be lifted in future versions. The class file
bytes are not checked, verified and installed until after the
transformations have been applied, if the resultant bytes are in error
this method will throw an exception.
On save action of your editor, call your load class method.
Earlier I put my properties file within my classpath src/. Now I would like to put it within a folder called config/. This way the end users can actually modify the file by themselves.
However now my code below does not work anymore
ResourceBundle.getBundle("Messages", Locale.getDefault());
What I meant by the code doesn't work anymore is, after I deploy the application and I modify the Messages_en_US.properties the changes do not take place.
How can I achieve what I want ? Should I use getBundle at all ?
EDIT
I have added config folder into the classpath as well, but I am not sure if this is relevant. This is a plain Java application where I am not using Maven or any building tools.
By default, a ResourceBundle is only loaded when it is first requested, and reused for subsequent requests. You can throw away the cached ResourceBundles with ResourceBundle.clearCache();
Additionally, by default, ResourceBundles are loaded from the classpath. You must therefore ensure that the classloader in question does not cache the resource either. Or you can provide your own ResourceBundle.Control to load the properties file by whatever means you prefer.
if I have dira,jarb and dirc in the classpath in that order, and I have a java app with 3 classloaders with a parent/child/grandchild relationship, will they all read the same directory ?
I guess I am trying to figure out where each classloader looks... is there a way to find this path given an instance of the classloader ?
In general no, a classloader is permitted to construct bytes however it likes. E.g. the JSP classloader might invoke the JSP compiler dynamically if the JSP file has a recent timestamp.
Running the JVM with the -verbose:class flag will enable a lot of logging which should help you if you're just using the standard bootstrap classloaders.
If there's some custom classloader, you could supply your own URLConnectionFactory and see what URLs are being fetched.
You have actually several questions here.
The classes in the classpath directories and jars will usually be loaded by one classloader (the application classloader), not by several ones for each entry.
If you have classloaders in a parent-child-relationship, the child one should first ask its parent to load the class and only lookup the bytecode itself when the parent did not find anything. (There are special-purpuse classloaders in some frameworks which do this the other way around. If each class exists only once, then this should not make a difference.)
If you have an URLClassLoader, then you can ask its getURLs() method to find out from where it loads. For other classloaders, there may or may not be a way to find this.
Take a look at the ClassLoader API and you will realise there is a method that passes a name and eventually the class loader passes a byte[] to define the class. Because it is a proper class it can grab those bytes from anywhere it wants to. ClassLoader is just another public class anyone can implement their own implementation and do their own thing. ClassLoaders are everywhere, we have the version that reads the classpath system property, in tomcat we have another that reads from a war file, in osgi it reads from a jar file. Each does a few extra things besides simply reading some file and tahts the beauty and flexibility of classloading.
There is no method on ClassLoader that returns a String, because what would it return given the above mentioned CLassLoaders ? A file path, a jar file path ? etc
In general no, but in practice you often want to find out where some class, resource is being loaded from and you can do,
System.out.println(someClassLoader.getResource("someResource.txt"));
Even more useful, if you are looking to find which .class file a Class is from, do
Class c = SomeClass.class;
System.out.println(c.getResource(c.getSimpleName() + ".class"));
The above is not guaranteed to work if the .class file is generated dynamically, but works in most situations.
The intended machine doesn't have a connection to the internet and I do not want to load it using a fixed location.
Aim: To load DTDs from a jar, the jar will be a dependency.
To load any file from the classpath (it is, the space where youre classes reside, usually a bunch of jars) you can do:
InputStream is = this.getClass().getResourceAsStream("my/package/ResourceFile.dtd");
And then you can use the input stream where you want.
Note: getResourceAsStream() loads the resource using the class loader that loaded the class. If you are making an application any class from your application (and hence loaded by the same classloader with your jars) will be fine.
There is no standard way to provide a local cache (CATALOG if I recall correctly) of DTD's.
Hence, you will need to investigate the parser that will use the local copies, and use its non-standard configuration API to let it know about these local copies so that the trip to the net is avoided.
Use the getResourceAsStream() method to pick out entries from the classpath.