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

Why Tomcat Server does not needs restart if changes are done in servlet and JSP?
Tomcat is capable of adding/modifying classpath to Web Application classloader at runtime. Tomcat will be having their custom Classloader implementation which allows them to add the classpaths at runtime.a new classloader is created for the Servlet/JSP with Application classloader as parent classloader. And the new classloader will load the modified class again.

It is always best to reload the entire application incase changes are done to servlet. If you were simply to reload one class, in isolation, you might break dependencies or miss some initialization steps. Therefore, it’s much safer to reload the entire application clicking deploy option in tomcat server.JSPs on the other hand, when properly coded, shouldn’t have anything in them by markup text. So reloading a single JSP, without reloading the entire app, should be safe. By default, tomcat is started in development mode, which means JSP-derived servlets recompiled when a change is detected.

In the web.xml you need to set the below config

<servlet>
   .
   .
    <!-- Add the following init-param -->
    <init-param>
        <param-name>development</param-name>
        <param-value>true</param-value>
    </init-param>
   .
   .
   .
</servlet>

In the Server.xml reloadable should be set to true

<Context path="/simple" docBase="webapps/simple"  debug="0" reloadable="true" ></Context>

More on how CustomClass loader works for Hot Deployment here

How it Works

  1. We have JSON file with Location and Name of Factory Class
  2. Using Configuration.java we read the JSON File and read the Configuration
  3. We run TasteFoodFromMenu.java by supplying the JSON file Location as Input
  4. Now by the above method the JAR’s could be built independently and by changing the JSON file we could make changes and deploy the code without restarting the Server

For more detail refer here

Configuration.java

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

import com.fasterxml.jackson.databind.ObjectMapper;

public class Configuration {

    public static Configuration loadConfiguration(String fileName) throws IOException {    	
    	//Read Name of File
        Path path = FileSystems.getDefault().getPath(fileName);
        
        //Read Contents of File
        String contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
        
        ObjectMapper mapper = new ObjectMapper();
        
        //Map Location and FactoryType
        Configuration config = mapper.readValue(contents, Configuration.class);
        return config;
    }

    private String factoryType;
    private String location;


    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public String getFactoryType() {
        return factoryType;
    }

    public void setFactoryType(String factoryType) {
        this.factoryType = factoryType;
    }
}

TasteFoodFromMenu.java

public class TasteFoodFromMenu {
	  public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
	        Configuration configuration = Configuration.loadConfiguration(args[0]);
	        String location = configuration.getLocation();
	        URL url = new URL(location);
	        URLClassLoader ucl = new URLClassLoader(new URL[]{url});
	        Class<IFoodFactory> cls = (Class<IFoodFactory>) Class.forName(configuration.getFactoryType(), true, ucl);
	        IFoodFactory cameraFactory = cls.newInstance();
	        IFood camera = cameraFactory.prepareFood(); 
	        camera.serveFood();
	    }
}

config.json

{
  "factoryType": "com.mugil.food.ChineseFoodFactory",
  "location": "file:///D:/java/ReadConfFromJSON/lib/FoodMenu.jar"
}

Abstract Factory Pattern
The Abstract Factory Pattern consists of an AbstractFactory, ConcreteFactory, AbstractProduct, ConcreteProduct and Client. We code against AbstractFactory and AbstractProduct and let the implementation to others to define ConcreteFactory and ConcreteProduct

Why Abstract Factory Introduced?
Another layer of abstraction over factory pattern. Abstract Factory patterns work around a super-factory which creates other factories. Factory pattern deals with creating objects of a single type(AppleFactory manufactures Iphones), while the Abstract Factory pattern deals with creating objects of related types(MobileFactory manufactures Mobiles). Abstract Factory pattern is more robust and consistent and uses composition to delegate responsibility of object creation.

When to use Abstract Factory?
When higher level of Abstraction is required.Provides an interface for creating families of related objects

MobileManufactureFactory.java

public interface MobileManufactureFactory {
    public Mobiles getManufacturerDetails();
}

Mobiles.java

public interface Mobiles {
    public void getMobileModels();
}

AppleFactory.java

public class AppleFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Iphone();
    }
}

GoogleFactory.java

public class GoogleFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Android();
    }
}

MicrosoftFactory.java

public class MicrosoftFactory implements MobileManufactureFactory {
    @Override
    public Mobiles getManufacturerDetails() {
        return new Windows();
    }
}

IphoneMobiles.java

public class IphoneMobiles implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Iphone 13,14 and 15");
    }
}

AndroidMobiles.java

public class AndroidMobiles implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Oneplus, Realme, Samsung");
    }
}

WindowsMobile.java

public class WindowsMobile implements Mobiles {
    @Override
    public void getMobileModels() {
        System.out.println("Samsung Focus, Nokia Lumia, Pocket PC");
    }
}

MobileFactory.java

public class MobileFactory {
    private MobileFactory(){
    }

    public static MobileManufactureFactory getMobilesBasedOnManufacturer(MobileCompany manufacturerName){
        if(manufacturerName.equals(MobileCompany.APPLE)){
            return new AppleFactory();
        }else if(manufacturerName.equals(MobileCompany.MICROSOFT)){
            return new MicrosoftFactory();
        }else if(manufacturerName.equals(MobileCompany.GOOGLE)){
            return new GoogleFactory();
        }
        return null;
    }
}

MobileCompany.java

public enum MobileCompany {
    APPLE, MICROSOFT, GOOGLE
}

Client.java

public class Client {
    public static void main(String[] args) {
        MobileManufactureFactory objMobileManufa = MobileFactory.getMobilesBasedOnManufacturer(MobileCompany.APPLE);
        Mobiles objMobile = objMobileManufa.getManufacturerDetails();
        objMobile.getMobileModels();
    }
}

Output

 Iphone 13,14 and 15