1

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?

4
  • Maybe you should use service for that? docs.oracle.com/javase/tutorial/sound/SPI-intro.html so interface would be in your normal class loader, but implementation inside that dynamic one. Commented Jul 17, 2018 at 8:50
  • And what is that system class? Commented Jul 17, 2018 at 8:51
  • @GotoFinal It's a class that, when loaded, loads and builds the rest of the system on top of it, since I don't use java.lang.System I saw no problem. But how do I pass such Interface down the chain? Assuming I still use different class loaders (which I guess I cant avoid). Commented Jul 17, 2018 at 11:14
  • This interface should be separate from your code you want to be able to reload, and this one will not be able to change like that with rest of them. It would need to be in that main/normal class loader. Commented Jul 17, 2018 at 11:22

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Using the Class.forName I can't isolate the static environment of the system. It seems I'll have to rely on reflection...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.