Reputation: 1740
I'm writting a web application that will run on Tomcat8, that should be able to update while it's still running.
In order to do that, it will create a new ClassLoader and load the whole API again on top of that, every time a given "reload" button is pressed.
// get the urls from the current loader
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
urls.addAll(Arrays.asList(loader.getURLs()));
// get the urls from the tomcat loader
loader = (URLClassLoader) loader.getParent();
urls.addAll(Arrays.asList(loader.getURLs()));
URL urlArray[] = new URL[urls.size()];
urlArray = urls.toArray(urlArray);
// my loader
loader = new URLClassLoader(urlArray, loader.getParent());
// this will throw ClassCastException
// because the newInstance will not return the System object
// that this loader knows
System newSystem = (System) loader.loadClass(System.class.getCanonicalName()).newInstance();
But! The problem begins when I need to call a shutdown
method of the system that's about do die.
If I try to store the "system" in a variable, to be able to call shutdown
later, I'll get a ClassCastException
because, for Java, the System class I've loaded from that other ClassLoader is not the same thing as the System class Tomcat knows about.
How could I call the System.shutdown()
I need from the servlet context?
Or is there a very different approach to handle this kind of situation?
Upvotes: 1
Views: 2787
Reputation: 3675
The issue seems to be that you have that class in multiple class loaders then - you should not have this class to load from your main class loader as then you would not be able to actually reload that code.
Load class by raw name like that:
System newSystem = (System) Class.forName("my.system.System", true, myClassLoader).newInstance();
newSystem.shutdown();
Or you can use reflections to call method too:
Class<?> systemClass = Class.forName("my.system.System", true, myClassLoader);
Method shutdown = systemClass.getMethod("shutdown");
Object newSystem = systemClass.newInstance();
shutdown.invoke(newSystem);
Or you could use java services, and have interface in your main class loader, and implementation only in that dynamic one you want to be able to reload: https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html
Upvotes: 1