Below is a code example of CustomClass Loader which Servers use internally for HotCode Swap without restarting the server.When you change the Quote in ServerImpl.java file the file should be reloaded by selecting the RELOAD option while running Client.java
IServer.java
public interface IServer { public String getQuote(); }
ServerImpl.java
public class ServerImpl implements IServer{ @Override public String getQuote() { return "Its Working Man"; } }
Client.java
import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.io.BufferedReader; import java.io.InputStreamReader; public class Client { static ClassLoader cl; static IServer server; public static void reloadServer() throws Exception { URL[] urls = new URL[]{new URL("file:///D:/java/HotDeplyment/appclasses")}; System.out.println("Reloaded....."); cl = new URLClassLoader(urls); server = (IServer) cl.loadClass("com.mugil.org.ServerImpl").newInstance(); } public static void main(String [] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); reloadServer(); while (true) { System.out.print("Enter QUOTE, RELOAD, or QUIT: "); String cmdRead = br.readLine(); String cmd = cmdRead.toUpperCase(); if (cmd.equals("QUIT")) { return; } else if (cmd.equals("QUOTE")) { System.out.println( server.getQuote()); } else if (cmd.equals("RELOAD")) { reloadServer(); } } } }
The Above code is not working as windows is not clearing cached .class files or JAR files. So the alternative is to try with the below Custom Class Loader(MyURLClassLoader) which in turn extends URLClassLoader again.
MyURLClassLoader.java
import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; import java.util.Collection; import java.util.jar.JarFile; public class MyURLClassLoader extends URLClassLoader { public MyURLClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } /** * Closes all open jar files */ public void close() { try { Class clazz = java.net.URLClassLoader.class; Field ucp = clazz.getDeclaredField("ucp"); ucp.setAccessible(true); Object sunMiscURLClassPath = ucp.get(this); Field loaders = sunMiscURLClassPath.getClass().getDeclaredField("loaders"); loaders.setAccessible(true); Object collection = loaders.get(sunMiscURLClassPath); for (Object sunMiscURLClassPathJarLoader : ((Collection) collection).toArray()) { try { Field loader = sunMiscURLClassPathJarLoader.getClass().getDeclaredField("jar"); loader.setAccessible(true); Object jarFile = loader.get(sunMiscURLClassPathJarLoader); ((JarFile) jarFile).close(); } catch (Throwable t) { // if we got this far, this is probably not a JAR loader so skip it } } } catch (Throwable t) { // probably not a SUN VM } return; } }
In the below code the CustomClass Loader is called to load the classes which inturn calls the Super Class loader which is again Loaders from URL Class Loader.Once it is done we have defined a custom close method which closes the JAR files or .class files which is loaded by class loader.
TestClassLoader.java
package com.mugil.org; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; public class TestClassLoader { static ClassLoader cl; static IServer server; public static void main(String[] args) throws Exception { while(true) { try { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); TestClassLoader obj = new TestClassLoader(); obj.loadAndInstantiate(); System.out.println(server.getQuote()); } catch (Exception e){ } finally { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } void loadAndInstantiate() throws Exception { MyURLClassLoader cl = null; try { File file = new File("D:\\java\\HotDeplyment\\bin\\Sample.jar"); String classToLoad = "com.mugil.org.ServerImpl"; URL jarUrl = new URL("file:" + file.getAbsolutePath()); cl = new MyURLClassLoader(new URL[] {jarUrl}, getClass().getClassLoader()); Class loadedClass = cl.loadClass(classToLoad); Object o = loadedClass.getConstructor().newInstance(); server = (IServer) o; } finally { if(cl != null) cl.close(); } } }
Since the infinite while loop is called indefinitely with the thread sleep interval of every 3 seconds we replace the JAR file in the middle which takes the class from the new JAR file loaded.You need to change the ServerImpl.java file and build the JDK before you want to see the changes
Output
. . Its Working Man Its Working Man Its Working Man Its Working Man Its Working Its Working Its Working . .
The Above code is not working either