001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * SonarQube is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.process.monitor;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.slf4j.LoggerFactory;
024    import org.sonar.process.ProcessCommands;
025    import org.sonar.process.ProcessEntryPoint;
026    import org.sonar.process.ProcessUtils;
027    
028    import java.io.File;
029    import java.io.FileOutputStream;
030    import java.io.OutputStream;
031    import java.util.ArrayList;
032    import java.util.Arrays;
033    import java.util.List;
034    import java.util.Properties;
035    
036    public class JavaProcessLauncher {
037    
038      private final Timeouts timeouts;
039    
040      public JavaProcessLauncher(Timeouts timeouts) {
041        this.timeouts = timeouts;
042      }
043    
044      ProcessRef launch(JavaCommand command) {
045        Process process = null;
046        try {
047          // cleanup existing monitor files
048          ProcessCommands commands = new ProcessCommands(command.getTempDir(), command.getKey());
049          commands.prepare();
050    
051          ProcessBuilder processBuilder = create(command);
052          LoggerFactory.getLogger(getClass()).info("Launch process[{}]: {}",
053            command.getKey(), StringUtils.join(processBuilder.command(), " "));
054          process = processBuilder.start();
055          StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), command.getKey());
056          inputGobbler.start();
057    
058          return new ProcessRef(command.getKey(), commands, process, inputGobbler);
059    
060        } catch (Exception e) {
061          // just in case
062          ProcessUtils.sendKillSignal(process);
063          throw new IllegalStateException("Fail to launch " + command.getKey(), e);
064        }
065      }
066    
067      private ProcessBuilder create(JavaCommand javaCommand) {
068        List<String> commands = new ArrayList<String>();
069        commands.add(buildJavaPath());
070        commands.addAll(javaCommand.getJavaOptions());
071        // TODO warning - does it work if temp dir contains a whitespace ?
072        commands.add(String.format("-Djava.io.tmpdir=%s", javaCommand.getTempDir().getAbsolutePath()));
073        commands.addAll(buildClasspath(javaCommand));
074        commands.add(javaCommand.getClassName());
075        commands.add(buildPropertiesFile(javaCommand).getAbsolutePath());
076    
077        ProcessBuilder processBuilder = new ProcessBuilder();
078        processBuilder.command(commands);
079        processBuilder.directory(javaCommand.getWorkDir());
080        processBuilder.environment().putAll(javaCommand.getEnvVariables());
081        processBuilder.redirectErrorStream(true);
082        return processBuilder;
083      }
084    
085      private String buildJavaPath() {
086        String separator = System.getProperty("file.separator");
087        return new File(new File(System.getProperty("java.home")),
088          "bin" + separator + "java").getAbsolutePath();
089      }
090    
091      private List<String> buildClasspath(JavaCommand javaCommand) {
092        return Arrays.asList("-cp", StringUtils.join(javaCommand.getClasspath(), System.getProperty("path.separator")));
093      }
094    
095      private File buildPropertiesFile(JavaCommand javaCommand) {
096        File propertiesFile = null;
097        try {
098          propertiesFile = File.createTempFile("sq-process", "properties");
099          Properties props = new Properties();
100          props.putAll(javaCommand.getArguments());
101          props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_KEY, javaCommand.getKey());
102          props.setProperty(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout()));
103          props.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, javaCommand.getTempDir().getAbsolutePath());
104          OutputStream out = new FileOutputStream(propertiesFile);
105          props.store(out, String.format("Temporary properties file for command [%s]", javaCommand.getKey()));
106          out.close();
107          return propertiesFile;
108        } catch (Exception e) {
109          throw new IllegalStateException("Cannot write temporary settings to " + propertiesFile, e);
110        }
111      }
112    }