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 */ 020package org.sonar.process.monitor; 021 022import org.apache.commons.lang.StringUtils; 023import org.slf4j.LoggerFactory; 024import org.sonar.process.ProcessCommands; 025import org.sonar.process.ProcessEntryPoint; 026import org.sonar.process.ProcessUtils; 027 028import java.io.File; 029import java.io.FileOutputStream; 030import java.io.OutputStream; 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.List; 034import java.util.Properties; 035 036public 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.getProcessIndex()); 049 050 ProcessBuilder processBuilder = create(command); 051 LoggerFactory.getLogger(getClass()).info("Launch process[{}]: {}", 052 command.getKey(), StringUtils.join(processBuilder.command(), " ")); 053 process = processBuilder.start(); 054 StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), command.getKey()); 055 inputGobbler.start(); 056 057 return new ProcessRef(command.getKey(), commands, process, inputGobbler); 058 059 } catch (Exception e) { 060 // just in case 061 ProcessUtils.sendKillSignal(process); 062 throw new IllegalStateException("Fail to launch " + command.getKey(), e); 063 } 064 } 065 066 private ProcessBuilder create(JavaCommand javaCommand) { 067 List<String> commands = new ArrayList<String>(); 068 commands.add(buildJavaPath()); 069 commands.addAll(javaCommand.getJavaOptions()); 070 // TODO warning - does it work if temp dir contains a whitespace ? 071 commands.add(String.format("-Djava.io.tmpdir=%s", javaCommand.getTempDir().getAbsolutePath())); 072 commands.addAll(buildClasspath(javaCommand)); 073 commands.add(javaCommand.getClassName()); 074 commands.add(buildPropertiesFile(javaCommand).getAbsolutePath()); 075 076 ProcessBuilder processBuilder = new ProcessBuilder(); 077 processBuilder.command(commands); 078 processBuilder.directory(javaCommand.getWorkDir()); 079 processBuilder.environment().putAll(javaCommand.getEnvVariables()); 080 processBuilder.redirectErrorStream(true); 081 return processBuilder; 082 } 083 084 private String buildJavaPath() { 085 String separator = System.getProperty("file.separator"); 086 return new File(new File(System.getProperty("java.home")), 087 "bin" + separator + "java").getAbsolutePath(); 088 } 089 090 private List<String> buildClasspath(JavaCommand javaCommand) { 091 return Arrays.asList("-cp", StringUtils.join(javaCommand.getClasspath(), System.getProperty("path.separator"))); 092 } 093 094 private File buildPropertiesFile(JavaCommand javaCommand) { 095 File propertiesFile = null; 096 try { 097 propertiesFile = File.createTempFile("sq-process", "properties"); 098 Properties props = new Properties(); 099 props.putAll(javaCommand.getArguments()); 100 props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_KEY, javaCommand.getKey()); 101 props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_INDEX, "" + javaCommand.getProcessIndex()); 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}