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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. * Sonar Standalone Runner
  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;
  21. import org.sonar.runner.bootstrapper.BootstrapClassLoader;
  22. import org.sonar.runner.bootstrapper.BootstrapException;
  23. import org.sonar.runner.bootstrapper.Bootstrapper;
  24. import org.sonar.runner.bootstrapper.BootstrapperIOUtils;
  25. import java.io.File;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.lang.reflect.Constructor;
  29. import java.lang.reflect.InvocationTargetException;
  30. import java.lang.reflect.Method;
  31. import java.net.URL;
  32. import java.util.Properties;
  33. /**
  34. * @since 1.1
  35. */
  36. public final class Runner {
  37. public static final String PROPERTY_PROJECT_DIR = "sonar.runner.projectDir";
  38. public static final String PROPERTY_RUNNER_VERSION = "sonar.runner.version";
  39. /**
  40. * @deprecated Replaced by sonar.verbose since 1.2
  41. */
  42. @Deprecated
  43. public static final String PROPERTY_OLD_DEBUG_MODE = "runner.debug";
  44. /**
  45. * @since 1.2
  46. */
  47. public static final String PROPERTY_VERBOSE = "sonar.verbose";
  48. /**
  49. * @since 1.4
  50. */
  51. public static final String PROPERTY_WORK_DIRECTORY = "sonar.working.directory";
  52. public static final String DEF_VALUE_WORK_DIRECTORY = ".sonar";
  53. /**
  54. * Array of prefixes of versions of Sonar without support of this runner.
  55. */
  56. private static final String[] UNSUPPORTED_VERSIONS = {"1", "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "2.10"};
  57. /**
  58. * Array of all mandatory properties required to execute runner.
  59. */
  60. private static final String[] MANDATORY_PROPERTIES = {"sonar.projectKey", "sonar.projectName", "sonar.projectVersion", "sources"};
  61. private File projectDir;
  62. private File workDir;
  63. private Properties properties;
  64. private Runner(Properties props) {
  65. this.properties = props;
  66. initDirs();
  67. }
  68. public static Runner create(Properties props) {
  69. return new Runner(props);
  70. }
  71. public void execute() {
  72. checkMandatoryProperties();
  73. Bootstrapper bootstrapper = new Bootstrapper("SonarRunner/" + getRunnerVersion(), getServerURL(), getWorkDir());
  74. checkSonarVersion(bootstrapper);
  75. delegateExecution(createClassLoader(bootstrapper));
  76. }
  77. void checkMandatoryProperties() {
  78. StringBuilder missing = new StringBuilder();
  79. for (String mandatoryProperty : MANDATORY_PROPERTIES) {
  80. if (!properties.containsKey(mandatoryProperty)) {
  81. if (missing.length() > 0) {
  82. missing.append(", ");
  83. }
  84. missing.append(mandatoryProperty);
  85. }
  86. }
  87. if (missing.length() != 0) {
  88. throw new RunnerException("You must define mandatory properties: " + missing);
  89. }
  90. }
  91. public String getServerURL() {
  92. return properties.getProperty("sonar.host.url", "http://localhost:9000");
  93. }
  94. private void initDirs() {
  95. String path = properties.getProperty("project.home", ".");
  96. projectDir = new File(path);
  97. if (!projectDir.isDirectory() || !projectDir.exists()) {
  98. throw new IllegalArgumentException("Project home must be an existing directory: " + path);
  99. }
  100. // project home exist, add its absolute path as "sonar.runner.projectDir" property
  101. properties.put(PROPERTY_PROJECT_DIR, projectDir.getAbsolutePath());
  102. workDir = initWorkDir();
  103. }
  104. private File initWorkDir() {
  105. String customWorkDir = properties.getProperty(PROPERTY_WORK_DIRECTORY);
  106. if (customWorkDir == null || customWorkDir.trim().length() == 0) {
  107. return new File(projectDir, DEF_VALUE_WORK_DIRECTORY);
  108. }
  109. return defineCustomizedWorkDir(new File(customWorkDir));
  110. }
  111. private File defineCustomizedWorkDir(File customWorkDir) {
  112. if (customWorkDir.isAbsolute()) {
  113. return customWorkDir;
  114. }
  115. return new File(projectDir, customWorkDir.getPath());
  116. }
  117. public File getProjectDir() {
  118. return projectDir;
  119. }
  120. /**
  121. * @return work directory, default is ".sonar" in project directory
  122. */
  123. public File getWorkDir() {
  124. return workDir;
  125. }
  126. /**
  127. * @return global properties, project properties and command-line properties
  128. */
  129. public Properties getProperties() {
  130. return properties;
  131. }
  132. public String getRunnerVersion() {
  133. InputStream in = null;
  134. try {
  135. in = Runner.class.getResourceAsStream("/org/sonar/runner/version.txt");
  136. Properties props = new Properties();
  137. props.load(in);
  138. return props.getProperty("version");
  139. } catch (IOException e) {
  140. throw new BootstrapException("Could not load the version information for Sonar Standalone Runner", e);
  141. } finally {
  142. BootstrapperIOUtils.closeQuietly(in);
  143. }
  144. }
  145. protected void checkSonarVersion(Bootstrapper bootstrapper) {
  146. String serverVersion = bootstrapper.getServerVersion();
  147. if (isUnsupportedVersion(serverVersion)) {
  148. throw new BootstrapException("Sonar " + serverVersion
  149. + " does not support Standalone Runner. Please upgrade Sonar to version 2.6 or more.");
  150. }
  151. }
  152. private BootstrapClassLoader createClassLoader(Bootstrapper bootstrapper) {
  153. URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
  154. return bootstrapper.createClassLoader(
  155. new URL[] {url}, // Add JAR with Sonar Runner - it's a Jar which contains this class
  156. getClass().getClassLoader());
  157. }
  158. static boolean isUnsupportedVersion(String version) {
  159. for (String unsupportedVersion : UNSUPPORTED_VERSIONS) {
  160. if (isVersion(version, unsupportedVersion)) {
  161. return true;
  162. }
  163. }
  164. return false;
  165. }
  166. static boolean isVersion(String version, String prefix) {
  167. return version.startsWith(prefix + ".") || version.equals(prefix);
  168. }
  169. /**
  170. * Loads {@link Launcher} from specified {@link org.sonar.batch.bootstrapper.BootstrapClassLoader} and passes control to it.
  171. *
  172. * @see Launcher#execute()
  173. */
  174. private void delegateExecution(BootstrapClassLoader sonarClassLoader) {
  175. ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
  176. try {
  177. Thread.currentThread().setContextClassLoader(sonarClassLoader);
  178. Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.runner.Launcher");
  179. Constructor<?> constructor = launcherClass.getConstructor(Properties.class);
  180. Object launcher = constructor.newInstance(getProperties());
  181. Method method = launcherClass.getMethod("execute");
  182. method.invoke(launcher);
  183. } catch (InvocationTargetException e) {
  184. // Unwrap original exception
  185. throw new BootstrapException(e.getTargetException());
  186. } catch (Exception e) {
  187. // Catch all other exceptions, which relates to reflection
  188. throw new BootstrapException(e);
  189. } finally {
  190. Thread.currentThread().setContextClassLoader(oldContextClassLoader);
  191. }
  192. }
  193. }