]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2603 generate server key at startup
authorSimon Brandhof <simon.brandhof@gmail.com>
Tue, 9 Aug 2011 16:57:41 +0000 (18:57 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Tue, 9 Aug 2011 16:57:41 +0000 (18:57 +0200)
18 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/startup/ServerMetadataPersister.java
sonar-server/src/main/webapp/WEB-INF/app/models/server.rb
sonar-server/src/test/java/org/sonar/server/platform/ServerImplTest.java
sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/platform/ServerLifecycleNotifierTest.java
sonar-server/src/test/java/org/sonar/server/startup/ServerMetadataPersisterTest.java
sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testSaveProperties-result.xml
sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties-result.xml
sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testUpdateExistingProperties.xml

index 97b8e06428cf55c0080d31f647f1e9963d7a4861..13137d94a44162cf08df13253391d1323078a094 100644 (file)
@@ -44,6 +44,11 @@ import org.sonar.plugins.core.widgets.*;
 import java.util.List;
 
 @Properties({
+    @Property(
+        key = CoreProperties.ORGANIZATION,
+        name = "Organization",
+        description = "Identify your installation. Required to generate the server key and to benefit from licensed plugins. Server must be restarted for the change to take effect.",
+        global = true),
     @Property(
         key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY,
         defaultValue = "cobertura",
index b8accd4e6d19bb4c82a947a14ca72c90cd8e9362..6415c9e01e1153d6cd3c205c7bbc583f2e7cc644 100644 (file)
@@ -31,43 +31,39 @@ import java.util.Date;
 
 public class ServerMetadata extends Server {
 
-  private String id;
-  private String version;
-  private String url;
-  private Date startTime;
+  private Configuration conf;
 
   public ServerMetadata(Configuration conf) {
-    id = conf.getString(CoreProperties.SERVER_ID);
-    version = conf.getString(CoreProperties.SERVER_VERSION);
-    url = getURL(conf);
-    String dateString = conf.getString(CoreProperties.SERVER_STARTTIME);
-    if (dateString!=null) {
-      try {
-        this.startTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateString);
-
-      } catch (ParseException e) {
-        LoggerFactory.getLogger(getClass()).error("The property " + CoreProperties.SERVER_STARTTIME + " is badly formatted.", e);
-      }
-    }
-  }
-
-  public static String getURL(Configuration conf) {
-    return StringUtils.removeEnd(conf.getString("sonar.host.url", "http://localhost:9000"), "/");
+    this.conf = conf;
   }
 
   public String getId() {
-    return id;
+    return conf.getString(CoreProperties.SERVER_ID);
   }
 
   public String getVersion() {
-    return version;
+    return conf.getString(CoreProperties.SERVER_VERSION);
   }
 
   public Date getStartedAt() {
-    return startTime;
+    String dateString = conf.getString(CoreProperties.SERVER_STARTTIME);
+    if (dateString != null) {
+      try {
+        return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateString);
+
+      } catch (ParseException e) {
+        LoggerFactory.getLogger(getClass()).error("The property " + CoreProperties.SERVER_STARTTIME + " is badly formatted.", e);
+      }
+    }
+    return null;
   }
 
   public String getURL() {
-    return url;
+    return StringUtils.removeEnd(conf.getString("sonar.host.url", "http://localhost:9000"), "/");
+  }
+
+  @Override
+  public String getKey() {
+    return conf.getString(CoreProperties.SERVER_KEY);
   }
 }
index 8760081c43390a5b6ef0a694497ede9ae0ac7ad0..0fc4a6c2d751b24e4b69cfcd55aa98b0df09847d 100644 (file)
@@ -180,4 +180,14 @@ public interface CoreProperties {
   String TIMEMACHINE_DEFAULT_PERIOD_3 = "30";
   String TIMEMACHINE_DEFAULT_PERIOD_4 = "";
   String TIMEMACHINE_DEFAULT_PERIOD_5 = "";
+
+  /**
+   * @since 2.11
+   */
+  String ORGANIZATION = "sonar.organization";
+
+  /**
+   * @since 2.11
+   */
+  String SERVER_KEY = "sonar.serverKey.secured";
 }
index 37d06ad35d6712c3595dea3d3943b52a55d0cd3b..f5043073dbba04bafe6d448ea8f1ea4506aecd8d 100644 (file)
@@ -40,4 +40,9 @@ public abstract class Server implements BatchComponent, ServerComponent {
    * @since 2.4
    */
   public abstract String getURL();
+
+  /**
+   * @since 2.10
+   */
+  public abstract String getKey();
 }
index 2d8ef632fa9989a8a93e7d7e8126a079883d92aa..6da88e0004a29e909d1752908df92f580d88a61d 100644 (file)
@@ -138,7 +138,6 @@ public final class Platform {
     coreContainer = rootContainer.makeChildContainer();
     coreContainer.as(Characteristics.CACHE).addComponent(PluginDeployer.class);
     coreContainer.as(Characteristics.CACHE).addComponent(ServerPluginRepository.class);
-    coreContainer.as(Characteristics.CACHE).addComponent(ServerImpl.class);
     coreContainer.as(Characteristics.CACHE).addComponent(DefaultServerFileSystem.class);
     coreContainer.as(Characteristics.CACHE).addComponent(ThreadLocalDatabaseSessionFactory.class);
     coreContainer.as(Characteristics.CACHE).addComponent(HttpDownloader.class);
@@ -162,6 +161,7 @@ public final class Platform {
     ServerPluginRepository pluginRepository = servicesContainer.getComponent(ServerPluginRepository.class);
     pluginRepository.registerExtensions(servicesContainer);
 
+    servicesContainer.as(Characteristics.CACHE).addComponent(ServerImpl.class);
     servicesContainer.as(Characteristics.CACHE).addComponent(DefaultModelFinder.class); // depends on plugins
     servicesContainer.as(Characteristics.CACHE).addComponent(DefaultModelManager.class);
     servicesContainer.as(Characteristics.CACHE).addComponent(Plugins.class);
index 5267fbcc8ebe8fcf433c695a9641c1b5dcd33d6f..acc0bdb5a69dcbd293c5f27cc1a857d8d0e448ee 100644 (file)
  */
 package org.sonar.server.platform;
 
+import org.apache.commons.configuration.Configuration;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.platform.Server;
 
 import java.io.IOException;
@@ -31,28 +33,39 @@ import java.util.Properties;
 
 public final class ServerImpl extends Server {
 
-  private final String id;
-  private final String version;
+  private String id;
+  private String version;
   private final Date startedAt;
+  private String key;
+  private Configuration conf;
 
-  public ServerImpl() {
+  public ServerImpl(Configuration conf) {
+    this(conf, new Date());
+  }
+
+  ServerImpl(Configuration conf, Date startedAt) {
+    this.conf = conf;
+    this.startedAt = startedAt;
+  }
+
+  public void start() {
     try {
-      this.startedAt = new Date();
-      this.id = new SimpleDateFormat("yyyyMMddHHmmss").format(startedAt);
-      this.version = loadVersionFromManifest("/META-INF/maven/org.codehaus.sonar/sonar-plugin-api/pom.properties");
-      if (StringUtils.isBlank(this.version)) {
+      id = new SimpleDateFormat("yyyyMMddHHmmss").format(startedAt);
+      key = initKey(conf);
+      version = loadVersionFromManifest("/META-INF/maven/org.codehaus.sonar/sonar-plugin-api/pom.properties");
+      if (StringUtils.isBlank(version)) {
         throw new ServerStartException("Unknown Sonar version");
       }
 
     } catch (IOException e) {
-      throw new ServerStartException("Can not load Sonar metadata", e);
+      throw new ServerStartException("Can not load metadata", e);
     }
   }
 
-  public ServerImpl(String id, String version, Date startedAt) {
-    this.id = id;
-    this.version = version;
-    this.startedAt = startedAt;
+  private String initKey(Configuration conf) {
+    String organization = conf.getString(CoreProperties.ORGANIZATION);
+    String previousKey = conf.getString(CoreProperties.SERVER_KEY);
+    return new ServerKeyGenerator().generate(organization, previousKey);
   }
 
   public String getId() {
@@ -91,6 +104,10 @@ public final class ServerImpl extends Server {
     return null;
   }
 
+  public String getKey() {
+    return key;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java
new file mode 100644 (file)
index 0000000..e57db95
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.server.platform;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.Logs;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+import java.util.List;
+
+public class ServerKeyGenerator {
+
+  /**
+   * Increment this version each time the algorithm is changed. Do not exceed 9.
+   */
+  static final String VERSION = "1";
+
+  private static final int CHECKSUM_SIZE = 9;
+
+  public String generate(String organization) {
+    return generate(organization, null);
+  }
+
+  public String generate(String organization, String previousKey) {
+    List<ServerKey> serverKeys = generateForOrganization(organization);
+    String external = null;
+    String best = null;
+    for (ServerKey serverKey : serverKeys) {
+      if (StringUtils.equals(previousKey, serverKey.getKey())) {
+        best = serverKey.getKey();
+      }
+      if (serverKey.isExternal()) {
+        // External addresses are prefered to internal addresses.
+        external = serverKey.getKey();
+      }
+    }
+    if (best == null) {
+      if (external!=null) {
+        best = external;
+      } else if (!serverKeys.isEmpty()) {
+        best = serverKeys.get(0).getKey();
+      }
+    }
+    log(previousKey, best);
+    return best;
+  }
+
+  private void log(String previousKey, String newKey) {
+    if (StringUtils.isNotBlank(newKey) && StringUtils.isNotBlank(previousKey) && !previousKey.equals(newKey)) {
+      LoggerFactory.getLogger(getClass()).warn("Server key has changed. Licensed plugins may be disabled. "
+          + "Please check the organization name (Settings page) and the server IP addresses.");
+    }
+    if (StringUtils.isNotBlank(newKey)) {
+      Logs.INFO.info("Server key: " + newKey);
+
+    } else if (StringUtils.isNotBlank(previousKey)) {
+      LoggerFactory.getLogger(getClass()).warn("Server key has been removed. Licensed plugins may be disabled. "
+          + "Please check the organization name (Settings page) and the server IP addresses.");
+    }
+  }
+
+  List<ServerKey> generateForOrganization(String organization) {
+    List<ServerKey> keys = Lists.newArrayList();
+    if (StringUtils.isNotBlank(organization)) {
+      try {
+        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+        while (networkInterfaces.hasMoreElements()) {
+          NetworkInterface networkInterface = networkInterfaces.nextElement();
+          Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
+          while (addresses.hasMoreElements()) {
+            ServerKey key = new ServerKey(organization, addresses.nextElement());
+            if (key.isValid()) {
+              keys.add(key);
+            }
+          }
+        }
+      } catch (SocketException e) {
+        LoggerFactory.getLogger(getClass()).error("Fail to generate server key. Network interfaces can't be browsed.", e);
+      }
+    }
+    return keys;
+  }
+
+  static class ServerKey {
+    private String organization;
+    private InetAddress address;
+
+    ServerKey(String organization, InetAddress address) {
+      this.organization = organization;
+      this.address = address;
+    }
+
+    boolean isExternal() {
+      return !address.isLoopbackAddress() && !address.isSiteLocalAddress() && !address.isLinkLocalAddress();
+    }
+
+    boolean isValid() {
+      // Loopback addresses are in the range 127/8.
+      // Link local addresses are in the range 169.254/16 (IPv4) or fe80::/10 (IPv6). They are "autoconfiguration" addresses.
+      // They can assigned pseudorandomly, so they don't guarantee to be the same between two server startups.
+      return !address.isLoopbackAddress() && !address.isLinkLocalAddress();
+    }
+
+    String getKey() {
+      String key = new StringBuilder().append(organization).append("-").append(address.getHostAddress()).toString();
+      return VERSION + DigestUtils.shaHex(key.getBytes()).substring(0, CHECKSUM_SIZE);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      ServerKey serverKey = (ServerKey) o;
+      if (!address.equals(serverKey.address)) {
+        return false;
+      }
+      if (!organization.equals(serverKey.organization)) {
+        return false;
+      }
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = organization.hashCode();
+      result = 31 * result + address.hashCode();
+      return result;
+    }
+
+    @Override
+    public String toString() {
+      return getKey();
+    }
+  }
+}
index 6a6e4092c52a96b86ff6e8d0c0ba7a179074b0fb..2cb351d5d99c5a2d958e30759a2978a294a9974d 100644 (file)
@@ -39,19 +39,24 @@ public class ServerMetadataPersister {
   public void start() {
     setProperty(CoreProperties.SERVER_ID, server.getId());
     setProperty(CoreProperties.SERVER_VERSION, server.getVersion());
-    if (server.getStartedAt() != null) {
-      setProperty(CoreProperties.SERVER_STARTTIME, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(server.getStartedAt()));
-    }
+    setProperty(CoreProperties.SERVER_KEY, server.getKey());
+    setProperty(CoreProperties.SERVER_STARTTIME, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(server.getStartedAt()));
     session.commit();
   }
 
   private void setProperty(String key, String value) {
     Property prop = session.getSingleResult(Property.class, "key", key);
-    if (prop == null) {
-      prop = new Property(key, value);
-    } else {
-      prop.setValue(value);
+
+    if (value == null && prop != null) {
+      session.removeWithoutFlush(prop);
+
+    } else if (value != null) {
+      if (prop == null) {
+        prop = new Property(key, value);
+      } else {
+        prop.setValue(value);
+      }
+      session.saveWithoutFlush(prop);
     }
-    session.save(prop);
   }
-}
+}
\ No newline at end of file
index f0baab37f59064b98712678c94726bdb566b5e8a..6afbae60f565ca7770cd08c9a4c5446e5cbdee52 100644 (file)
@@ -59,6 +59,7 @@ class Server
 
   def sonar_info
     sonar_info=[]
+    add_property(sonar_info, 'Server Key') {org.sonar.server.platform.Platform.getServer().getKey()}
     add_property(sonar_info, 'Version') {org.sonar.server.platform.Platform.getServer().getVersion()}
     add_property(sonar_info, 'ID') {org.sonar.server.platform.Platform.getServer().getId()}
     add_property(sonar_info, 'Database') {"#{jdbc_metadata. getDatabaseProductName()} #{jdbc_metadata. getDatabaseProductVersion()}"}
index 583772a50bb0eeebe538c0757c2c86a08fcb33e6..faaf814c84680b4296b69eeb3a54aa55d635207e 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.platform;
 
+import org.apache.commons.configuration.PropertiesConfiguration;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -30,7 +31,8 @@ public class ServerImplTest {
 
   @Test
   public void alwaysReturnTheSameValues() {
-    ServerImpl server = new ServerImpl();
+    ServerImpl server = new ServerImpl(new PropertiesConfiguration());
+    server.start();
 
     assertNotNull(server.getId());
     assertEquals(server.getId(), server.getId());
@@ -44,21 +46,21 @@ public class ServerImplTest {
 
   @Test
   public void getVersionFromFile() throws IOException {
-    assertEquals("1.0", new ServerImpl().loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-with-version.properties"));
+    assertEquals("1.0", new ServerImpl(new PropertiesConfiguration()).loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-with-version.properties"));
   }
 
   @Test
   public void testFileWithNoVersion() throws IOException {
-    assertEquals("", new ServerImpl().loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-without-version.properties"));
+    assertEquals("", new ServerImpl(new PropertiesConfiguration()).loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-without-version.properties"));
   }
 
   @Test
   public void testFileWithEmptyVersionParameter() throws IOException {
-    assertEquals("", new ServerImpl().loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties"));
+    assertEquals("", new ServerImpl(new PropertiesConfiguration()).loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/pom-with-empty-version.properties"));
   }
 
   @Test
   public void shouldNotFailIfFileNotFound() throws IOException {
-    assertEquals("", new ServerImpl().loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/unknown-file.properties"));
+    assertEquals("", new ServerImpl(new PropertiesConfiguration()).loadVersionFromManifest("/org/sonar/server/platform/ServerImplTest/unknown-file.properties"));
   }
 }
diff --git a/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java b/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java
new file mode 100644 (file)
index 0000000..4041825
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.server.platform;
+
+import org.apache.commons.lang.StringUtils;
+import org.hamcrest.core.Is;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import static org.hamcrest.text.StringStartsWith.startsWith;
+import static org.junit.Assert.*;
+
+public class ServerKeyGeneratorTest {
+
+  private static InetAddress localhost;
+
+  @BeforeClass
+  public static void init() throws UnknownHostException {
+    localhost = InetAddress.getLocalHost();
+  }
+
+  @Test
+  public void keyShouldHaveTenCharacters() {
+    ServerKeyGenerator.ServerKey key = new ServerKeyGenerator.ServerKey("SonarSource", localhost);
+    assertThat(key.getKey().length(), Is.is(10)); // first character is version + 9 characters for checksum
+    assertThat(StringUtils.isBlank(key.getKey()), Is.is(false));
+  }
+
+  @Test
+  public void keyShouldStartWithVersion() {
+    ServerKeyGenerator.ServerKey key = new ServerKeyGenerator.ServerKey("SonarSource", localhost);
+    assertThat(key.getKey(), startsWith(ServerKeyGenerator.VERSION));
+  }
+
+  @Test
+  public void loopbackAddressesShouldNotBeValid() throws UnknownHostException {
+    assertThat(new ServerKeyGenerator.ServerKey("SonarSource", InetAddress.getByName("127.0.0.1")).isValid(), Is.is(false));
+  }
+
+  @Test
+  public void testEqualsAndHashCode() {
+    ServerKeyGenerator.ServerKey key1 = new ServerKeyGenerator.ServerKey("Corp One", localhost);
+    ServerKeyGenerator.ServerKey key2 = new ServerKeyGenerator.ServerKey("Corp Two", localhost);
+    assertEquals(key1, key1);
+    assertEquals(key1.hashCode(), key1.hashCode());
+
+    assertThat(key1.equals(key2), Is.is(false));
+    assertThat(key2.equals(key1), Is.is(false));
+
+    assertThat(key1.equals("string"), Is.is(false));
+  }
+
+  @Test
+  public void shouldGenerateKey() {
+    String key = new ServerKeyGenerator().generate("SonarSource");
+    assertThat(StringUtils.isNotBlank(key), Is.is(true));
+  }
+
+  @Test
+  public void organizationShouldBeMandatory() {
+    assertNull(new ServerKeyGenerator().generate(null));
+    assertNull(new ServerKeyGenerator().generate(""));
+    assertNull(new ServerKeyGenerator().generate("    "));
+  }
+
+  @Test
+  public void keyShouldBeUniquePerOrganization() {
+    ServerKeyGenerator generator = new ServerKeyGenerator();
+    String k1 = generator.generate("Corp One");
+    String k2 = generator.generate("Corp Two");
+    assertThat(StringUtils.equals(k1, k2), Is.is(false));
+  }
+
+  @Test
+  public void keyShouldBeReproducible() {
+    ServerKeyGenerator generator = new ServerKeyGenerator();
+    String k1 = generator.generate("SonarSource");
+    String k2 = generator.generate("SonarSource");
+    assertThat(StringUtils.equals(k1, k2), Is.is(true));
+  }
+
+  @Test
+  public void shouldNotKeepPreviousKeyIfNotValid() {
+    ServerKeyGenerator generator = new ServerKeyGenerator();
+    String key = generator.generate("SonarSource", "unvalid");
+    assertNotNull(key);
+    assertThat(StringUtils.equals(key, "unvalid"), Is.is(false));
+  }
+
+
+}
index 0e980f3983d5f8839e2c95ec7c274d49c22d748b..851164b38de3f7fc4316dfa02073b3b0b6edc4e4 100644 (file)
@@ -99,4 +99,8 @@ class FakeServer extends Server {
   public String getURL() {
     return null;
   }
+
+  public String getKey() {
+    return null;
+  }
 }
index 81ed38316ddcf9b7d461081a5a48fb03ba514194..51b0cf38bafdf4401cf50872564ae11de8e27996 100644 (file)
  */
 package org.sonar.server.startup;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
 import org.sonar.api.platform.Server;
-import org.sonar.server.platform.ServerImpl;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.TimeZone;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 public class ServerMetadataPersisterTest extends AbstractDbUnitTestCase {
 
+  private TimeZone initialTimeZone;
+
+  @Before
+  public void fixTimeZone() {
+    initialTimeZone = TimeZone.getDefault();
+    TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
+  }
+
+  @After
+  public void revertTimeZone() {
+    TimeZone.setDefault(initialTimeZone);
+  }
+
   @Test
   public void testSaveProperties() throws ParseException {
     setupData("testSaveProperties");
-    persist();
+    persist(newServer());
     checkTables("testSaveProperties", "properties");
   }
 
   @Test
   public void testUpdateExistingProperties() throws ParseException {
     setupData("testUpdateExistingProperties");
-    persist();
+    persist(newServer());
     checkTables("testUpdateExistingProperties", "properties");
   }
 
-  private void persist() throws ParseException {
-    TimeZone initialZone = TimeZone.getDefault();
-    try {
-      TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
-      Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2010-05-18 17:59");
-      Server server = new ServerImpl("123", "2.2", date);
-      ServerMetadataPersister persister = new ServerMetadataPersister(server, getSession());
-      persister.start();
-    } finally {
-      TimeZone.setDefault(initialZone);
-    }
+  @Test
+  public void testDeleteProperties() throws ParseException {
+    setupData("testDeleteProperties");
+    Server server = mock(Server.class);
+    when(server.getStartedAt()).thenReturn(new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2010-05-18 17:59"));//this is a mandatory not-null property
+    persist(server);
+    checkTables("testDeleteProperties", "properties");
+  }
+
+  private void persist(Server server) {
+    ServerMetadataPersister persister = new ServerMetadataPersister(server, getSession());
+    persister.start();
+  }
+
+  private Server newServer() throws ParseException {
+    Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm").parse("2010-05-18 17:59");
+    Server server = mock(Server.class);
+    when(server.getKey()).thenReturn("1abcdef");
+    when(server.getId()).thenReturn("123");
+    when(server.getVersion()).thenReturn("2.2");
+    when(server.getStartedAt()).thenReturn(date);
+
+    return server;
+
   }
 }
diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties-result.xml
new file mode 100644 (file)
index 0000000..0ade6f4
--- /dev/null
@@ -0,0 +1,7 @@
+<dataset>
+
+  <properties id="1" prop_key="other" resource_id="[null]" text_value="some text" user_id="[null]"/>
+
+  <!-- not null property -->
+  <properties id="5" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2010-05-18T17:59:00+0000" user_id="[null]"/>
+</dataset>
\ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml b/sonar-server/src/test/resources/org/sonar/server/startup/ServerMetadataPersisterTest/testDeleteProperties.xml
new file mode 100644 (file)
index 0000000..8a3add2
--- /dev/null
@@ -0,0 +1,10 @@
+<dataset>
+
+  <properties id="1" prop_key="other" resource_id="[null]" text_value="some text" user_id="[null]" />
+
+  <properties id="2" prop_key="sonar.core.id" resource_id="[null]" text_value="123" user_id="[null]"/>
+  <properties id="3" prop_key="sonar.core.version" resource_id="[null]" text_value="2.2" user_id="[null]"/>
+  <properties id="4" prop_key="sonar.serverKey.secured" resource_id="[null]" text_value="1abcdef" user_id="[null]"/>
+  <properties id="5" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2010-05-18T17:59:00+0000" user_id="[null]"/>
+
+</dataset>
\ No newline at end of file
index ec292a1388df8f2663de0e8a4649805abaa458e6..8a3add2a585381e336b19f2f4f7e194ac6d69b46 100644 (file)
@@ -4,6 +4,7 @@
 
   <properties id="2" prop_key="sonar.core.id" resource_id="[null]" text_value="123" user_id="[null]"/>
   <properties id="3" prop_key="sonar.core.version" resource_id="[null]" text_value="2.2" user_id="[null]"/>
-  <properties id="4" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2010-05-18T17:59:00+0000" user_id="[null]"/>
+  <properties id="4" prop_key="sonar.serverKey.secured" resource_id="[null]" text_value="1abcdef" user_id="[null]"/>
+  <properties id="5" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2010-05-18T17:59:00+0000" user_id="[null]"/>
 
 </dataset>
\ No newline at end of file
index 365acc0ccd3b465afdb25c816331881a55e089f1..5d4648e011aefb26c2f281a21b2998de7f374215 100644 (file)
@@ -5,5 +5,5 @@
   <properties id="2" prop_key="sonar.core.id" resource_id="[null]" text_value="123" user_id="[null]"/>
   <properties id="3" prop_key="sonar.core.version" resource_id="[null]" text_value="2.2" user_id="[null]"/>
   <properties id="4" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2010-05-18T17:59:00+0000" user_id="[null]"/>
-
+  <properties id="5" prop_key="sonar.serverKey.secured" resource_id="[null]" text_value="1abcdef" user_id="[null]"/>
 </dataset>
\ No newline at end of file
index 824615a0cc51b456783d84432898d2e2f5c34a54..bf4537c513d656fa72b43d98ba9c5162fd08d753 100644 (file)
@@ -5,5 +5,6 @@
   <properties id="2" prop_key="sonar.core.id" resource_id="[null]" text_value="65" user_id="[null]"/>
   <properties id="3" prop_key="sonar.core.version" resource_id="[null]" text_value="1.9" user_id="[null]"/>
   <properties id="4" prop_key="sonar.core.startTime" resource_id="[null]" text_value="2008-04-18T17:59:00+0000" user_id="[null]"/>
+  <properties id="5" prop_key="sonar.serverKey.secured" resource_id="[null]" text_value="other" user_id="[null]"/>
 
 </dataset>
\ No newline at end of file