@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
    public void shouldStartSubprocess_withOverriddenArgs() throws InterruptedException {
        final List<String> cmd = new ArrayList<>();
        AgentProcessParentImpl bootstrapper = createBootstrapper(cmd);
        int returnCode = bootstrapper.run("launcher_version", "bar", getURLGenerator(), Map.of(AgentProcessParentImpl.AGENT_STARTUP_ARGS, "foo bar  baz with%20some%20space"), context());
        String expectedAgentMd5 = TEST_AGENT.getMd5();
        String expectedAgentPluginsMd5 = TEST_AGENT_PLUGINS.getMd5();
        String expectedTfsMd5 = TEST_TFS_IMPL.getMd5();

        assertThat(returnCode, is(42));
        assertThat(cmd.toArray(new String[]{}), equalTo(new String[]{
                (getProperty("java.home") + FileSystems.getDefault().getSeparator() + "bin" + FileSystems.getDefault().getSeparator() + "java"),
                "foo",
                "bar",
                "baz",
                "with some space",
                "-Dagent.plugins.md5=" + expectedAgentPluginsMd5,
                "-Dagent.binary.md5=" + expectedAgentMd5,
                "-Dagent.launcher.md5=bar",
                "-Dagent.tfs.md5=" + expectedTfsMd5,
                "-Dagent.bootstrapper.version=UNKNOWN",
                "-jar",
                "agent.jar",
                "-serverUrl",
                "http://localhost:" + server.getPort() + "/go/",
                "-sslVerificationMode",
                "NONE",
                "-rootCertFile",
                new File("/path/to/cert.pem").getAbsolutePath()
        }));
    }