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.

EmbeddedRunner.java 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * SonarQube Runner - API
  3. * Copyright (C) 2011 SonarSource
  4. * sonarqube@googlegroups.com
  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.cache.Logger;
  22. import org.sonar.runner.impl.ClassloadRules;
  23. import java.nio.charset.Charset;
  24. import java.security.InvalidParameterException;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.HashSet;
  28. import java.util.List;
  29. import java.util.Locale;
  30. import java.util.Properties;
  31. import java.util.Set;
  32. import javax.annotation.Nullable;
  33. import org.sonar.runner.batch.IsolatedLauncher;
  34. import org.sonar.runner.impl.InternalProperties;
  35. import org.sonar.runner.impl.IsolatedLauncherFactory;
  36. import org.sonar.runner.impl.VersionUtils;
  37. /**
  38. * Entry point to run SonarQube analysis programmatically.
  39. * @since 2.2
  40. */
  41. public class EmbeddedRunner {
  42. private final IsolatedLauncherFactory launcherFactory;
  43. private IsolatedLauncher launcher;
  44. private final LogOutput logOutput;
  45. private final Properties globalProperties = new Properties();
  46. private final List<Object> extensions = new ArrayList<>();
  47. private final Logger logger;
  48. private final Set<String> classloaderMask = new HashSet<>();
  49. private final Set<String> classloaderUnmask = new HashSet<>();
  50. EmbeddedRunner(IsolatedLauncherFactory bl, Logger logger, LogOutput logOutput) {
  51. this.logger = logger;
  52. this.launcherFactory = bl;
  53. this.logOutput = logOutput;
  54. this.classloaderUnmask.add("org.sonar.runner.batch.");
  55. }
  56. public static EmbeddedRunner create(final LogOutput logOutput) {
  57. Logger logger = new LoggerAdapter(logOutput);
  58. return new EmbeddedRunner(new IsolatedLauncherFactory(logger), logger, logOutput);
  59. }
  60. public Properties globalProperties() {
  61. Properties clone = new Properties();
  62. clone.putAll(globalProperties);
  63. return clone;
  64. }
  65. public EmbeddedRunner unmask(String fqcnPrefix) {
  66. checkLauncherDoesntExist();
  67. classloaderUnmask.add(fqcnPrefix);
  68. return this;
  69. }
  70. public EmbeddedRunner mask(String fqcnPrefix) {
  71. checkLauncherDoesntExist();
  72. classloaderMask.add(fqcnPrefix);
  73. return this;
  74. }
  75. /**
  76. * Declare Sonar properties, for example sonar.projectKey=>foo.
  77. * These might be used at different stages (on {@link #start() or #runAnalysis(Properties)}, depending on the
  78. * property and SQ version.
  79. *
  80. * @see #setProperty(String, String)
  81. */
  82. public EmbeddedRunner addGlobalProperties(Properties p) {
  83. globalProperties.putAll(p);
  84. return this;
  85. }
  86. /**
  87. * Declare a SonarQube property.
  88. * These might be used at different stages (on {@link #start() or #runAnalysis(Properties)}, depending on the
  89. * property and SQ version.
  90. *
  91. * @see RunnerProperties
  92. * @see ScanProperties
  93. */
  94. public EmbeddedRunner setGlobalProperty(String key, String value) {
  95. globalProperties.setProperty(key, value);
  96. return this;
  97. }
  98. public String globalProperty(String key, @Nullable String defaultValue) {
  99. return globalProperties.getProperty(key, defaultValue);
  100. }
  101. /**
  102. * User-agent used in the HTTP requests to the SonarQube server
  103. */
  104. public EmbeddedRunner setApp(String app, String version) {
  105. setGlobalProperty(InternalProperties.RUNNER_APP, app);
  106. setGlobalProperty(InternalProperties.RUNNER_APP_VERSION, version);
  107. return this;
  108. }
  109. public String app() {
  110. return globalProperty(InternalProperties.RUNNER_APP, null);
  111. }
  112. /**
  113. * Add extensions to the batch's object container.
  114. * Only supported until SQ 5.1. For more recent versions, an exception is thrown
  115. * @param objs
  116. */
  117. public EmbeddedRunner addExtensions(Object... objs) {
  118. checkLauncherExists();
  119. if (VersionUtils.isAtLeast52(launcher.getVersion())) {
  120. throw new IllegalStateException("not supported in current SonarQube version: " + launcher.getVersion());
  121. }
  122. extensions.addAll(Arrays.asList(objs));
  123. return this;
  124. }
  125. public String appVersion() {
  126. return globalProperty(InternalProperties.RUNNER_APP_VERSION, null);
  127. }
  128. /**
  129. * Launch an analysis.
  130. * Runner must have been started - see {@link #start()}.
  131. */
  132. public void runAnalysis(Properties analysisProperties) {
  133. runAnalysis(analysisProperties, null);
  134. }
  135. /**
  136. * Launch an analysis, providing optionally a issue listener.
  137. * Runner must have been started - see {@link #start()}.
  138. * Issue listener is supported starting in SQ 5.2. If a non-null listener is given for older versions, an exception is thrown
  139. */
  140. public void runAnalysis(Properties analysisProperties, @Nullable IssueListener issueListener) {
  141. checkLauncherExists();
  142. Properties copy = new Properties();
  143. copy.putAll(analysisProperties);
  144. initAnalysisProperties(copy);
  145. doExecute(copy, issueListener);
  146. }
  147. /**
  148. * Synchronizes the project's data in the local cache with the server, allowing analysis of the project to be done offline.
  149. * Runner must have been started - see {@link #start()}.
  150. * Only supported starting in SQ 5.2. For older versions, an exception is thrown
  151. */
  152. public void syncProject(String projectKey) {
  153. checkLauncherExists();
  154. if (!VersionUtils.isAtLeast52(launcher.getVersion())) {
  155. throw new IllegalStateException("not supported in current SonarQube version: " + launcher.getVersion());
  156. }
  157. launcher.syncProject(projectKey);
  158. }
  159. public void start() {
  160. start(false);
  161. }
  162. public void start(boolean preferCache) {
  163. initGlobalDefaultValues();
  164. doStart(preferCache);
  165. }
  166. /**
  167. * Stops the batch.
  168. * Only supported starting in SQ 5.2. For older versions, this is a no-op.
  169. */
  170. public void stop() {
  171. checkLauncherExists();
  172. doStop();
  173. }
  174. public String serverVersion() {
  175. checkLauncherExists();
  176. return launcher.getVersion();
  177. }
  178. /**
  179. * @deprecated since 2.5 use {@link #start()}, {@link #runAnalysis(Properties)} and then {@link #stop()}
  180. */
  181. @Deprecated
  182. public final void execute() {
  183. start();
  184. runAnalysis(new Properties());
  185. stop();
  186. }
  187. private void initGlobalDefaultValues() {
  188. setGlobalDefaultValue(RunnerProperties.HOST_URL, "http://localhost:9000");
  189. setGlobalDefaultValue(InternalProperties.RUNNER_APP, "SonarQubeRunner");
  190. setGlobalDefaultValue(InternalProperties.RUNNER_APP_VERSION, RunnerVersion.version());
  191. }
  192. private void initAnalysisProperties(Properties p) {
  193. initSourceEncoding(p);
  194. new Dirs(logger).init(p);
  195. }
  196. void initSourceEncoding(Properties p) {
  197. boolean onProject = Utils.taskRequiresProject(p);
  198. if (onProject) {
  199. String sourceEncoding = p.getProperty(ScanProperties.PROJECT_SOURCE_ENCODING, "");
  200. boolean platformDependent = false;
  201. if ("".equals(sourceEncoding)) {
  202. sourceEncoding = Charset.defaultCharset().name();
  203. platformDependent = true;
  204. p.setProperty(ScanProperties.PROJECT_SOURCE_ENCODING, sourceEncoding);
  205. }
  206. logger.info("Default locale: \"" + Locale.getDefault() + "\", source code encoding: \"" + sourceEncoding + "\""
  207. + (platformDependent ? " (analysis is platform dependent)" : ""));
  208. }
  209. }
  210. private void setGlobalDefaultValue(String key, String value) {
  211. if (!globalProperties.containsKey(key)) {
  212. setGlobalProperty(key, value);
  213. }
  214. }
  215. protected void doStart(boolean preferCache) {
  216. checkLauncherDoesntExist();
  217. ClassloadRules rules = new ClassloadRules(classloaderMask, classloaderUnmask);
  218. launcher = launcherFactory.createLauncher(globalProperties(), rules, preferCache);
  219. if (VersionUtils.isAtLeast52(launcher.getVersion())) {
  220. launcher.start(globalProperties(), new org.sonar.runner.batch.LogOutput() {
  221. @Override
  222. public void log(String formattedMessage, Level level) {
  223. logOutput.log(formattedMessage, LogOutput.Level.valueOf(level.name()));
  224. }
  225. }, preferCache);
  226. }
  227. }
  228. protected void doStop() {
  229. if (VersionUtils.isAtLeast52(launcher.getVersion())) {
  230. launcher.stop();
  231. launcher = null;
  232. }
  233. }
  234. protected void doExecute(Properties analysisProperties, @Nullable IssueListener issueListener) {
  235. if (VersionUtils.isAtLeast52(launcher.getVersion())) {
  236. if (issueListener != null) {
  237. launcher.execute(analysisProperties, new IssueListenerAdapter(issueListener));
  238. } else {
  239. launcher.execute(analysisProperties);
  240. }
  241. } else {
  242. if (issueListener != null) {
  243. throw new InvalidParameterException("Issue listeners not supported in current version: " + launcher.getVersion());
  244. }
  245. Properties prop = new Properties();
  246. prop.putAll(globalProperties());
  247. prop.putAll(analysisProperties);
  248. launcher.executeOldVersion(prop, extensions);
  249. }
  250. }
  251. private void checkLauncherExists() {
  252. if (launcher == null) {
  253. throw new IllegalStateException("not started");
  254. }
  255. }
  256. private void checkLauncherDoesntExist() {
  257. if (launcher != null) {
  258. throw new IllegalStateException("already started");
  259. }
  260. }
  261. }