]> source.dussan.org Git - sonarqube.git/commitdiff
Update Center improvements
authorSimon Brandhof <simon.brandhof@gmail.com>
Sat, 14 Jul 2012 17:32:45 +0000 (19:32 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Sat, 14 Jul 2012 17:32:45 +0000 (19:32 +0200)
* SONAR-3661 API: new component org.sonar.api.utils.UriReader

* SONAR-3660 The property sonar.updatecenter.url must support local files

* SONAR-3659 Availability of Update Center with non-RELEASE versions of Sonar

* SONAR-2008 Enable updates from SNAPSHOT versions for plugins

16 files changed:
pom.xml
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapModule.java
sonar-core/src/test/java/org/sonar/core/plugins/DefaultPluginMetadataTest.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/UriReader.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/UriReaderTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/resources/org/sonar/api/utils/UriReaderTest/foo.txt [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java
sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterMatrix.java
sonar-server/src/main/webapp/WEB-INF/app/views/updatecenter/available.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/updatecenter/system_updates.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/updatecenter/updates.html.erb
sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java
sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixTest.java

diff --git a/pom.xml b/pom.xml
index 3ead717e790736c3992f1081e9c6c687f3ce2f98..af072cd328a7fce8598de4432d9650b227ebf042 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       <dependency>
         <groupId>org.codehaus.sonar</groupId>
         <artifactId>sonar-update-center-common</artifactId>
-        <version>1.1</version>
+        <version>1.3-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>org.codehaus.sonar</groupId>
index 11adc8adcd3b3454353e8af7780907f5739f5485..528186df9f4c9acfd7516cf255846ca34e1ffd7b 100644 (file)
@@ -23,6 +23,7 @@ import org.apache.commons.configuration.PropertiesConfiguration;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.config.EmailSettings;
 import org.sonar.api.utils.HttpDownloader;
+import org.sonar.api.utils.UriReader;
 import org.sonar.batch.FakeMavenPluginExecutor;
 import org.sonar.batch.MavenPluginExecutor;
 import org.sonar.batch.RemoteServerMetadata;
@@ -64,6 +65,7 @@ public class BootstrapModule extends Module {
     addCoreSingleton(ServerMetadata.class);// registered here because used by BootstrapClassLoader
     addCoreSingleton(TempDirectories.class);// registered here because used by BootstrapClassLoader
     addCoreSingleton(HttpDownloader.class);// registered here because used by BootstrapClassLoader
+    addCoreSingleton(UriReader.class);// registered here because used by BootstrapClassLoader
     addCoreSingleton(ArtifactDownloader.class);// registered here because used by BootstrapClassLoader
     addCoreSingleton(JdbcDriverHolder.class);
     addCoreSingleton(EmailSettings.class);
index c2e34ec93b2ab59bb2727fb3e9b08d22eb38a7d3..5ac3f1dcc0ad64a5d8d3c4bb9f708c611505267f 100644 (file)
@@ -125,7 +125,7 @@ public class DefaultPluginMetadataTest {
     assertThat(pluginWithVersion("3.1-RC2").isCompatibleWith("3.2-SNAPSHOT")).isTrue();
     assertThat(pluginWithVersion("3.1-RC1").isCompatibleWith("3.2-RC2")).isTrue();
     assertThat(pluginWithVersion("3.1-RC1").isCompatibleWith("3.1-RC2")).isTrue();
-    assertThat(pluginWithVersion("3.1-RC2").isCompatibleWith("3.1-RC1")).isTrue();
+    assertThat(pluginWithVersion("3.1-RC2").isCompatibleWith("3.1-RC1")).isFalse();
 
     assertThat(pluginWithVersion(null).isCompatibleWith("0")).isTrue();
     assertThat(pluginWithVersion(null).isCompatibleWith("3.1")).isTrue();
index 1f99d62f46ae67dba974822c5a7d124fb1ad1d2c..1f7b1c54613880cc00f03f38a2b9d5442879af3d 100644 (file)
@@ -34,6 +34,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.*;
+import java.nio.charset.Charset;
 import java.util.List;
 
 /**
@@ -41,7 +42,7 @@ import java.util.List;
  *
  * @since 2.2
  */
-public class HttpDownloader implements BatchComponent, ServerComponent {
+public class HttpDownloader implements UriReader.SchemeProcessor, BatchComponent, ServerComponent {
 
   public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
 
@@ -91,9 +92,13 @@ public class HttpDownloader implements BatchComponent, ServerComponent {
     return Joiner.on(", ").join(descriptions);
   }
 
+  public String description(URI uri) {
+    return String.format("%s (%s)", uri.toString(), getProxySynthesis(uri));
+  }
+
   private void registerProxyCredentials(Settings settings) {
     Authenticator.setDefault(new ProxyAuthenticator(settings.getString("http.proxyUser"), settings
-        .getString("http.proxyPassword")));
+      .getString("http.proxyPassword")));
   }
 
   private boolean requiresProxyAuthentication(Settings settings) {
@@ -165,6 +170,18 @@ public class HttpDownloader implements BatchComponent, ServerComponent {
     }
   }
 
+  public String[] getSupportedSchemes() {
+    return new String[]{"http", "https"};
+  }
+
+  public byte[] readBytes(URI uri) {
+    return download(uri);
+  }
+
+  public String readString(URI uri, Charset charset) {
+    return downloadPlainText(uri, charset.name());
+  }
+
   public InputStream openStream(URI uri) {
     try {
       HttpURLConnection connection = newHttpConnection(uri);
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/UriReader.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/UriReader.java
new file mode 100644 (file)
index 0000000..105d984
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.api.utils;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Reads different types of URI. Supported schemes are http and file.
+ *
+ * @since 3.2
+ */
+public class UriReader implements BatchComponent, ServerComponent {
+
+  private final Map<String, SchemeProcessor> processorsByScheme = Maps.newHashMap();
+
+  public UriReader(SchemeProcessor[] processors) {
+    List<SchemeProcessor> allProcessors = Lists.asList(new FileProcessor(), processors);
+    for (SchemeProcessor processor : allProcessors) {
+      for (String scheme : processor.getSupportedSchemes()) {
+        processorsByScheme.put(scheme.toLowerCase(Locale.ENGLISH), processor);
+      }
+    }
+  }
+
+  /**
+   * Reads all bytes from uri. It throws an unchecked exception if an error occurs.
+   */
+  public byte[] readBytes(URI uri) {
+    return searchForSupportedProcessor(uri).readBytes(uri);
+  }
+
+  /**
+   * Reads all characters from uri, using the given character set.
+   * It throws an unchecked exception if an error occurs.
+   */
+  public String readString(URI uri, Charset charset) {
+    return searchForSupportedProcessor(uri).readString(uri, charset);
+  }
+
+  /**
+   * Opens an input stream over the given uri.
+   */
+  public InputStream openStream(URI uri) {
+    return searchForSupportedProcessor(uri).openStream(uri);
+  }
+
+  /**
+   * Returns a detailed description of the given uri. For example HTTP URIs are completed
+   * with the configured HTTP proxy.
+   */
+  public String description(URI uri) {
+    SchemeProcessor reader = searchForSupportedProcessor(uri);
+
+    return reader.description(uri);
+  }
+
+  @VisibleForTesting
+  SchemeProcessor searchForSupportedProcessor(URI uri) {
+    SchemeProcessor processor = processorsByScheme.get(uri.getScheme().toLowerCase(Locale.ENGLISH));
+    Preconditions.checkArgument(processor != null, "URI schema is not supported: " + uri.getScheme());
+    return processor;
+  }
+
+  static interface SchemeProcessor extends BatchComponent, ServerComponent {
+    String[] getSupportedSchemes();
+
+    byte[] readBytes(URI uri);
+
+    String readString(URI uri, Charset charset);
+
+    InputStream openStream(URI uri);
+
+    String description(URI uri);
+  }
+
+
+  /**
+   * This implementation is not exposed in API and is kept private.
+   */
+  private static class FileProcessor implements SchemeProcessor {
+
+    public String[] getSupportedSchemes() {
+      return new String[]{"file"};
+    }
+
+    public byte[] readBytes(URI uri) {
+      try {
+        return Files.toByteArray(new File(uri));
+      } catch (IOException e) {
+        throw Throwables.propagate(e);
+      }
+    }
+
+    public String readString(URI uri, Charset charset) {
+      try {
+        return Files.toString(new File(uri), charset);
+      } catch (IOException e) {
+        throw Throwables.propagate(e);
+      }
+    }
+
+    public InputStream openStream(URI uri) {
+      try {
+        return Files.newInputStreamSupplier(new File(uri)).getInput();
+      } catch (IOException e) {
+        throw Throwables.propagate(e);
+      }
+    }
+
+    public String description(URI uri) {
+      return new File(uri).getAbsolutePath();
+    }
+  }
+}
index f72d9b6843f6cad1c388cf8418422e170a6e17f9..c8bc80d83e26dcd7443ab197d28ffb2f02cd0800 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.utils;
 
+import com.google.common.base.Charsets;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.SystemUtils;
@@ -31,15 +32,14 @@ import org.sonar.api.platform.Server;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.*;
 import java.util.Arrays;
 import java.util.Properties;
 
-import static org.hamcrest.Matchers.greaterThan;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assume.assumeThat;
-import static org.junit.internal.matchers.StringContains.containsString;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -161,21 +161,28 @@ public class HttpDownloaderTest {
   }
 
   @Test
-  public void downloadBytes() throws URISyntaxException {
-    byte[] bytes = new HttpDownloader(new Settings()).download(new URI(baseUrl));
-    assertThat(bytes.length, greaterThan(10));
+  public void downloadBytes() throws Exception {
+    byte[] bytes = new HttpDownloader(new Settings()).readBytes(new URI(baseUrl));
+    assertThat(bytes.length).isGreaterThan(10);
   }
 
   @Test
-  public void downloadPlainText() throws URISyntaxException {
-    String text = new HttpDownloader(new Settings()).downloadPlainText(new URI(baseUrl), "UTF-8");
-    assertThat(text.length(), greaterThan(10));
+  public void readString() throws Exception {
+    String text = new HttpDownloader(new Settings()).readString(new URI(baseUrl), Charsets.UTF_8);
+    assertThat(text.length()).isGreaterThan(10);
+  }
+
+  @Test
+  public void openStream() throws Exception {
+    InputStream input = new HttpDownloader(new Settings()).openStream(new URI(baseUrl));
+    assertThat(IOUtils.toByteArray(input).length).isGreaterThan(10);
+    IOUtils.closeQuietly(input);
   }
 
   @Test(expected = SonarException.class)
-  public void failIfServerDown() throws URISyntaxException {
+  public void failIfServerDown() throws Exception {
     // I hope that the port 1 is not used !
-    new HttpDownloader(new Settings()).download(new URI("http://localhost:1/unknown"));
+    new HttpDownloader(new Settings()).readBytes(new URI("http://localhost:1/unknown"));
   }
 
   @Test
@@ -186,8 +193,8 @@ public class HttpDownloaderTest {
     File toFile = new File(toDir, "downloadToFile.txt");
 
     new HttpDownloader(new Settings()).download(new URI(baseUrl), toFile);
-    assertThat(toFile.exists(), is(true));
-    assertThat(toFile.length(), greaterThan(10l));
+    assertThat(toFile).exists();
+    assertThat(toFile.length()).isGreaterThan(10l);
   }
 
   @Test
@@ -201,7 +208,7 @@ public class HttpDownloaderTest {
       // I hope that the port 1 is not used !
       new HttpDownloader(new Settings()).download(new URI("http://localhost:1/unknown"), toFile);
     } catch (SonarException e) {
-      assertThat(toFile.exists(), is(false));
+      assertThat(toFile).doesNotExist();
     }
   }
 
@@ -210,30 +217,42 @@ public class HttpDownloaderTest {
     Server server = mock(Server.class);
     when(server.getVersion()).thenReturn("2.2");
 
-    byte[] bytes = new HttpDownloader(server, new Settings()).download(new URI(baseUrl));
+    byte[] bytes = new HttpDownloader(server, new Settings()).readBytes(new URI(baseUrl));
     Properties props = new Properties();
     props.load(IOUtils.toInputStream(new String(bytes)));
-    assertThat(props.getProperty("agent"), is("Sonar 2.2"));
+    assertThat(props.getProperty("agent")).isEqualTo("Sonar 2.2");
   }
 
   @Test
   public void followRedirect() throws URISyntaxException {
-    byte[] bytes = new HttpDownloader(new Settings()).download(new URI(baseUrl + "/redirect/"));
-    assertThat(new String(bytes), containsString("count"));
+    byte[] bytes = new HttpDownloader(new Settings()).readBytes(new URI(baseUrl + "/redirect/"));
+    assertThat(new String(bytes)).contains("count");
   }
 
   @Test
   public void shouldGetDirectProxySynthesis() throws URISyntaxException {
     ProxySelector proxySelector = mock(ProxySelector.class);
     when(proxySelector.select((URI) anyObject())).thenReturn(Arrays.asList(Proxy.NO_PROXY));
-    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector), is("no proxy"));
+    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("no proxy");
   }
 
   @Test
   public void shouldGetProxySynthesis() throws URISyntaxException {
     ProxySelector proxySelector = mock(ProxySelector.class);
     when(proxySelector.select((URI) anyObject())).thenReturn(Arrays.asList((Proxy) new FakeProxy()));
-    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector), is("proxy: http://proxy_url:4040"));
+    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("proxy: http://proxy_url:4040");
+  }
+
+  @Test
+  public void supported_schemes() {
+    assertThat(new HttpDownloader(new Settings()).getSupportedSchemes()).contains("http");
+  }
+
+  @Test
+  public void uri_description() throws Exception {
+    HttpDownloader downloader = new HttpDownloader(new Settings());
+    String description = downloader.description(new URI("http://sonarsource.org"));
+    assertThat(description).matches("http://sonarsource.org \\(.*\\)");
   }
 }
 
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/UriReaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/UriReaderTest.java
new file mode 100644 (file)
index 0000000..c37a635
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 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.api.utils;
+
+import com.google.common.base.Charsets;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UriReaderTest {
+
+  private static URI testFile;
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Before
+  public void init() throws URISyntaxException {
+    testFile = UriReaderTest.class.getResource("/org/sonar/api/utils/UriReaderTest/foo.txt").toURI();
+  }
+
+  @Test
+  public void file_processor_is_always_available() throws URISyntaxException {
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+
+    assertThat(uriReader.searchForSupportedProcessor(testFile)).isNotNull();
+  }
+
+  @Test
+  public void file_readString() throws Exception {
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    assertThat(uriReader.readString(testFile, Charsets.UTF_8)).isEqualTo("in foo");
+  }
+
+  @Test
+  public void file_readBytes() throws Exception {
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    assertThat(new String(uriReader.readBytes(testFile))).isEqualTo("in foo");
+  }
+
+  @Test
+  public void file_openStream() throws Exception {
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    InputStream input = uriReader.openStream(testFile);
+    assertThat(IOUtils.toString(input)).isEqualTo("in foo");
+    IOUtils.closeQuietly(input);
+  }
+
+  @Test
+  public void file_readString_fails_if_file_not_found() throws Exception {
+    thrown.expect(RuntimeException.class);
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    uriReader.readString(new URI("file:/notfound"), Charsets.UTF_8);
+  }
+
+  @Test
+  public void file_readBytes_fails_if_file_not_found() throws Exception {
+    thrown.expect(RuntimeException.class);
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    uriReader.readBytes(new URI("file:/notfound"));
+  }
+
+  @Test
+  public void file_openStream_fails_if_file_not_found() throws Exception {
+    thrown.expect(RuntimeException.class);
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    uriReader.openStream(new URI("file:/notfound"));
+  }
+
+  @Test
+  public void file_description() {
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+
+    // the prefix file:/ is removed
+    assertThat(uriReader.description(testFile)).doesNotMatch("file:/.*");
+    assertThat(uriReader.description(testFile)).matches(".*foo\\.txt");
+  }
+
+  @Test
+  public void fail_if_unknown_scheme() throws Exception {
+    thrown.expect(IllegalArgumentException.class);
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[0]);
+    uriReader.readBytes(new URI("ftp://sonarsource.org"));
+  }
+
+  @Test
+  public void register_processors() throws Exception {
+    UriReader.SchemeProcessor processor = mock(UriReader.SchemeProcessor.class);
+    when(processor.getSupportedSchemes()).thenReturn(new String[]{"ftp"});
+    UriReader uriReader = new UriReader(new UriReader.SchemeProcessor[]{processor});
+
+    assertThat(uriReader.searchForSupportedProcessor(new URI("ftp://sonarsource.org"))).isNotNull();
+  }
+}
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/utils/UriReaderTest/foo.txt b/sonar-plugin-api/src/test/resources/org/sonar/api/utils/UriReaderTest/foo.txt
new file mode 100644 (file)
index 0000000..be36d7b
--- /dev/null
@@ -0,0 +1 @@
+in foo
\ No newline at end of file
index 287f8e2f541bf4bb72d26c95ac097989cbcf5da9..b3295221436f1bc0a5c527dfd128222de6295d3e 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.api.rules.XMLRuleParser;
 import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.IocContainer;
 import org.sonar.api.utils.TimeProfiler;
+import org.sonar.api.utils.UriReader;
 import org.sonar.api.workflow.internal.DefaultWorkflow;
 import org.sonar.core.PicoUtils;
 import org.sonar.core.config.Logback;
@@ -193,6 +194,7 @@ public final class Platform {
     servicesContainer.addSingleton(WorkflowEngine.class);
 
     servicesContainer.addSingleton(HttpDownloader.class);
+    servicesContainer.addSingleton(UriReader.class);
     servicesContainer.addSingleton(UpdateCenterClient.class);
     servicesContainer.addSingleton(UpdateCenterMatrixFactory.class);
     servicesContainer.addSingleton(PluginDownloader.class);
index 191c0e88cbd9a218ba952a868827b7498a76ac7c..7d461004756dc7778d60554149b4d23a10c38124 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.plugins;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.commons.io.IOUtils;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.Properties;
@@ -26,8 +27,7 @@ import org.sonar.api.Property;
 import org.sonar.api.PropertyType;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.config.Settings;
-import org.sonar.api.utils.HttpDownloader;
-import org.sonar.api.utils.Logs;
+import org.sonar.api.utils.UriReader;
 import org.sonar.updatecenter.common.UpdateCenter;
 import org.sonar.updatecenter.common.UpdateCenterDeserializer;
 
@@ -67,19 +67,17 @@ public class UpdateCenterClient implements ServerComponent {
   private URI uri;
   private UpdateCenter center = null;
   private long lastRefreshDate = 0;
-  private HttpDownloader downloader;
+  private UriReader uriReader;
 
-  /**
-   * for unit tests
-   */
-  UpdateCenterClient(HttpDownloader downloader, URI uri) {
-    this.downloader = downloader;
+  @VisibleForTesting
+  UpdateCenterClient(UriReader uriReader, URI uri) {
+    this.uriReader = uriReader;
     this.uri = uri;
-    Logs.INFO.info("Update center: " + uri + " (" + downloader.getProxySynthesis(uri) + ")");
+    LoggerFactory.getLogger(getClass()).info("Update center: " + uriReader.description(uri));
   }
 
-  public UpdateCenterClient(HttpDownloader downloader, Settings configuration) throws URISyntaxException {
-    this(downloader, new URI(configuration.getString(URL_PROPERTY)));
+  public UpdateCenterClient(UriReader uriReader, Settings settings) throws URISyntaxException {
+    this(uriReader, new URI(settings.getString(URL_PROPERTY)));
   }
 
   public UpdateCenter getCenter() {
@@ -88,7 +86,7 @@ public class UpdateCenterClient implements ServerComponent {
 
   public UpdateCenter getCenter(boolean forceRefresh) {
     if (center == null || forceRefresh || needsRefresh()) {
-      center = download();
+      center = init();
       lastRefreshDate = System.currentTimeMillis();
     }
     return center;
@@ -102,10 +100,10 @@ public class UpdateCenterClient implements ServerComponent {
     return lastRefreshDate + PERIOD_IN_MILLISECONDS < System.currentTimeMillis();
   }
 
-  private UpdateCenter download() {
+  private UpdateCenter init() {
     InputStream input = null;
     try {
-      input = downloader.openStream(uri);
+      input = uriReader.openStream(uri);
       if (input != null) {
         java.util.Properties properties = new java.util.Properties();
         properties.load(input);
@@ -113,7 +111,7 @@ public class UpdateCenterClient implements ServerComponent {
       }
 
     } catch (Exception e) {
-      LoggerFactory.getLogger(getClass()).error("Fail to download data from update center", e);
+      LoggerFactory.getLogger(getClass()).error("Fail to connect to update center", e);
 
     } finally {
       IOUtils.closeQuietly(input);
index e3f6e48decbf778b75bd5d7176d60e22f738acb3..b7caf70ca76215494c8503d45c8684700ef0e902 100644 (file)
@@ -41,18 +41,6 @@ public final class UpdateCenterMatrix {
     this.installedSonarVersion = installedSonarVersion;
   }
 
-  public UpdateCenterMatrix(UpdateCenter center, String installedSonarVersion) {
-    this(center, Version.create(installedSonarVersion));
-  }
-
-  public UpdateCenter getCenter() {
-    return center;
-  }
-
-  public Version getInstalledSonarVersion() {
-    return installedSonarVersion;
-  }
-
   public UpdateCenterMatrix registerInstalledPlugin(String pluginKey, Version pluginVersion) {
     Plugin plugin = center.getPlugin(pluginKey);
     if (plugin != null) {
@@ -61,21 +49,30 @@ public final class UpdateCenterMatrix {
     return this;
   }
 
+  /**
+   * Used by ruby webapp
+   */
+  public UpdateCenter getCenter() {
+    return center;
+  }
+
   public UpdateCenterMatrix registerPendingPluginsByFilename(String filename) {
     pendingPluginFilenames.add(filename);
     return this;
   }
 
   public List<PluginUpdate> findAvailablePlugins() {
+    Version adjustedSonarVersion = getAdjustedSonarVersion();
+
     List<PluginUpdate> availables = Lists.newArrayList();
     for (Plugin plugin : center.getPlugins()) {
       if (!installedPlugins.containsKey(plugin) && !isAlreadyDownloaded(plugin)) {
-        Release release = plugin.getLastCompatibleRelease(installedSonarVersion);
+        Release release = plugin.getLastCompatibleRelease(adjustedSonarVersion);
         if (release != null) {
           availables.add(PluginUpdate.createWithStatus(release, PluginUpdate.Status.COMPATIBLE));
 
         } else {
-          release = plugin.getLastCompatibleReleaseIfUpgrade(installedSonarVersion);
+          release = plugin.getLastCompatibleReleaseIfUpgrade(adjustedSonarVersion);
           if (release != null) {
             availables.add(PluginUpdate.createWithStatus(release, PluginUpdate.Status.REQUIRE_SONAR_UPGRADE));
           }
@@ -96,13 +93,15 @@ public final class UpdateCenterMatrix {
   }
 
   public List<PluginUpdate> findPluginUpdates() {
+    Version adjustedSonarVersion = getAdjustedSonarVersion();
+
     List<PluginUpdate> updates = Lists.newArrayList();
     for (Map.Entry<Plugin, Version> entry : installedPlugins.entrySet()) {
       Plugin plugin = entry.getKey();
       if (!isAlreadyDownloaded(plugin)) {
         Version pluginVersion = entry.getValue();
         for (Release release : plugin.getReleasesGreaterThan(pluginVersion)) {
-          updates.add(PluginUpdate.createForPluginRelease(release, installedSonarVersion));
+          updates.add(PluginUpdate.createForPluginRelease(release, adjustedSonarVersion));
         }
       }
     }
@@ -156,4 +155,13 @@ public final class UpdateCenterMatrix {
     this.date = d;
     return this;
   }
+
+  /**
+   * Update center declares RELEASE versions of Sonar, for instance 3.2 but not 3.2-SNAPSHOT.
+   * We assume that SNAPSHOT, milestones and release candidates of Sonar support the
+   * same plugins than related RELEASE.
+   */
+  private Version getAdjustedSonarVersion() {
+    return Version.createRelease(installedSonarVersion.toString());
+  }
 }
index d96b398f17afcdbc66d13189ea0cea72048cf6a0..0f5defd15afaa4d998ba18214c9b235870191ac4 100644 (file)
@@ -1,7 +1,7 @@
 <script>
 function installPlugin(key) {
   /* check terms & conditions */
-  var tc=$('tc-' + key)
+  var tc=$('tc-' + key);
   if (tc!=null && !tc.checked) {
     alert('Please accept the Terms and Conditions');
     return false;
@@ -14,7 +14,8 @@ function installPlugin(key) {
   return false;
 }
 </script>
- <%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'available'} -%>
+<h1 class="marginbottom10"><%= message('update_center.page') -%></h1>
+<%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'available'} -%>
 <div class="tabs-panel">
 
 <%= render :partial => 'updatecenter/operations' -%>
index a52a6d8996840f9191947bc2c2092499ef5f2b0b..a4f707b32f5f1bd86a0cfc039b4f5635edbf849e 100644 (file)
- <style>
-   ol.bulletpoints li {
-     list-style-type: decimal;
-     list-style-position: inside;
-   }
- </style>
- <script>
- function submitForm(elt) {
-   elt.submit();
-   elt.disable();
-   return false;
-}
+<style>
+  ol.bulletpoints li {
+    list-style-type: decimal;
+    list-style-position: inside;
+  }
+</style>
+<script>
 function submitForm(elt) {
+    elt.submit();
+    elt.disable();
+    return false;
+  }
 </script>
- <%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'system_updates'} -%>
+<h1 class="marginbottom10"><%= message('update_center.page') -%></h1>
+<%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'system_updates'} -%>
 
- <div class="tabs-panel">
+<div class="tabs-panel">
 
-   <%= render :partial => 'updatecenter/operations' -%>
+  <%= render :partial => 'updatecenter/operations' -%>
 
-   <% if @center %>
-     <% if @sonar_updates.empty? %>
-       <table class="data width100 marginbottom10">
-         <thead>
-           <tr>
-             <td> </td>
-           </tr>
-         </thead>
-         <tbody>
-           <tr>
-             <td>System is up to date</td>
-           </tr>
-         </tbody>
-       </table>
-     <% else %>
+  <% if @center %>
+    <% if @sonar_updates.empty? %>
+      <table class="data width100 marginbottom10">
+        <thead>
+        <tr>
+          <td></td>
+        </tr>
+        </thead>
+        <tbody>
+        <tr>
+          <td>System is up to date</td>
+        </tr>
+        </tbody>
+      </table>
+    <% else %>
 
-     <% @sonar_updates.to_a.reverse.each do |update|
+      <% @sonar_updates.to_a.reverse.each do |update|
         release=update.getRelease()
-     %>
-       <table class="data width100" id="sonar-<%= release.getVersion() -%>">
-        <thead>
+      %>
+        <table class="data width100" id="sonar-<%= release.getVersion() -%>">
+          <thead>
           <tr>
             <th><h2>Sonar  <%= release.getVersion() -%></h2></th>
           </tr>
-        </thead>
-        <tbody>
+          </thead>
+          <tbody>
           <tr class="even">
             <td>
               <table class="width100 spaced">
                 <tr>
                   <td class="thin nowrap"><b>Date: </b></td>
-                  <td class="sep"> </td>
+                  <td class="sep"></td>
                   <td><%= release_date(release.getDate()) if release.getDate() -%></td>
                 </tr>
                 <tr>
-                  <td class="thin nowrap"><b><%= link_to_if release.getChangelogUrl(), 'Release Notes', release.getChangelogUrl(), :class => 'external' %>: </b></td>
-                  <td class="sep"> </td>
+                  <td class="thin nowrap">
+                    <b><%= link_to_if release.getChangelogUrl(), 'Release Notes', release.getChangelogUrl(), :class => 'external' %>
+                      : </b></td>
+                  <td class="sep"></td>
                   <td><%= release.getDescription() -%></td>
                 </tr>
                 <tr>
                   <td class="thin nowrap" valign="top"><b>How to upgrade: </b></td>
-                  <td class="sep"> </td>
+                  <td class="sep"></td>
                   <td>
                     <% if update.hasWarnings() %>
-                     Follow those steps to upgrade Sonar from version <%= sonar_version -%> to version <%= release.getVersion() -%> :
-                    <ol class="bulletpoints">
-                   <% update.getIncompatiblePlugins().each do |incompatible_plugin| %>
-                      <li>
-                      <form method="post" action="<%= ApplicationController.root_context -%>/updatecenter/uninstall?key=<%= incompatible_plugin.getKey() -%>&amp;from=system_updates" style="display: inline-block">
-                        <%= image_tag 'warning.png' -%> <input type="submit" value="Uninstall" class="red-button" onClick="return submitForm(this);"/> the plugin <%= incompatible_plugin.getName() -%> which is not compatible with Sonar <%= release.getVersion() -%>.
-                      </form>
-                   </li>
-             <% end %>
-           <% update.getPluginsToUpgrade().each do |plugin_to_upgrade| %>
-                   <li>
-                    <form method="post" id="upgrade-form-<%= plugin_to_upgrade.getArtifact().getKey() -%>" action="<%= ApplicationController.root_context -%>/updatecenter/install?key=<%= plugin_to_upgrade.getArtifact().getKey() -%>&amp;version=<%= plugin_to_upgrade.getVersion() -%>&amp;from=system_updates" style="display: inline-block">
-                      <input type="submit" id="upgrade-submit-<%= plugin_to_upgrade.getArtifact().getKey() -%>" value="Upgrade" onClick="return submitForm(this);"/>
-                      the plugin <%= plugin_to_upgrade.getArtifact().getName() -%> to version <%= plugin_to_upgrade.getVersion() -%>
-                    </form>
-                 </li>
-                 <% end %>
-                 <li>Stop Sonar</li>
-                 <li><%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install Sonar <%= release.getVersion() -%> after having carefully read the upgrade guide.</li>
-                 <li>Start Sonar</li>
-                    </ol>
+                      Follow those steps to upgrade Sonar from version <%= sonar_version -%> to
+                      version <%= release.getVersion() -%> :
+                      <ol class="bulletpoints">
+                        <% update.getIncompatiblePlugins().each do |incompatible_plugin| %>
+                          <li>
+                            <form method="post" action="<%= ApplicationController.root_context -%>/updatecenter/uninstall?key=<%= incompatible_plugin.getKey() -%>&amp;from=system_updates" style="display: inline-block">
+                              <%= image_tag 'warning.png' -%>
+                              <input type="submit" value="Uninstall" class="red-button" onClick="return submitForm(this);"/>
+                              the plugin <%= incompatible_plugin.getName() -%> which is not compatible with
+                              Sonar <%= release.getVersion() -%>.
+                            </form>
+                          </li>
+                        <% end %>
+                        <% update.getPluginsToUpgrade().each do |plugin_to_upgrade| %>
+                          <li>
+                            <form method="post" id="upgrade-form-<%= plugin_to_upgrade.getArtifact().getKey() -%>" action="<%= ApplicationController.root_context -%>/updatecenter/install?key=<%= plugin_to_upgrade.getArtifact().getKey() -%>&amp;version=<%= plugin_to_upgrade.getVersion() -%>&amp;from=system_updates" style="display: inline-block">
+                              <input type="submit" id="upgrade-submit-<%= plugin_to_upgrade.getArtifact().getKey() -%>" value="Upgrade" onClick="return submitForm(this);"/>
+                              the plugin <%= plugin_to_upgrade.getArtifact().getName() -%> to
+                              version <%= plugin_to_upgrade.getVersion() -%>
+                            </form>
+                          </li>
+                        <% end %>
+                        <li>Stop Sonar</li>
+                        <li><%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install
+                          Sonar <%= release.getVersion() -%> after having carefully read the upgrade guide.
+                        </li>
+                        <li>Start Sonar</li>
+                      </ol>
 
                     <% else %>
-                    <%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install Sonar <%= release.getVersion() -%> after having carefully read the upgrade guide.
+                      <%= link_to 'Download', release.getDownloadUrl(), :class => 'external' -%> and install
+                      Sonar <%= release.getVersion() -%> after having carefully read the upgrade guide.
                     <% end %>
                   </td>
                 </tr>
               </table>
             </td>
           </tr>
-        </tbody>
-       </table>
-       <div class="break30"> </div>
-     <% end
-     end
- end %>
+          </tbody>
+        </table>
+        <div class="break30"></div>
+      <% end
+         end
        end %>
 
-   <%= render :partial => 'updatecenter/status', :locals => {:action => 'system_updates' } %>
- </div>
+  <%= render :partial => 'updatecenter/status', :locals => {:action => 'system_updates'} %>
+</div>
index 1966fd952416d6bcbdf5311ce6644ff554528dc1..d313e67535331fa23e20d3625a83b063585857f1 100644 (file)
@@ -7,8 +7,8 @@ function upgradePlugin(key) {
   return false;
 }
 </script>
-
- <%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'updates'} -%>
+<h1 class="marginbottom10"><%= message('update_center.page') -%></h1>
+<%= render :partial => 'updatecenter/tabs', :locals => {:tab => 'updates'} -%>
 
  <div class="tabs-panel">
 
index d170dbbe9356c91cc95579f129242a614b67df33..339659b709addb70722cdd97e29819aaa176101a 100644 (file)
@@ -22,64 +22,61 @@ package org.sonar.server.plugins;
 import org.apache.commons.io.IOUtils;
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.api.utils.HttpDownloader;
 import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.UriReader;
 import org.sonar.updatecenter.common.UpdateCenter;
 import org.sonar.updatecenter.common.Version;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 
-import static junit.framework.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
-import static org.mockito.Matchers.anyObject;
+import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Mockito.*;
 
 public class UpdateCenterClientTest {
 
   private UpdateCenterClient client;
-  private HttpDownloader downloader;
+  private UriReader reader;
   private static final String BASE_URL = "http://update.sonarsource.org";
 
   @Before
   public void startServer() throws Exception {
-    downloader = mock(HttpDownloader.class);
-    client = new UpdateCenterClient(downloader, new URI(BASE_URL));
+    reader = mock(UriReader.class);
+    client = new UpdateCenterClient(reader, new URI(BASE_URL));
   }
 
   @Test
   public void downloadUpdateCenter() throws URISyntaxException {
-    when(downloader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
+    when(reader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
     UpdateCenter center = client.getCenter();
-    verify(downloader, times(1)).openStream(new URI(BASE_URL));
-    assertThat(center.getSonar().getVersions(), hasItems(Version.create("2.2"), Version.create("2.3")));
+    verify(reader, times(1)).openStream(new URI(BASE_URL));
+    assertThat(center.getSonar().getVersions()).containsOnly(Version.create("2.2"), Version.create("2.3"));
   }
 
   @Test
-  public void ignoreWhenServerIsDown() {
-    when(downloader.download((URI) anyObject())).thenThrow(new SonarException());
-    assertNull(client.getCenter());
+  public void ignore_connection_errors() {
+    when(reader.openStream(any(URI.class))).thenThrow(new SonarException());
+    assertThat(client.getCenter()).isNull();
   }
 
 
   @Test
-  public void cacheData() throws URISyntaxException {
-    when(downloader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
+  public void cache_data() throws Exception {
+    when(reader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
 
     client.getCenter();
     client.getCenter();
 
-    verify(downloader, times(1)).openStream(new URI(BASE_URL));
+    verify(reader, times(1)).openStream(new URI(BASE_URL));
   }
 
   @Test
-  public void forceRefresh() throws URISyntaxException {
-    when(downloader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
+  public void forceRefresh() throws Exception {
+    when(reader.openStream(new URI(BASE_URL))).thenReturn(IOUtils.toInputStream("sonar.versions=2.2,2.3"));
 
     client.getCenter();
     client.getCenter(true);
 
-    verify(downloader, times(2)).openStream(new URI(BASE_URL));
+    verify(reader, times(2)).openStream(new URI(BASE_URL));
   }
 }
\ No newline at end of file
index 3a07a915cca226ed731d43b195f530de6d2f5f3d..d6b287b7d6278e026d2da9db3f3b9afac7b43668 100644 (file)
@@ -28,9 +28,7 @@ import org.sonar.updatecenter.common.Version;
 
 import java.util.List;
 
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.fest.assertions.Assertions.assertThat;
 
 public class UpdateCenterMatrixTest {
   private UpdateCenter center;
@@ -66,29 +64,29 @@ public class UpdateCenterMatrixTest {
 
   @Test
   public void findPluginUpdates() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.1");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.1"));
     matrix.registerInstalledPlugin("foo", Version.create("1.0"));
     List<PluginUpdate> updates = matrix.findPluginUpdates();
-    assertThat(updates.size(), is(2));
+    assertThat(updates).hasSize(2);
 
-    assertThat(updates.get(0).getRelease(), is(foo11));
-    assertThat(updates.get(0).isCompatible(), is(true));
+    assertThat(updates.get(0).getRelease()).isEqualTo(foo11);
+    assertThat(updates.get(0).isCompatible()).isTrue();
 
-    assertThat(updates.get(1).getRelease(), is(foo12));
-    assertThat(updates.get(1).isCompatible(), is(false));
-    assertThat(updates.get(1).requiresSonarUpgrade(), is(true));
+    assertThat(updates.get(1).getRelease()).isEqualTo(foo12);
+    assertThat(updates.get(1).isCompatible()).isFalse();
+    assertThat(updates.get(1).requiresSonarUpgrade()).isTrue();
   }
 
   @Test
   public void noPluginUpdatesIfLastReleaseIsInstalled() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.3");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.3"));
     matrix.registerInstalledPlugin("foo", Version.create("1.2"));
-    assertTrue(matrix.findPluginUpdates().isEmpty());
+    assertThat(matrix.findPluginUpdates()).isEmpty();
   }
 
   @Test
   public void availablePluginsAreOnlyTheBestReleases() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.2");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.2"));
     matrix.registerInstalledPlugin("foo", Version.create("1.0"));
 
     List<PluginUpdate> availables = matrix.findAvailablePlugins();
@@ -96,14 +94,14 @@ public class UpdateCenterMatrixTest {
     // bar 1.0 is compatible with the installed sonar
     // bar 1.1 requires sonar to be upgraded to 2.2.2 or 2.3
     // => available plugin to install is bar 1.0
-    assertThat(availables.size(), is(1));
-    assertThat(availables.get(0).getRelease(), is(bar10));
-    assertThat(availables.get(0).isCompatible(), is(true));
+    assertThat(availables.size()).isEqualTo(1);
+    assertThat(availables.get(0).getRelease()).isEqualTo(bar10);
+    assertThat(availables.get(0).isCompatible()).isTrue();
   }
 
   @Test
   public void availablePluginsRequireSonarUpgrade() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.2.1");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.2.1"));
     matrix.registerInstalledPlugin("foo", Version.create("1.0"));
 
     List<PluginUpdate> availables = matrix.findAvailablePlugins();
@@ -111,9 +109,9 @@ public class UpdateCenterMatrixTest {
     // bar 1.0 is not compatible with the installed sonar
     // bar 1.1 requires sonar to be upgraded to 2.2.2 or 2.3
     // => available plugin to install is bar 1.1
-    assertThat(availables.size(), is(1));
-    assertThat(availables.get(0).getRelease(), is(bar11));
-    assertThat(availables.get(0).requiresSonarUpgrade(), is(true));
+    assertThat(availables.size()).isEqualTo(1);
+    assertThat(availables.get(0).getRelease()).isEqualTo(bar11);
+    assertThat(availables.get(0).requiresSonarUpgrade()).isTrue();
   }
 
   @Test
@@ -121,13 +119,13 @@ public class UpdateCenterMatrixTest {
     center.getSonar().addRelease(Version.create("2.3"));
     center.getSonar().addRelease(Version.create("2.4"));
 
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.2");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.2"));
     List<SonarUpdate> updates = matrix.findSonarUpdates();
 
     // no plugins are installed, so both sonar versions are compatible
-    assertThat(updates.size(), is(2));
-    assertThat(updates.get(0).hasWarnings(), is(false));
-    assertThat(updates.get(1).hasWarnings(), is(false));
+    assertThat(updates).hasSize(2);
+    assertThat(updates.get(0).hasWarnings()).isFalse();
+    assertThat(updates.get(1).hasWarnings()).isFalse();
   }
 
   @Test
@@ -135,40 +133,40 @@ public class UpdateCenterMatrixTest {
     center.getSonar().addRelease(Version.create("2.3"));
     center.getSonar().addRelease(Version.create("2.4"));
 
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.2");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.2"));
     matrix.registerInstalledPlugin("foo", Version.create("1.0"));
     matrix.registerInstalledPlugin("bar", Version.create("1.0"));
     List<SonarUpdate> updates = matrix.findSonarUpdates();
 
-    assertThat(updates.size(), is(2));
+    assertThat(updates).hasSize(2);
 
     // sonar 2.3 supports foo 1.1/1.2 and bar 1.1
     // => 2 plugin upgrades are required
-    assertThat(updates.get(0).hasWarnings(), is(true));
-    assertThat(updates.get(0).requiresPluginUpgrades(), is(true));
-    assertThat(updates.get(0).getPluginsToUpgrade().size(), is(2));
+    assertThat(updates.get(0).hasWarnings()).isTrue();
+    assertThat(updates.get(0).requiresPluginUpgrades()).isTrue();
+    assertThat(updates.get(0).getPluginsToUpgrade()).hasSize(2);
 
     // sonar 2.4 supports no plugins
-    assertThat(updates.get(1).hasWarnings(), is(true));
-    assertThat(updates.get(1).isIncompatible(), is(true));
-    assertThat(updates.get(1).getIncompatiblePlugins().size(), is(2));
+    assertThat(updates.get(1).hasWarnings()).isTrue();
+    assertThat(updates.get(1).isIncompatible()).isTrue();
+    assertThat(updates.get(1).getIncompatiblePlugins()).hasSize(2);
   }
 
   @Test
   public void excludePendingDownloadsFromPluginUpdates() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.1");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.1"));
     matrix.registerInstalledPlugin("foo", Version.create("1.0"));
     matrix.registerPendingPluginsByFilename("foo-1.0.jar");
     List<PluginUpdate> updates = matrix.findPluginUpdates();
-    assertThat(updates.size(), is(0));
+    assertThat(updates.size()).isEqualTo(0);
   }
 
   @Test
   public void excludePendingDownloadsFromAvailablePlugins() {
-    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, "2.1");
+    UpdateCenterMatrix matrix = new UpdateCenterMatrix(center, Version.create("2.1"));
     matrix.registerPendingPluginsByFilename("foo-1.0.jar");
     matrix.registerPendingPluginsByFilename("bar-1.1.jar");
     List<PluginUpdate> updates = matrix.findAvailablePlugins();
-    assertThat(updates.size(), is(0));
+    assertThat(updates.size()).isEqualTo(0);
   }
 }