<description>Package the standalone distribution</description>
<dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<artifactId>http-request</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>test</scope>
- </dependency>
-
</dependencies>
<build>
# Web context. When set, it must start with forward slash (for example /sonarqube).
# The default value is root context (empty value).
-#sonar.web.context=
+#sonar.web.context=/
# TCP port for incoming HTTP connections. Disabled when value is -1.
#sonar.web.port=9000
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import javax.annotation.Nullable;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Key;
+import java.security.SecureRandom;
+
+final class AesCipher extends Cipher {
+
+ // Can't be increased because of Java 6 policy files :
+ // https://confluence.terena.org/display/~visser/No+256+bit+ciphers+for+Java+apps
+ // http://java.sun.com/javase/6/webnotes/install/jre/README
+ public static final int KEY_SIZE_IN_BITS = 128;
+
+ private static final String CRYPTO_KEY = "AES";
+
+ /**
+ * Duplication from CoreProperties.ENCRYPTION_SECRET_KEY_PATH
+ */
+ private static final String ENCRYPTION_SECRET_KEY_PATH = "sonar.secretKeyPath";
+
+ private String pathToSecretKey;
+
+ AesCipher(@Nullable String pathToSecretKey) {
+ this.pathToSecretKey = pathToSecretKey;
+ }
+
+ @Override
+ String encrypt(String clearText) {
+ try {
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
+ cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile());
+ return new String(Base64.encodeBase64(cipher.doFinal(clearText.getBytes("UTF-8"))));
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ String decrypt(String encryptedText) {
+ try {
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
+ cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile());
+ byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
+ return new String(cipherData);
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ /**
+ * This method checks the existence of the file, but not the validity of the contained key.
+ */
+ boolean hasSecretKey() {
+ String path = getPathToSecretKey();
+ if (StringUtils.isNotBlank(path)) {
+ File file = new File(path);
+ return file.exists() && file.isFile();
+ }
+ return false;
+ }
+
+ private Key loadSecretFile() throws IOException {
+ String path = getPathToSecretKey();
+ return loadSecretFileFromFile(path);
+ }
+
+ @VisibleForTesting
+ Key loadSecretFileFromFile(@Nullable String path) throws IOException {
+ if (StringUtils.isBlank(path)) {
+ throw new IllegalStateException("Secret key not found. Please set the property " + ENCRYPTION_SECRET_KEY_PATH);
+ }
+ File file = new File(path);
+ if (!file.exists() || !file.isFile()) {
+ throw new IllegalStateException("The property " + ENCRYPTION_SECRET_KEY_PATH + " does not link to a valid file: " + path);
+ }
+ String s = FileUtils.readFileToString(file);
+ if (StringUtils.isBlank(s)) {
+ throw new IllegalStateException("No secret key in the file: " + path);
+ }
+ return new SecretKeySpec(Base64.decodeBase64(StringUtils.trim(s)), CRYPTO_KEY);
+ }
+
+ String generateRandomSecretKey() {
+ try {
+ KeyGenerator keyGen = KeyGenerator.getInstance(CRYPTO_KEY);
+ keyGen.init(KEY_SIZE_IN_BITS, new SecureRandom());
+ SecretKey secretKey = keyGen.generateKey();
+ return new String(Base64.encodeBase64(secretKey.getEncoded()));
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to generate secret key", e);
+ }
+ }
+
+ @VisibleForTesting
+ String getPathToSecretKey() {
+ if (StringUtils.isBlank(pathToSecretKey)) {
+ pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath();
+ }
+ return pathToSecretKey;
+ }
+
+ public void setPathToSecretKey(@Nullable String pathToSecretKey) {
+ this.pathToSecretKey = pathToSecretKey;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import org.apache.commons.codec.binary.Base64;
+
+final class Base64Cipher extends Cipher {
+ @Override
+ String encrypt(String clearText) {
+ return new String(Base64.encodeBase64(clearText.getBytes()));
+ }
+
+ @Override
+ String decrypt(String encryptedText) {
+ return new String(Base64.decodeBase64(encryptedText));
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+abstract class Cipher {
+ abstract String encrypt(String clearText);
+ abstract String decrypt(String encryptedText);
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import org.apache.commons.lang.text.StrSubstitutor;
+
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Properties;
+
+public final class ConfigurationUtils {
+
+ private ConfigurationUtils() {
+ // Utility class
+ }
+
+ static Properties interpolateEnvVariables(Properties properties) {
+ return interpolateVariables(properties, System.getenv());
+ }
+
+ static Properties interpolateVariables(Properties properties, Map<String, String> variables) {
+ Properties result = new Properties();
+ Enumeration keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ String value = (String) properties.get(key);
+ String interpolatedValue = StrSubstitutor.replace(value, variables, "${env:", "}");
+ result.setProperty(key, interpolatedValue);
+ }
+ return result;
+ }
+}
tomcat.getHost().setDeployOnStartup(true);
Props props = Props.create(env);
+
Logging.configure(tomcat, env, props);
Connectors.configure(tomcat, props);
Webapp.configure(tomcat, env, props);
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import com.google.common.collect.ImmutableMap;
+
+import javax.annotation.Nullable;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @since 3.0
+ */
+public final class Encryption {
+
+ private static final String BASE64_ALGORITHM = "b64";
+
+ private static final String AES_ALGORITHM = "aes";
+ private final AesCipher aesCipher;
+
+ private final Map<String, Cipher> ciphers;
+ private static final Pattern ENCRYPTED_PATTERN = Pattern.compile("\\{(.*?)\\}(.*)");
+
+ public Encryption(@Nullable String pathToSecretKey) {
+ aesCipher = new AesCipher(pathToSecretKey);
+ ciphers = ImmutableMap.of(
+ BASE64_ALGORITHM, new Base64Cipher(),
+ AES_ALGORITHM, aesCipher);
+ }
+ public boolean isEncrypted(String value) {
+ return value.indexOf('{') == 0 && value.indexOf('}') > 1;
+ }
+
+ public String decrypt(String encryptedText) {
+ Matcher matcher = ENCRYPTED_PATTERN.matcher(encryptedText);
+ if (matcher.matches()) {
+ Cipher cipher = ciphers.get(matcher.group(1).toLowerCase(Locale.ENGLISH));
+ if (cipher != null) {
+ return cipher.decrypt(matcher.group(2));
+ }
+ }
+ return encryptedText;
+ }
+
+}
import org.apache.commons.io.IOUtils;
import javax.annotation.Nullable;
+
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileReader;
-import java.io.IOException;
+import java.util.Map;
import java.util.Properties;
-/**
- * TODO support env substitution and encryption
- */
class Props {
+
private final Properties props;
Props(Properties props) {
FileReader reader = null;
try {
reader = new FileReader(propsFile);
+
+ // order is important : the last override the first
p.load(reader);
+ p.putAll(System.getenv());
p.putAll(System.getProperties());
+
+ p = ConfigurationUtils.interpolateEnvVariables(p);
+ p = decrypt(p);
+
+ // Set all properties as system properties to pass them to PlatformServletContextListener
+ System.setProperties(p);
+
return new Props(p);
} catch (Exception e) {
IOUtils.closeQuietly(reader);
}
}
+
+ static Properties decrypt(Properties properties) {
+ Encryption encryption = new Encryption(null);
+ Properties result = new Properties();
+
+ for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ if (encryption.isEncrypted(value)) {
+ value = encryption.decrypt(value);
+ }
+ result.setProperty(key, value);
+ }
+ return result;
+ }
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import com.google.common.io.Resources;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import javax.crypto.BadPaddingException;
+
+import java.io.File;
+import java.security.InvalidKeyException;
+import java.security.Key;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+
+public class AesCipherTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void generateRandomSecretKey() {
+ AesCipher cipher = new AesCipher(null);
+
+ String key = cipher.generateRandomSecretKey();
+
+ assertThat(StringUtils.isNotBlank(key)).isTrue();
+ assertThat(Base64.isArrayByteBase64(key.getBytes())).isTrue();
+ }
+
+ @Test
+ public void encrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ String encryptedText = cipher.encrypt("this is a secret");
+
+ assertThat(StringUtils.isNotBlank(encryptedText)).isTrue();
+ assertThat(Base64.isArrayByteBase64(encryptedText.getBytes())).isTrue();
+ }
+
+ @Test
+ public void encrypt_bad_key() throws Exception {
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("Invalid AES key");
+
+ AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt"));
+
+ cipher.encrypt("this is a secret");
+ }
+
+ @Test
+ public void decrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ // the following value has been encrypted with the key /org/sonar/api/config/AesCipherTest/aes_secret_key.txt
+ String clearText = cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+
+ assertThat(clearText).isEqualTo("this is a secret");
+ }
+
+ @Test
+ public void decrypt_bad_key() throws Exception {
+ AesCipher cipher = new AesCipher(getPath("bad_secret_key.txt"));
+
+ try {
+ cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+ fail();
+
+ } catch (RuntimeException e) {
+ assertThat(e.getCause()).isInstanceOf(InvalidKeyException.class);
+ }
+ }
+
+ @Test
+ public void decrypt_other_key() throws Exception {
+ AesCipher cipher = new AesCipher(getPath("other_secret_key.txt"));
+
+ try {
+ // text encrypted with another key
+ cipher.decrypt("9mx5Zq4JVyjeChTcVjEide4kWCwusFl7P2dSVXtg9IY=");
+ fail();
+
+ } catch (RuntimeException e) {
+ assertThat(e.getCause()).isInstanceOf(BadPaddingException.class);
+ }
+ }
+
+ @Test
+ public void encryptThenDecrypt() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ assertThat(cipher.decrypt(cipher.encrypt("foo"))).isEqualTo("foo");
+ }
+
+ @Test
+ public void testDefaultPathToSecretKey() {
+ AesCipher cipher = new AesCipher(null);
+
+ String path = cipher.getPathToSecretKey();
+
+ assertThat(StringUtils.isNotBlank(path)).isTrue();
+ assertThat(new File(path).getName()).isEqualTo("sonar-secret.txt");
+ }
+
+ @Test
+ public void loadSecretKeyFromFile() throws Exception {
+ AesCipher cipher = new AesCipher(null);
+ Key secretKey = cipher.loadSecretFileFromFile(pathToSecretKey());
+ assertThat(secretKey.getAlgorithm()).isEqualTo("AES");
+ assertThat(secretKey.getEncoded().length).isGreaterThan(10);
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_trim_content() throws Exception {
+ String path = getPath("non_trimmed_secret_key.txt");
+ AesCipher cipher = new AesCipher(null);
+
+ Key secretKey = cipher.loadSecretFileFromFile(path);
+
+ assertThat(secretKey.getAlgorithm()).isEqualTo("AES");
+ assertThat(secretKey.getEncoded().length).isGreaterThan(10);
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_file_does_not_exist() throws Exception {
+ thrown.expect(IllegalStateException.class);
+
+ AesCipher cipher = new AesCipher(null);
+ cipher.loadSecretFileFromFile("/file/does/not/exist");
+ }
+
+ @Test
+ public void loadSecretKeyFromFile_no_property() throws Exception {
+ thrown.expect(IllegalStateException.class);
+
+ AesCipher cipher = new AesCipher(null);
+ cipher.loadSecretFileFromFile(null);
+ }
+
+ @Test
+ public void hasSecretKey() throws Exception {
+ AesCipher cipher = new AesCipher(pathToSecretKey());
+
+ assertThat(cipher.hasSecretKey()).isTrue();
+ }
+
+ @Test
+ public void doesNotHaveSecretKey() throws Exception {
+ AesCipher cipher = new AesCipher("/my/twitter/id/is/SimonBrandhof");
+
+ assertThat(cipher.hasSecretKey()).isFalse();
+ }
+
+ private static String getPath(String file){
+ return Resources.getResource(AesCipherTest.class, "AesCipherTest/" + file).getPath();
+ }
+
+ private static String pathToSecretKey() throws Exception {
+ return getPath("aes_secret_key.txt");
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import com.google.common.collect.Maps;
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.Properties;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class ConfigurationUtilsTest {
+ @Test
+ public void shouldInterpolateVariables() {
+ Properties input = new Properties();
+ input.setProperty("hello", "world");
+ input.setProperty("url", "${env:SONAR_JDBC_URL}");
+ input.setProperty("do_not_change", "${SONAR_JDBC_URL}");
+ Map<String, String> variables = Maps.newHashMap();
+ variables.put("SONAR_JDBC_URL", "jdbc:h2:mem");
+
+ Properties output = ConfigurationUtils.interpolateVariables(input, variables);
+
+ assertThat(output.size(), is(3));
+ assertThat(output.getProperty("hello"), is("world"));
+ assertThat(output.getProperty("url"), is("jdbc:h2:mem"));
+ assertThat(output.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
+
+ // input is not changed
+ assertThat(input.size(), is(3));
+ assertThat(input.getProperty("hello"), is("world"));
+ assertThat(input.getProperty("url"), is("${env:SONAR_JDBC_URL}"));
+ assertThat(input.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.sonar.application;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class EncryptionTest {
+
+ @Test
+ public void isEncrypted() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.isEncrypted("{aes}ADASDASAD"), is(true));
+ assertThat(encryption.isEncrypted("{b64}ADASDASAD"), is(true));
+ assertThat(encryption.isEncrypted("{abc}ADASDASAD"), is(true));
+
+ assertThat(encryption.isEncrypted("{}"), is(false));
+ assertThat(encryption.isEncrypted("{foo"), is(false));
+ assertThat(encryption.isEncrypted("foo{aes}"), is(false));
+ }
+
+ @Test
+ public void decrypt() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("{b64}Zm9v"), is("foo"));
+ }
+
+ @Test
+ public void decrypt_unknown_algorithm() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("{xxx}Zm9v"), is("{xxx}Zm9v"));
+ }
+
+ @Test
+ public void decrypt_uncrypted_text() {
+ Encryption encryption = new Encryption(null);
+ assertThat(encryption.decrypt("foo"), is("foo"));
+ }
+}
*/
package org.sonar.application;
+import com.google.common.io.Resources;
import org.apache.commons.io.FilenameUtils;
import org.junit.Test;
import static org.mockito.Mockito.when;
public class PropsTest {
+
@Test
public void of() throws Exception {
Properties p = new Properties();
@Test
public void load_file_and_system_properties() throws Exception {
+ System.setProperty("hello", "bar");
+
Env env = mock(Env.class);
- File propsFile = new File(getClass().getResource("/org/sonar/application/PropsTest/sonar.properties").toURI());
+ File propsFile = new File(Resources.getResource(getClass(), "PropsTest/sonar.properties").getFile());
when(env.file("conf/sonar.properties")).thenReturn(propsFile);
Props props = Props.create(env);
assertThat(props.of("java.version")).isNotNull();
// system properties override file properties
+ assertThat(props.of("hello")).isEqualTo("bar");
assertThat(props.of("java.io.tmpdir")).isNotEmpty().isNotEqualTo("/should/be/overridden");
+
+ assertThat(System.getProperty("foo")).isEqualTo("bar");
+ assertThat(System.getProperty("hello")).isEqualTo("bar");
}
@Test
--- /dev/null
+0PZz+G+f8mjr3sPn4+AhHg==
\ No newline at end of file
--- /dev/null
+badbadbad==
\ No newline at end of file
--- /dev/null
+
+ 0PZz+G+f8mjr3sPn4+AhHg==
+
--- /dev/null
+IBxEUxZ41c8XTxyaah1Qlg==
\ No newline at end of file
+hello: world
foo=bar
java.io.tmpdir=/should/be/overridden
import org.sonar.api.config.Settings;
import org.sonar.api.platform.Server;
+import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
return null;
}
+ @Override
+ public File getRootDir() {
+ return null;
+ }
+
+ @Override
+ public File getDeployDir() {
+ return null;
+ }
+
+ @Override
+ public String getContextPath() {
+ return null;
+ }
+
@Override
public String getURL() {
return client.getURL();
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.sonar.core.config;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.text.StrSubstitutor;
-
-import javax.annotation.WillClose;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Enumeration;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * @since 2.12
- */
-public final class ConfigurationUtils {
-
- private ConfigurationUtils() {
- }
-
- public static void copyProperties(Properties from, Map<String, String> to) {
- for (Map.Entry<Object, Object> entry : from.entrySet()) {
- String key = (String) entry.getKey();
- to.put(key, entry.getValue().toString());
- }
- }
-
- public static Properties openProperties(File file) throws IOException {
- FileInputStream input = FileUtils.openInputStream(file);
- return readInputStream(input);
- }
-
- /**
- * Note that the input stream is closed in this method.
- */
- public static Properties readInputStream(@WillClose InputStream input) throws IOException {
- try {
- Properties p = new Properties();
- p.load(input);
- return p;
-
- } finally {
- IOUtils.closeQuietly(input);
- }
- }
-
- public static Properties interpolateEnvVariables(Properties properties) {
- return interpolateVariables(properties, System.getenv());
- }
-
- public static Properties interpolateVariables(Properties properties, Map<String, String> variables) {
- Properties result = new Properties();
- Enumeration keys = properties.keys();
- while (keys.hasMoreElements()) {
- String key = (String) keys.nextElement();
- String value = (String) properties.get(key);
- String interpolatedValue = StrSubstitutor.replace(value, variables, "${env:", "}");
- result.setProperty(key, interpolatedValue);
- }
- return result;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube 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.sonar.core.config;
-
-import com.google.common.collect.Maps;
-import org.junit.Test;
-
-import java.util.Map;
-import java.util.Properties;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-public class ConfigurationUtilsTest {
- @Test
- public void shouldInterpolateVariables() {
- Properties input = new Properties();
- input.setProperty("hello", "world");
- input.setProperty("url", "${env:SONAR_JDBC_URL}");
- input.setProperty("do_not_change", "${SONAR_JDBC_URL}");
- Map<String, String> variables = Maps.newHashMap();
- variables.put("SONAR_JDBC_URL", "jdbc:h2:mem");
-
- Properties output = ConfigurationUtils.interpolateVariables(input, variables);
-
- assertThat(output.size(), is(3));
- assertThat(output.getProperty("hello"), is("world"));
- assertThat(output.getProperty("url"), is("jdbc:h2:mem"));
- assertThat(output.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
-
- // input is not changed
- assertThat(input.size(), is(3));
- assertThat(input.getProperty("hello"), is("world"));
- assertThat(input.getProperty("url"), is("${env:SONAR_JDBC_URL}"));
- assertThat(input.getProperty("do_not_change"), is("${SONAR_JDBC_URL}"));
- }
-
- @Test
- public void shouldCopyProperties() {
- Properties input = new Properties();
- input.setProperty("hello", "world");
- input.setProperty("foo", "bar");
- Map<String, String> output = Maps.newHashMap();
-
- ConfigurationUtils.copyProperties(input, output);
-
- assertThat(output.size(), is(2));
- assertThat(output.get("hello"), is("world"));
- assertThat(output.get("foo"), is("bar"));
-
- // input is not changed
- assertThat(input.size(), is(2));
- assertThat(input.getProperty("hello"), is("world"));
- assertThat(input.getProperty("foo"), is("bar"));
- }
-}
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
</executions>
</plugin>
</plugins>
+
+ <resources>
+ <resource>
+ <!-- Used to set SonarQube version in sq-version.txt file -->
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
</build>
</project>
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
+import java.io.File;
import java.util.Date;
/**
public abstract Date getStartedAt();
+ public abstract File getRootDir();
+
+ public abstract File getDeployDir();
+
+ public abstract String getContextPath();
+
/**
* @return the server URL when executed from batch, else null.
* @since 2.4
--- /dev/null
+${project.version}
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
-import org.apache.commons.lang.StringUtils;
import org.picocontainer.Startable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
+import org.sonar.api.platform.Server;
import org.sonar.api.platform.ServerFileSystem;
import org.sonar.core.persistence.Database;
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultServerFileSystem.class);
private Database database;
- private File deployDir;
+ private final Server server;
private File homeDir;
- public DefaultServerFileSystem(Database database, Settings settings) {
+ public DefaultServerFileSystem(Database database, Settings settings, Server server) {
this.database = database;
+ this.server = server;
this.homeDir = new File(settings.getString(CoreProperties.SONAR_HOME));
-
- String deployPath = settings.getString(ServerSettings.DEPLOY_DIR);
- if (StringUtils.isNotBlank(deployPath)) {
- this.deployDir = new File(deployPath);
- }
}
/**
* for unit tests
*/
- public DefaultServerFileSystem(Database database, File homeDir, File deployDir) {
+ public DefaultServerFileSystem(Database database, File homeDir, Server server) {
this.database = database;
- this.deployDir = deployDir;
this.homeDir = homeDir;
+ this.server = server;
}
@Override
throw new IllegalStateException("SonarQube home directory does not exist");
}
- if (deployDir == null) {
- throw new IllegalStateException("The target directory to deploy libraries is not set");
- }
-
try {
- LOGGER.info("Deploy dir: " + deployDir.getAbsolutePath());
- FileUtils.forceMkdir(deployDir);
- for (File subDirectory : deployDir.listFiles((FileFilter) FileFilterUtils.directoryFileFilter())) {
+ if (getDeployDir() == null) {
+ throw new IllegalArgumentException("Web app directory does not exist: " + getDeployDir());
+ }
+
+ LOGGER.info("Deploy dir: " + getDeployDir().getAbsolutePath());
+ FileUtils.forceMkdir(getDeployDir());
+ for (File subDirectory : getDeployDir().listFiles((FileFilter) FileFilterUtils.directoryFileFilter())) {
FileUtils.cleanDirectory(subDirectory);
}
} catch (IOException e) {
- throw new IllegalStateException("The following directory can not be created: " + deployDir.getAbsolutePath(), e);
+ throw new IllegalStateException("The following directory can not be created: " + getDeployDir().getAbsolutePath(), e);
}
File deprecated = getDeprecatedPluginsDir();
}
public File getDeployDir() {
- return deployDir;
+ return server.getDeployDir();
}
public File getDeployedJdbcDriverIndex() {
- return new File(deployDir, "jdbc-driver.txt");
+ return new File(getDeployDir(), "jdbc-driver.txt");
}
public File getDeployedPluginsDir() {
- return new File(deployDir, "plugins");
+ return new File(getDeployDir(), "plugins");
}
public File getDownloadedPluginsDir() {
import org.sonar.core.persistence.DatabaseVersion;
import javax.annotation.CheckForNull;
-import javax.servlet.ServletContext;
+
+import java.util.Properties;
/**
* @since 2.2
private boolean dbConnected = false;
private boolean started = false;
- private Platform() {
+ public Platform() {
}
public static Platform getInstance() {
return null;
}
- public void init(ServletContext servletContext) {
- serverComponents = new ServerComponents(this, servletContext);
+ public void init(Properties properties) {
+ serverComponents = new ServerComponents(this, properties);
if (!dbConnected) {
startLevel1Container();
startLevel2Container();
public void contextInitialized(ServletContextEvent event) {
try {
configureLogback(event);
- Platform.getInstance().init(event.getServletContext());
+ Platform.getInstance().init(System.getProperties());
Platform.getInstance().doStart();
} catch (Throwable t) {
// Tomcat 7 "limitations":
package org.sonar.server.platform;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
+import com.google.common.io.Resources;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.picocontainer.Startable;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.Server;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
private final Settings settings;
private final Date startedAt;
private final String buildProperties;
- private final String pomProperties;
+ private final String versionPath;
private String id;
private String version;
private String implementationBuild;
+ private String contextPath;
+ private File sonarHome;
+ private File deployDir;
public ServerImpl(Settings settings) {
- this(settings, "/build.properties", "/META-INF/maven/org.codehaus.sonar/sonar-plugin-api/pom.properties");
+ this(settings, "/build.properties", "/sq-version.txt");
}
@VisibleForTesting
- ServerImpl(Settings settings, String buildProperties, String pomProperties) {
+ ServerImpl(Settings settings, String buildProperties, String versionPath) {
this.settings = settings;
this.startedAt = new Date();
this.buildProperties = buildProperties;
- this.pomProperties = pomProperties;
+ this.versionPath = versionPath;
}
@Override
try {
id = new SimpleDateFormat("yyyyMMddHHmmss").format(startedAt);
- version = read(pomProperties).getProperty("version", "");
+ version = readVersion(versionPath);
implementationBuild = read(buildProperties).getProperty("Implementation-Build");
+ contextPath = StringUtils.defaultIfBlank(settings.getString("sonar.web.context"), "/");
- if (StringUtils.isBlank(version)) {
- throw new IllegalStateException("Unknown SonarQube version");
+ sonarHome = new File(settings.getString(CoreProperties.SONAR_HOME));
+ if (!sonarHome.isDirectory()) {
+ throw new IllegalStateException("SonarQube home directory is not valid");
}
+ deployDir = new File(sonarHome, "/web/deploy/");
+
LOG.info("SonarQube {}", Joiner.on(" / ").skipNulls().join("Server", version, implementationBuild));
} catch (IOException e) {
return startedAt;
}
+ @Override
+ public File getRootDir() {
+ return sonarHome;
+ }
+
+ @Override
+ public File getDeployDir() {
+ return deployDir;
+ }
+
+ @Override
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ private static String readVersion(String filename) throws IOException {
+ URL url = ServerImpl.class.getResource(filename);
+ if (url != null) {
+ String version = Resources.toString(url, Charsets.UTF_8);
+ if (!StringUtils.isBlank(version)) {
+ return StringUtils.deleteWhitespace(version);
+ }
+ }
+ throw new IllegalStateException("Unknown SonarQube version");
+ }
+
private static Properties read(String filename) throws IOException {
Properties properties = new Properties();
return properties;
}
+
@Override
public String getURL() {
return null;
*/
package org.sonar.server.platform;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.configuration.Configuration;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
-import org.sonar.core.config.ConfigurationUtils;
import javax.annotation.Nullable;
-import javax.servlet.ServletContext;
-import java.io.File;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
*/
public class ServerSettings extends Settings {
- public static final String DEPLOY_DIR = "sonar.web.deployDir";
-
+ private final Properties properties;
private Configuration deprecatedConfiguration;
- private File deployDir;
- private File sonarHome;
-
- public ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, ServletContext servletContext) {
- this(definitions, deprecatedConfiguration, getDeployDir(servletContext), SonarHome.getHome());
- }
- @VisibleForTesting
- ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, File deployDir, File sonarHome) {
+ public ServerSettings(PropertyDefinitions definitions, Configuration deprecatedConfiguration, Properties properties) {
super(definitions);
this.deprecatedConfiguration = deprecatedConfiguration;
- this.deployDir = deployDir;
- this.sonarHome = sonarHome;
- load(Collections.<String, String> emptyMap());
+ this.properties = properties;
+ load(Collections.<String, String>emptyMap());
// Secret key is loaded from conf/sonar.properties
getEncryption().setPathToSecretKey(getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
}
private ServerSettings load(Map<String, String> databaseSettings) {
clear();
- setProperty(CoreProperties.SONAR_HOME, sonarHome.getAbsolutePath());
- setProperty(DEPLOY_DIR, deployDir.getAbsolutePath());
// order is important : the last override the first
addProperties(databaseSettings);
- loadPropertiesFile(sonarHome);
- addEnvironmentVariables();
- addSystemProperties();
+ addProperties(properties);
return this;
}
- private void loadPropertiesFile(File sonarHome) {
- File propertiesFile = new File(sonarHome, "conf/sonar.properties");
- if (!propertiesFile.isFile() || !propertiesFile.exists()) {
- throw new IllegalStateException("Properties file does not exist: " + propertiesFile);
- }
- try {
- Properties p = ConfigurationUtils.openProperties(propertiesFile);
- addProperties(ConfigurationUtils.interpolateEnvVariables(p));
- } catch (Exception e) {
- throw new IllegalStateException("Fail to load configuration file: " + propertiesFile, e);
- }
- }
-
- static File getDeployDir(ServletContext servletContext) {
- String dirname = servletContext.getRealPath("/deploy/");
- if (dirname == null) {
- throw new IllegalArgumentException("Web app directory not found : /deploy/");
- }
- File dir = new File(dirname);
- if (!dir.exists()) {
- throw new IllegalArgumentException("Web app directory does not exist: " + dir);
- }
- return dir;
- }
-
@Override
protected void doOnSetProperty(String key, @Nullable String value) {
deprecatedConfiguration.setProperty(key, value);
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
import java.io.File;
* </ol>
*
* @since 2.12
+ *
+ * TODO Delete it as it's now useless (SONAR_HOME is set by Tomcat)
*/
final class SonarHome {
// only static methods
}
- static final String SONAR_HOME = "SONAR_HOME";
-
static Supplier<File> homeSupplier = Suppliers.memoize(new Supplier<File>() {
public File get() {
File home = locate();
- System.setProperty(SONAR_HOME, home.getAbsolutePath());
+ System.setProperty(CoreProperties.SONAR_HOME, home.getAbsolutePath());
return home;
}
});
}
static File locate() {
- String value = System.getProperty(SONAR_HOME);
+ String value = System.getProperty(CoreProperties.SONAR_HOME);
if (StringUtils.isBlank(value)) {
- value = System.getenv(SONAR_HOME);
+ value = System.getenv(CoreProperties.SONAR_HOME);
}
if (StringUtils.isBlank(value)) {
- throw new IllegalStateException("The system property or env variable " + SONAR_HOME + " is not set");
+ throw new IllegalStateException("The system property or env variable " + CoreProperties.SONAR_HOME + " is not set");
}
File dir = new File(value);
if (!dir.isDirectory() || !dir.exists()) {
- throw new IllegalStateException(SONAR_HOME + " is not valid: " + value + ". Please fix the env variable/system " +
- "property " + SONAR_HOME);
+ throw new IllegalStateException(CoreProperties.SONAR_HOME + " is not valid: " + value + ". Please fix the env variable/system " +
+ "property " + CoreProperties.SONAR_HOME);
}
return dir;
}
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.server.startup.GenerateBootstrapIndex;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintWriter;
/**
* This servlet allows to load libraries from directory "WEB-INF/lib" in order to provide them for batch-bootstrapper.
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename = filename(request);
if (StringUtils.isBlank(filename)) {
- PrintWriter writer = null;
- try {
- response.setContentType("text/plain");
- writer = response.getWriter();
- writer.print(StringUtils.join(GenerateBootstrapIndex.getLibs(getServletContext()), ','));
- } catch (IOException e) {
- LOG.error("Unable to provide list of batch resources", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- } finally {
- IOUtils.closeQuietly(writer);
- }
+ throw new IllegalArgumentException("Filename is missing.");
} else {
InputStream in = null;
OutputStream out = null;
*/
package org.sonar.server.source;
+import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.ServerExtension;
import org.sonar.api.utils.Logs;
Logs.INFO.info("Code colorizer, supported languages: " + StringUtils.join(byLang.keySet(), ","));
}
+ /**
+ * Used when no plugin is defining some CodeColorizerFormat
+ */
+ public CodeColorizers() {
+ this(Lists.<CodeColorizerFormat>newArrayList());
+ }
+
public String toHtml(String code, String language) {
CodeColorizerFormat format = byLang.get(language);
List<Tokenizer> tokenizers;
*/
package org.sonar.server.startup;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.lang.CharUtils;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.platform.Server;
import org.sonar.home.cache.FileHashes;
import org.sonar.server.platform.DefaultServerFileSystem;
-import javax.servlet.ServletContext;
-
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStream;
+import java.util.Collection;
import java.util.List;
-import java.util.Set;
/**
* @since 3.5
private static final String[] IGNORE = {"jtds", "mysql", "postgresql", "jruby", "jfreechart", "eastwood",
"elasticsearch", "lucene"};
- private final ServletContext servletContext;
+ private static final String LIB_DIR = "/web/WEB-INF/lib";
+
+ private final Server server;
private final DefaultServerFileSystem fileSystem;
- public GenerateBootstrapIndex(DefaultServerFileSystem fileSystem, ServletContext servletContext) {
- this.servletContext = servletContext;
+ public GenerateBootstrapIndex(DefaultServerFileSystem fileSystem, Server server) {
+ this.server = server;
this.fileSystem = fileSystem;
}
FileUtils.forceMkdir(indexFile.getParentFile());
FileWriter writer = new FileWriter(indexFile, false);
try {
- for (String path : getLibs(servletContext)) {
- writer.append(path);
- InputStream is = servletContext.getResourceAsStream("/WEB-INF/lib/" + path);
- writer.append("|").append(new FileHashes().of(is));
- writer.append(CharUtils.LF);
+ File libDir = new File(server.getRootDir(), LIB_DIR);
+ // TODO hack for Medium tests
+ if (libDir.exists()) {
+ for (String path : getLibs(libDir)) {
+ writer.append(path);
+ File is = new File(libDir, path);
+ writer.append("|").append(new FileHashes().of(is));
+ writer.append(CharUtils.LF);
+ }
+ writer.flush();
}
- writer.flush();
} finally {
IOUtils.closeQuietly(writer);
}
}
- public static List<String> getLibs(ServletContext servletContext) {
+ @VisibleForTesting
+ static List<String> getLibs(File libDir) {
List<String> libs = Lists.newArrayList();
- Set<String> paths = servletContext.getResourcePaths("/WEB-INF/lib/");
- for (String path : paths) {
+
+ Collection<File> files = FileUtils.listFiles(libDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
+ for (File file : files) {
+ String path = file.getPath();
if (StringUtils.endsWith(path, ".jar")) {
- String filename = StringUtils.removeStart(path, "/WEB-INF/lib/");
+ String filename = StringUtils.removeStart(path, libDir.getAbsolutePath() + "/");
if (!isIgnored(filename)) {
libs.add(filename);
}
*/
package org.sonar.server.startup;
+import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.config.Settings;
+import org.sonar.api.platform.Server;
import org.sonar.api.utils.Logs;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.api.utils.ZipUtils;
import org.sonar.api.web.GwtExtension;
-import org.sonar.server.platform.ServerSettings;
import java.io.File;
import java.io.IOException;
public class GwtPublisher {
private static final Logger LOG = LoggerFactory.getLogger(GwtPublisher.class);
- private Settings settings;
+ private Server server;
private GwtExtension[] extensions = null;
private File outputDir = null;
- public GwtPublisher(GwtExtension[] extensions, Settings settings) {
+ public GwtPublisher(GwtExtension[] extensions, Settings settings, Server server) {
this.extensions = extensions;
- this.settings = settings;
+ this.server = server;
}
+ /**
+ * Used when no plugin is defining some GwtExtension
+ */
+ public GwtPublisher(Settings settings, Server server) {
+ this(new GwtExtension[]{}, settings, server);
+ }
+
+ @VisibleForTesting
GwtPublisher(GwtExtension[] extensions, File outputDir) {
this.extensions = extensions;
this.outputDir = outputDir;
TimeProfiler profiler = new TimeProfiler().start("Deploy GWT plugins");
try {
cleanDirectory();
- this.outputDir = new File(settings.getString(ServerSettings.DEPLOY_DIR), "gwt");
+ this.outputDir = new File(server.getDeployDir(), "gwt");
LoggerFactory.getLogger(GwtPublisher.class).debug("Deploy {} GWT extensions to {}", extensions.length, outputDir);
publish();
this.loadedTemplateDao = loadedTemplateDao;
}
+ /**
+ * Used when no plugin is defining some DashboardTemplate
+ */
+ public RegisterDashboards(DashboardDao dashboardDao, ActiveDashboardDao activeDashboardDao, LoadedTemplateDao loadedTemplateDao) {
+ this(new DashboardTemplate[]{}, dashboardDao, activeDashboardDao, loadedTemplateDao);
+ }
+
@Override
public void start() {
TimeProfiler profiler = new TimeProfiler(LOG).start("Register dashboards");
this.conditionDao = conditionDao;
}
+ /**
+ * Used when no plugin is defining Metrics
+ */
+ public RegisterMetrics(MeasuresDao measuresDao, QualityGateConditionDao conditionDao) {
+ this(measuresDao, conditionDao, new Metrics[]{});
+ }
+
public void start() {
TimeProfiler profiler = new TimeProfiler().start("Load metrics");
measuresDao.disableAutomaticMetrics();
this.loadedTemplateDao = loadedTemplateDao;
}
+ /**
+ * Used when no plugin is defining some FilterTemplate
+ */
+ public RegisterNewMeasureFilters(MeasureFilterDao filterDao, LoadedTemplateDao loadedTemplateDao) {
+ this(new FilterTemplate[]{}, filterDao, loadedTemplateDao);
+ }
+
public void start() {
TimeProfiler profiler = new TimeProfiler(LOG).start("Register measure filters");
import com.google.common.collect.ImmutableList;
import org.sonar.api.ServerComponent;
+import org.sonar.api.platform.Server;
-import javax.servlet.ServletContext;
import java.util.List;
public class MacroInterpreter implements ServerComponent {
private final List<Macro> macros;
- public MacroInterpreter(ServletContext servletContext) {
+ public MacroInterpreter(Server server) {
this.macros = ImmutableList.<Macro>of(
- new RuleMacro(servletContext.getContextPath())
+ new RuleMacro(server.getContextPath())
);
}
import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.properties.PropertyDto;
-import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
+import java.util.Properties;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.argThat;
@Before
public void init() throws URISyntaxException {
dao = mock(PropertiesDao.class);
+
settings = new ServerSettings(
new PropertyDefinitions(),
new PropertiesConfiguration(),
- new File("."),
- new File(PersistentSettingsTest.class.getResource("/org/sonar/server/platform/PersistentSettingsTest/").toURI()));
+ new Properties());
}
@Test
package org.sonar.server.platform;
import org.hamcrest.core.Is;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.Settings;
+import java.io.File;
+
import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.assertThat;
public class ServerImplTest {
+
@Rule
public ExpectedException exception = ExpectedException.none();
+ @Rule
+ public TemporaryFolder sonarHome = new TemporaryFolder();
+
+ Settings settings;
+
+ ServerImpl server;
+
+ @Before
+ public void setUp() throws Exception {
+ settings = new Settings().setProperty(CoreProperties.SONAR_HOME, sonarHome.getRoot().getAbsolutePath());
+ new File(sonarHome.getRoot(), "web/deploy").mkdirs();
+
+ server = new ServerImpl(settings, "/org/sonar/server/platform/ServerImplTest/build.properties", "/org/sonar/server/platform/ServerImplTest/version.txt");
+ }
+
@Test
- public void alwaysReturnTheSameValues() {
- ServerImpl server = new ServerImpl(new Settings(), "", "/org/sonar/server/platform/ServerImplTest/pom-with-version.properties");
+ public void always_return_the_same_values() {
server.start();
assertThat(server.getId()).isNotNull();
}
@Test
- public void getVersionFromFile() {
- ServerImpl server = new ServerImpl(new Settings(), "", "/org/sonar/server/platform/ServerImplTest/pom-with-version.properties");
+ public void read_version_from_file() {
server.start();
assertThat(server.getVersion()).isEqualTo("1.0");
}
@Test
- public void getImplementationBuildFromManifest() {
- ServerImpl server = new ServerImpl(new Settings(),
- "/org/sonar/server/platform/ServerImplTest/build.properties",
- "/org/sonar/server/platform/ServerImplTest/pom-with-version.properties");
+ public void read_implementation_build_from_manifest() {
server.start();
assertThat(server.getImplementationBuild()).isEqualTo("0b9545a8b74aca473cb776275be4dc93a327c363");
}
@Test
- public void testFileWithNoVersion() {
+ public void read_file_with_no_version() {
exception.expect(IllegalStateException.class);
exception.expectMessage("Unknown SonarQube version");
- ServerImpl server = new ServerImpl(new Settings(), "", "/org/sonar/server/platform/ServerImplTest/pom-without-version.properties");
+ ServerImpl server = new ServerImpl(settings, "", "/org/sonar/server/platform/ServerImplTest/empty-version.txt");
server.start();
}
@Test
- public void testFileWithEmptyVersionParameter() {
+ public void read_file_with_empty_version() {
exception.expect(IllegalStateException.class);
exception.expectMessage("Unknown SonarQube version");
- ServerImpl server = new ServerImpl(new Settings(), "", "/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties");
+ ServerImpl server = new ServerImpl(settings, "", "/org/sonar/server/platform/ServerImplTest/empty-version.txt");
server.start();
}
@Test
- public void shouldFailIfFileNotFound() {
+ public void fail_if_version_file_not_found() {
exception.expect(IllegalStateException.class);
exception.expectMessage("Unknown SonarQube version");
- ServerImpl server = new ServerImpl(new Settings(), "", "/org/sonar/server/platform/ServerImplTest/unknown-file.properties");
+ ServerImpl server = new ServerImpl(settings, "", "/org/sonar/server/platform/ServerImplTest/unknown-file.properties");
server.start();
}
@Test
- public void shouldLoadServerIdFromDatabase() {
+ public void load_server_id_from_database() {
Settings settings = new Settings();
settings.setProperty(CoreProperties.PERMANENT_SERVER_ID, "abcde");
assertThat(server.getPermanentServerId(), Is.is("abcde"));
}
+
+ @Test
+ public void use_default_context_path() {
+ server.start();
+ assertThat(server.getContextPath()).isEqualTo("/");
+ }
+
+ @Test
+ public void get_context_path_from_settings() {
+ settings.setProperty("sonar.web.context", "/my_path");
+ server.start();
+ assertThat(server.getContextPath()).isEqualTo("/my_path");
+ }
+
}
import org.junit.Before;
import org.junit.Test;
+import org.sonar.api.platform.Server;
import org.sonar.api.platform.ServerStartHandler;
import org.sonar.api.platform.ServerStopHandler;
-import org.sonar.api.platform.Server;
+import java.io.File;
import java.util.Date;
import static org.mockito.Mockito.*;
return null;
}
+ @Override
+ public File getRootDir() {
+ return null;
+ }
+
+ @Override
+ public File getDeployDir() {
+ return null;
+ }
+
+ @Override
+ public String getContextPath() {
+ return null;
+ }
+
@Override
public String getURL() {
return null;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.configuration.BaseConfiguration;
+import org.junit.Before;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinitions;
-import java.io.File;
-import java.net.URISyntaxException;
import java.util.Map;
+import java.util.Properties;
import static org.fest.assertions.Assertions.assertThat;
public class ServerSettingsTest {
- private static File home = getHome();
+ Properties properties;
- @Test
- public void load_properties_file() {
- ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home);
+ ServerSettings settings;
- assertThat(settings.getString("hello")).isEqualTo("world");
+ @Before
+ public void before() throws Exception {
+ properties = new Properties();
+ properties.put("hello", "world");
+ properties.put("in_file", "true");
+ properties.put("ServerSettingsTestEnv", "in_file");
+ settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), properties);
}
@Test
- public void systemPropertiesShouldOverridePropertiesFile() {
- System.setProperty("ServerSettingsTestEnv", "in_env");
- ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home);
-
- assertThat(settings.getString("ServerSettingsTestEnv")).isEqualTo("in_env");
- }
-
- @Test(expected = IllegalStateException.class)
- public void fail_if_properties_file_is_not_found() {
- File sonarHome = new File("unknown/path");
- new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), sonarHome);
+ public void load_properties_file() {
+ assertThat(settings.getString("hello")).isEqualTo("world");
}
@Test
- public void activateDatabaseSettings() {
- ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home);
-
+ public void activate_database_settings() {
Map<String, String> databaseProperties = ImmutableMap.of("in_db", "true");
settings.activateDatabaseSettings(databaseProperties);
@Test
public void file_settings_override_db_settings() {
- ServerSettings settings = new ServerSettings(new PropertyDefinitions(), new BaseConfiguration(), new File("."), home);
assertThat(settings.getString("in_file")).isEqualTo("true");
Map<String, String> databaseProperties = ImmutableMap.of("in_file", "false");
@Test
public void synchronize_deprecated_commons_configuration() {
BaseConfiguration deprecated = new BaseConfiguration();
- ServerSettings settings = new ServerSettings(new PropertyDefinitions(), deprecated, new File("."), home);
+ ServerSettings settings = new ServerSettings(new PropertyDefinitions(), deprecated, properties);
assertThat(settings.getString("in_file")).isEqualTo("true");
assertThat(deprecated.getString("in_file")).isEqualTo("true");
settings.removeProperty("foo");
assertThat(deprecated.getString("foo")).isNull();
}
-
- private static File getHome() {
- try {
- return new File(ServerSettingsTest.class.getResource("/org/sonar/server/platform/ServerSettingsTest/").toURI());
- } catch (URISyntaxException e) {
- throw new IllegalStateException(e);
- }
- }
}
public TemporaryFolder temp = new TemporaryFolder();
DefaultServerFileSystem fileSystem;
- File homeDir, deployDir, pluginsDir, downloadsDir, bundledDir, trashDir, coreDir;
+ File homeDir, pluginsDir, downloadsDir, bundledDir, trashDir, coreDir;
ServerPluginJarInstaller jarInstaller;
ServerPluginJarsInstaller jarsInstaller;
Server server = mock(Server.class);
bundledDir = new File(homeDir, "lib/bundled-plugins");
coreDir = new File(homeDir, "lib/core-plugins");
FileUtils.forceMkdir(bundledDir);
- deployDir = temp.newFolder("deploy");
- fileSystem = new DefaultServerFileSystem(mock(Database.class), homeDir, deployDir);
+ fileSystem = new DefaultServerFileSystem(mock(Database.class), homeDir, server);
jarInstaller = new ServerPluginJarInstaller();
jarsInstaller = new ServerPluginJarsInstaller(server, upgradeStatus, fileSystem, jarInstaller);
}
*/
package org.sonar.server.startup;
-import com.google.common.collect.Sets;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
-import javax.servlet.ServletContext;
-
-import java.util.Set;
+import java.io.File;
+import java.io.IOException;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class GenerateBootstrapIndexTest {
+ @Rule
+ public TemporaryFolder rootDir = new TemporaryFolder();
+
@Before
public void setUp() throws Exception {
}
@Test
- public void shouldDetermineListOfResources() {
- ServletContext servletContext = mock(ServletContext.class);
- Set<String> libs = Sets.newHashSet();
- libs.add("/WEB-INF/lib/sonar-core-2.6.jar");
- libs.add("/WEB-INF/lib/treemap.rb");
- libs.add("/WEB-INF/lib/directory/");
- when(servletContext.getResourcePaths(anyString())).thenReturn(libs);
+ public void determine_list_of_resources() throws IOException {
+ new File(rootDir.getRoot(), "/web/WEB-INF/lib").mkdirs();
+ File webInf = new File(rootDir.getRoot(), "/web/WEB-INF");
+ File lib = new File(rootDir.getRoot(), "/web/WEB-INF/lib");
+ new File(webInf, "directory").mkdir();
+ new File(lib, "sonar-core-2.6.jar").createNewFile();
+ new File(lib, "treemap.rbr").createNewFile();
+ new File(lib, "sonar-core-2.6.jar").createNewFile();
- assertThat(GenerateBootstrapIndex.getLibs(servletContext)).hasSize(1);
- assertThat(GenerateBootstrapIndex.getLibs(servletContext).get(0)).isEqualTo("sonar-core-2.6.jar");
+ assertThat(GenerateBootstrapIndex.getLibs(lib)).hasSize(1);
+ assertThat(GenerateBootstrapIndex.getLibs(lib).get(0)).isEqualTo("sonar-core-2.6.jar");
}
@Test
- public void shouldIgnore() {
+ public void ignore_some_jars() {
assertThat(GenerateBootstrapIndex.isIgnored("sonar-batch-2.6-SNAPSHOT.jar")).isFalse();
assertThat(GenerateBootstrapIndex.isIgnored("mysql-connector-java-5.1.13.jar")).isTrue();
assertThat(GenerateBootstrapIndex.isIgnored("postgresql-9.0-801.jdbc3.jar")).isTrue();
outputDir = new File("./target/test-tmp/org/sonar/server/startup/GwtPublisherTest/output");
if (outputDir.exists()) {
FileUtils.forceDelete(outputDir);
-
}
}
import org.junit.Before;
import org.junit.Test;
-
-import javax.servlet.ServletContext;
+import org.sonar.api.platform.Server;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@Before
public void setUp() {
- ServletContext servletContext = mock(ServletContext.class);
- when(servletContext.getContextPath()).thenReturn(path);
- interpreter = new MacroInterpreter(servletContext);
+ Server server = mock(Server.class);
+ when(server.getContextPath()).thenReturn(path);
+ interpreter = new MacroInterpreter(server);
}
@Test
+++ /dev/null
-in_file: true
\ No newline at end of file
+++ /dev/null
-#Generated by Maven
-#Fri Nov 23 14:23:53 CET 2007
-groupId=org.codehaus.sonar
-artifactId=sonar-core
-version=
\ No newline at end of file
+++ /dev/null
-#Generated by Maven
-#Fri Nov 23 14:23:53 CET 2007
-version=1.0
-groupId=org.codehaus.sonar
-artifactId=sonar-core
+++ /dev/null
-#Generated by Maven
-#Fri Nov 23 14:23:53 CET 2007
-groupId=org.codehaus.sonar
-artifactId=sonar-core
+++ /dev/null
-hello: world
-in_file: true
-ServerSettingsTestEnv: in_file
\ No newline at end of file