// Path: java.multiversion/v1/src/main/sample/multiversion/Core.java
// public interface Core {
// 
//     String getVersion();
//     String getDependencyVersion();
// }
// 
// Path: java.multiversion/v3/src/main/sample/multiversion/ImportantCore.java
// public class ImportantCore implements Core {
// 
//     private Utility utility;
//     private CoreDependency coreDependency;
// 
//     public ImportantCore() {
//         utility = new Utility();
//         coreDependency = new CoreDependency();
//     }
// 
//     public String getVersion() {
//         return utility.getVersion();
//     }
// 
//     public String getDependencyVersion() {
//         return coreDependency.getVersion();
//     }
// }


import sample.multiversion.Core;
import sample.multiversion.ImportantCore;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
package sample.multiversion.versiontext;



public class TestMain {

    public static void main(String[] args)
            throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        // multiple versions of the same library to be used at the same time
        URL v1 = Paths.get("./../v1/build/libs/v1.jar").toUri().toURL();
        URL v2 = Paths.get("./../v2/build/libs/v2.jar").toUri().toURL();

        // library dependencies
        URL v1Dep = Paths.get("./../v1dep/build/libs/v1dep.jar").toUri().toURL();
        URL v2Dep = Paths.get("./../v2dep/build/libs/v2dep.jar").toUri().toURL();

        /**
         * version 1 and 2 loaders
         * - these loaders do not use the root loader - Thread.currentThread().getContextClassLoader()
         * - using the root loader new URLClassLoader(new URL[]{v1, v1Dep}, Thread.currentThread().getContextClassLoader());
         * will solve any class with the root loader and if no class is found then the child loader will be used
         * - because version 3 is loaded from classpath, the root loader should not be used to load version 1 and 2
         * - null needs to be passed to parent argument, else will not work
         */
        URLClassLoader loaderV1 = new URLClassLoader(new URL[]{v1, v1Dep}, null);
        URLClassLoader loaderV2 = new URLClassLoader(new URL[]{v2, v2Dep}, null);

        /**
         * Use custom class loader for loading classes first from children and last from parent
         */
        ParentLastClassLoader loaderV1Alt = new ParentLastClassLoader(new URL[]{v1, v1Dep});
        ParentLastClassLoader loaderV2Alt = new ParentLastClassLoader(new URL[]{v2, v2Dep});
        ParentLastClassLoader loaderV3Alt = new ParentLastClassLoader(new URL[]{});

        // get class from loader
        Class<?> coreV1Class = loaderV1.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV2Class = loaderV2.loadClass("sample.multiversion.ImportantCore");

        // get class from loader - custom version
        Class<?> coreV1AltClass = loaderV1Alt.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV2AltClass = loaderV2Alt.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV3AltClass = loaderV3Alt.loadClass("sample.multiversion.ImportantCore");

        // create class instance
        Object coreV1Instance = coreV1Class.newInstance();
        Object coreV2Instance = coreV2Class.newInstance();

        // create class instance - obtained from custom class loader
        Object coreV1AltInstance = coreV1AltClass.newInstance();
        Object coreV2AltInstance = coreV2AltClass.newInstance();

        // note that this is loaded from classpath
// Path: java.multiversion/v1/src/main/sample/multiversion/Core.java
// public interface Core {
// 
//     String getVersion();
//     String getDependencyVersion();
// }
// 
// Path: java.multiversion/v3/src/main/sample/multiversion/ImportantCore.java
// public class ImportantCore implements Core {
// 
//     private Utility utility;
//     private CoreDependency coreDependency;
// 
//     public ImportantCore() {
//         utility = new Utility();
//         coreDependency = new CoreDependency();
//     }
// 
//     public String getVersion() {
//         return utility.getVersion();
//     }
// 
//     public String getDependencyVersion() {
//         return coreDependency.getVersion();
//     }
// }


// Path: java.multiversion/versiontest/src/main/sample/multiversion/versiontext/TestMain.java
import sample.multiversion.Core;
import sample.multiversion.ImportantCore;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;

package sample.multiversion.versiontext;



public class TestMain {

    public static void main(String[] args)
            throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        // multiple versions of the same library to be used at the same time
        URL v1 = Paths.get("./../v1/build/libs/v1.jar").toUri().toURL();
        URL v2 = Paths.get("./../v2/build/libs/v2.jar").toUri().toURL();

        // library dependencies
        URL v1Dep = Paths.get("./../v1dep/build/libs/v1dep.jar").toUri().toURL();
        URL v2Dep = Paths.get("./../v2dep/build/libs/v2dep.jar").toUri().toURL();

        /**
         * version 1 and 2 loaders
         * - these loaders do not use the root loader - Thread.currentThread().getContextClassLoader()
         * - using the root loader new URLClassLoader(new URL[]{v1, v1Dep}, Thread.currentThread().getContextClassLoader());
         * will solve any class with the root loader and if no class is found then the child loader will be used
         * - because version 3 is loaded from classpath, the root loader should not be used to load version 1 and 2
         * - null needs to be passed to parent argument, else will not work
         */
        URLClassLoader loaderV1 = new URLClassLoader(new URL[]{v1, v1Dep}, null);
        URLClassLoader loaderV2 = new URLClassLoader(new URL[]{v2, v2Dep}, null);

        /**
         * Use custom class loader for loading classes first from children and last from parent
         */
        ParentLastClassLoader loaderV1Alt = new ParentLastClassLoader(new URL[]{v1, v1Dep});
        ParentLastClassLoader loaderV2Alt = new ParentLastClassLoader(new URL[]{v2, v2Dep});
        ParentLastClassLoader loaderV3Alt = new ParentLastClassLoader(new URL[]{});

        // get class from loader
        Class<?> coreV1Class = loaderV1.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV2Class = loaderV2.loadClass("sample.multiversion.ImportantCore");

        // get class from loader - custom version
        Class<?> coreV1AltClass = loaderV1Alt.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV2AltClass = loaderV2Alt.loadClass("sample.multiversion.ImportantCore");
        Class<?> coreV3AltClass = loaderV3Alt.loadClass("sample.multiversion.ImportantCore");

        // create class instance
        Object coreV1Instance = coreV1Class.newInstance();
        Object coreV2Instance = coreV2Class.newInstance();

        // create class instance - obtained from custom class loader
        Object coreV1AltInstance = coreV1AltClass.newInstance();
        Object coreV2AltInstance = coreV2AltClass.newInstance();

        // note that this is loaded from classpath
        Core coreV3Instance = new ImportantCore();