• Post Reply Bookmark Topic Watch Topic
  • New Topic

Unload or Reload a Dymnically Loaded Class  RSS feed

 
Bartender
Posts: 1603
232
Android Angular Framework Eclipse IDE Java Linux MySQL Database Redhat TypeScript
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am building a telephony routing application which includes a digit translation and routing engine. The engine uses a number of rules provisioned in a database to make decisions on how to route (or reject) calls and how calling attributes such as calling-party number, called-party number calling-line-id should be altered before being routed to the next hop. Some decisions are too complex to describe in the database-provisioned rules and/or rely on information outside of the application. To support this, there is a rule type which allows a plug-in to be called to assist in the decision-making and attribute manipulation. The routing application needs to be highly available, so I decided to dynamically load the plug-ins rather than bundle them with the application so that they could be loaded (and unloaded) without the need to stop and start the application.

The class below is responsible for loading the plug-in JARs s from a directory, and making them available to the routing engine when needed. It loads the plug-ins when instantiated, and attempts to reload the set of plug-in when requested.

What I am finding is, that when I try to reload the plug-ins, that the old classes stay loaded and do not get updated. I assumed that if I removed the entries from the registry map, and made sure that there were no references to any plug-in class objects, that the class loader instances would have no more references, and the class loaders would go-away, and that I would be able to load the new versions of the plug-ins.

Working with class loaders is new for me, so any hints or words of wisdom will be appreciated.

 
Rancher
Posts: 42972
73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
From a quick look it seems you're always using the same class loader - that will not work, as you can't load an updated version of a class through the same class loader. Each time you load the jar file(s) you need to instantiate a new class loader instance for that. Even then it will only work if all the classes loaded through the old class loader have been unloaded, and the previously used class loader as well (which can be tricky to make happen). You may want to check out OSGi for this - reloading classes at runtime is one of the main points of using it.
 
Ron McLeod
Bartender
Posts: 1603
232
Android Angular Framework Eclipse IDE Java Linux MySQL Database Redhat TypeScript
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In line #95, my intent is to instantiate a new class loader for each class found in each JAR file. I'll take a look at OSGi .

Thanks.

 
Ulf Dittmer
Rancher
Posts: 42972
73
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ah, I missed that part. But still, the previously used classloader needs to be unloaded first, and I'm not sure there is a definite series of steps one can execute that ensures this will happen.

Be aware that OSGi has a non-trivial learning curve. You may want to use it via a container such as Apache Karaf.
 
Ranch Hand
Posts: 121
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What happens with TranslationPlugins loaded by getPlugin method? You cannot update code of already existing classes that way. All you can do is to create new plugin instances with a new code. But all previous instances would use old code. Moreover, that old instances mark corresponding classes as used and ineligible for Garbage Collection. This in turn marks a classloaders as ineligible for GC. To unload a classloader you should first ensure that there are no instances of classes loaded by that classloader. And some additional flags may be required to allow classes collection, read about CMSClassUnloading. Remember, all that can't reload code of existing class/object. It can only help you to remove unneeded classes and classloaders from memory.

Maybe there is some way to update class files on-the-fly using JVMTI, but I do not recommend this for a production environment.
 
Ron McLeod
Bartender
Posts: 1603
232
Android Angular Framework Eclipse IDE Java Linux MySQL Database Redhat TypeScript
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Maxim Karvonen wrote:What happens with TranslationPlugins loaded by getPlugin method?

The instances are used briefly, and then discarded.

I did finally find the issue which was causing the problem. I ignored a warning for line #95 (ClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);), stating: Resource Leak 'classLoader' is never closed. It turns-out this actually needed to be addressed - because the class loader would never unload as long as there was a dangling reference.

Since there is no ClassLoader method to explicitly close it, I changed my class loader object to be an instance of URLClassLoader (which implements AutoCloseable), and used a try-with-resources statement to instantiate the class loader object (try (URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader)) { ... }), so that it would be automatically closed at the end of the block. It worked!

My next step is to clean-up what I have done, and add a directory watcher thread to automatically register and de-register plug-ins as they are added/removed/replaced.

Ron
 
Maxim Karvonen
Ranch Hand
Posts: 121
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, Ron.

Thank you for sharing your solution! I never noticed that close method before.
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!