private static void executeCliThread()

in src/main/java/software/amazon/smithy/gradle/SmithyUtils.java [253:335]


    private static void executeCliThread(Project project, List<String> arguments, FileCollection classpath) {
        // URL caching must be disabled when running the Smithy CLI using a
        // custom class loader. An "empty" resource was added to the Gradle
        // plugin to provide access to the global URL-wide caching behavior
        // provided by URLConnection#setDefaultUseCaches. Setting that to
        // false on any URLConnection disables caching on all URLConnections.
        // Not doing this will lead to consistent errors like
        // java.util.zip.ZipException: ZipFile invalid LOC header (bad signature)
        // The default caching setting is restored after invoking the CLI.
        URLConnection cacheBuster;
        boolean isCachingEnabled;
        try {
            cacheBuster = SmithyUtils.class.getResource("empty").openConnection();
            isCachingEnabled = cacheBuster.getDefaultUseCaches();
            cacheBuster.setDefaultUseCaches(false);
        } catch (IOException e) {
            throw new SmithyBuildException(e);
        }

        // Create a custom class loader to run within the context of.
        Set<File> files = classpath.getFiles();
        URL[] paths = new URL[files.size()];
        int i = 0;

        for (File file : files) {
            try {
                paths[i++] = file.toURI().toURL();
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }

        Logger logger = project.getLogger();

        // Need to run this in a doPrivileged to pass SpotBugs.
        try (URLClassLoader classLoader = AccessController.doPrivileged(
                (PrivilegedExceptionAction<URLClassLoader>) () -> new URLClassLoader(paths))) {

            // Reflection is used to make calls on the loaded SmithyCli object.
            String smithyCliName = SmithyCli.class.getCanonicalName();
            String cliName = Cli.class.getCanonicalName();

            Thread thread = new Thread(() -> {
                try {
                    Class cliClass = classLoader.loadClass(cliName);
                    Class smithyCliClass = classLoader.loadClass(smithyCliName);
                    Object cli = smithyCliClass.getDeclaredMethod("create").invoke(null);
                    smithyCliClass.getDeclaredMethod("classLoader", ClassLoader.class).invoke(cli, classLoader);
                    overrideCliStdout(cliClass, logger);
                    smithyCliClass.getDeclaredMethod("run", List.class).invoke(cli, arguments);
                } catch (ReflectiveOperationException e) {
                    logger.info("Error executing Smithy CLI (ReflectiveOperationException)", e);
                    throw new RuntimeException(e);
                }
            });

            // Configure the thread to re-throw exception and use our custom class loader.
            thread.setContextClassLoader(classLoader);
            ExceptionHandler handler = new ExceptionHandler();
            thread.setUncaughtExceptionHandler(handler);
            thread.start();
            thread.join();

            if (handler.e != null) {
                logger.info("Error executing Smithy CLI (thread handler)", handler.e);
                throw handler.e;
            }

        } catch (Throwable e) {
            // Find the originating exception message.
            String message;
            Throwable current = e;
            do {
                message = current.getMessage();
                current = current.getCause();
            } while (current != null);
            logger.error(message);
            throw new GradleException(message, e);
        } finally {
            // Restore URL caching to the previous value.
            cacheBuster.setDefaultUseCaches(isCachingEnabled);
        }
    }