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.

Conf.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * SonarQube Scanner
  3. * Copyright (C) 2011-2016 SonarSource SA
  4. * mailto:contact AT sonarsource DOT 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 License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonarsource.scanner.cli;
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.text.MessageFormat;
  26. import java.util.ArrayList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Map.Entry;
  30. import java.util.Properties;
  31. class Conf {
  32. private static final String RUNNER_HOME = "runner.home";
  33. private static final String RUNNER_SETTINGS = "runner.settings";
  34. private static final String PROJECT_HOME = "project.home";
  35. private static final String PROJECT_SETTINGS = "project.settings";
  36. private static final String PROPERTY_MODULES = "sonar.modules";
  37. private static final String PROPERTY_PROJECT_BASEDIR = "sonar.projectBaseDir";
  38. private static final String PROPERTY_PROJECT_CONFIG_FILE = "sonar.projectConfigFile";
  39. private static final String SONAR_PROJECT_PROPERTIES_FILENAME = "sonar-project.properties";
  40. private final Cli cli;
  41. private final Logs logger;
  42. Conf(Cli cli, Logs logger) {
  43. this.cli = cli;
  44. this.logger = logger;
  45. }
  46. Properties properties() throws IOException {
  47. Properties result = new Properties();
  48. result.putAll(loadGlobalProperties());
  49. result.putAll(loadProjectProperties());
  50. result.putAll(System.getProperties());
  51. result.putAll(cli.properties());
  52. result.remove(PROJECT_HOME);
  53. return result;
  54. }
  55. private Properties loadGlobalProperties() throws IOException {
  56. File settingsFile = locatePropertiesFile(cli.properties(), RUNNER_HOME, "conf/sonar-runner.properties", RUNNER_SETTINGS);
  57. if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
  58. logger.info("Scanner configuration file: " + settingsFile.getAbsolutePath());
  59. return toProperties(settingsFile);
  60. }
  61. logger.info("Scanner configuration file: NONE");
  62. return new Properties();
  63. }
  64. private Properties loadProjectProperties() throws IOException {
  65. Properties cliProps = cli.properties();
  66. File rootSettingsFile = locatePropertiesFile(cliProps, cliProps.containsKey(PROPERTY_PROJECT_BASEDIR) ? PROPERTY_PROJECT_BASEDIR : PROJECT_HOME,
  67. SONAR_PROJECT_PROPERTIES_FILENAME,
  68. PROJECT_SETTINGS);
  69. if (rootSettingsFile != null && rootSettingsFile.isFile() && rootSettingsFile.exists()) {
  70. logger.info("Project configuration file: " + rootSettingsFile.getAbsolutePath());
  71. Properties projectProps = new Properties();
  72. Properties rootProps = toProperties(rootSettingsFile);
  73. projectProps.putAll(rootProps);
  74. initRootProjectBaseDir(cliProps, rootProps);
  75. loadModulesProperties(rootProps, projectProps, "");
  76. return projectProps;
  77. }
  78. logger.info("Project configuration file: NONE");
  79. return new Properties();
  80. }
  81. private static void initRootProjectBaseDir(Properties cliProps, Properties rootProps) {
  82. if (!cliProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
  83. String baseDir = cliProps.getProperty(PROJECT_HOME);
  84. rootProps.put(PROPERTY_PROJECT_BASEDIR, baseDir);
  85. } else {
  86. rootProps.put(PROPERTY_PROJECT_BASEDIR, cliProps.getProperty(PROPERTY_PROJECT_BASEDIR));
  87. }
  88. }
  89. private void loadModulesProperties(Properties parentProps, Properties projectProps, String prefix) {
  90. File parentBaseDir = new File(parentProps.getProperty(PROPERTY_PROJECT_BASEDIR));
  91. if (parentProps.containsKey(PROPERTY_MODULES)) {
  92. for (String module : getListFromProperty(parentProps, PROPERTY_MODULES)) {
  93. Properties moduleProps = extractModuleProperties(module, parentProps);
  94. moduleProps = loadChildConfigFile(parentBaseDir, moduleProps, module);
  95. // the child project may have children as well
  96. loadModulesProperties(moduleProps, projectProps, prefix + module + ".");
  97. // and finally add this child properties to global props
  98. merge(projectProps, prefix, module, moduleProps);
  99. }
  100. }
  101. }
  102. private static void merge(Properties projectProps, String prefix, String module, Properties moduleProps) {
  103. for (Map.Entry<Object, Object> entry : moduleProps.entrySet()) {
  104. projectProps.put(prefix + module + "." + entry.getKey(), entry.getValue());
  105. }
  106. }
  107. private Properties loadChildConfigFile(File parentBaseDir, Properties moduleProps, String moduleId) {
  108. final File baseDir;
  109. if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
  110. baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), parentBaseDir);
  111. setProjectBaseDir(baseDir, moduleProps, moduleId);
  112. try {
  113. if (!parentBaseDir.getCanonicalFile().equals(baseDir.getCanonicalFile())) {
  114. tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId);
  115. }
  116. } catch (IOException e) {
  117. throw new IllegalStateException("Error when resolving baseDir", e);
  118. }
  119. } else if (moduleProps.containsKey(PROPERTY_PROJECT_CONFIG_FILE)) {
  120. baseDir = loadPropsFile(parentBaseDir, moduleProps, moduleId);
  121. setProjectBaseDir(baseDir, moduleProps, moduleId);
  122. moduleProps.remove(PROPERTY_PROJECT_CONFIG_FILE);
  123. } else {
  124. baseDir = new File(parentBaseDir, moduleId);
  125. setProjectBaseDir(baseDir, moduleProps, moduleId);
  126. tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId);
  127. }
  128. return moduleProps;
  129. }
  130. private static void setProjectBaseDir(File baseDir, Properties childProps, String moduleId) {
  131. if (!baseDir.isDirectory()) {
  132. throw new IllegalStateException(MessageFormat.format("The base directory of the module ''{0}'' does not exist: {1}", moduleId, baseDir.getAbsolutePath()));
  133. }
  134. childProps.put(PROPERTY_PROJECT_BASEDIR, baseDir.getAbsolutePath());
  135. }
  136. protected static Properties extractModuleProperties(String module, Properties properties) {
  137. Properties moduleProps = new Properties();
  138. String propertyPrefix = module + ".";
  139. int prefixLength = propertyPrefix.length();
  140. for (Map.Entry<Object, Object> entry : properties.entrySet()) {
  141. String key = (String) entry.getKey();
  142. if (key.startsWith(propertyPrefix)) {
  143. moduleProps.put(key.substring(prefixLength), entry.getValue());
  144. }
  145. }
  146. return moduleProps;
  147. }
  148. private static File locatePropertiesFile(Properties props, String homeKey, String relativePathFromHome, String settingsKey) {
  149. File settingsFile = null;
  150. String runnerHome = props.getProperty(homeKey, "");
  151. if (!"".equals(runnerHome)) {
  152. settingsFile = new File(runnerHome, relativePathFromHome);
  153. }
  154. if (settingsFile == null || !settingsFile.exists()) {
  155. String settingsPath = props.getProperty(settingsKey, "");
  156. if (!"".equals(settingsPath)) {
  157. settingsFile = new File(settingsPath);
  158. }
  159. }
  160. return settingsFile;
  161. }
  162. private static Properties toProperties(File file) {
  163. InputStream in = null;
  164. try {
  165. Properties properties = new Properties();
  166. in = new FileInputStream(file);
  167. properties.load(in);
  168. // Trim properties
  169. for (String propKey : properties.stringPropertyNames()) {
  170. properties.setProperty(propKey, properties.getProperty(propKey).trim());
  171. }
  172. return properties;
  173. } catch (Exception e) {
  174. throw new IllegalStateException("Fail to load file: " + file.getAbsolutePath(), e);
  175. } finally {
  176. if (in != null) {
  177. try {
  178. in.close();
  179. } catch (IOException e) {
  180. // Ignore errors
  181. }
  182. }
  183. }
  184. }
  185. /**
  186. * @return baseDir
  187. */
  188. protected File loadPropsFile(File parentBaseDir, Properties moduleProps, String moduleId) {
  189. File propertyFile = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_CONFIG_FILE), parentBaseDir);
  190. if (propertyFile.isFile()) {
  191. Properties propsFromFile = toProperties(propertyFile);
  192. for (Entry<Object, Object> entry : propsFromFile.entrySet()) {
  193. moduleProps.put(entry.getKey(), entry.getValue());
  194. }
  195. File baseDir = null;
  196. if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
  197. baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile());
  198. } else {
  199. baseDir = propertyFile.getParentFile();
  200. }
  201. setProjectBaseDir(baseDir, moduleProps, moduleId);
  202. return baseDir;
  203. } else {
  204. throw new IllegalStateException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath());
  205. }
  206. }
  207. private static void tryToFindAndLoadPropsFile(File baseDir, Properties moduleProps, String moduleId) {
  208. File propertyFile = new File(baseDir, SONAR_PROJECT_PROPERTIES_FILENAME);
  209. if (propertyFile.isFile()) {
  210. Properties propsFromFile = toProperties(propertyFile);
  211. for (Entry<Object, Object> entry : propsFromFile.entrySet()) {
  212. moduleProps.put(entry.getKey(), entry.getValue());
  213. }
  214. if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
  215. File overwrittenBaseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile());
  216. setProjectBaseDir(overwrittenBaseDir, moduleProps, moduleId);
  217. }
  218. }
  219. }
  220. /**
  221. * Returns the file denoted by the given path, may this path be relative to "baseDir" or absolute.
  222. */
  223. protected static File getFileFromPath(String path, File baseDir) {
  224. File propertyFile = new File(path.trim());
  225. if (!propertyFile.isAbsolute()) {
  226. propertyFile = new File(baseDir, propertyFile.getPath());
  227. }
  228. return propertyFile;
  229. }
  230. /**
  231. * Transforms a comma-separated list String property in to a array of trimmed strings.
  232. *
  233. * This works even if they are separated by whitespace characters (space char, EOL, ...)
  234. *
  235. */
  236. static String[] getListFromProperty(Properties properties, String key) {
  237. String value = properties.getProperty(key, "").trim();
  238. if (value.isEmpty()) {
  239. return new String[0];
  240. }
  241. String[] values = value.split(",");
  242. List<String> trimmedValues = new ArrayList<>();
  243. for (int i = 0; i < values.length; i++) {
  244. String trimmedValue = values[i].trim();
  245. if (!trimmedValue.isEmpty()) {
  246. trimmedValues.add(trimmedValue);
  247. }
  248. }
  249. return trimmedValues.toArray(new String[trimmedValues.size()]);
  250. }
  251. }