You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ForkedRunner.java 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * Sonar 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.apache.commons.io.IOUtils;
  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. /**
  34. * Runner executed in a new JVM.
  35. *
  36. * @since 2.2
  37. */
  38. public class ForkedRunner extends Runner<ForkedRunner> {
  39. private static final int ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
  40. private final Map<String, String> jvmEnvVariables = new HashMap<String, String>();
  41. private final List<String> jvmArguments = new ArrayList<String>();
  42. private String javaExecutable;
  43. private StreamConsumer stdOut = null, stdErr = null;
  44. private final JarExtractor jarExtractor;
  45. private final CommandExecutor commandExecutor;
  46. ForkedRunner(JarExtractor jarExtractor, CommandExecutor commandExecutor) {
  47. this.jarExtractor = jarExtractor;
  48. this.commandExecutor = commandExecutor;
  49. }
  50. /**
  51. * Create new instance. Never return null.
  52. */
  53. public static ForkedRunner create() {
  54. return new ForkedRunner(new JarExtractor(), CommandExecutor.create());
  55. }
  56. /**
  57. * Path to the java executable. The JVM of the client app is used by default
  58. * (see the system property java.home)
  59. */
  60. public ForkedRunner setJavaExecutable(@Nullable String s) {
  61. this.javaExecutable = s;
  62. return this;
  63. }
  64. /**
  65. * See {@link #addJvmArguments(java.util.List)}
  66. */
  67. public ForkedRunner addJvmArguments(String... s) {
  68. return addJvmArguments(Arrays.asList(s));
  69. }
  70. /**
  71. * JVM arguments, for example "-Xmx512m"
  72. */
  73. public ForkedRunner addJvmArguments(List<String> args) {
  74. jvmArguments.addAll(args);
  75. return this;
  76. }
  77. /**
  78. * Set a JVM environment variable. By default no variables are set.
  79. */
  80. public ForkedRunner setJvmEnvVariable(String key, String value) {
  81. jvmEnvVariables.put(key, value);
  82. return this;
  83. }
  84. /**
  85. * Add some JVM environment variables. By default no variables are set.
  86. */
  87. public ForkedRunner addJvmEnvVariables(Map<String, String> map) {
  88. jvmEnvVariables.putAll(map);
  89. return this;
  90. }
  91. /**
  92. * Subscribe to the standard output. By default output is {@link System.out}
  93. */
  94. public ForkedRunner setStdOut(@Nullable StreamConsumer stream) {
  95. this.stdOut = stream;
  96. return this;
  97. }
  98. /**
  99. * Subscribe to the error output. By default output is {@link System.err}
  100. */
  101. public ForkedRunner setStdErr(@Nullable StreamConsumer stream) {
  102. this.stdErr = stream;
  103. return this;
  104. }
  105. @Override
  106. protected void doExecute() {
  107. fork(createCommand());
  108. }
  109. private Command createCommand() {
  110. File propertiesFile = writeProperties();
  111. File jarFile = jarExtractor.extract("sonar-runner-impl");
  112. if (javaExecutable == null) {
  113. javaExecutable = new Os().thisJavaExe().getAbsolutePath();
  114. }
  115. return Command.builder()
  116. .setExecutable(javaExecutable)
  117. .addEnvVariables(jvmEnvVariables)
  118. .addArguments(jvmArguments)
  119. .addArguments("-cp", jarFile.getAbsolutePath(), BatchLauncherMain.class.getName(), propertiesFile.getAbsolutePath())
  120. .build();
  121. }
  122. private File writeProperties() {
  123. OutputStream output = null;
  124. try {
  125. File file = File.createTempFile("sonar-project", ".properties");
  126. output = new FileOutputStream(file);
  127. properties().store(output, "Generated by sonar-runner");
  128. return file;
  129. } catch (Exception e) {
  130. throw new IllegalStateException("Fail to export sonar-runner properties", e);
  131. } finally {
  132. IOUtils.closeQuietly(output);
  133. }
  134. }
  135. private void fork(Command command) {
  136. if (stdOut == null) {
  137. stdOut = new PrintStreamConsumer(System.out);
  138. }
  139. if (stdErr == null) {
  140. stdErr = new PrintStreamConsumer(System.err);
  141. }
  142. int status = commandExecutor.execute(command, stdOut, stdErr, ONE_DAY_IN_MILLISECONDS);
  143. if (status != 0) {
  144. throw new IllegalStateException("Error status: " + status);
  145. }
  146. }
  147. }