@Override
    public int run(String launcherVersion, String launcherMd5, ServerUrlGenerator urlGenerator, Map<String, String> env, Map<String, String> context) {
        int exitValue = 0;
        LOG.info("Agent launcher is version: {}", CurrentGoCDVersion.getInstance().fullVersion());
        String[] command = new String[]{};

        try {
            AgentBootstrapperArgs bootstrapperArgs = AgentBootstrapperArgs.fromProperties(context);

            ServerBinaryDownloader agentDownloader = new ServerBinaryDownloader(urlGenerator, bootstrapperArgs);
            agentDownloader.downloadIfNecessary(DownloadableFile.AGENT);

            ServerBinaryDownloader pluginZipDownloader = new ServerBinaryDownloader(urlGenerator, bootstrapperArgs);
            pluginZipDownloader.downloadIfNecessary(DownloadableFile.AGENT_PLUGINS);

            ServerBinaryDownloader tfsImplDownloader = new ServerBinaryDownloader(urlGenerator, bootstrapperArgs);
            tfsImplDownloader.downloadIfNecessary(DownloadableFile.TFS_IMPL);

            command = agentInvocationCommand(agentDownloader.getMd5(), launcherMd5, pluginZipDownloader.getMd5(), tfsImplDownloader.getMd5(),
                    env, context, agentDownloader.getExtraProperties());
            LOG.info("Launching Agent with command: {}", join(command, " "));

            Process agent = invoke(command);

            // The next lines prevent the child process from blocking on Windows

            AgentOutputAppender agentOutputAppenderForStdErr = new AgentOutputAppender(GO_AGENT_STDERR_LOG);
            AgentOutputAppender agentOutputAppenderForStdOut = new AgentOutputAppender(GO_AGENT_STDOUT_LOG);

            if (new SystemEnvironment().consoleOutToStdout()) {
                agentOutputAppenderForStdErr.writeTo(AgentOutputAppender.Outstream.STDERR);
                agentOutputAppenderForStdOut.writeTo(AgentOutputAppender.Outstream.STDOUT);
            }

            agent.getOutputStream().close();
            AgentConsoleLogThread stdErrThd = new AgentConsoleLogThread(agent.getErrorStream(), agentOutputAppenderForStdErr);
            stdErrThd.start();
            AgentConsoleLogThread stdOutThd = new AgentConsoleLogThread(agent.getInputStream(), agentOutputAppenderForStdOut);
            stdOutThd.start();

            Shutdown shutdownHook = new Shutdown(agent);
            Runtime.getRuntime().addShutdownHook(shutdownHook);
            try {
                exitValue = agent.waitFor();
            } catch (InterruptedException ie) {
                LOG.error("Agent was interrupted. Terminating agent and respawning. {}", ie.toString());
                agent.destroy();
            } finally {
                removeShutdownHook(shutdownHook);
                stdErrThd.stopAndJoin();
                stdOutThd.stopAndJoin();
            }
        } catch (Exception e) {
            LOG.error("Exception while executing command: {} - {}", join(command, " "), e.toString());
            exitValue = EXCEPTION_OCCURRED;
        }
        return exitValue;
    }
@Test
    @DisabledOnOs(OS.WINDOWS)
    public void shouldDownload_TfsImplJar_IfTheCurrentJarIsStale() throws Exception {
        File staleFile = randomFile(TFS_IMPL_JAR);
        long original = staleFile.length();

        AgentProcessParentImpl bootstrapper = createBootstrapper(new ArrayList<>());
        bootstrapper.run("launcher_version", "bar", getURLGenerator(), Map.of(AgentProcessParentImpl.AGENT_STARTUP_ARGS, "foo bar  baz with%20some%20space"), context());

        assertThat(staleFile.length(), not(original));
    }