From a24898959f415d84ac6a71b5ca480def88d889c1 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 24 Oct 2017 15:54:17 +0200 Subject: SONAR-10020 Refactor system/info WS --- .../platform/monitoring/WebSystemInfoModule.java | 8 +- .../sonar/server/platform/ws/BaseInfoWsAction.java | 120 --------------------- .../server/platform/ws/ClusterInfoAction.java | 106 ------------------ .../platform/ws/ClusterSystemInfoWriter.java | 95 ++++++++++++++++ .../org/sonar/server/platform/ws/InfoAction.java | 67 +++++------- .../platform/ws/StandaloneSystemInfoWriter.java | 64 +++++++++++ .../sonar/server/platform/ws/SystemInfoWriter.java | 95 ++++++++++++++++ .../platform/ws/ClusterSystemInfoWriterTest.java | 87 +++++++++++++++ .../sonar/server/platform/ws/InfoActionTest.java | 43 ++------ .../ws/StandaloneSystemInfoWriterTest.java | 88 +++++++++++++++ .../org/sonar/server/platform/ws/SystemWsTest.java | 10 +- 11 files changed, 468 insertions(+), 315 deletions(-) delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/BaseInfoWsAction.java delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterInfoAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterSystemInfoWriter.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriter.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWriter.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/ws/ClusterSystemInfoWriterTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriterTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/WebSystemInfoModule.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/WebSystemInfoModule.java index fa3a32f3b67..fc491460414 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/WebSystemInfoModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/WebSystemInfoModule.java @@ -30,8 +30,9 @@ import org.sonar.server.platform.monitoring.cluster.NodeSystemSection; import org.sonar.server.platform.monitoring.cluster.ProcessInfoProvider; import org.sonar.server.platform.monitoring.cluster.EsClusterStateSection; import org.sonar.server.platform.monitoring.cluster.SearchNodesInfoLoaderImpl; -import org.sonar.server.platform.ws.ClusterInfoAction; +import org.sonar.server.platform.ws.ClusterSystemInfoWriter; import org.sonar.server.platform.ws.InfoAction; +import org.sonar.server.platform.ws.StandaloneSystemInfoWriter; public class WebSystemInfoModule { @@ -53,7 +54,7 @@ public class WebSystemInfoModule { StandaloneSystemSection.class, OfficialDistribution.class, - + StandaloneSystemInfoWriter.class, InfoAction.class }; } @@ -79,7 +80,8 @@ public class WebSystemInfoModule { GlobalInfoLoader.class, AppNodesInfoLoaderImpl.class, SearchNodesInfoLoaderImpl.class, - ClusterInfoAction.class + ClusterSystemInfoWriter.class, + InfoAction.class }; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/BaseInfoWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/BaseInfoWsAction.java deleted file mode 100644 index f6c8ff5aa05..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/BaseInfoWsAction.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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.platform.ws; - -import java.util.Collection; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.text.JsonWriter; -import org.sonar.process.systeminfo.SystemInfoUtils; -import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; -import org.sonar.server.health.Health; -import org.sonar.server.telemetry.TelemetryDataLoader; -import org.sonar.server.user.UserSession; - -import static org.sonar.server.telemetry.TelemetryDataJsonWriter.writeTelemetryData; - -public abstract class BaseInfoWsAction implements SystemWsAction { - - private static final String[] ORDERED_SECTION_NAMES = { - // standalone - "System", "Database", "Plugins", - - // cluster - "Web JVM State", "Web Database Connection", "Web Logging", "Web JVM Properties", - "Compute Engine Tasks", "Compute Engine JVM State", "Compute Engine Database Connection", "Compute Engine Logging", "Compute Engine JVM Properties", - "Search State", "Search Indexes"}; - - private final UserSession userSession; - private final TelemetryDataLoader telemetry; - - public BaseInfoWsAction(UserSession userSession, TelemetryDataLoader telemetry) { - this.userSession = userSession; - this.telemetry = telemetry; - } - - @Override - public void define(WebService.NewController controller) { - controller.createAction("info") - .setDescription("Get detailed information about system configuration.
" + - "Requires 'Administer' permissions.
" + - "Since 5.5, this web service becomes internal in order to more easily update result.") - .setSince("5.1") - .setInternal(true) - .setResponseExample(getClass().getResource("/org/sonar/server/platform/ws/info-example.json")) - .setHandler(this); - } - - @Override - public void handle(Request request, Response response) throws InterruptedException { - userSession.checkIsSystemAdministrator(); - doHandle(request, response); - } - - protected abstract void doHandle(Request request, Response response) throws InterruptedException; - - protected void writeSections(Collection sections, JsonWriter json) { - SystemInfoUtils - .order(sections, ORDERED_SECTION_NAMES) - .forEach(section -> writeSection(section, json)); - } - - private void writeSection(ProtobufSystemInfo.Section section, JsonWriter json) { - json.name(section.getName()); - json.beginObject(); - for (ProtobufSystemInfo.Attribute attribute : section.getAttributesList()) { - writeAttribute(attribute, json); - } - json.endObject(); - } - - private void writeAttribute(ProtobufSystemInfo.Attribute attribute, JsonWriter json) { - switch (attribute.getValueCase()) { - case BOOLEAN_VALUE: - json.prop(attribute.getKey(), attribute.getBooleanValue()); - break; - case LONG_VALUE: - json.prop(attribute.getKey(), attribute.getLongValue()); - break; - case DOUBLE_VALUE: - json.prop(attribute.getKey(), attribute.getDoubleValue()); - break; - case STRING_VALUE: - json.prop(attribute.getKey(), attribute.getStringValue()); - break; - case VALUE_NOT_SET: - json.name(attribute.getKey()).beginArray().values(attribute.getStringValuesList()).endArray(); - break; - default: - throw new IllegalArgumentException("Unsupported type: " + attribute.getValueCase()); - } - } - - protected void writeHealth(Health health, JsonWriter json) { - json.prop("Health", health.getStatus().name()); - json.name("Health Causes").beginArray().values(health.getCauses()).endArray(); - } - - protected void writeTelemetry(JsonWriter json) { - json.name("Statistics"); - writeTelemetryData(json, telemetry.load()); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterInfoAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterInfoAction.java deleted file mode 100644 index 35c43865026..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterInfoAction.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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.platform.ws; - -import java.util.Collection; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.utils.text.JsonWriter; -import org.sonar.server.health.ClusterHealth; -import org.sonar.server.health.HealthChecker; -import org.sonar.server.platform.monitoring.cluster.AppNodesInfoLoader; -import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader; -import org.sonar.server.platform.monitoring.cluster.NodeInfo; -import org.sonar.server.platform.monitoring.cluster.SearchNodesInfoLoader; -import org.sonar.server.telemetry.TelemetryDataLoader; -import org.sonar.server.user.UserSession; - -public class ClusterInfoAction extends BaseInfoWsAction { - - private final GlobalInfoLoader globalInfoLoader; - private final AppNodesInfoLoader appNodesInfoLoader; - private final SearchNodesInfoLoader searchNodesInfoLoader; - private final HealthChecker healthChecker; - - public ClusterInfoAction(UserSession userSession, GlobalInfoLoader globalInfoLoader, - AppNodesInfoLoader appNodesInfoLoader, SearchNodesInfoLoader searchNodesInfoLoader, - HealthChecker healthChecker, TelemetryDataLoader telemetry) { - super(userSession, telemetry); - this.globalInfoLoader = globalInfoLoader; - this.appNodesInfoLoader = appNodesInfoLoader; - this.searchNodesInfoLoader = searchNodesInfoLoader; - this.healthChecker = healthChecker; - } - - @Override - protected void doHandle(Request request, Response response) throws InterruptedException { - ClusterHealth clusterHealth = healthChecker.checkCluster(); - try (JsonWriter json = response.newJsonWriter()) { - json.beginObject(); - - writeHealth(clusterHealth.getHealth(), json); - writeGlobalSections(json); - writeApplicationNodes(json, clusterHealth); - writeSearchNodes(json, clusterHealth); - writeTelemetry(json); - - json.endObject(); - } - } - - private void writeGlobalSections(JsonWriter json) { - writeSections(globalInfoLoader.load(), json); - } - - private void writeApplicationNodes(JsonWriter json, ClusterHealth clusterHealth) - throws InterruptedException { - json.name("Application Nodes").beginArray(); - - Collection appNodes = appNodesInfoLoader.load(); - for (NodeInfo applicationNode : appNodes) { - writeNodeInfo(applicationNode, clusterHealth, json); - } - json.endArray(); - } - - private void writeSearchNodes(JsonWriter json, ClusterHealth clusterHealth) { - json.name("Search Nodes").beginArray(); - - Collection searchNodes = searchNodesInfoLoader.load(); - searchNodes.forEach(node -> writeNodeInfo(node, clusterHealth, json)); - json.endArray(); - } - - private void writeNodeInfo(NodeInfo nodeInfo, ClusterHealth clusterHealth, JsonWriter json) { - json.beginObject(); - json.prop("Name", nodeInfo.getName()); - json.prop("Error", nodeInfo.getErrorMessage().orElse(null)); - json.prop("Host", nodeInfo.getHost().orElse(null)); - json.prop("Started At", nodeInfo.getStartedAt().orElse(null)); - - clusterHealth.getNodeHealth(nodeInfo.getName()).ifPresent(h -> { - json.prop("Health", h.getStatus().name()); - json.name("Health Causes").beginArray().values(h.getCauses()).endArray(); - }); - - writeSections(nodeInfo.getSections(), json); - json.endObject(); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterSystemInfoWriter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterSystemInfoWriter.java new file mode 100644 index 00000000000..cae97214131 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/ClusterSystemInfoWriter.java @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.ws; + +import java.util.Collection; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.server.health.ClusterHealth; +import org.sonar.server.health.HealthChecker; +import org.sonar.server.platform.monitoring.cluster.AppNodesInfoLoader; +import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader; +import org.sonar.server.platform.monitoring.cluster.NodeInfo; +import org.sonar.server.platform.monitoring.cluster.SearchNodesInfoLoader; +import org.sonar.server.telemetry.TelemetryDataLoader; + +public class ClusterSystemInfoWriter extends SystemInfoWriter { + private final GlobalInfoLoader globalInfoLoader; + private final AppNodesInfoLoader appNodesInfoLoader; + private final SearchNodesInfoLoader searchNodesInfoLoader; + private final HealthChecker healthChecker; + + public ClusterSystemInfoWriter(GlobalInfoLoader globalInfoLoader, AppNodesInfoLoader appNodesInfoLoader, SearchNodesInfoLoader searchNodesInfoLoader, + HealthChecker healthChecker, TelemetryDataLoader telemetry) { + super(telemetry); + this.globalInfoLoader = globalInfoLoader; + this.appNodesInfoLoader = appNodesInfoLoader; + this.searchNodesInfoLoader = searchNodesInfoLoader; + this.healthChecker = healthChecker; + } + + @Override + public void write(JsonWriter json) throws InterruptedException { + ClusterHealth clusterHealth = healthChecker.checkCluster(); + writeHealth(clusterHealth.getHealth(), json); + writeGlobalSections(json); + writeApplicationNodes(json, clusterHealth); + writeSearchNodes(json, clusterHealth); + writeTelemetry(json); + } + + private void writeGlobalSections(JsonWriter json) { + writeSections(globalInfoLoader.load(), json); + } + + private void writeApplicationNodes(JsonWriter json, ClusterHealth clusterHealth) throws InterruptedException { + json.name("Application Nodes").beginArray(); + + Collection appNodes = appNodesInfoLoader.load(); + for (NodeInfo applicationNode : appNodes) { + writeNodeInfo(applicationNode, clusterHealth, json); + } + json.endArray(); + } + + private void writeSearchNodes(JsonWriter json, ClusterHealth clusterHealth) { + json.name("Search Nodes").beginArray(); + + Collection searchNodes = searchNodesInfoLoader.load(); + searchNodes.forEach(node -> writeNodeInfo(node, clusterHealth, json)); + json.endArray(); + } + + private void writeNodeInfo(NodeInfo nodeInfo, ClusterHealth clusterHealth, JsonWriter json) { + json.beginObject(); + json.prop("Name", nodeInfo.getName()); + json.prop("Error", nodeInfo.getErrorMessage().orElse(null)); + json.prop("Host", nodeInfo.getHost().orElse(null)); + json.prop("Started At", nodeInfo.getStartedAt().orElse(null)); + + clusterHealth.getNodeHealth(nodeInfo.getName()).ifPresent(h -> { + json.prop("Health", h.getStatus().name()); + json.name("Health Causes").beginArray().values(h.getCauses()).endArray(); + }); + + writeSections(nodeInfo.getSections(), json); + json.endObject(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java index 7bcc238d740..177d26965c1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java @@ -19,64 +19,45 @@ */ package org.sonar.server.platform.ws; -import java.util.List; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.ce.http.CeHttpClient; -import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.process.systeminfo.SystemInfoSection; -import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; -import org.sonar.server.health.Health; -import org.sonar.server.health.HealthChecker; -import org.sonar.server.telemetry.TelemetryDataLoader; import org.sonar.server.user.UserSession; -import static java.util.Arrays.stream; - /** * Implementation of the {@code info} action for the System WebService. */ -public class InfoAction extends BaseInfoWsAction { +public class InfoAction implements SystemWsAction { + private final SystemInfoWriter systemInfoWriter; + private final UserSession userSession; + + public InfoAction(UserSession userSession, SystemInfoWriter systemInfoWriter) { + this.userSession = userSession; + this.systemInfoWriter = systemInfoWriter; + } - private final CeHttpClient ceHttpClient; - private final SystemInfoSection[] systemInfoSections; - private final HealthChecker healthChecker; + @Override + public void define(WebService.NewController controller) { + controller.createAction("info") + .setDescription("Get detailed information about system configuration.
" + + "Requires 'Administer' permissions.
" + + "Since 5.5, this web service becomes internal in order to more easily update result.") + .setSince("5.1") + .setInternal(true) + .setResponseExample(getClass().getResource("/org/sonar/server/platform/ws/info-example.json")) + .setHandler(this); - public InfoAction(UserSession userSession, TelemetryDataLoader telemetry, CeHttpClient ceHttpClient, HealthChecker healthChecker, - SystemInfoSection... systemInfoSections) { - super(userSession, telemetry); - this.ceHttpClient = ceHttpClient; - this.healthChecker = healthChecker; - this.systemInfoSections = systemInfoSections; } @Override - protected void doHandle(Request request, Response response) { + public void handle(Request request, Response response) throws Exception { + userSession.checkIsSystemAdministrator(); try (JsonWriter json = response.newJsonWriter()) { - writeJson(json); + json.beginObject(); + systemInfoWriter.write(json); + json.endObject(); } } - private void writeJson(JsonWriter json) { - json.beginObject(); - - writeHealth(json); - - List sections = stream(systemInfoSections) - .map(SystemInfoSection::toProtobuf) - .collect(MoreCollectors.toArrayList()); - ceHttpClient.retrieveSystemInfo() - .ifPresent(ce -> sections.addAll(ce.getSectionsList())); - - writeSections(sections, json); - writeTelemetry(json); - - json.endObject(); - } - - private void writeHealth(JsonWriter json) { - Health health = healthChecker.checkNode(); - writeHealth(health, json); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriter.java new file mode 100644 index 00000000000..5eb0636fbea --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriter.java @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.ws; + +import java.util.List; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.ce.http.CeHttpClient; +import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.process.systeminfo.SystemInfoSection; +import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; +import org.sonar.server.health.Health; +import org.sonar.server.health.HealthChecker; +import org.sonar.server.telemetry.TelemetryDataLoader; + +import static java.util.Arrays.stream; + +public class StandaloneSystemInfoWriter extends SystemInfoWriter { + private final CeHttpClient ceHttpClient; + private final HealthChecker healthChecker; + private final SystemInfoSection[] systemInfoSections; + + public StandaloneSystemInfoWriter(TelemetryDataLoader telemetry, CeHttpClient ceHttpClient, HealthChecker healthChecker, SystemInfoSection... systemInfoSections) { + super(telemetry); + this.ceHttpClient = ceHttpClient; + this.healthChecker = healthChecker; + this.systemInfoSections = systemInfoSections; + } + + @Override + public void write(JsonWriter json) { + writeHealth(json); + + List sections = stream(systemInfoSections) + .map(SystemInfoSection::toProtobuf) + .collect(MoreCollectors.toArrayList()); + ceHttpClient.retrieveSystemInfo() + .ifPresent(ce -> sections.addAll(ce.getSectionsList())); + + writeSections(sections, json); + writeTelemetry(json); + } + + private void writeHealth(JsonWriter json) { + Health health = healthChecker.checkNode(); + writeHealth(health, json); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWriter.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWriter.java new file mode 100644 index 00000000000..caad58b66ec --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWriter.java @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.ws; + +import java.util.Collection; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.process.systeminfo.SystemInfoUtils; +import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; +import org.sonar.server.health.Health; +import org.sonar.server.telemetry.TelemetryDataLoader; + +import static org.sonar.server.telemetry.TelemetryDataJsonWriter.writeTelemetryData; + +public abstract class SystemInfoWriter { + private static final String[] ORDERED_SECTION_NAMES = { + // standalone + "System", "Database", "Plugins", + + // cluster + "Web JVM State", "Web Database Connection", "Web Logging", "Web JVM Properties", + "Compute Engine Tasks", "Compute Engine JVM State", "Compute Engine Database Connection", "Compute Engine Logging", "Compute Engine JVM Properties", + "Search State", "Search Indexes"}; + + private final TelemetryDataLoader telemetry; + + SystemInfoWriter(TelemetryDataLoader telemetry) { + this.telemetry = telemetry; + } + + public abstract void write(JsonWriter json) throws Exception; + + protected void writeSections(Collection sections, JsonWriter json) { + SystemInfoUtils + .order(sections, ORDERED_SECTION_NAMES) + .forEach(section -> writeSection(section, json)); + } + + private void writeSection(ProtobufSystemInfo.Section section, JsonWriter json) { + json.name(section.getName()); + json.beginObject(); + for (ProtobufSystemInfo.Attribute attribute : section.getAttributesList()) { + writeAttribute(attribute, json); + } + json.endObject(); + } + + private void writeAttribute(ProtobufSystemInfo.Attribute attribute, JsonWriter json) { + switch (attribute.getValueCase()) { + case BOOLEAN_VALUE: + json.prop(attribute.getKey(), attribute.getBooleanValue()); + break; + case LONG_VALUE: + json.prop(attribute.getKey(), attribute.getLongValue()); + break; + case DOUBLE_VALUE: + json.prop(attribute.getKey(), attribute.getDoubleValue()); + break; + case STRING_VALUE: + json.prop(attribute.getKey(), attribute.getStringValue()); + break; + case VALUE_NOT_SET: + json.name(attribute.getKey()).beginArray().values(attribute.getStringValuesList()).endArray(); + break; + default: + throw new IllegalArgumentException("Unsupported type: " + attribute.getValueCase()); + } + } + + protected void writeHealth(Health health, JsonWriter json) { + json.prop("Health", health.getStatus().name()); + json.name("Health Causes").beginArray().values(health.getCauses()).endArray(); + } + + protected void writeTelemetry(JsonWriter json) { + json.name("Statistics"); + writeTelemetryData(json, telemetry.load()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ClusterSystemInfoWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ClusterSystemInfoWriterTest.java new file mode 100644 index 00000000000..c8eafa47687 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/ClusterSystemInfoWriterTest.java @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.ws; + +import java.io.StringWriter; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Attribute; +import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo.Section; +import org.sonar.server.health.ClusterHealth; +import org.sonar.server.health.Health; +import org.sonar.server.health.HealthChecker; +import org.sonar.server.platform.monitoring.cluster.AppNodesInfoLoader; +import org.sonar.server.platform.monitoring.cluster.GlobalInfoLoader; +import org.sonar.server.platform.monitoring.cluster.NodeInfo; +import org.sonar.server.platform.monitoring.cluster.SearchNodesInfoLoader; +import org.sonar.server.telemetry.TelemetryDataLoader; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ClusterSystemInfoWriterTest { + private GlobalInfoLoader globalInfoLoader = mock(GlobalInfoLoader.class); + private AppNodesInfoLoader appNodesInfoLoader = mock(AppNodesInfoLoader.class); + private SearchNodesInfoLoader searchNodesInfoLoader = mock(SearchNodesInfoLoader.class); + private HealthChecker healthChecker = mock(HealthChecker.class); + private TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS); + private ClusterSystemInfoWriter underTest = new ClusterSystemInfoWriter(globalInfoLoader, appNodesInfoLoader, searchNodesInfoLoader, healthChecker, telemetry); + + @Before + public void before() throws InterruptedException { + when(globalInfoLoader.load()).thenReturn(Collections.singletonList(createSection("globalInfo"))); + when(appNodesInfoLoader.load()).thenReturn(Collections.singletonList(createNodeInfo("appNodes"))); + when(searchNodesInfoLoader.load()).thenReturn(Collections.singletonList(createNodeInfo("searchNodes"))); + Health health = Health.newHealthCheckBuilder().setStatus(Health.Status.GREEN).build(); + when(healthChecker.checkCluster()).thenReturn(new ClusterHealth(health, Collections.emptySet())); + } + + @Test + public void writeInfo() throws InterruptedException { + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(writer); + jsonWriter.beginObject(); + underTest.write(jsonWriter); + jsonWriter.endObject(); + + assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\"," + + "\"Health Causes\":[],\"\":{\"name\":\"globalInfo\"}," + + "\"Application Nodes\":[{\"Name\":\"appNodes\",\"\":{\"name\":\"appNodes\"}}]," + + "\"Search Nodes\":[{\"Name\":\"searchNodes\",\"\":{\"name\":\"searchNodes\"}}]," + + "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[]," + + "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"lines\":0,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}"); + } + + private static NodeInfo createNodeInfo(String name) { + NodeInfo info = new NodeInfo(name); + info.addSection(createSection(name)); + return info; + } + + private static Section createSection(String name) { + return Section.newBuilder() + .addAttributes(Attribute.newBuilder().setKey("name").setStringValue(name).build()) + .build(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java index 389a013d44a..9d3b0c4d00d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java @@ -19,26 +19,16 @@ */ package org.sonar.server.platform.ws; -import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.mockito.Mockito; -import org.sonar.ce.http.CeHttpClient; -import org.sonar.ce.http.CeHttpClientImpl; -import org.sonar.process.systeminfo.SystemInfoSection; -import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; +import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.health.TestStandaloneHealthChecker; -import org.sonar.server.telemetry.TelemetryDataLoader; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestResponse; 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; -import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute; public class InfoActionTest { @Rule @@ -48,13 +38,13 @@ public class InfoActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - private SystemInfoSection section1 = mock(SystemInfoSection.class); - private SystemInfoSection section2 = mock(SystemInfoSection.class); - private CeHttpClient ceHttpClient = mock(CeHttpClientImpl.class, Mockito.RETURNS_MOCKS); - private TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker(); - private TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS); - - private InfoAction underTest = new InfoAction(userSessionRule, telemetry, ceHttpClient, healthChecker, section1, section2); + private SystemInfoWriter jsonWriter = new SystemInfoWriter(null) { + @Override + public void write(JsonWriter json) throws Exception { + json.prop("key", "value"); + } + }; + private InfoAction underTest = new InfoAction(userSessionRule, jsonWriter); private WsActionTester ws = new WsActionTester(underTest); @Test @@ -85,23 +75,8 @@ public class InfoActionTest { public void write_json() { logInAsSystemAdministrator(); - ProtobufSystemInfo.Section.Builder attributes1 = ProtobufSystemInfo.Section.newBuilder() - .setName("Section One"); - setAttribute(attributes1, "foo", "bar"); - when(section1.toProtobuf()).thenReturn(attributes1.build()); - - ProtobufSystemInfo.Section.Builder attributes2 = ProtobufSystemInfo.Section.newBuilder() - .setName("Section Two"); - setAttribute(attributes2, "one", 1); - setAttribute(attributes2, "two", 2); - when(section2.toProtobuf()).thenReturn(attributes2.build()); - when(ceHttpClient.retrieveSystemInfo()).thenReturn(Optional.empty()); - TestResponse response = ws.newRequest().execute(); - // response does not contain empty "Section Three" - assertThat(response.getInput()).isEqualTo("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," + - "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],\"userCount\":0,\"projectCount\":0,\"usingBranches\":false," + - "\"lines\":0,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}"); + assertThat(response.getInput()).isEqualTo("{\"key\":\"value\"}"); } private void logInAsSystemAdministrator() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriterTest.java new file mode 100644 index 00000000000..095a86db945 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriterTest.java @@ -0,0 +1,88 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.platform.ws; + +import java.io.StringWriter; +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.ce.http.CeHttpClient; +import org.sonar.ce.http.CeHttpClientImpl; +import org.sonar.process.systeminfo.SystemInfoSection; +import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo; +import org.sonar.server.health.TestStandaloneHealthChecker; +import org.sonar.server.telemetry.TelemetryDataLoader; +import org.sonar.server.tester.UserSessionRule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.process.systeminfo.SystemInfoUtils.setAttribute; + +public class StandaloneSystemInfoWriterTest { + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone() + .logIn("login") + .setName("name"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private SystemInfoSection section1 = mock(SystemInfoSection.class); + private SystemInfoSection section2 = mock(SystemInfoSection.class); + private CeHttpClient ceHttpClient = mock(CeHttpClientImpl.class, Mockito.RETURNS_MOCKS); + private TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker(); + private TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS); + + private StandaloneSystemInfoWriter underTest = new StandaloneSystemInfoWriter(telemetry, ceHttpClient, healthChecker, section1, section2); + + @Test + public void write_json() { + logInAsSystemAdministrator(); + + ProtobufSystemInfo.Section.Builder attributes1 = ProtobufSystemInfo.Section.newBuilder() + .setName("Section One"); + setAttribute(attributes1, "foo", "bar"); + when(section1.toProtobuf()).thenReturn(attributes1.build()); + + ProtobufSystemInfo.Section.Builder attributes2 = ProtobufSystemInfo.Section.newBuilder() + .setName("Section Two"); + setAttribute(attributes2, "one", 1); + setAttribute(attributes2, "two", 2); + when(section2.toProtobuf()).thenReturn(attributes2.build()); + when(ceHttpClient.retrieveSystemInfo()).thenReturn(Optional.empty()); + + StringWriter writer = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(writer); + jsonWriter.beginObject(); + underTest.write(jsonWriter); + jsonWriter.endObject(); + // response does not contain empty "Section Three" + assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," + + "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],\"userCount\":0,\"projectCount\":0,\"usingBranches\":false," + + "\"lines\":0,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}"); + } + + private void logInAsSystemAdministrator() { + userSessionRule.logIn().setSystemAdministrator(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java index 441c239822f..14181fa0940 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java @@ -21,14 +21,9 @@ package org.sonar.server.platform.ws; import org.junit.Test; import org.sonar.api.server.ws.WebService; -import org.sonar.ce.http.CeHttpClient; -import org.sonar.ce.http.CeHttpClientImpl; import org.sonar.server.app.ProcessCommandWrapper; import org.sonar.server.app.RestartFlagHolder; -import org.sonar.server.health.HealthChecker; -import org.sonar.server.health.TestStandaloneHealthChecker; import org.sonar.server.platform.WebServer; -import org.sonar.server.telemetry.TelemetryDataLoader; import org.sonar.server.tester.AnonymousMockUserSession; import org.sonar.server.user.UserSession; @@ -37,14 +32,11 @@ import static org.mockito.Mockito.mock; public class SystemWsTest { - private CeHttpClient ceHttpClient = mock(CeHttpClientImpl.class); - private HealthChecker healthChecker = new TestStandaloneHealthChecker(); - @Test public void define() { RestartAction action1 = new RestartAction(mock(UserSession.class), mock(ProcessCommandWrapper.class), mock(RestartFlagHolder.class), mock(WebServer.class)); - InfoAction action2 = new InfoAction(new AnonymousMockUserSession(), mock(TelemetryDataLoader.class), ceHttpClient, healthChecker); + InfoAction action2 = new InfoAction(new AnonymousMockUserSession(), mock(SystemInfoWriter.class)); SystemWs ws = new SystemWs(action1, action2); WebService.Context context = new WebService.Context(); -- cgit v1.2.3