From df0aa9bad95dd1dc3181f5ab59bb9746123cd679 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Thu, 23 Feb 2017 09:55:29 +0100 Subject: [PATCH] SQSCANNER-9 Allow to use variables in the configuration file --- .../org/sonarsource/scanner/cli/Conf.java | 7 + .../scanner/cli/PropertyResolver.java | 104 +++++++++++++++ .../org/sonarsource/scanner/cli/MainTest.java | 2 +- .../scanner/cli/PropertyResolverTest.java | 123 ++++++++++++++++++ 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/sonarsource/scanner/cli/PropertyResolver.java create mode 100644 src/test/java/org/sonarsource/scanner/cli/PropertyResolverTest.java diff --git a/src/main/java/org/sonarsource/scanner/cli/Conf.java b/src/main/java/org/sonarsource/scanner/cli/Conf.java index 9facab1..8bfe39c 100644 --- a/src/main/java/org/sonarsource/scanner/cli/Conf.java +++ b/src/main/java/org/sonarsource/scanner/cli/Conf.java @@ -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); } diff --git a/src/main/java/org/sonarsource/scanner/cli/PropertyResolver.java b/src/main/java/org/sonarsource/scanner/cli/PropertyResolver.java new file mode 100644 index 0000000..d1fe2c8 --- /dev/null +++ b/src/main/java/org/sonarsource/scanner/cli/PropertyResolver.java @@ -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 queue; + private Map env; + + public PropertyResolver(Properties props, Map env) { + this.props = props; + this.env = env; + this.resolved = new Properties(); + this.queue = new LinkedList<>(); + } + + public Properties resolve() { + for (Map.Entry 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; + + } +} diff --git a/src/test/java/org/sonarsource/scanner/cli/MainTest.java b/src/test/java/org/sonarsource/scanner/cli/MainTest.java index 59606b2..59da205 100644 --- a/src/test/java/org/sonarsource/scanner/cli/MainTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/MainTest.java @@ -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); diff --git a/src/test/java/org/sonarsource/scanner/cli/PropertyResolverTest.java b/src/test/java/org/sonarsource/scanner/cli/PropertyResolverTest.java new file mode 100644 index 0000000..659d4a8 --- /dev/null +++ b/src/test/java/org/sonarsource/scanner/cli/PropertyResolverTest.java @@ -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 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 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 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 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 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(); + } +} -- 2.39.5