]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10591 add WS api/plugins/download
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 10 May 2018 19:46:42 +0000 (21:46 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 11 May 2018 18:20:47 +0000 (20:20 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/DownloadAction.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/DownloadActionTest.java [new file with mode: 0644]

index 21d6a286cb1addfd6b358ed688b5c2dbf7b45dac..2ca98540648ffeb5a0161a187cb9dd4e15aefee5 100644 (file)
@@ -143,6 +143,7 @@ import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper;
 import org.sonar.server.plugins.privileged.PrivilegedPluginsStopper;
 import org.sonar.server.plugins.ws.AvailableAction;
 import org.sonar.server.plugins.ws.CancelAllAction;
+import org.sonar.server.plugins.ws.DownloadAction;
 import org.sonar.server.plugins.ws.InstallAction;
 import org.sonar.server.plugins.ws.InstalledAction;
 import org.sonar.server.plugins.ws.PendingAction;
@@ -529,6 +530,7 @@ public class PlatformLevel4 extends PlatformLevel {
       PluginUpdateAggregator.class,
       InstalledAction.class,
       AvailableAction.class,
+      DownloadAction.class,
       UpdatesAction.class,
       PendingAction.class,
       InstallAction.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/DownloadAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/DownloadAction.java
new file mode 100644 (file)
index 0000000..b4342ea
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins.ws;
+
+import java.io.InputStream;
+import java.util.Optional;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.plugins.InstalledPlugin;
+import org.sonar.server.plugins.InstalledPlugin.FileAndMd5;
+import org.sonar.server.plugins.PluginFileSystem;
+
+public class DownloadAction implements PluginsWsAction {
+
+  private static final String PACK200 = "pack200";
+  private static final String ACCEPT_COMPRESSIONS_PARAM = "acceptCompressions";
+  private static final String PLUGIN_PARAM = "plugin";
+
+  private final PluginFileSystem pluginFileSystem;
+
+  public DownloadAction(PluginFileSystem pluginFileSystem) {
+    this.pluginFileSystem = pluginFileSystem;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("download")
+      .setSince("7.2")
+      .setDescription("Download plugin JAR, for usage by scanner engine")
+      .setInternal(true)
+      .setHandler(this);
+
+    action.createParam(PLUGIN_PARAM)
+      .setRequired(true)
+      .setDescription("The key identifying the plugin to download")
+      .setExampleValue("cobol");
+
+    action.createParam(ACCEPT_COMPRESSIONS_PARAM)
+      .setExampleValue(PACK200);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String pluginKey = request.mandatoryParam(PLUGIN_PARAM);
+
+    Optional<InstalledPlugin> file = pluginFileSystem.getInstalledPlugin(pluginKey);
+    if (!file.isPresent()) {
+      throw new NotFoundException("Plugin " + pluginKey + " not found");
+    }
+
+    FileAndMd5 downloadedFile;
+    FileAndMd5 compressedJar = file.get().getCompressedJar();
+    if (compressedJar != null && PACK200.equals(request.param(ACCEPT_COMPRESSIONS_PARAM))) {
+      response.stream().setMediaType("application/octet-stream");
+
+      response.setHeader("Sonar-Compression", PACK200);
+      response.setHeader("Sonar-UncompressedMD5", file.get().getLoadedJar().getMd5());
+      downloadedFile = compressedJar;
+    } else {
+      response.stream().setMediaType("application/java-archive");
+      downloadedFile = file.get().getLoadedJar();
+    }
+    response.setHeader("Sonar-MD5", downloadedFile.getMd5());
+    try (InputStream input = FileUtils.openInputStream(downloadedFile.getFile())) {
+      IOUtils.copyLarge(input, response.stream().output());
+    }
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/DownloadActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/DownloadActionTest.java
new file mode 100644 (file)
index 0000000..feb1593
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins.ws;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Optional;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.platform.PluginInfo;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.plugins.InstalledPlugin;
+import org.sonar.server.plugins.InstalledPlugin.FileAndMd5;
+import org.sonar.server.plugins.PluginFileSystem;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsAction;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DownloadActionTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private PluginFileSystem pluginFileSystem = mock(PluginFileSystem.class);
+  private WsAction underTest = new DownloadAction(pluginFileSystem);
+  private WsActionTester tester = new WsActionTester(underTest);
+
+  @Test
+  public void test_definition() {
+    WebService.Action def = tester.getDef();
+
+    assertThat(def.isInternal()).isTrue();
+    assertThat(def.since()).isEqualTo("7.2");
+    assertThat(def.params())
+      .extracting(WebService.Param::key)
+      .containsExactlyInAnyOrder("plugin", "acceptCompressions");
+  }
+
+  @Test
+  public void return_404_if_plugin_not_found() {
+    when(pluginFileSystem.getInstalledPlugin("foo")).thenReturn(Optional.empty());
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Plugin foo not found");
+
+    tester.newRequest()
+      .setParam("plugin", "foo")
+      .execute();
+  }
+
+  @Test
+  public void return_jar_if_plugin_exists() throws Exception {
+    InstalledPlugin plugin = newPlugin();
+    when(pluginFileSystem.getInstalledPlugin(plugin.getPluginInfo().getKey())).thenReturn(Optional.of(plugin));
+
+    TestResponse response = tester.newRequest()
+      .setParam("plugin", plugin.getPluginInfo().getKey())
+      .execute();
+
+    assertThat(response.getHeader("Sonar-MD5")).isEqualTo(plugin.getLoadedJar().getMd5());
+    assertThat(response.getHeader("Sonar-Compression")).isNull();
+    assertThat(response.getMediaType()).isEqualTo("application/java-archive");
+    verifySameContent(response, plugin.getLoadedJar().getFile());
+  }
+
+  @Test
+  public void return_uncompressed_jar_if_client_does_not_accept_compression() throws Exception {
+    InstalledPlugin plugin = newCompressedPlugin();
+    when(pluginFileSystem.getInstalledPlugin(plugin.getPluginInfo().getKey())).thenReturn(Optional.of(plugin));
+
+    TestResponse response = tester.newRequest()
+      .setParam("plugin", plugin.getPluginInfo().getKey())
+      .execute();
+
+    assertThat(response.getHeader("Sonar-MD5")).isEqualTo(plugin.getLoadedJar().getMd5());
+    assertThat(response.getHeader("Sonar-Compression")).isNull();
+    assertThat(response.getHeader("Sonar-UncompressedMD5")).isNull();
+    assertThat(response.getMediaType()).isEqualTo("application/java-archive");
+    verifySameContent(response, plugin.getLoadedJar().getFile());
+  }
+
+  @Test
+  public void return_uncompressed_jar_if_client_requests_unsupported_compression() throws Exception {
+    InstalledPlugin plugin = newCompressedPlugin();
+    when(pluginFileSystem.getInstalledPlugin(plugin.getPluginInfo().getKey())).thenReturn(Optional.of(plugin));
+
+    TestResponse response = tester.newRequest()
+      .setParam("plugin", plugin.getPluginInfo().getKey())
+      .setParam("acceptCompressions", "zip")
+      .execute();
+
+    assertThat(response.getHeader("Sonar-MD5")).isEqualTo(plugin.getLoadedJar().getMd5());
+    assertThat(response.getHeader("Sonar-Compression")).isNull();
+    assertThat(response.getHeader("Sonar-UncompressedMD5")).isNull();
+    assertThat(response.getMediaType()).isEqualTo("application/java-archive");
+    verifySameContent(response, plugin.getLoadedJar().getFile());
+  }
+
+  @Test
+  public void return_compressed_jar_if_client_accepts_pack200() throws Exception {
+    InstalledPlugin plugin = newCompressedPlugin();
+    when(pluginFileSystem.getInstalledPlugin(plugin.getPluginInfo().getKey())).thenReturn(Optional.of(plugin));
+
+    TestResponse response = tester.newRequest()
+      .setParam("plugin", plugin.getPluginInfo().getKey())
+      .setParam("acceptCompressions", "pack200")
+      .execute();
+
+    assertThat(response.getHeader("Sonar-MD5")).isEqualTo(plugin.getCompressedJar().getMd5());
+    assertThat(response.getHeader("Sonar-UncompressedMD5")).isEqualTo(plugin.getLoadedJar().getMd5());
+    assertThat(response.getHeader("Sonar-Compression")).isEqualTo("pack200");
+    assertThat(response.getMediaType()).isEqualTo("application/octet-stream");
+    verifySameContent(response, plugin.getCompressedJar().getFile());
+  }
+
+  private InstalledPlugin newPlugin() throws IOException {
+    FileAndMd5 jar = new FileAndMd5(temp.newFile());
+    return new InstalledPlugin(new PluginInfo("foo"), jar, null);
+  }
+
+  private InstalledPlugin newCompressedPlugin() throws IOException {
+    FileAndMd5 jar = new FileAndMd5(temp.newFile());
+    FileAndMd5 compressedJar = new FileAndMd5(temp.newFile());
+    return new InstalledPlugin(new PluginInfo("foo"), jar, compressedJar);
+  }
+
+  private static void verifySameContent(TestResponse response, File file) throws IOException {
+    assertThat(IOUtils.toByteArray(response.getInputStream())).isEqualTo(FileUtils.readFileToByteArray(file));
+  }
+}