]> source.dussan.org Git - sonarqube.git/commitdiff
Replacement injection of ServletContext by Properties in Platform
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 16 Apr 2014 17:05:09 +0000 (19:05 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 16 Apr 2014 17:05:09 +0000 (19:05 +0200)
53 files changed:
sonar-application/pom.xml
sonar-application/src/main/assembly/conf/sonar.properties
sonar-application/src/main/java/org/sonar/application/AesCipher.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/Base64Cipher.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/Cipher.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/EmbeddedTomcat.java
sonar-application/src/main/java/org/sonar/application/Encryption.java [new file with mode: 0644]
sonar-application/src/main/java/org/sonar/application/Props.java
sonar-application/src/test/java/org/sonar/application/AesCipherTest.java [new file with mode: 0644]
sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java [new file with mode: 0644]
sonar-application/src/test/java/org/sonar/application/EncryptionTest.java [new file with mode: 0644]
sonar-application/src/test/java/org/sonar/application/PropsTest.java
sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt [new file with mode: 0644]
sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt [new file with mode: 0644]
sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt [new file with mode: 0644]
sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt [new file with mode: 0644]
sonar-application/src/test/resources/org/sonar/application/PropsTest/sonar.properties
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerMetadata.java
sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java [deleted file]
sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java [deleted file]
sonar-plugin-api/pom.xml
sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java
sonar-plugin-api/src/main/resources/sq-version.txt [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/platform/PlatformServletContextListener.java
sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
sonar-server/src/main/java/org/sonar/server/platform/ServerSettings.java
sonar-server/src/main/java/org/sonar/server/platform/SonarHome.java
sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java
sonar-server/src/main/java/org/sonar/server/source/CodeColorizers.java
sonar-server/src/main/java/org/sonar/server/startup/GenerateBootstrapIndex.java
sonar-server/src/main/java/org/sonar/server/startup/GwtPublisher.java
sonar-server/src/main/java/org/sonar/server/startup/RegisterDashboards.java
sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java
sonar-server/src/main/java/org/sonar/server/startup/RegisterNewMeasureFilters.java
sonar-server/src/main/java/org/sonar/server/text/MacroInterpreter.java
sonar-server/src/test/java/org/sonar/server/platform/PersistentSettingsTest.java
sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java
sonar-server/src/test/java/org/sonar/server/platform/ServerLifecycleNotifierTest.java
sonar-server/src/test/java/org/sonar/server/platform/ServerSettingsTest.java
sonar-server/src/test/java/org/sonar/server/plugins/ServerPluginJarsInstallerTest.java
sonar-server/src/test/java/org/sonar/server/startup/GenerateBootstrapIndexTest.java
sonar-server/src/test/java/org/sonar/server/startup/GwtPublisherTest.java
sonar-server/src/test/java/org/sonar/server/text/MacroInterpreterTest.java
sonar-server/src/test/resources/org/sonar/server/platform/PersistentSettingsTest/conf/sonar.properties [deleted file]
sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/empty-version.txt [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties [deleted file]
sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-version.properties [deleted file]
sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-without-version.properties [deleted file]
sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/version.txt [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties [deleted file]

index 3f6a630951b62be69bfdb94d49243a6ef2fa8c76..34c0f89f99ab22871093a454ded62fa4498aded1 100644 (file)
   <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>
index 6c058aa32278866d07c30703f02fc89642ebed07..65954622bb9ab36980ff22a4eab265cd1a52c0a3 100644 (file)
@@ -82,7 +82,7 @@ sonar.jdbc.timeBetweenEvictionRunsMillis=30000
 
 # 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
diff --git a/sonar-application/src/main/java/org/sonar/application/AesCipher.java b/sonar-application/src/main/java/org/sonar/application/AesCipher.java
new file mode 100644 (file)
index 0000000..d006146
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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;
+  }
+}
diff --git a/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java b/sonar-application/src/main/java/org/sonar/application/Base64Cipher.java
new file mode 100644 (file)
index 0000000..5abbeb8
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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));
+  }
+}
diff --git a/sonar-application/src/main/java/org/sonar/application/Cipher.java b/sonar-application/src/main/java/org/sonar/application/Cipher.java
new file mode 100644 (file)
index 0000000..44abfbb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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);
+}
diff --git a/sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java b/sonar-application/src/main/java/org/sonar/application/ConfigurationUtils.java
new file mode 100644 (file)
index 0000000..a5a563a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+  }
+}
index ddb1efad898eb8967ef7e151313e258aa85b9a5f..a677f8bcf11efdfbefb9d85384d9412b15911a29 100644 (file)
@@ -65,6 +65,7 @@ class EmbeddedTomcat {
     tomcat.getHost().setDeployOnStartup(true);
 
     Props props = Props.create(env);
+
     Logging.configure(tomcat, env, props);
     Connectors.configure(tomcat, props);
     Webapp.configure(tomcat, env, props);
diff --git a/sonar-application/src/main/java/org/sonar/application/Encryption.java b/sonar-application/src/main/java/org/sonar/application/Encryption.java
new file mode 100644 (file)
index 0000000..60e732f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+  }
+
+}
index 7b33d918cc64b3e59d447b280f6fa8bde0e49080..51a6cdfe49e2d3e97ae64c738745a8f3adde6631 100644 (file)
@@ -22,16 +22,14 @@ package org.sonar.application;
 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) {
@@ -80,8 +78,18 @@ class 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) {
@@ -91,4 +99,19 @@ class Props {
       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;
+  }
 }
diff --git a/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java b/sonar-application/src/test/java/org/sonar/application/AesCipherTest.java
new file mode 100644 (file)
index 0000000..9f09709
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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");
+  }
+
+}
diff --git a/sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java b/sonar-application/src/test/java/org/sonar/application/ConfigurationUtilsTest.java
new file mode 100644 (file)
index 0000000..29a722b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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}"));
+  }
+
+}
diff --git a/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java b/sonar-application/src/test/java/org/sonar/application/EncryptionTest.java
new file mode 100644 (file)
index 0000000..85b3c56
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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"));
+  }
+}
index 9209174030131f745dfbf5ed2fd4f92f193fb0c2..46ca5e8dd26cdc04c449fab0e4d0d36fc8d865d9 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.application;
 
+import com.google.common.io.Resources;
 import org.apache.commons.io.FilenameUtils;
 import org.junit.Test;
 
@@ -31,6 +32,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 public class PropsTest {
+
   @Test
   public void of() throws Exception {
     Properties p = new Properties();
@@ -99,8 +101,10 @@ public class PropsTest {
 
   @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);
@@ -109,7 +113,11 @@ public class PropsTest {
     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
diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/aes_secret_key.txt
new file mode 100644 (file)
index 0000000..65b98c5
--- /dev/null
@@ -0,0 +1 @@
+0PZz+G+f8mjr3sPn4+AhHg==
\ No newline at end of file
diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/bad_secret_key.txt
new file mode 100644 (file)
index 0000000..b33e179
--- /dev/null
@@ -0,0 +1 @@
+badbadbad==
\ No newline at end of file
diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/non_trimmed_secret_key.txt
new file mode 100644 (file)
index 0000000..ab83e4a
--- /dev/null
@@ -0,0 +1,3 @@
+
+   0PZz+G+f8mjr3sPn4+AhHg==
+
diff --git a/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt b/sonar-application/src/test/resources/org/sonar/application/AesCipherTest/other_secret_key.txt
new file mode 100644 (file)
index 0000000..23f5ecf
--- /dev/null
@@ -0,0 +1 @@
+IBxEUxZ41c8XTxyaah1Qlg==
\ No newline at end of file
index cf664e315dfde37bb998ef7fffba2f4e1cc558c5..01d2b5915c1574f1b08319732bacad9e5c05e4ad 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.CoreProperties;
 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;
@@ -69,6 +70,21 @@ public class ServerMetadata extends Server implements BatchComponent {
     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();
diff --git a/sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java b/sonar-core/src/main/java/org/sonar/core/config/ConfigurationUtils.java
deleted file mode 100644 (file)
index d3875f7..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java b/sonar-core/src/test/java/org/sonar/core/config/ConfigurationUtilsTest.java
deleted file mode 100644 (file)
index 92452b2..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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"));
-  }
-}
index 74f16c9a01e6520c9b980d9a2eea76c33cfc440b..2cd8b5bea8222ed795c49783fb68e0ed5264d000 100644 (file)
@@ -1,5 +1,6 @@
 <?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>
index d6c39401c2d1501d7e57618290ead11a3d46de12..7fd8afd1b1ed9e51e8cf7f994af0cf30c37d95d2 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.platform;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
 
+import java.io.File;
 import java.util.Date;
 
 /**
@@ -35,6 +36,12 @@ public abstract class Server implements BatchComponent, ServerComponent {
 
   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
diff --git a/sonar-plugin-api/src/main/resources/sq-version.txt b/sonar-plugin-api/src/main/resources/sq-version.txt
new file mode 100644 (file)
index 0000000..ad96e7c
--- /dev/null
@@ -0,0 +1 @@
+${project.version}
index 473fbd0e8f8bb3901c03a8c0e33c2779559be6fa..cf7a2d6ac0fb7c308706f5158643ffb914e0b959 100644 (file)
@@ -21,12 +21,12 @@ package org.sonar.server.platform;
 
 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;
 
@@ -47,26 +47,22 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
   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
@@ -76,19 +72,19 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
       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();
@@ -117,15 +113,15 @@ public class DefaultServerFileSystem implements ServerFileSystem, Startable {
   }
 
   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() {
index 353893e7d0bf90abc70087769fcfa364d03924dd..510b3f6f620c8d943e4a90ad43efeabab22c53af 100644 (file)
@@ -25,7 +25,8 @@ import org.sonar.api.platform.Server;
 import org.sonar.core.persistence.DatabaseVersion;
 
 import javax.annotation.CheckForNull;
-import javax.servlet.ServletContext;
+
+import java.util.Properties;
 
 /**
  * @since 2.2
@@ -40,7 +41,7 @@ public class Platform {
   private boolean dbConnected = false;
   private boolean started = false;
 
-  private Platform() {
+  public Platform() {
   }
 
   public static Platform getInstance() {
@@ -65,8 +66,8 @@ public class Platform {
     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();
index 6257c03551de03f2719d4c56cd8ca17d6c4a74a5..82e2e6595962f90d85a54ac8986ebfb9f3991685 100644 (file)
@@ -50,7 +50,7 @@ public final class PlatformServletContextListener implements ServletContextListe
   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":
index 79d4e11ac18aa1f56e1ea334c9f554d09a96f012..8ceb686e0a08ac0e9d806c62dc35653939cb1ffe 100644 (file)
@@ -20,7 +20,9 @@
 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;
@@ -30,8 +32,10 @@ import org.sonar.api.CoreProperties;
 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;
@@ -42,21 +46,24 @@ public final class ServerImpl extends Server implements Startable {
   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
@@ -64,13 +71,17 @@ public final class ServerImpl extends Server implements Startable {
     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) {
@@ -107,6 +118,32 @@ public final class ServerImpl extends Server implements Startable {
     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();
 
@@ -122,6 +159,7 @@ public final class ServerImpl extends Server implements Startable {
 
     return properties;
   }
+
   @Override
   public String getURL() {
     return null;
index 81d5116b6dcb182af6d2afb03bb36c3b047615cd..21c301b3cfe44937a073cf0ab2281199fcd40b61 100644 (file)
  */
 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;
@@ -47,23 +43,14 @@ 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));
   }
@@ -74,43 +61,14 @@ public class ServerSettings extends Settings {
 
   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);
index 26cf45ed572c65a24b47d1fa14ea03caf20b8783..2f1b7bcfafd03d8ffc9cbb4cfbc3948b72ad0521 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.platform;
 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;
 
@@ -33,6 +34,8 @@ 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 {
 
@@ -40,12 +43,10 @@ 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;
     }
   });
@@ -55,19 +56,19 @@ final class SonarHome {
   }
 
   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;
   }
index bcb7546ed086ff56fa2590974afd670fb0207924..f8daf5ae57eccae8ef7354b0c2633dafd8130e35 100644 (file)
@@ -23,7 +23,6 @@ import org.apache.commons.io.IOUtils;
 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;
@@ -33,7 +32,6 @@ import javax.servlet.http.HttpServletResponse;
 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.
@@ -48,17 +46,7 @@ public class BatchResourcesServlet extends HttpServlet {
   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;
index 16509e8b9da1e0c70d7ebd75e3a68cdaf670200d..67803e8a6e5b12cabd25bdd3e5b039676eceddbb 100644 (file)
@@ -19,6 +19,7 @@
  */
 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;
@@ -49,6 +50,13 @@ public class CodeColorizers implements ServerExtension {
     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;
index 70def7e35b0dfb78eb97e0492f50c84a8aaff37c..4b71498785af07096a9e14588689c65bad1eaf1f 100644 (file)
  */
 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
@@ -45,11 +46,13 @@ public final class GenerateBootstrapIndex {
   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;
   }
 
@@ -61,25 +64,32 @@ public final class GenerateBootstrapIndex {
     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);
         }
index 8980988255bcaf34813e5cd9af3d62de72aa5ab5..a69b50a758cafc6bb17c27db84a6ca7eaf835509 100644 (file)
  */
 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;
@@ -41,15 +42,23 @@ import java.util.zip.ZipEntry;
 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;
@@ -62,7 +71,7 @@ public class GwtPublisher {
     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();
 
index 11f85d0e53de5e948f4064d538c469159543296b..317e08d9a472d84dc14c2440b56d9d946ae2645b 100644 (file)
@@ -57,6 +57,13 @@ public class RegisterDashboards implements Startable {
     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");
index 689348d9ca02f20ff0a8e158b2af2d68d0fe14d5..5e5b6cfc582090135b4ae0a815879a2099806598 100644 (file)
@@ -51,6 +51,13 @@ public class RegisterMetrics {
     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();
index e2c8574b83856aa0683b64b2a04dcb1645c42440..755a1eed6a6ea807720b68aa5bd9d95168744a87 100644 (file)
@@ -54,6 +54,13 @@ public final class RegisterNewMeasureFilters {
     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");
 
index 0f8c60ac33500c7aa0e02742c13ded18cf1ecbe6..0971feae8a07a363109e3dc29f3406551f803abd 100644 (file)
@@ -22,17 +22,17 @@ package org.sonar.server.text;
 
 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())
     );
   }
 
index cc6f008d474f9be387018f5fabff6f0f5a97493d..3612c1045268bfde8fc645e30530a279bc9c16bd 100644 (file)
@@ -28,9 +28,9 @@ import org.sonar.api.config.PropertyDefinitions;
 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;
@@ -44,11 +44,11 @@ public class PersistentSettingsTest {
   @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
index 2101db16a7857abe573efd97b81b6fceaa388324..44954c85fb6cc5b845bb692172f4a26c2c03f8ac 100644 (file)
 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();
@@ -49,52 +68,48 @@ public class ServerImplTest {
   }
 
   @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");
 
@@ -102,4 +117,18 @@ public class ServerImplTest {
 
     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");
+  }
+
 }
index 4acb5baf2ce812b4cdb5c0b0ccd480c3f4ac1e8a..8907b3e6d5d08d1a70c7c4d645f84cc8cac574c1 100644 (file)
@@ -21,10 +21,11 @@ package org.sonar.server.platform;
 
 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.*;
@@ -99,6 +100,21 @@ class FakeServer extends Server {
     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;
index 4b7ef9b0edf467e654d65337380f85a542ec7ae9..af0cc558e8f326cc5db97ce3a35d64405b201daa 100644 (file)
@@ -21,44 +21,37 @@ package org.sonar.server.platform;
 
 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);
 
@@ -67,7 +60,6 @@ public class ServerSettingsTest {
 
   @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");
@@ -79,7 +71,7 @@ public class ServerSettingsTest {
   @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");
@@ -90,12 +82,4 @@ public class ServerSettingsTest {
     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);
-    }
-  }
 }
index caf2b8f7accce30c49a21f6dc74252075e3b82a1..fc0ef01ada4231f1a1d2b04a73a48ebab888c17f 100644 (file)
@@ -50,7 +50,7 @@ public class ServerPluginJarsInstallerTest {
   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);
@@ -67,8 +67,7 @@ public class ServerPluginJarsInstallerTest {
     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);
   }
index 3b4686a461c0ca9b4149d5afc6f6c528305696ec..ca6f7c74e0eb5299c61a07586c0bbaff58e5732b 100644 (file)
  */
 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();
index fc6ffdc39450864fe0a2d61bb6aaa9d47c925cbb..8e900809fc40cbe4ee7c6411c23ad45043f20c35 100644 (file)
@@ -45,7 +45,6 @@ public class GwtPublisherTest {
     outputDir = new File("./target/test-tmp/org/sonar/server/startup/GwtPublisherTest/output");
     if (outputDir.exists()) {
       FileUtils.forceDelete(outputDir);
-
     }
   }
 
index ef9ad6775ae32be62a8d6c8d4fe3cf9621c72d70..b993dbfd058e3abe13996bd74ba97aff933fe9ab 100644 (file)
@@ -22,8 +22,7 @@ package org.sonar.server.text;
 
 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;
@@ -36,9 +35,9 @@ public class MacroInterpreterTest {
 
   @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
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/PersistentSettingsTest/conf/sonar.properties b/sonar-server/src/test/resources/org/sonar/server/platform/PersistentSettingsTest/conf/sonar.properties
deleted file mode 100644 (file)
index b4a8077..0000000
+++ /dev/null
@@ -1 +0,0 @@
-in_file: true
\ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/empty-version.txt b/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/empty-version.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties b/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties
deleted file mode 100644 (file)
index 287ce48..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#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
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-version.properties b/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-with-version.properties
deleted file mode 100644 (file)
index 6fff0c5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#Generated by Maven
-#Fri Nov 23 14:23:53 CET 2007
-version=1.0
-groupId=org.codehaus.sonar
-artifactId=sonar-core
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-without-version.properties b/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/pom-without-version.properties
deleted file mode 100644 (file)
index 8d70c22..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#Generated by Maven
-#Fri Nov 23 14:23:53 CET 2007
-groupId=org.codehaus.sonar
-artifactId=sonar-core
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/version.txt b/sonar-server/src/test/resources/org/sonar/server/platform/ServerImplTest/version.txt
new file mode 100644 (file)
index 0000000..d3827e7
--- /dev/null
@@ -0,0 +1 @@
+1.0
diff --git a/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties b/sonar-server/src/test/resources/org/sonar/server/platform/ServerSettingsTest/conf/sonar.properties
deleted file mode 100644 (file)
index 020777d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-hello: world
-in_file: true
-ServerSettingsTestEnv: in_file
\ No newline at end of file