import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Properties;
class Conf {
private static final String RUNNER_SETTINGS = "runner.settings";
private static final String PROJECT_HOME = "project.home";
private static final String PROJECT_SETTINGS = "project.settings";
+ private static final String PROPERTY_MODULES = "sonar.modules";
+ private static final String PROPERTY_PROJECT_BASEDIR = "sonar.projectBaseDir";
+ private static final String PROPERTY_PROJECT_CONFIG_FILE = "sonar.projectConfigFile";
private final Cli cli;
result.putAll(loadProjectProperties());
result.putAll(System.getProperties());
result.putAll(cli.properties());
-
- if (result.containsKey(PROJECT_HOME)) {
- // the real property of the Sonar Runner is "sonar.projectBaseDir"
- String baseDir = result.getProperty(PROJECT_HOME);
- result.remove(PROJECT_HOME);
- result.put("sonar.projectBaseDir", baseDir);
- }
+ result.remove(PROJECT_HOME);
return result;
}
}
private Properties loadProjectProperties() throws IOException {
- File settingsFile = locatePropertiesFile(cli.properties(), PROJECT_HOME, "sonar-project.properties", PROJECT_SETTINGS);
- if (settingsFile != null && settingsFile.isFile() && settingsFile.exists()) {
- Logs.info("Project configuration file: " + settingsFile.getAbsolutePath());
- return toProperties(settingsFile);
+ Properties cliProps = cli.properties();
+ File rootSettingsFile = locatePropertiesFile(cliProps, cliProps.containsKey(PROPERTY_PROJECT_BASEDIR) ? PROPERTY_PROJECT_BASEDIR : PROJECT_HOME, "sonar-project.properties",
+ PROJECT_SETTINGS);
+ if (rootSettingsFile != null && rootSettingsFile.isFile() && rootSettingsFile.exists()) {
+ Logs.info("Project configuration file: " + rootSettingsFile.getAbsolutePath());
+ Properties projectProps = new Properties();
+ Properties rootProps = toProperties(rootSettingsFile);
+ projectProps.putAll(rootProps);
+ initRootProjectBaseDir(cliProps, rootProps);
+ loadModulesProperties(rootProps, projectProps, "");
+ return projectProps;
}
Logs.info("Project configuration file: NONE");
return new Properties();
}
+ private void initRootProjectBaseDir(Properties cliProps, Properties rootProps) {
+ if (!cliProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
+ String baseDir = cliProps.getProperty(PROJECT_HOME);
+ rootProps.put(PROPERTY_PROJECT_BASEDIR, baseDir);
+ } else {
+ rootProps.put(PROPERTY_PROJECT_BASEDIR, cliProps.getProperty(PROPERTY_PROJECT_BASEDIR));
+ }
+ }
+
+ private void loadModulesProperties(Properties parentProps, Properties projectProps, String prefix) {
+ File parentBaseDir = new File(parentProps.getProperty(PROPERTY_PROJECT_BASEDIR));
+ if (parentProps.containsKey(PROPERTY_MODULES)) {
+ for (String module : getListFromProperty(parentProps, PROPERTY_MODULES)) {
+ Properties moduleProps = extractModuleProperties(module, parentProps);
+ moduleProps = loadChildConfigFile(parentBaseDir, moduleProps, module);
+
+ // the child project may have children as well
+ loadModulesProperties(moduleProps, projectProps, module + ".");
+ // and finally add this child properties to global props
+ merge(projectProps, prefix, module, moduleProps);
+ }
+ }
+
+ }
+
+ private void merge(Properties projectProps, String prefix, String module, Properties moduleProps) {
+ for (Map.Entry<Object, Object> entry : moduleProps.entrySet()) {
+ projectProps.put(prefix + module + "." + entry.getKey(), entry.getValue());
+ }
+ }
+
+ private Properties loadChildConfigFile(File parentBaseDir, Properties moduleProps, String moduleId) {
+ final File baseDir;
+ if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
+ baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), parentBaseDir);
+ setProjectBaseDir(baseDir, moduleProps, moduleId);
+ try {
+ if (!parentBaseDir.getCanonicalFile().equals(baseDir.getCanonicalFile())) {
+ tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Error when resolving baseDir", e);
+ }
+ } else if (moduleProps.containsKey(PROPERTY_PROJECT_CONFIG_FILE)) {
+ baseDir = loadPropsFile(parentBaseDir, moduleProps, moduleId);
+ moduleProps.remove(PROPERTY_PROJECT_CONFIG_FILE);
+ } else {
+ baseDir = new File(parentBaseDir, moduleId);
+ setProjectBaseDir(baseDir, moduleProps, moduleId);
+ tryToFindAndLoadPropsFile(baseDir, moduleProps, moduleId);
+ }
+
+ return moduleProps;
+ }
+
+ private static void setProjectBaseDir(File baseDir, Properties childProps, String moduleId) {
+ if (!baseDir.isDirectory()) {
+ throw new IllegalStateException("The base directory of the module '" + moduleId + "' does not exist: " + baseDir.getAbsolutePath());
+ }
+ childProps.put(PROPERTY_PROJECT_BASEDIR, baseDir.getAbsolutePath());
+ }
+
+ protected static Properties extractModuleProperties(String module, Properties properties) {
+ Properties moduleProps = new Properties();
+ String propertyPrefix = module + ".";
+ int prefixLength = propertyPrefix.length();
+ for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+ String key = (String) entry.getKey();
+ if (key.startsWith(propertyPrefix)) {
+ moduleProps.put(key.substring(prefixLength), entry.getValue());
+ }
+ }
+ return moduleProps;
+ }
+
private File locatePropertiesFile(Properties props, String homeKey, String relativePathFromHome, String settingsKey) {
File settingsFile = null;
String runnerHome = props.getProperty(homeKey, "");
return settingsFile;
}
- private static Properties toProperties(File file) throws IOException {
+ private static Properties toProperties(File file) {
InputStream in = null;
try {
Properties properties = new Properties();
} finally {
if (in != null) {
- in.close();
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Ignore errors
+ }
+ }
+ }
+ }
+
+ /**
+ * @return baseDir
+ */
+ protected File loadPropsFile(File parentBaseDir, Properties moduleProps, String moduleId) {
+ File propertyFile = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_CONFIG_FILE), parentBaseDir);
+ if (propertyFile.isFile()) {
+ Properties propsFromFile = toProperties(propertyFile);
+ for (Entry<Object, Object> entry : propsFromFile.entrySet()) {
+ moduleProps.put(entry.getKey(), entry.getValue());
+ }
+ File baseDir = null;
+ if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
+ baseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile());
+ } else {
+ baseDir = propertyFile.getParentFile();
}
+ setProjectBaseDir(baseDir, moduleProps, moduleId);
+ return baseDir;
+ } else {
+ throw new IllegalStateException("The properties file of the module '" + moduleId + "' does not exist: " + propertyFile.getAbsolutePath());
+ }
+ }
+
+ private void tryToFindAndLoadPropsFile(File baseDir, Properties moduleProps, String moduleId) {
+ File propertyFile = new File(baseDir, "sonar-project.properties");
+ if (propertyFile.isFile()) {
+ Properties propsFromFile = toProperties(propertyFile);
+ for (Entry<Object, Object> entry : propsFromFile.entrySet()) {
+ moduleProps.put(entry.getKey(), entry.getValue());
+ }
+ if (moduleProps.containsKey(PROPERTY_PROJECT_BASEDIR)) {
+ File overwrittenBaseDir = getFileFromPath(moduleProps.getProperty(PROPERTY_PROJECT_BASEDIR), propertyFile.getParentFile());
+ setProjectBaseDir(overwrittenBaseDir, moduleProps, moduleId);
+ }
+ }
+ }
+
+ /**
+ * Returns the file denoted by the given path, may this path be relative to "baseDir" or absolute.
+ */
+ protected static File getFileFromPath(String path, File baseDir) {
+ File propertyFile = new File(path.trim());
+ if (!propertyFile.isAbsolute()) {
+ propertyFile = new File(baseDir, propertyFile.getPath());
+ }
+ return propertyFile;
+ }
+
+ /**
+ * Transforms a comma-separated list String property in to a array of trimmed strings.
+ *
+ * This works even if they are separated by whitespace characters (space char, EOL, ...)
+ *
+ */
+ static String[] getListFromProperty(Properties properties, String key) {
+ String value = properties.getProperty(key, "");
+ String[] values = value.split(",");
+ for (int i = 0; i < values.length; i++) {
+ values[i] = values[i].trim();
}
+ return values;
}
}
package org.sonar.runner;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.util.Properties;
public class ConfTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
Properties args = new Properties();
Cli cli = mock(Cli.class);
Conf conf = new Conf(cli);
assertThat(conf.properties().get("sonar.prop")).isEqualTo("otherValue");
}
- // @Test
- // public void shouldLoadCompleteConfiguration() throws Exception {
- // File runnerHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/runner").toURI());
- // File projectHome = new File(getClass().getResource("/org/sonar/runner/MainTest/shouldLoadCompleteConfiguration/project").toURI());
- // Main main = new Main();
- // Properties args = main.parseArguments(new String[] {
- // "-D", "runner.home=" + runnerHome.getCanonicalPath(),
- // "-D", "project.home=" + projectHome.getCanonicalPath()
- // });
- // main.loadProperties(args);
- //
- // assertThat(main.projectProperties.getProperty("project.prop")).isEqualTo("foo");
- // assertThat(main.projectProperties.getProperty("overridden.prop")).isEqualTo("project scope");
- // assertThat(main.globalProperties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar");
- // }
+ @Test
+ public void shouldLoadCompleteConfiguration() throws Exception {
+ File runnerHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadCompleteConfiguration/runner").toURI());
+ File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadCompleteConfiguration/project").toURI());
+ args.setProperty("runner.home", runnerHome.getCanonicalPath());
+ args.setProperty("project.home", projectHome.getCanonicalPath());
+
+ Properties properties = conf.properties();
+
+ assertThat(properties.getProperty("project.prop")).isEqualTo("foo");
+ assertThat(properties.getProperty("overridden.prop")).isEqualTo("project scope");
+ assertThat(properties.getProperty("global.prop")).isEqualTo("jdbc:mysql:localhost/sonar");
+ }
+
+ @Test
+ public void shouldLoadModuleConfiguration() throws Exception {
+ File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project").toURI());
+ args.setProperty("project.home", projectHome.getCanonicalPath());
+
+ Properties properties = conf.properties();
+
+ assertThat(properties.getProperty("module1.sonar.projectName")).isEqualTo("Module 1");
+ assertThat(properties.getProperty("module2.sonar.projectName")).isEqualTo("Module 2");
+ }
+
+ @Test
+ public void shouldSupportSettingBaseDirFromCli() throws Exception {
+ File projectHome = new File(getClass().getResource("/org/sonar/runner/ConfTest/shouldLoadModuleConfiguration/project").toURI());
+ args.setProperty("project.home", temp.newFolder().getCanonicalPath());
+ args.setProperty("sonar.projectBaseDir", projectHome.getCanonicalPath());
+
+ Properties properties = conf.properties();
+
+ assertThat(properties.getProperty("module1.sonar.projectName")).isEqualTo("Module 1");
+ assertThat(properties.getProperty("module2.sonar.projectName")).isEqualTo("Module 2");
+ }
}