org.sonar.process.monitor.JavaProcessLauncher.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.process.monitor.JavaProcessLauncher.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.process.monitor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.process.AllProcessesCommands;
import org.sonar.process.ProcessCommands;
import org.sonar.process.ProcessEntryPoint;
import org.sonar.process.ProcessUtils;

import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_KEY;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT;

class JavaProcessLauncher {

    private final Timeouts timeouts;
    private final File tempDir;
    private final AllProcessesCommands allProcessesCommands;

    JavaProcessLauncher(Timeouts timeouts, File tempDir) {
        this.timeouts = timeouts;
        this.tempDir = tempDir;
        this.allProcessesCommands = new AllProcessesCommands(tempDir);
    }

    public void close() {
        allProcessesCommands.close();
    }

    ProcessRef launch(JavaCommand command) {
        Process process = null;
        try {
            ProcessCommands commands = allProcessesCommands.createAfterClean(command.getProcessId().getIpcIndex());

            ProcessBuilder processBuilder = create(command);
            LoggerFactory.getLogger(getClass()).info("Launch process[{}]: {}", command.getProcessId().getKey(),
                    StringUtils.join(processBuilder.command(), " "));
            process = processBuilder.start();
            StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(),
                    command.getProcessId().getKey());
            inputGobbler.start();

            return new ProcessRef(command.getProcessId().getKey(), commands, process, inputGobbler);

        } catch (Exception e) {
            // just in case
            ProcessUtils.sendKillSignal(process);
            throw new IllegalStateException("Fail to launch [" + command.getProcessId().getKey() + "]", e);
        }
    }

    private ProcessBuilder create(JavaCommand javaCommand) {
        List<String> commands = new ArrayList<>();
        commands.add(buildJavaPath());
        commands.addAll(javaCommand.getJavaOptions());
        // TODO warning - does it work if temp dir contains a whitespace ?
        commands.add(String.format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath()));
        commands.add(getJmxAgentCommand());
        commands.addAll(buildClasspath(javaCommand));
        commands.add(javaCommand.getClassName());
        commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());

        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command(commands);
        processBuilder.directory(javaCommand.getWorkDir());
        processBuilder.environment().putAll(javaCommand.getEnvVariables());
        processBuilder.redirectErrorStream(true);
        return processBuilder;
    }

    /**
     * JVM option to enable the agent that allows inter-process communication through JMX without
     * opening new ports. The agent is available in JRE of OpenJDK/OracleJDK only.
     * @see ProcessEntryPoint
     */
    private static String getJmxAgentCommand() {
        return "-javaagent:" + System.getProperty("java.home") + File.separator + "lib" + File.separator
                + "management-agent.jar";
    }

    private String buildJavaPath() {
        String separator = System.getProperty("file.separator");
        return new File(new File(System.getProperty("java.home")), "bin" + separator + "java").getAbsolutePath();
    }

    private List<String> buildClasspath(JavaCommand javaCommand) {
        return Arrays.asList("-cp",
                StringUtils.join(javaCommand.getClasspath(), System.getProperty("path.separator")));
    }

    private File buildPropertiesFile(JavaCommand javaCommand) {
        File propertiesFile = null;
        try {
            propertiesFile = File.createTempFile("sq-process", "properties", tempDir);
            Properties props = new Properties();
            props.putAll(javaCommand.getArguments());
            props.setProperty(PROPERTY_PROCESS_KEY, javaCommand.getProcessId().getKey());
            props.setProperty(PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessId().getIpcIndex()));
            props.setProperty(PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout()));
            props.setProperty(PROPERTY_SHARED_PATH, tempDir.getAbsolutePath());
            try (OutputStream out = new FileOutputStream(propertiesFile)) {
                props.store(out, String.format("Temporary properties file for command [%s]",
                        javaCommand.getProcessId().getKey()));
            }
            return propertiesFile;
        } catch (Exception e) {
            throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
        }
    }
}