@@ -62,12 +62,19 @@ class Conf { | |||
result.putAll(System.getProperties()); | |||
result.putAll(loadEnvironmentProperties()); | |||
result.putAll(cli.properties()); | |||
result = resolve(result); | |||
// root project base directory must be present and be absolute | |||
result.setProperty(PROPERTY_PROJECT_BASEDIR, getRootProjectBaseDir(result).toString()); | |||
result.remove(PROJECT_HOME); | |||
return result; | |||
} | |||
private Properties resolve(Properties props) { | |||
PropertyResolver resolver = new PropertyResolver(props, env); | |||
return resolver.resolve(); | |||
} | |||
private Properties loadEnvironmentProperties() { | |||
return Utils.loadEnvironmentProperties(env); | |||
} |
@@ -0,0 +1,104 @@ | |||
/* | |||
* SonarQube Scanner | |||
* Copyright (C) 2011-2017 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonarsource.scanner.cli; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
public class PropertyResolver { | |||
private static final Pattern placeholderPattern = Pattern.compile("\\$\\{([\\w\\.]+)\\}|\\$([\\w\\.]+)"); | |||
private final Properties props; | |||
private final Properties resolved; | |||
private final List<String> queue; | |||
private Map<String, String> env; | |||
public PropertyResolver(Properties props, Map<String, String> env) { | |||
this.props = props; | |||
this.env = env; | |||
this.resolved = new Properties(); | |||
this.queue = new LinkedList<>(); | |||
} | |||
public Properties resolve() { | |||
for (Map.Entry<Object, Object> e : props.entrySet()) { | |||
if (resolved.containsKey(e.getKey())) { | |||
continue; | |||
} | |||
resolveProperty((String) e.getKey()); | |||
} | |||
return resolved; | |||
} | |||
private String getValue(String key) { | |||
String propValue; | |||
if (key.startsWith("env.")) { | |||
String envKey = key.substring(4); | |||
propValue = env.get(envKey); | |||
} else { | |||
propValue = props.getProperty(key); | |||
} | |||
return propValue != null ? propValue : ""; | |||
} | |||
private String resolveProperty(String propKey) { | |||
String propValue = getValue(propKey); | |||
if (propValue.isEmpty()) { | |||
return propValue; | |||
} | |||
Matcher m = placeholderPattern.matcher(propValue); | |||
StringBuffer sb = new StringBuffer(); | |||
while (m.find()) { | |||
String varName = (null == m.group(1)) ? m.group(2) : m.group(1); | |||
if (queue.contains(varName)) { | |||
throw new IllegalArgumentException("Found a loop resolving place holders in properties, for variable: " + varName); | |||
} | |||
String placeholderValue = resolveVar(varName); | |||
m.appendReplacement(sb, Matcher.quoteReplacement(placeholderValue)); | |||
} | |||
m.appendTail(sb); | |||
String resolvedPropValue = sb.toString(); | |||
resolved.setProperty(propKey, resolvedPropValue); | |||
return resolvedPropValue; | |||
} | |||
private String resolveVar(String varName) { | |||
String placeholderValue; | |||
if (resolved.containsKey(varName)) { | |||
placeholderValue = resolved.getProperty(varName); | |||
} else { | |||
queue.add(varName); | |||
placeholderValue = resolveProperty(varName); | |||
queue.remove(varName); | |||
} | |||
return placeholderValue; | |||
} | |||
} |
@@ -64,7 +64,7 @@ public class MainTest { | |||
when(runnerFactory.create(any(Properties.class))).thenReturn(runner); | |||
when(conf.properties()).thenReturn(properties); | |||
} | |||
@Test | |||
public void should_execute_runner() { | |||
Main main = new Main(exit, cli, conf, runnerFactory, logs); |
@@ -0,0 +1,123 @@ | |||
/* | |||
* SonarQube Scanner | |||
* Copyright (C) 2011-2017 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonarsource.scanner.cli; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Properties; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
public class PropertyResolverTest { | |||
@Rule | |||
public ExpectedException exception = ExpectedException.none(); | |||
@Test | |||
public void resolve_properties() { | |||
Properties map = new Properties(); | |||
Map<String, String> env = new HashMap<>(); | |||
// resolve | |||
map.put("A", "value a"); | |||
map.put("B", "value b"); | |||
map.put("C", "${A} ${B} ${nonexisting}"); | |||
PropertyResolver resolver = new PropertyResolver(map, env); | |||
Properties resolved = resolver.resolve(); | |||
assertThat(resolved.get("A")).isEqualTo("value a"); | |||
assertThat(resolved.get("B")).isEqualTo("value b"); | |||
assertThat(resolved.get("C")).isEqualTo("value a value b "); | |||
map.clear(); | |||
map.put("sonar.login", "admin"); | |||
map.put("sonar.password", "${sonar.login}"); | |||
resolver = new PropertyResolver(map, env); | |||
resolved = resolver.resolve(); | |||
assertThat(resolved.get("sonar.password")).isEqualTo("admin"); | |||
} | |||
@Test | |||
public void use_env() { | |||
Properties map = new Properties(); | |||
Map<String, String> env = new HashMap<>(); | |||
// resolve | |||
map.put("A", "invalid"); | |||
map.put("B", "value b"); | |||
map.put("C", "${env.A} ${B} ${nonexisting}"); | |||
env.put("A", "value a"); | |||
PropertyResolver resolver = new PropertyResolver(map, env); | |||
Properties resolved = resolver.resolve(); | |||
assertThat(resolved.get("A")).isEqualTo("invalid"); | |||
assertThat(resolved.get("B")).isEqualTo("value b"); | |||
assertThat(resolved.get("C")).isEqualTo("value a value b "); | |||
} | |||
@Test | |||
public void resolve_recursively() { | |||
Properties map = new Properties(); | |||
Map<String, String> env = new HashMap<>(); | |||
map.put("A", "value a"); | |||
map.put("B", "${A}"); | |||
map.put("C", "${A} ${B}"); | |||
PropertyResolver resolver = new PropertyResolver(map, env); | |||
Properties resolved = resolver.resolve(); | |||
assertThat(resolved.get("A")).isEqualTo("value a"); | |||
assertThat(resolved.get("B")).isEqualTo("value a"); | |||
assertThat(resolved.get("C")).isEqualTo("value a value a"); | |||
} | |||
@Test | |||
public void dont_resolve_nested() { | |||
Properties map = new Properties(); | |||
Map<String, String> env = new HashMap<>(); | |||
map.put("A", "value a"); | |||
map.put("B", "value b"); | |||
map.put("C", "${A ${B}}"); | |||
PropertyResolver resolver = new PropertyResolver(map, env); | |||
Properties resolved = resolver.resolve(); | |||
assertThat(resolved.get("A")).isEqualTo("value a"); | |||
assertThat(resolved.get("B")).isEqualTo("value b"); | |||
assertThat(resolved.get("C")).isEqualTo("${A value b}"); | |||
} | |||
@Test | |||
public void fail_loop_properties_resolution() { | |||
Properties map = new Properties(); | |||
Map<String, String> env = new HashMap<>(); | |||
// resolve | |||
map.put("A", "${B}"); | |||
map.put("B", "${A}"); | |||
exception.expect(IllegalArgumentException.class); | |||
exception.expectMessage("variable: B"); | |||
PropertyResolver resolver = new PropertyResolver(map, env); | |||
resolver.resolve(); | |||
} | |||
} |