aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorantoine.vinot <antoine.vinot@sonarsource.com>2024-04-30 12:05:46 +0200
committersonartech <sonartech@sonarsource.com>2024-05-03 20:02:50 +0000
commitd0eae15ce508fb65f718ef7167aa3cfca698a8e8 (patch)
treeec7929d5278bc7db8dc8be25484a8763ecc9689e
parent0672a5f7bfdac4279ee9c33b4abc899a3705845b (diff)
downloadsonarqube-d0eae15ce508fb65f718ef7167aa3cfca698a8e8.tar.gz
sonarqube-d0eae15ce508fb65f718ef7167aa3cfca698a8e8.zip
SONAR-22034 New Endpoints to get JREs metadata and download JRE
-rw-r--r--server/sonar-webserver-webapi-v2/build.gradle1
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java1
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/DefaultJresController.java51
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/JresController.java67
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/JreInfoRestResponse.java30
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandler.java33
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandlerImpl.java148
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java14
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultJresControllerTest.java132
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultVersionControllerTest.java6
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/service/JresHandlerImplTest.java130
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java27
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/resources/jres-metadata-tests.json22
15 files changed, 699 insertions, 9 deletions
diff --git a/server/sonar-webserver-webapi-v2/build.gradle b/server/sonar-webserver-webapi-v2/build.gradle
index 760f4a25198..2acb3187201 100644
--- a/server/sonar-webserver-webapi-v2/build.gradle
+++ b/server/sonar-webserver-webapi-v2/build.gradle
@@ -17,6 +17,7 @@ dependencies {
testImplementation 'javax.servlet:javax.servlet-api'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.skyscreamer:jsonassert:1.5.1'
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
index 336ae93ad2e..9ba51c1c17a 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
@@ -48,6 +48,7 @@ public class WebApiEndpoints {
public static final String ANALYSIS_ENDPOINT = "/analysis";
public static final String VERSION_ENDPOINT = ANALYSIS_ENDPOINT + "/version";
+ public static final String JRE_ENDPOINT = ANALYSIS_ENDPOINT + "/jres";
private WebApiEndpoints() {
}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/DefaultJresController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/DefaultJresController.java
new file mode 100644
index 00000000000..da3e643ce87
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/DefaultJresController.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.controller;
+
+import java.util.List;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+import org.sonar.server.v2.api.analysis.service.JresHandler;
+import org.springframework.core.io.InputStreamResource;
+
+public class DefaultJresController implements JresController {
+
+ private final JresHandler jresHandler;
+
+ public DefaultJresController(JresHandler jresHandler) {
+ this.jresHandler = jresHandler;
+ }
+
+ @Override
+ public List<JreInfoRestResponse> getJresMetadata(String os, String arch) {
+ return jresHandler.getJresMetadata(os, arch);
+ }
+
+ @Override
+ public JreInfoRestResponse getJreMetadata(String id) {
+ return jresHandler.getJreMetadata(id);
+ }
+
+ @Override
+ public InputStreamResource downloadJre(String id) {
+ JreInfoRestResponse jreInfoRestResponse = jresHandler.getJreMetadata(id);
+ return new InputStreamResource(jresHandler.getJreBinary(jreInfoRestResponse.filename()));
+ }
+
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/JresController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/JresController.java
new file mode 100644
index 00000000000..7e18f63bc1a
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/controller/JresController.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import java.util.List;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import static io.swagger.v3.oas.annotations.enums.ParameterIn.PATH;
+import static org.sonar.server.v2.WebApiEndpoints.JRE_ENDPOINT;
+import static org.springframework.http.HttpStatus.OK;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
+
+@RequestMapping(value = JRE_ENDPOINT, produces = APPLICATION_JSON_VALUE)
+@ResponseStatus(OK)
+@RestController
+public interface JresController {
+
+ String OS_PARAM_DESCRIPTION = "Filter the JRE by operating system. Accepted values are 'windows', 'linux', 'macos', 'alpine' (case-insensitive), with some aliases";
+ String ARCH_PARAM_DESCRIPTION = "Filter the JRE by CPU architecture. Accepted values are 'x64' and 'aarch64' (case-insensitive), with some aliases.";
+ String GET_JRE_DESCRIPTION =
+ "This endpoint return the JRE metadata by default. To download the JRE binary asset, set the Accept header of the request to 'application/octet-stream'.";
+ String GET_JRE_SUMMARY = "JRE download/metadata";
+ String ID_PARAM_DESCRIPTION = "The ID of the JRE";
+
+ @GetMapping
+ @Operation(summary = "All JREs metadata", description = "Get metadata of all available JREs")
+ List<JreInfoRestResponse> getJresMetadata(
+ @RequestParam(value = "os", required = false) @Parameter(description = OS_PARAM_DESCRIPTION) String os,
+ @RequestParam(value = "arch", required = false) @Parameter(description = ARCH_PARAM_DESCRIPTION) String arch);
+
+ @GetMapping(value = "/{id}")
+ @Operation(summary = GET_JRE_SUMMARY, description = GET_JRE_DESCRIPTION)
+ JreInfoRestResponse getJreMetadata(
+ @PathVariable(value = "id") @Parameter(description = ID_PARAM_DESCRIPTION, required = true, in = PATH) String id);
+
+ @GetMapping(value = "/{id}", produces = APPLICATION_OCTET_STREAM_VALUE)
+ @Operation(summary = GET_JRE_SUMMARY, description = GET_JRE_DESCRIPTION)
+ InputStreamResource downloadJre(
+ @PathVariable(value = "id") @Parameter(description = ID_PARAM_DESCRIPTION, required = true, in = PATH) String id);
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/JreInfoRestResponse.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/JreInfoRestResponse.java
new file mode 100644
index 00000000000..63f1853b259
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/JreInfoRestResponse.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.response;
+
+public record JreInfoRestResponse(
+ String id,
+ String filename,
+ String sha256,
+ String javaPath,
+ String os,
+ String arch
+) {
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/package-info.java
new file mode 100644
index 00000000000..3f469e853a8
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/response/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.analysis.response;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandler.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandler.java
new file mode 100644
index 00000000000..3f4f56e02fb
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandler.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.service;
+
+import java.io.InputStream;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+
+public interface JresHandler {
+ List<JreInfoRestResponse> getJresMetadata(@Nullable String os, @Nullable String arch);
+
+ JreInfoRestResponse getJreMetadata(String id);
+
+ InputStream getJreBinary(String jreFilename);
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandlerImpl.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandlerImpl.java
new file mode 100644
index 00000000000..53a06aaf4e0
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/JresHandlerImpl.java
@@ -0,0 +1,148 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.annotations.VisibleForTesting;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Predicate;
+import javax.annotation.Nullable;
+import javax.annotation.PostConstruct;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+
+import static java.lang.String.join;
+import static org.apache.commons.lang.StringUtils.isBlank;
+
+public class JresHandlerImpl implements JresHandler {
+
+ private static final String JRES_METADATA_FILENAME = "jres-metadata.json";
+
+ private final String jresMetadataFilename;
+ private final Map<String, JreInfoRestResponse> metadata = new HashMap<>();
+
+ public JresHandlerImpl() {
+ this(JRES_METADATA_FILENAME);
+ }
+
+ @VisibleForTesting
+ public JresHandlerImpl(String jresMetadataFilename) {
+ this.jresMetadataFilename = jresMetadataFilename;
+ }
+
+ @PostConstruct
+ void initMetadata() {
+ metadata.clear();
+ readJresMetadata().forEach(jre -> metadata.put(jre.id(), jre));
+ }
+
+ private List<JreInfoRestResponse> readJresMetadata() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try (InputStream is = getClass().getClassLoader().getResourceAsStream(jresMetadataFilename)) {
+ return objectMapper.readValue(is, objectMapper.getTypeFactory().constructCollectionType(List.class, JreInfoRestResponse.class));
+ } catch (IOException ioException) {
+ throw new UncheckedIOException(ioException);
+ }
+ }
+
+ @Override
+ public List<JreInfoRestResponse> getJresMetadata(@Nullable String os, @Nullable String arch) {
+ Predicate<JreInfoRestResponse> osFilter = isBlank(os) ? jre -> true : (jre -> OS.from(jre.os()) == OS.from(os));
+ Predicate<JreInfoRestResponse> archFilter = isBlank(arch) ? jre -> true : (jre -> Arch.from(jre.arch()) == Arch.from(arch));
+ return metadata.values().stream()
+ .filter(osFilter)
+ .filter(archFilter)
+ .toList();
+ }
+
+ @Override
+ public JreInfoRestResponse getJreMetadata(String id) {
+ return Optional.ofNullable(metadata.get(id))
+ .orElseThrow(() -> new NotFoundException("JRE not found for id: " + id));
+ }
+
+ @Override
+ public InputStream getJreBinary(String jreFilename) {
+ try {
+ return new FileInputStream("jres/" + jreFilename);
+ } catch (FileNotFoundException fileNotFoundException) {
+ throw new NotFoundException(String.format("Unable to find JRE '%s'", jreFilename));
+ }
+ }
+
+ enum OS {
+ WINDOWS("win", "windows", "win32"),
+ LINUX("linux"),
+ MACOS("mac", "macos", "darwin"),
+ ALPINE("alpine");
+
+ private final List<String> aliases;
+
+ OS(String... aliases) {
+ this.aliases = Arrays.stream(aliases).toList();
+ }
+
+ private static OS from(String alias) {
+ return Arrays.stream(values())
+ .filter(os -> os.aliases.contains(alias))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(String.format("Unsupported OS: '%s'. Supported values are '%s'", alias, join(", ", supportedValues()))));
+ }
+
+ private static List<String> supportedValues() {
+ return Arrays.stream(values())
+ .flatMap(os -> os.aliases.stream())
+ .toList();
+ }
+ }
+
+ enum Arch {
+ X64("x86_64", "x86-64", "amd64", "x64"),
+ AARCH64("arm64", "aarch64");
+
+ private final List<String> aliases;
+
+ Arch(String... aliases) {
+ this.aliases = Arrays.stream(aliases).toList();
+ }
+
+ private static Arch from(String alias) {
+ return Arrays.stream(values())
+ .filter(arch -> arch.aliases.contains(alias))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(String.format("Unsupported architecture: '%s'. Supported values are '%s'", alias, join(", ", supportedValues()))));
+ }
+
+ private static List<String> supportedValues() {
+ return Arrays.stream(values())
+ .flatMap(arch -> arch.aliases.stream())
+ .toList();
+ }
+ }
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/package-info.java
new file mode 100644
index 00000000000..2a8ba4631e8
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/analysis/service/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.analysis.service;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
index 8dd3091614a..186a5dd4c6c 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
@@ -43,8 +43,12 @@ import org.sonar.server.platform.NodeInformation;
import org.sonar.server.rule.RuleDescriptionFormatter;
import org.sonar.server.user.SystemPasscode;
import org.sonar.server.user.UserSession;
+import org.sonar.server.v2.api.analysis.controller.DefaultJresController;
import org.sonar.server.v2.api.analysis.controller.DefaultVersionController;
+import org.sonar.server.v2.api.analysis.controller.JresController;
import org.sonar.server.v2.api.analysis.controller.VersionController;
+import org.sonar.server.v2.api.analysis.service.JresHandler;
+import org.sonar.server.v2.api.analysis.service.JresHandlerImpl;
import org.sonar.server.v2.api.dop.controller.DefaultDopSettingsController;
import org.sonar.server.v2.api.dop.controller.DopSettingsController;
import org.sonar.server.v2.api.gitlab.config.controller.DefaultGitlabConfigurationController;
@@ -159,4 +163,14 @@ public class PlatformLevel4WebConfig {
return new DefaultVersionController(server);
}
+ @Bean
+ public JresHandler jresHandler() {
+ return new JresHandlerImpl();
+ }
+
+ @Bean
+ public JresController jresController(JresHandler jresHandler) {
+ return new DefaultJresController(jresHandler);
+ }
+
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultJresControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultJresControllerTest.java
new file mode 100644
index 00000000000..c5f3a1cd343
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultJresControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.controller;
+
+import java.io.ByteArrayInputStream;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+import org.sonar.server.v2.api.analysis.service.JresHandler;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.v2.WebApiEndpoints.JRE_ENDPOINT;
+import static org.sonar.server.v2.api.ControllerTester.getMockMvc;
+import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
+import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+class DefaultJresControllerTest {
+
+ private final JresHandler jresHandler = mock(JresHandler.class);
+
+ private final MockMvc mockMvc = getMockMvc(new DefaultJresController(jresHandler));
+
+ @ParameterizedTest
+ @MethodSource("osAndArch")
+ void getJres_shoudlReturnMetadaAsJson(String os, String arch) throws Exception {
+ JreInfoRestResponse metadata1 = new JreInfoRestResponse("id_1", "filename_1", "sha256_1", "javaPath_1", "os_1", "arch_1");
+ JreInfoRestResponse metadata2 = new JreInfoRestResponse("id_2", "filename_2", "sha256_2", "javaPath_2", "os_2", "arch_2");
+ when(jresHandler.getJresMetadata(os, arch)).thenReturn(List.of(metadata1, metadata2));
+ String expectedJson =
+ """
+ [
+ {
+ "id": "id_1",
+ "filename": "filename_1",
+ "sha256": "sha256_1",
+ "javaPath": "javaPath_1",
+ "os": "os_1",
+ "arch": "arch_1"
+ },
+ {
+ "id": "id_2",
+ "filename": "filename_2",
+ "sha256": "sha256_2",
+ "javaPath": "javaPath_2",
+ "os": "os_2",
+ "arch": "arch_2"
+ }
+ ]
+ """;
+
+ mockMvc.perform(get(JRE_ENDPOINT)
+ .param("os", os)
+ .param("arch", arch))
+ .andExpect(status().isOk())
+ .andExpect(content().json(expectedJson));
+ }
+
+ @Test
+ void getJres_shoudlReturnEmptyJsonArray_whenNoResults() throws Exception {
+ when(jresHandler.getJresMetadata(null, null)).thenReturn(List.of());
+
+ mockMvc.perform(get(JRE_ENDPOINT))
+ .andExpect(status().isOk())
+ .andExpect(content().json("[]"));
+ }
+
+ private static Stream<Arguments> osAndArch() {
+ return Stream.of(
+ arguments(null, null),
+ arguments("windows", null),
+ arguments(null, "x64"),
+ arguments("linux", "aarch64")
+ );
+ }
+
+ @Test
+ void getJre_shouldReturnMetadataAsJson() throws Exception {
+ String anyId = "anyId";
+ JreInfoRestResponse jreInfoRestResponse = new JreInfoRestResponse(anyId, "filename", "sha256", "javaPath", "os", "arch");
+ when(jresHandler.getJreMetadata(anyId)).thenReturn(jreInfoRestResponse);
+ String expectedJson = "{\"id\":\"" + anyId + "\",\"filename\":\"filename\",\"sha256\":\"sha256\",\"javaPath\":\"javaPath\",\"os\":\"os\",\"arch\":\"arch\"}";
+
+ mockMvc.perform(get(JRE_ENDPOINT + "/" + anyId))
+ .andExpect(status().isOk())
+ .andExpect(content().json(expectedJson));
+ }
+
+ @Test
+ void getJre_shouldDownloadJre_whenHeaderIsOctetStream() throws Exception {
+ String anyId = "anyId";
+ String anyFilename = "anyFilename";
+ JreInfoRestResponse jreInfoRestResponse = new JreInfoRestResponse(anyId, anyFilename, "sha256", "javaPath", "os", "arch");
+ when(jresHandler.getJreMetadata(anyId)).thenReturn(jreInfoRestResponse);
+ byte[] anyBinary = {1, 2, 3};
+
+ when(jresHandler.getJreBinary(anyFilename)).thenReturn(new ByteArrayInputStream(anyBinary));
+
+ mockMvc.perform(get(JRE_ENDPOINT + "/" + anyId)
+ .header("Accept", APPLICATION_OCTET_STREAM_VALUE))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(APPLICATION_OCTET_STREAM))
+ .andExpect(content().bytes(anyBinary));
+ }
+
+}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultVersionControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultVersionControllerTest.java
index 5dfdf29a2a3..b907cb0f572 100644
--- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultVersionControllerTest.java
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/controller/DefaultVersionControllerTest.java
@@ -19,7 +19,7 @@
*/
package org.sonar.server.v2.api.analysis.controller;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
import org.sonar.api.platform.Server;
import org.springframework.test.web.servlet.MockMvc;
@@ -31,14 +31,14 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-public class DefaultVersionControllerTest {
+class DefaultVersionControllerTest {
private final Server server = mock(Server.class);
private final MockMvc mockMvc = getMockMvc(new DefaultVersionController(server));
@Test
- public void getVersion_shouldReturnServerVersion() throws Exception {
+ void getVersion_shouldReturnServerVersion() throws Exception {
String serverVersion = "10.6";
when(server.getVersion()).thenReturn(serverVersion);
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/service/JresHandlerImplTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/service/JresHandlerImplTest.java
new file mode 100644
index 00000000000..210abf41c8a
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/analysis/service/JresHandlerImplTest.java
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.v2.api.analysis.service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.v2.api.analysis.response.JreInfoRestResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class JresHandlerImplTest {
+
+ private static final Map<String, JreInfoRestResponse> JRE_METADATA = Map.of(
+ "1", new JreInfoRestResponse("1", "jre1", "checksum1", "java1", "alpine", "aarch64"),
+ "2", new JreInfoRestResponse("2", "jre2", "checksum2", "java2", "linux", "aarch64"),
+ "3", new JreInfoRestResponse("3", "jre3", "checksum3", "java3", "alpine", "x64")
+ );
+
+ private static final JresHandlerImpl jresHandler = new JresHandlerImpl("jres-metadata-tests.json");
+
+ @BeforeAll
+ static void setup() {
+ jresHandler.initMetadata();
+ }
+
+ @Test
+ void getJresMetadata_shouldReturnAllMetadata_whenNoFiltering() {
+ List<JreInfoRestResponse> result = jresHandler.getJresMetadata(null, null);
+
+ assertThat(result).extracting(JreInfoRestResponse::id).containsExactly("1", "2", "3");
+ }
+
+ @ParameterizedTest
+ @MethodSource("filteredMetadata")
+ void getJresMetadata_shouldReturnFilteredMetadata_whenFiltering(String os, String arch, JreInfoRestResponse expected) {
+ List<JreInfoRestResponse> resultList = jresHandler.getJresMetadata(os, arch);
+
+ assertThat(resultList).hasSize(1);
+ JreInfoRestResponse result = resultList.get(0);
+ assertThat(result).usingRecursiveComparison().isEqualTo(expected);
+ }
+
+ @Test
+ void getJresMetadata_shouldFail_whenFilteredWithUnsupportedOsValue() {
+ String anyUnsupportedOS = "not-supported";
+
+ assertThatThrownBy(() -> jresHandler.getJresMetadata(anyUnsupportedOS, null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Unsupported OS: '" + anyUnsupportedOS + "'");
+ }
+
+ @Test
+ void getJresMetadata_shouldFail_whenFilteredWithUnsupportedArchValue() {
+ String anyUnsupportedArch = "not-supported";
+
+ assertThatThrownBy(() -> jresHandler.getJresMetadata(null, anyUnsupportedArch))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Unsupported architecture: '" + anyUnsupportedArch + "'");
+ }
+
+ private static Stream<Arguments> filteredMetadata() {
+ return Stream.of(
+ arguments("alpine", "aarch64", JRE_METADATA.get("1")),
+ arguments("linux", null, JRE_METADATA.get("2")),
+ arguments(null, "x64", JRE_METADATA.get("3"))
+ );
+ }
+
+ @Test
+ void getJresMetadata_shouldReturnEmptyList_whenNoMetadata() {
+ List<JreInfoRestResponse> result = jresHandler.getJresMetadata("windows", "x64");
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ void getJresMetadata_shouldReturnEmptyList_whenNoFilteringAndNoMetadata() {
+ JresHandlerImpl noMetadataHandler = new JresHandlerImpl("");
+ List<JreInfoRestResponse> result = noMetadataHandler.getJresMetadata(null, null);
+ assertThat(result).isEmpty();
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"1", "2", "3"})
+ void getJreMetadata(String id) {
+ JreInfoRestResponse result = jresHandler.getJreMetadata(id);
+
+ assertThat(result).usingRecursiveComparison().isEqualTo(JRE_METADATA.get(id));
+ }
+
+ @Test
+ void getJreMetadata_shouldFail_whenJreNotFound() {
+ assertThatThrownBy(() -> jresHandler.getJreMetadata("4"))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("JRE not found for id: 4");
+ }
+
+ @Test
+ void getJreBinary_shouldFail_whenFileNotFound() {
+ assertThatThrownBy(() -> jresHandler.getJreBinary("jre1"))
+ .isInstanceOf(NotFoundException.class)
+ .hasMessage("Unable to find JRE 'jre1'");
+ }
+}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
index 5a032d3591f..0723c2d49b1 100644
--- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/config/PlatformLevel4WebConfigTest.java
@@ -19,20 +19,35 @@
*/
package org.sonar.server.v2.config;
-import org.junit.Test;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.sonar.api.platform.Server;
+import org.sonar.server.v2.api.analysis.controller.DefaultJresController;
import org.sonar.server.v2.api.analysis.controller.DefaultVersionController;
+import org.sonar.server.v2.api.analysis.service.JresHandler;
+import org.sonar.server.v2.api.analysis.service.JresHandlerImpl;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.mock;
public class PlatformLevel4WebConfigTest {
- private final PlatformLevel4WebConfig platformLevel4WebConfig = new PlatformLevel4WebConfig();
+ private static final PlatformLevel4WebConfig platformLevel4WebConfig = new PlatformLevel4WebConfig();
- @Test
- public void versionController() {
- assertThat(platformLevel4WebConfig.versionController(mock(Server.class))).isNotNull()
- .isInstanceOf(DefaultVersionController.class);
+ private static Stream<Arguments> components() {
+ return Stream.of(
+ arguments(platformLevel4WebConfig.versionController(mock(Server.class)), DefaultVersionController.class),
+ arguments(platformLevel4WebConfig.jresHandler(), JresHandlerImpl.class),
+ arguments(platformLevel4WebConfig.jresController(mock(JresHandler.class)), DefaultJresController.class)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("components")
+ void components_shouldBeInjectedInPlatformLevel4WebConfig(Object component, Class<?> instanceClass) {
+ assertThat(component).isNotNull().isInstanceOf(instanceClass);
}
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/resources/jres-metadata-tests.json b/server/sonar-webserver-webapi-v2/src/test/resources/jres-metadata-tests.json
new file mode 100644
index 00000000000..2e3138021f6
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/test/resources/jres-metadata-tests.json
@@ -0,0 +1,22 @@
+[ {
+ "id" : "1",
+ "filename" : "jre1",
+ "sha256" : "checksum1",
+ "javaPath" : "java1",
+ "os" : "alpine",
+ "arch" : "aarch64"
+}, {
+ "id" : "2",
+ "filename" : "jre2",
+ "sha256" : "checksum2",
+ "javaPath" : "java2",
+ "os" : "linux",
+ "arch" : "aarch64"
+}, {
+ "id" : "3",
+ "filename" : "jre3",
+ "sha256" : "checksum3",
+ "javaPath" : "java3",
+ "os" : "alpine",
+ "arch" : "x64"
+} ]