Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

ForkedRunner.java 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * SonarQube Runner - API
  3. * Copyright (C) 2011 SonarSource
  4. * dev@sonar.codehaus.org
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
  19. */
  20. package org.sonar.runner.api;
  21. import org.sonar.runner.impl.Logs;
  22. import org.sonar.runner.impl.BatchLauncherMain;
  23. import org.sonar.runner.impl.JarExtractor;
  24. import javax.annotation.Nullable;
  25. import java.io.File;
  26. import java.io.FileOutputStream;
  27. import java.io.OutputStream;
  28. import java.util.ArrayList;
  29. import java.util.Arrays;
  30. import java.util.HashMap;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.Properties;
  34. /**
  35. * Runner executed in a new JVM.
  36. *
  37. * @since 2.2
  38. */
  39. public class ForkedRunner extends Runner<ForkedRunner> {
  40. private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
  41. private final Map<String, String> jvmEnvVariables = new HashMap<String, String>();
  42. private final List<String> jvmArguments = new ArrayList<String>();
  43. private String javaExecutable;
  44. private StreamConsumer stdOut = null;
  45. private StreamConsumer stdErr = null;
  46. private final JarExtractor jarExtractor;
  47. private final CommandExecutor commandExecutor;
  48. private ProcessMonitor processMonitor;
  49. ForkedRunner(JarExtractor jarExtractor, CommandExecutor commandExecutor, @Nullable ProcessMonitor processMonitor) {
  50. this.jarExtractor = jarExtractor;
  51. this.commandExecutor = commandExecutor;
  52. this.processMonitor = processMonitor;
  53. }
  54. ForkedRunner(JarExtractor jarExtractor, CommandExecutor commandExecutor) {
  55. this(jarExtractor, commandExecutor, null);
  56. }
  57. /**
  58. * Create new instance. Never return null.
  59. */
  60. public static ForkedRunner create() {
  61. return new ForkedRunner(new JarExtractor(), CommandExecutor.create());
  62. }
  63. /**
  64. * Create new instance. Never return null.
  65. */
  66. public static ForkedRunner create(ProcessMonitor processMonitor) {
  67. return new ForkedRunner(new JarExtractor(), CommandExecutor.create(), processMonitor);
  68. }
  69. /**
  70. * Path to the java executable. The JVM of the client app is used by default
  71. * (see the system property java.home)
  72. */
  73. public ForkedRunner setJavaExecutable(@Nullable String s) {
  74. this.javaExecutable = s;
  75. return this;
  76. }
  77. public List<String> jvmArguments() {
  78. return new ArrayList<String>(jvmArguments);
  79. }
  80. /**
  81. * See {@link #addJvmArguments(java.util.List)}
  82. */
  83. public ForkedRunner addJvmArguments(String... s) {
  84. return addJvmArguments(Arrays.asList(s));
  85. }
  86. /**
  87. * JVM arguments, for example "-Xmx512m"
  88. */
  89. public ForkedRunner addJvmArguments(List<String> args) {
  90. jvmArguments.addAll(args);
  91. return this;
  92. }
  93. /**
  94. * Set a JVM environment variable. By default no variables are set.
  95. */
  96. public ForkedRunner setJvmEnvVariable(String key, String value) {
  97. jvmEnvVariables.put(key, value);
  98. return this;
  99. }
  100. /**
  101. * Add some JVM environment variables. By default no variables are set.
  102. */
  103. public ForkedRunner addJvmEnvVariables(Map<String, String> map) {
  104. jvmEnvVariables.putAll(map);
  105. return this;
  106. }
  107. /**
  108. * @deprecated Since 2.5. Use {@link ForkedRunner#setLogListener} instead.
  109. * Subscribe to the standard output from the forked process. By default, logs messages are sent with INFO level to the log listener set.
  110. */
  111. public ForkedRunner setStdOut(@Nullable StreamConsumer stream) {
  112. this.stdOut = stream;
  113. return this;
  114. }
  115. /**
  116. * @deprecated Since 2.5. Use {@link ForkedRunner#setLogListener}instead.
  117. * Subscribe to the error output from the forked process. By default, logs messages are sent with ERROR level to the log listener set.
  118. */
  119. public ForkedRunner setStdErr(@Nullable StreamConsumer stream) {
  120. this.stdErr = stream;
  121. return this;
  122. }
  123. @Override
  124. protected void doExecute(Properties props) {
  125. // merge both global and analysis-specific properties because it will be used both to start and to execute.
  126. Properties p = new Properties();
  127. p.putAll(globalProperties());
  128. p.putAll(props);
  129. ForkCommand forkCommand = createCommand(p);
  130. try {
  131. fork(forkCommand);
  132. } finally {
  133. deleteTempFiles(forkCommand);
  134. }
  135. }
  136. @Override
  137. protected void doStop() {
  138. // nothing to do
  139. }
  140. @Override
  141. protected void doStart() {
  142. // nothing to do
  143. }
  144. ForkCommand createCommand(Properties p) {
  145. File propertiesFile = writeProperties(p);
  146. File jarFile = jarExtractor.extractToTemp("sonar-runner-impl");
  147. if (javaExecutable == null) {
  148. javaExecutable = new Os().thisJavaExe().getAbsolutePath();
  149. }
  150. Command command = Command.builder()
  151. .setExecutable(javaExecutable)
  152. .addEnvVariables(jvmEnvVariables)
  153. .addArguments(jvmArguments)
  154. .addArguments("-cp", jarFile.getAbsolutePath(), BatchLauncherMain.class.getName(), propertiesFile.getAbsolutePath())
  155. .build();
  156. return new ForkCommand(command, jarFile, propertiesFile);
  157. }
  158. File writeProperties(Properties p) {
  159. try {
  160. File file = File.createTempFile("sonar-project", ".properties");
  161. try (OutputStream output = new FileOutputStream(file)) {
  162. p.store(output, "Generated by sonar-runner");
  163. return file;
  164. }
  165. } catch (Exception e) {
  166. throw new IllegalStateException("Fail to export sonar-runner properties", e);
  167. }
  168. }
  169. private static void deleteTempFiles(ForkCommand forkCommand) {
  170. Utils.deleteQuietly(forkCommand.jarFile);
  171. Utils.deleteQuietly(forkCommand.propertiesFile);
  172. }
  173. private void fork(ForkCommand forkCommand) {
  174. if (stdOut == null) {
  175. stdOut = new StreamConsumer() {
  176. @Override
  177. public void consumeLine(String line) {
  178. Logs.info(line);
  179. }
  180. };
  181. }
  182. if (stdErr == null) {
  183. stdErr = new StreamConsumer() {
  184. @Override
  185. public void consumeLine(String line) {
  186. Logs.error(line);
  187. }
  188. };
  189. }
  190. int status = commandExecutor.execute(forkCommand.command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS, processMonitor);
  191. if (status != 0) {
  192. if (processMonitor != null && processMonitor.stop()) {
  193. stdOut.consumeLine(String.format("SonarQube Runner was stopped [status=%s]", status));
  194. } else {
  195. throw new IllegalStateException("Error status [command: " + forkCommand.command + "]: " + status);
  196. }
  197. }
  198. }
  199. static class ForkCommand {
  200. Command command;
  201. File jarFile;
  202. File propertiesFile;
  203. private ForkCommand(Command command, File jarFile, File propertiesFile) {
  204. this.command = command;
  205. this.jarFile = jarFile;
  206. this.propertiesFile = propertiesFile;
  207. }
  208. }
  209. }