diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2015-02-18 17:39:40 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-02-22 23:00:00 +0100 |
commit | c4e7fb294ca6b445db2a59336913642dd7273f2b (patch) | |
tree | 39de9ec1fecf7fb30e5c9249f13752de29774253 /server | |
parent | d50911dd0c6e8ce6618efc15acfdd52f082c9358 (diff) | |
download | sonarqube-c4e7fb294ca6b445db2a59336913642dd7273f2b.tar.gz sonarqube-c4e7fb294ca6b445db2a59336913642dd7273f2b.zip |
Technical monitoring of web server - SONAR-4318 SONAR-5936
Diffstat (limited to 'server')
35 files changed, 1679 insertions, 239 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/DatabaseServerCompatibility.java b/server/sonar-server/src/main/java/org/sonar/server/platform/DatabaseServerCompatibility.java index 94066a1eaa9..8dcd286c73a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/DatabaseServerCompatibility.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/DatabaseServerCompatibility.java @@ -36,7 +36,7 @@ public class DatabaseServerCompatibility implements Startable { public void start() { DatabaseVersion.Status status = version.getStatus(); if (status == DatabaseVersion.Status.REQUIRES_DOWNGRADE) { - throw MessageException.of("Database relates to a more recent version of sonar. Please check your settings."); + throw MessageException.of("Database relates to a more recent version of SonarQube. Please check your settings."); } if (status == DatabaseVersion.Status.REQUIRES_UPGRADE) { Loggers.get(DatabaseServerCompatibility.class).warn("Database must be upgraded. Please browse /setup"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 288a7cbee5b..7965e3f04a9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -140,10 +140,8 @@ import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.permission.InternalPermissionTemplateService; import org.sonar.server.permission.PermissionFinder; import org.sonar.server.permission.ws.PermissionsWs; -import org.sonar.server.platform.ws.L10nWs; -import org.sonar.server.platform.ws.RestartHandler; -import org.sonar.server.platform.ws.ServerWs; -import org.sonar.server.platform.ws.SystemWs; +import org.sonar.server.platform.monitoring.*; +import org.sonar.server.platform.ws.*; import org.sonar.server.plugins.*; import org.sonar.server.properties.ProjectSettingsFactory; import org.sonar.server.qualitygate.QgateProjectFinder; @@ -308,11 +306,7 @@ class ServerComponents { JRubyI18n.class, DefaultI18n.class, RuleI18nManager.class, - Durations.class, - - // ws - RestartHandler.class, - SystemWs.class + Durations.class ); } @@ -638,6 +632,20 @@ class ServerComponents { // Design pico.addSingleton(FileDesignWidget.class); + // System + pico.addSingletons(Arrays.asList( + SystemRestartWsAction.class, + SystemInfoWsAction.class, + SystemWs.class, + SystemMonitor.class, + SonarQubeMonitor.class, + EsClusterMonitor.class, + EsNodesMonitor.class, + PluginsMonitor.class, + JvmPropertiesMonitor.class, + DatabaseMonitor.class + )); + // Compute engine pico.addSingleton(AnalysisReportQueue.class); pico.addSingleton(ComputationThreadLauncher.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/BaseMonitorMBean.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/BaseMonitorMBean.java new file mode 100644 index 00000000000..6b982a7ba7d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/BaseMonitorMBean.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.picocontainer.Startable; + +import javax.management.InstanceNotFoundException; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.OperationsException; + +import java.lang.management.ManagementFactory; + +public abstract class BaseMonitorMBean implements Monitor, Startable { + + /** + * Auto-registers to MBean server + */ + @Override + public void start() { + try { + ManagementFactory.getPlatformMBeanServer().registerMBean(this, objectName()); + } catch (OperationsException | MBeanRegistrationException e) { + throw new IllegalStateException("Fail to register MBean " + name(), e); + } + } + + @Override + public void stop() { + try { + ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName()); + } catch (InstanceNotFoundException ignored) { + // ignore, was not correctly started + } catch (Exception e) { + throw new IllegalStateException("Fail to unregister MBean " + name(), e); + } + } + + ObjectName objectName() throws MalformedObjectNameException { + return new ObjectName(String.format("SonarQube:name=%s", name())); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitor.java new file mode 100644 index 00000000000..9ce60700212 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitor.java @@ -0,0 +1,145 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; + +public class DatabaseMonitor extends BaseMonitorMBean implements DatabaseMonitorMBean { + + private final DatabaseVersion dbVersion; + private final DbClient dbClient; + + public DatabaseMonitor(DatabaseVersion dbVersion, DbClient dbClient) { + this.dbVersion = dbVersion; + this.dbClient = dbClient; + } + + @Override + public String name() { + return "Database"; + } + + @Override + public String getMigrationStatus() { + return dbVersion.getStatus().name(); + } + + @Override + public int getPoolActiveConnections() { + return commonsDbcp().getNumActive(); + } + + @Override + public int getPoolMaxActiveConnections() { + return commonsDbcp().getMaxActive(); + } + + @Override + public int getPoolIdleConnections() { + return commonsDbcp().getNumIdle(); + } + + @Override + public int getPoolMaxIdleConnections() { + return commonsDbcp().getMaxIdle(); + } + + @Override + public int getPoolMinIdleConnections() { + return commonsDbcp().getMinIdle(); + } + + @Override + public int getPoolInitialSize() { + return commonsDbcp().getInitialSize(); + } + + @Override + public long getPoolMaxWaitMillis() { + return commonsDbcp().getMaxWait(); + } + + @Override + public boolean getPoolRemoveAbandoned() { + return commonsDbcp().getRemoveAbandoned(); + } + + @Override + public int getPoolRemoveAbandonedTimeoutSeconds() { + return commonsDbcp().getRemoveAbandonedTimeout(); + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + completeDbAttributes(attributes); + completePoolAttributes(attributes); + return attributes; + } + + private void completePoolAttributes(Map<String, Object> attributes) { + attributes.put("Pool Active Connections", getPoolActiveConnections()); + attributes.put("Pool Max Connections", getPoolMaxActiveConnections()); + attributes.put("Pool Initial Size", getPoolInitialSize()); + attributes.put("Pool Idle Connections", getPoolIdleConnections()); + attributes.put("Pool Min Idle Connections", getPoolMinIdleConnections()); + attributes.put("Pool Max Idle Connections", getPoolMaxIdleConnections()); + attributes.put("Pool Max Wait (ms)", getPoolMaxWaitMillis()); + attributes.put("Pool Remove Abandoned", getPoolRemoveAbandoned()); + attributes.put("Pool Remove Abandoned Timeout (seconds)", getPoolRemoveAbandonedTimeoutSeconds()); + } + + private BasicDataSource commonsDbcp() { + return (BasicDataSource) dbClient.database().getDataSource(); + } + + private void completeDbAttributes(Map<String, Object> attributes) { + DbSession dbSession = dbClient.openSession(false); + Connection connection = dbSession.getConnection(); + try { + DatabaseMetaData metadata = connection.getMetaData(); + attributes.put("Database", metadata.getDatabaseProductName()); + attributes.put("Database Version", metadata.getDatabaseProductVersion()); + attributes.put("Username", metadata.getUserName()); + attributes.put("URL", metadata.getURL()); + attributes.put("Driver", metadata.getDriverName()); + attributes.put("Driver Version", metadata.getDriverVersion()); + attributes.put("Version Status", getMigrationStatus()); + } catch (SQLException e) { + throw new IllegalStateException("Fail to get DB metadata", e); + + } finally { + DbUtils.closeQuietly(connection); + MyBatis.closeQuietly(dbSession); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitorMBean.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitorMBean.java new file mode 100644 index 00000000000..e1a177a243a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitorMBean.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +public interface DatabaseMonitorMBean { + + /** + * Is database schema up-to-date or should it be upgraded ? + */ + String getMigrationStatus(); + + /** + * + */ + int getPoolActiveConnections(); + + /** + * The maximum number of active connections that can be allocated from this pool at the same time, or negative for no limit. + */ + int getPoolMaxActiveConnections(); + + int getPoolIdleConnections(); + + /** + * The maximum number of connections that can remain idle in the pool, without extra ones being released, or negative for no limit. + */ + int getPoolMaxIdleConnections(); + + /** + * The minimum number of connections that can remain idle in the pool, without extra ones being created, or zero to create none. + */ + int getPoolMinIdleConnections(); + + /** + * The initial number of connections that are created when the pool is started. + */ + int getPoolInitialSize(); + + /** + * The maximum number of milliseconds that the pool will wait + * (when there are no available connections) for a connection to be returned before throwing an exception, or -1 to wait indefinitely. + */ + long getPoolMaxWaitMillis(); + + /** + * Flag to remove abandoned connections if they exceed the {@link #getPoolRemoveAbandonedTimeoutSeconds()}. + */ + boolean getPoolRemoveAbandoned(); + + /** + * Timeout in seconds before an abandoned connection can be removed. + */ + int getPoolRemoveAbandonedTimeoutSeconds(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitor.java new file mode 100644 index 00000000000..890a86f9b0c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitor.java @@ -0,0 +1,99 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import com.google.common.base.Joiner; +import org.sonar.server.search.ClusterHealth; +import org.sonar.server.search.IndexHealth; +import org.sonar.server.search.SearchHealth; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; + +public class EsClusterMonitor extends BaseMonitorMBean + implements EsClusterMonitorMBean { + + private final SearchHealth searchHealth; + + public EsClusterMonitor(SearchHealth searchHealth) { + this.searchHealth = searchHealth; + } + + @Override + public String getClusterState() { + return clusterHealth().isClusterAvailable() ? "Available" : "Unavailable"; + } + + @Override + public int getNumberOfNodes() { + return clusterHealth().getNumberOfNodes(); + } + + @Override + public String getIndexesHealth() { + Map<String, String> formattedIndexes = new HashMap<>(); + for (Map.Entry<String, IndexHealth> healthEntry : searchHealth.getIndexHealth().entrySet()) { + formattedIndexes.put(healthEntry.getKey(), formatIndexHealth(healthEntry.getValue())); + } + + return Joiner.on(" | ").withKeyValueSeparator(": ").join(formattedIndexes); + } + + @Override + public String name() { + return "ElasticSearchCluster"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + attributes.put("Cluster State", getClusterState()); + attributes.put("Number of Nodes", getNumberOfNodes()); + for (Map.Entry<String, IndexHealth> healthEntry : searchHealth.getIndexHealth().entrySet()) { + attributes.put(healthEntry.getKey() + " - Document Count", healthEntry.getValue().getDocumentCount()); + attributes.put(healthEntry.getKey() + " - Last Sync", formatDateTimeNullSafe(healthEntry.getValue().getLastSynchronization())); + attributes.put(healthEntry.getKey() + " - Optimization", formatIndexHealthOptimisation(healthEntry.getValue())); + } + return attributes; + } + + private String formatIndexHealthOptimisation(IndexHealth indexHealth) { + return indexHealth.isOptimized() ? "Optimized" : "Unoptimized " + + "(Segments: " + indexHealth.getSegmentcount() + ", Pending Deletions: " + + indexHealth.getPendingDeletion() + ")"; + } + + private ClusterHealth clusterHealth() { + return searchHealth.getClusterHealth(); + } + + private String formatIndexHealth(IndexHealth indexHealth) { + return new StringBuilder() + .append(indexHealth.getDocumentCount()) + .append("/") + .append(formatDateTimeNullSafe(indexHealth.getLastSynchronization())) + .append("/") + .append(formatIndexHealthOptimisation(indexHealth)).toString(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitorMBean.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitorMBean.java new file mode 100644 index 00000000000..a51e29c7668 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsClusterMonitorMBean.java @@ -0,0 +1,27 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +public interface EsClusterMonitorMBean { + String getClusterState(); + int getNumberOfNodes(); + String getIndexesHealth(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitor.java new file mode 100644 index 00000000000..ead31204d55 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitor.java @@ -0,0 +1,109 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import com.google.common.base.Joiner; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import org.sonar.server.search.NodeHealth; +import org.sonar.server.search.NodeHealth.Performance; +import org.sonar.server.search.SearchHealth; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.commons.io.FileUtils.byteCountToDisplaySize; +import static org.sonar.api.utils.DateUtils.formatDateTime; + +public class EsNodesMonitor extends BaseMonitorMBean implements EsNodesMonitorMBean { + + private final SearchHealth searchHealth; + + public EsNodesMonitor(SearchHealth searchHealth) { + this.searchHealth = searchHealth; + } + + @Override + public String getNodes() { + List<String> formattedNodes = new ArrayList<>(); + Table<String, String, String> nodes = nodes(); + + for (String nodeName : nodes.rowKeySet()) { + StringBuilder formattedNode = new StringBuilder(); + formattedNode.append(nodeName) + .append(" - ") + .append(Joiner.on(", ").withKeyValueSeparator(": ").join(nodes.row(nodeName))); + formattedNodes.add(formattedNode.toString()); + } + + return Joiner.on(" | ").join(formattedNodes); + } + + @Override + public String name() { + return "ElasticSearchNodes"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + Table<String, String, String> nodes = nodes(); + for (String nodeName : nodes.rowKeySet()) { + attributes.put(nodeName, nodes.row(nodeName)); + } + return attributes; + } + + private Table<String, String, String> nodes() { + Table<String, String, String> nodes = HashBasedTable.create(); + for (Map.Entry<String, NodeHealth> nodeEntry : searchHealth.getNodesHealth().entrySet()) { + String name = nodeEntry.getKey(); + nodes.put(name, "Name", nodeEntry.getKey()); + NodeHealth nodeHealth = nodeEntry.getValue(); + nodes.put(name, "Type", nodeHealth.isMaster() ? "Master" : "Slave"); + nodes.put(name, "Address", nodeHealth.getAddress()); + nodes.put(name, "JVM Heap Usage", nodeHealth.getJvmHeapUsedPercent()); + nodes.put(name, "JVM Threads", String.valueOf(nodeHealth.getJvmThreads())); + nodes.put(name, "JVM Started Since", formatDateTime(nodeHealth.getJvmUpSince())); + nodes.put(name, "Disk Usage", nodeHealth.getFsUsedPercent()); + nodes.put(name, "Open Files", String.valueOf(nodeHealth.getOpenFiles())); + nodes.put(name, "CPU Load Average", nodeHealth.getProcessCpuPercent()); + nodes.put(name, "Field Cache Size", byteCountToDisplaySize(nodeHealth.getFieldCacheMemory())); + nodes.put(name, "Filter Cache Size", byteCountToDisplaySize(nodeHealth.getFilterCacheMemory())); + + for (Performance performance : nodeHealth.getPerformanceStats()) { + String message = ""; + if (Performance.Status.ERROR.equals(performance.getStatus()) || Performance.Status.WARN.equals(performance.getStatus())) { + message = String.format("- %s: %s", performance.getStatus(), performance.getMessage()); + } + if (performance.getName().contains("Eviction")) { + nodes.put(name, performance.getName(), String.format("%f %s", performance.getValue(), message)); + } else { + nodes.put(name, performance.getName(), String.format("%.1f ms %s", performance.getValue(), message)); + } + } + } + + return nodes; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitorMBean.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitorMBean.java new file mode 100644 index 00000000000..fc855255497 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsNodesMonitorMBean.java @@ -0,0 +1,26 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +public interface EsNodesMonitorMBean { + + String getNodes(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitor.java new file mode 100644 index 00000000000..1d40dbd2d7e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitor.java @@ -0,0 +1,41 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +public class JvmPropertiesMonitor implements Monitor { + @Override + public String name() { + return "JvmProperties"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) { + attributes.put(Objects.toString(entry.getKey()), Objects.toString(entry.getValue())); + } + return attributes; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/Monitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/Monitor.java new file mode 100644 index 00000000000..661c86dba07 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/Monitor.java @@ -0,0 +1,29 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.sonar.api.ServerComponent; + +import java.util.LinkedHashMap; + +public interface Monitor extends ServerComponent { + LinkedHashMap<String,Object> attributes(); + String name(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java new file mode 100644 index 00000000000..ed93664394a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.sonar.api.platform.PluginMetadata; +import org.sonar.api.platform.PluginRepository; + +import java.util.LinkedHashMap; +import java.util.List; + +public class PluginsMonitor implements Monitor { + private final PluginRepository repository; + + public PluginsMonitor(PluginRepository repository) { + this.repository = repository; + } + + @Override + public String name() { + return "Plugins"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + for (PluginMetadata plugin : plugins()) { + attributes.put(plugin.getName(), plugin.getVersion()); + } + return attributes; + } + + private List<PluginMetadata> plugins() { + return Lists.newArrayList(Iterables.filter(repository.getMetadata(), new Predicate<PluginMetadata>() { + @Override + public boolean apply(PluginMetadata input) { + return !input.isCore(); + } + })); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitor.java new file mode 100644 index 00000000000..2846f282303 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitor.java @@ -0,0 +1,104 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.platform.Server; +import org.sonar.api.security.SecurityRealm; +import org.sonar.process.ProcessConstants; +import org.sonar.server.user.SecurityRealmFactory; + +import java.util.LinkedHashMap; + +import static org.sonar.api.utils.DateUtils.formatDateTime; + +public class SonarQubeMonitor extends BaseMonitorMBean implements SonarQubeMonitorMBean { + + private final Settings settings; + private final SecurityRealmFactory securityRealmFactory; + private final Server server; + + public SonarQubeMonitor(Settings settings, SecurityRealmFactory securityRealmFactory, + Server server) { + this.settings = settings; + this.securityRealmFactory = securityRealmFactory; + this.server = server; + } + + @Override + public String getServerId() { + return settings.getString(CoreProperties.PERMANENT_SERVER_ID); + } + + @Override + public String getVersion() { + return server.getVersion(); + } + + @Override + public String getStartedAt() { + return formatDateTime(server.getStartedAt()); + } + + public String getExternalUserAuthentication() { + SecurityRealm realm = securityRealmFactory.getRealm(); + if (realm == null) { + return ""; + } + return realm.getName(); + } + + public boolean getAutomaticUserCreation() { + return settings.getBoolean(CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS); + } + + public boolean getAllowUsersToSignUp() { + return settings.getBoolean(CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY); + } + + public boolean getForceAuthentication() { + return settings.getBoolean(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY); + } + + @Override + public String name() { + return "SonarQube"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + attributes.put("Server ID", getServerId()); + attributes.put("Version", getVersion()); + attributes.put("Started at", getStartedAt()); + attributes.put("External User Authentication", getExternalUserAuthentication()); + attributes.put("Automatic User Creation", getAutomaticUserCreation()); + attributes.put("Allow Users to Sign Up", getAllowUsersToSignUp()); + attributes.put("Force authentication", getForceAuthentication()); + attributes.put("Home Dir", settings.getString(ProcessConstants.PATH_HOME)); + attributes.put("Data Dir", settings.getString(ProcessConstants.PATH_DATA)); + attributes.put("Logs Dir", settings.getString(ProcessConstants.PATH_LOGS)); + attributes.put("Temp Dir", settings.getString(ProcessConstants.PATH_TEMP)); + return attributes; + + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitorMBean.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitorMBean.java new file mode 100644 index 00000000000..56b8f4744e7 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitorMBean.java @@ -0,0 +1,29 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +public interface SonarQubeMonitorMBean { + String getServerId(); + + String getVersion(); + + String getStartedAt(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemMonitor.java new file mode 100644 index 00000000000..b4ea751303c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemMonitor.java @@ -0,0 +1,171 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.sonar.api.utils.System2; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.util.Date; +import java.util.LinkedHashMap; + +import static org.sonar.api.utils.DateUtils.formatDateTime; + +public class SystemMonitor implements Monitor { + private final System2 system; + + public SystemMonitor() { + this(System2.INSTANCE); + } + + SystemMonitor(System2 system) { + this.system = system; + } + + @Override + public String name() { + return "System"; + } + + public String getSystemDate() { + return formatDateTime(new Date(system.now())); + } + + public String getJvmVendor() { + return runtimeMXBean().getVmVendor(); + } + + public String getJvmName() { + return runtimeMXBean().getVmName(); + } + + public String getJvmVersion() { + return runtimeMXBean().getVmVersion(); + } + + public int getProcessors() { + return runtime().availableProcessors(); + } + + public String getSystemClasspath() { + return runtimeMXBean().getClassPath(); + } + + public String getBootClasspath() { + return runtimeMXBean().getBootClassPath(); + } + + public String getLibraryPath() { + return runtimeMXBean().getLibraryPath(); + } + + public String getTotalMemory() { + return formatMemory(runtime().totalMemory()); + } + + public String getFreeMemory() { + return formatMemory(runtime().freeMemory()); + } + + public String getMaxMemory() { + return formatMemory(runtime().maxMemory()); + } + + public String getHeapMemory() { + return memoryMXBean().getHeapMemoryUsage().toString(); + } + + public String getNonHeapMemory() { + return memoryMXBean().getNonHeapMemoryUsage().toString(); + } + + public String getSystemLoadAverage() { + return String.format("%.1f%% (last minute)", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage() * 100.0); + } + + public String getLoadedClasses() { + return String.format("currently: %d, total: %d, unloaded: %d", + classLoadingMXBean().getLoadedClassCount(), + classLoadingMXBean().getTotalLoadedClassCount(), + classLoadingMXBean().getUnloadedClassCount()); + } + + public String getStartTime() { + return formatDateTime(new Date(runtimeMXBean().getStartTime())); + } + + public String getThreads() { + return String.format("total: %d, peak: %d, daemon: %d", + threadMXBean().getThreadCount(), + threadMXBean().getPeakThreadCount(), + threadMXBean().getDaemonThreadCount()); + } + + @Override + public LinkedHashMap<String, Object> attributes() { + LinkedHashMap<String, Object> attributes = new LinkedHashMap<>(); + attributes.put("System Date", getSystemDate()); + attributes.put("JVM Vendor", getJvmVendor()); + attributes.put("JVM Name", getJvmName()); + attributes.put("JVM Version", getJvmVersion()); + attributes.put("Processors", getProcessors()); + attributes.put("System Classpath", getSystemClasspath()); + attributes.put("BootClassPath", getBootClasspath()); + attributes.put("Library Path", getLibraryPath()); + attributes.put("Total Memory", getTotalMemory()); + attributes.put("Free Memory", getFreeMemory()); + attributes.put("Max Memory", getMaxMemory()); + attributes.put("Heap", getHeapMemory()); + attributes.put("Non Heap", getNonHeapMemory()); + attributes.put("System Load Average", getSystemLoadAverage()); + attributes.put("Loaded Classes", getLoadedClasses()); + attributes.put("Start Time", getStartTime()); + attributes.put("Threads", getThreads()); + return attributes; + } + + private RuntimeMXBean runtimeMXBean() { + return ManagementFactory.getRuntimeMXBean(); + } + + private Runtime runtime() { + return Runtime.getRuntime(); + } + + private MemoryMXBean memoryMXBean() { + return ManagementFactory.getMemoryMXBean(); + } + + private ClassLoadingMXBean classLoadingMXBean() { + return ManagementFactory.getClassLoadingMXBean(); + } + + private ThreadMXBean threadMXBean() { + return ManagementFactory.getThreadMXBean(); + } + + private String formatMemory(long memoryInBytes) { + return String.format("%d MB", memoryInBytes / 1_000_000); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWsAction.java new file mode 100644 index 00000000000..05a85daa0f4 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemInfoWsAction.java @@ -0,0 +1,73 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 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.core.permission.GlobalPermissions; +import org.sonar.server.platform.monitoring.Monitor; +import org.sonar.server.user.UserSession; + +import java.util.Map; + +public class SystemInfoWsAction implements SystemWsAction { + + private final Monitor[] monitors; + + public SystemInfoWsAction(Monitor... monitors) { + this.monitors = monitors; + } + + @Override + public void define(WebService.NewController controller) { + controller.createAction("info") + .setDescription("Detailed information about system configuration") + .setSince("5.1") + .setHandler(this); + } + + @Override + public void handle(Request request, Response response) { + UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + JsonWriter json = response.newJsonWriter(); + writeJson(json); + json.close(); + } + + private void writeJson(JsonWriter json) { + json.beginObject(); + for (Monitor monitor : monitors) { + json.name(monitor.name()); + json.beginObject(); + for (Map.Entry<String, Object> attribute : monitor.attributes().entrySet()) { + json.name(attribute.getKey()).valueObject(attribute.getValue()); + } + json.endObject(); + } + json.endObject(); + } + + Monitor[] getMonitors() { + return monitors; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemRestartWsAction.java index 79c0d51cbad..9227df4f4ac 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/RestartHandler.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemRestartWsAction.java @@ -22,31 +22,28 @@ package org.sonar.server.platform.ws; import org.sonar.api.config.Settings; import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.platform.Platform; -public class RestartHandler implements RequestHandler { +public class SystemRestartWsAction implements SystemWsAction { private final Settings settings; private final Platform platform; - private final System2 system; - public RestartHandler(Settings settings, Platform platform, System2 system) { + public SystemRestartWsAction(Settings settings, Platform platform) { this.settings = settings; this.platform = platform; - this.system = system; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { controller.createAction("restart") - .setDescription("Restart server. Available only on development mode (sonar.web.dev=true), except when using Java 6 " + - "on MS Windows. Ruby on Rails extensions are not reloaded") + .setDescription("Restart server. Available only on development mode (sonar.web.dev=true). " + + "Ruby on Rails extensions are not reloaded") .setSince("4.3") .setPost(true) .setHandler(this); @@ -54,7 +51,7 @@ public class RestartHandler implements RequestHandler { @Override public void handle(Request request, Response response) { - if (canRestart()) { + if (settings.getBoolean("sonar.web.dev")) { Logger logger = Loggers.get(getClass()); logger.info("Restart server"); platform.restart(); @@ -65,13 +62,4 @@ public class RestartHandler implements RequestHandler { throw new ForbiddenException(); } } - - private boolean canRestart() { - boolean ok = settings.getBoolean("sonar.web.dev"); - if (ok) { - ok = !system.isOsWindows() || system.isJavaAtLeast17(); - } - return ok; - } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWs.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWs.java index 3ed0f57e605..1f97ad6ad42 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWs.java @@ -23,19 +23,19 @@ import org.sonar.api.server.ws.WebService; public class SystemWs implements WebService { - private final RestartHandler restartHandler; + private final SystemWsAction[] actions; - public SystemWs(RestartHandler restartHandler) { - this.restartHandler = restartHandler; + public SystemWs(SystemWsAction... actions) { + this.actions = actions; } @Override public void define(Context context) { - NewController controller = context.createController("api/system") - .setDescription("Restart server") - .setSince("4.3"); + NewController controller = context.createController("api/system"); - restartHandler.define(controller); + for (SystemWsAction action : actions) { + action.define(controller); + } controller.done(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWsAction.java new file mode 100644 index 00000000000..21cacb6a149 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ws/SystemWsAction.java @@ -0,0 +1,29 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.WebService; + +public interface SystemWsAction extends RequestHandler { + + public void define(WebService.NewController controller); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java b/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java index e10254c4894..49c8911f4e7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java @@ -42,6 +42,40 @@ public class NodeHealth { private long filterCacheMemory; private List<Performance> performanceStats; + NodeHealth(NodeStats nodesStats) { + // Master/slave + setMaster(nodesStats.getNode().isMasterNode()); + + // Host IP and port + setAddress(nodesStats.getNode().getAddress().toString()); + + // JVM Heap Usage + setJvmHeapMax(nodesStats.getJvm().getMem().getHeapMax().bytes()); + setJvmHeapUsed(nodesStats.getJvm().getMem().getHeapUsed().bytes()); + + // Disk Usage + setFsTotal(nodesStats.getFs().getTotal().getTotal().bytes()); + setFsAvailable(nodesStats.getFs().getTotal().getAvailable().bytes()); + + // Ping ? + + // Threads + setJvmThreads(nodesStats.getJvm().getThreads().count()); + + // CPU + if (nodesStats.getProcess().getCpu() != null) { + setProcessCpuPercent(nodesStats.getProcess().cpu().getPercent()); + } + + // Open Files + setOpenFiles(nodesStats.getProcess().getOpenFileDescriptors()); + + // Uptime + setJvmUptimeMillis(nodesStats.getJvm().getUptime().getMillis()); + + initPerformanceStats(nodesStats); + } + public boolean isMaster() { return master; } @@ -58,22 +92,22 @@ public class NodeHealth { this.address = address; } - void setJvmHeapMax(long bytes) { - this.jvmHeapMax = bytes; - } - - void setJvmHeapUsed(long bytes) { - this.jvmHeapUsed = bytes; - } - public long getJvmHeapMax() { return jvmHeapMax; } + void setJvmHeapMax(long bytes) { + this.jvmHeapMax = bytes; + } + public long getJvmHeapUsed() { return jvmHeapUsed; } + void setJvmHeapUsed(long bytes) { + this.jvmHeapUsed = bytes; + } + public String getJvmHeapUsedPercent() { return formatPercent(getJvmHeapUsed(), getJvmHeapMax()); } @@ -94,38 +128,38 @@ public class NodeHealth { return String.format("%.1f%%", 100 * amount * 1.0D / total); } + public long getJvmThreads() { + return jvmThreads; + } + void setJvmThreads(long threads) { this.jvmThreads = threads; } - public long getJvmThreads() { - return jvmThreads; + public String getProcessCpuPercent() { + return formatPercent(cpuPercent, 100L); } void setProcessCpuPercent(int cpuPercent) { this.cpuPercent = cpuPercent; } - public String getProcessCpuPercent() { - return formatPercent(cpuPercent, 100L); + public long getOpenFiles() { + return openFiles; } void setOpenFiles(long avgOpenFileDescriptors) { this.openFiles = avgOpenFileDescriptors; } - long getOpenFiles() { - return openFiles; + public long getJvmUptimeMillis() { + return jvmUptimeMillis; } void setJvmUptimeMillis(long millis) { this.jvmUptimeMillis = millis; } - public long getJvmUptimeMillis() { - return jvmUptimeMillis; - } - public Date getJvmUpSince() { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(calendar.getTimeInMillis() - getJvmUptimeMillis()); @@ -144,40 +178,6 @@ public class NodeHealth { return filterCacheMemory; } - NodeHealth(NodeStats nodesStats) { - // Master/slave - setMaster(nodesStats.getNode().isMasterNode()); - - // Host IP and port - setAddress(nodesStats.getNode().getAddress().toString()); - - // JVM Heap Usage - setJvmHeapMax(nodesStats.getJvm().getMem().getHeapMax().bytes()); - setJvmHeapUsed(nodesStats.getJvm().getMem().getHeapUsed().bytes()); - - // Disk Usage - setFsTotal(nodesStats.getFs().getTotal().getTotal().bytes()); - setFsAvailable(nodesStats.getFs().getTotal().getAvailable().bytes()); - - // Ping ? - - // Threads - setJvmThreads(nodesStats.getJvm().getThreads().count()); - - // CPU - if (nodesStats.getProcess().getCpu() != null) { - setProcessCpuPercent(nodesStats.getProcess().cpu().getPercent()); - } - - // Open Files - setOpenFiles(nodesStats.getProcess().getOpenFileDescriptors()); - - // Uptime - setJvmUptimeMillis(nodesStats.getJvm().getUptime().getMillis()); - - initPerformanceStats(nodesStats); - } - private void initPerformanceStats(NodeStats nodesStats) { // Performance Stat performanceStats = new ArrayList<Performance>(); @@ -190,7 +190,7 @@ public class NodeHealth { .setWarnThreshold(10) .setErrorThreshold(50) .setMessage("Too complex documents or low IO/CPU") - .setValue(indexCount > 0L ? indexTotalTime / (double)indexCount : 0.0)); + .setValue(indexCount > 0L ? indexTotalTime / (double) indexCount : 0.0)); // Query stats long queryCount = nodesStats.getIndices().getSearch().getTotal().getQueryCount(); @@ -200,7 +200,7 @@ public class NodeHealth { .setWarnThreshold(50) .setErrorThreshold(500) .setMessage("Inefficient query and/or filters") - .setValue(queryCount > 0L ? queryTotalTime / (double)queryCount : 0.0)); + .setValue(queryCount > 0L ? queryTotalTime / (double) queryCount : 0.0)); // Fetch stats long fetchCount = nodesStats.getIndices().getSearch().getTotal().getFetchCount(); @@ -210,7 +210,7 @@ public class NodeHealth { .setWarnThreshold(8) .setErrorThreshold(15) .setMessage("Slow IO, fetch-size too large or documents too big") - .setValue(fetchCount > 0L ? fetchTotalTime / (double)fetchCount : 0.0)); + .setValue(fetchCount > 0L ? fetchTotalTime / (double) fetchCount : 0.0)); // Get stats long getCount = nodesStats.getIndices().getGet().getCount(); @@ -220,7 +220,7 @@ public class NodeHealth { .setWarnThreshold(5) .setErrorThreshold(10) .setMessage("Slow IO") - .setValue(getCount > 0L ? getTotalTime / (double)getCount : 0.0)); + .setValue(getCount > 0L ? getTotalTime / (double) getCount : 0.0)); // Refresh Stat long refreshCount = nodesStats.getIndices().getRefresh().getTotal(); @@ -230,7 +230,7 @@ public class NodeHealth { .setWarnThreshold(10) .setErrorThreshold(20) .setMessage("Slow IO") - .setValue(refreshCount > 0L ? refreshTotalTime / (double)refreshCount : 0.0)); + .setValue(refreshCount > 0L ? refreshTotalTime / (double) refreshCount : 0.0)); // Field Cache fieldCacheMemory = nodesStats.getIndices().getFieldData().getMemorySizeInBytes(); @@ -255,16 +255,11 @@ public class NodeHealth { public static class Performance { - public static enum Status { - OK, WARN, ERROR - } - private final String name; private String message; private double value; private double warnThreshold; private double errorThreshold; - public Performance(String name) { this.name = name; } @@ -310,5 +305,9 @@ public class NodeHealth { this.errorThreshold = errorThreshold; return this; } + + public static enum Status { + OK, WARN, ERROR + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/BaseMonitorMBeanTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/BaseMonitorMBeanTest.java new file mode 100644 index 00000000000..1dbb3dd70cf --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/BaseMonitorMBeanTest.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Test; + +import javax.annotation.CheckForNull; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectInstance; +import java.lang.management.ManagementFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BaseMonitorMBeanTest { + + FakeMonitor sut = new FakeMonitor(); + + @Test + public void test_registration() throws Exception { + assertThat(getMBean()).isNull(); + + sut.start(); + assertThat(getMBean()).isNotNull(); + + sut.stop(); + assertThat(getMBean()).isNull(); + } + + @Test + public void do_not_fail_when_stopping_unstarted() throws Exception { + sut.stop(); + assertThat(getMBean()).isNull(); + } + + @CheckForNull + private ObjectInstance getMBean() throws Exception { + try { + return ManagementFactory.getPlatformMBeanServer().getObjectInstance(sut.objectName()); + } catch (InstanceNotFoundException e) { + return null; + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/DatabaseMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/DatabaseMonitorTest.java new file mode 100644 index 00000000000..75aa97901f7 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/DatabaseMonitorTest.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.DbClient; + +import java.util.LinkedHashMap; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DatabaseMonitorTest { + + @Rule + public DbTester dbTester = new DbTester(); + + DatabaseMonitor sut; + + @Before + public void setUp() throws Exception { + DatabaseVersion dbVersion = new DatabaseVersion(dbTester.myBatis()); + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis()); + sut = new DatabaseMonitor(dbVersion, dbClient); + } + + @Test + public void db_info() { + LinkedHashMap<String, Object> attributes = sut.attributes(); + assertThat(attributes.get("Database")).isEqualTo("H2"); + assertThat(attributes.get("Database Version").toString()).startsWith("1."); + assertThat(attributes.get("Username")).isEqualTo("SONAR"); + assertThat(attributes.get("Driver Version").toString()).startsWith("1."); + } + + @Test + public void pool_info() { + LinkedHashMap<String, Object> attributes = sut.attributes(); + assertThat((int)attributes.get("Pool Max Connections")).isGreaterThan(0); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitor.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitor.java new file mode 100644 index 00000000000..2bfbf6de823 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitor.java @@ -0,0 +1,40 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import java.util.LinkedHashMap; + +public class FakeMonitor extends BaseMonitorMBean implements FakeMonitorMBean { + + @Override + public int getFake() { + return 42; + } + + @Override + public String name() { + return "fake"; + } + + @Override + public LinkedHashMap<String, Object> attributes() { + return new LinkedHashMap<>(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitorMBean.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitorMBean.java new file mode 100644 index 00000000000..91d77b2d33c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitorMBean.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +public interface FakeMonitorMBean { + int getFake(); +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitorTest.java new file mode 100644 index 00000000000..208516d7276 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitorTest.java @@ -0,0 +1,37 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Test; + +import java.util.LinkedHashMap; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JvmPropertiesMonitorTest { + + @Test + public void attributes() throws Exception { + JvmPropertiesMonitor sut = new JvmPropertiesMonitor(); + LinkedHashMap<String, Object> attributes = sut.attributes(); + + assertThat(attributes).containsKey("java.vm.vendor"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java new file mode 100644 index 00000000000..4ee0fe3eef2 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java @@ -0,0 +1,80 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Test; +import org.sonar.api.Plugin; +import org.sonar.api.platform.PluginMetadata; +import org.sonar.api.platform.PluginRepository; +import org.sonar.core.plugins.DefaultPluginMetadata; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PluginsMonitorTest { + + PluginsMonitor sut = new PluginsMonitor(new FakePluginRepository());; + + @Test + public void name() throws Exception { + assertThat(sut.name()).isEqualTo("Plugins"); + } + + @Test + public void plugin_name_and_version() throws Exception { + LinkedHashMap<String, Object> attributes = sut.attributes(); + + assertThat(attributes) + .containsEntry("plugin-1", "1.1") + .containsEntry("plugin-2", "2.2"); + } + + private static class FakePluginRepository implements PluginRepository { + + @Override + public Plugin getPlugin(String key) { + return null; + } + + @Override + public Collection<PluginMetadata> getMetadata() { + List<PluginMetadata> plugins = new ArrayList<>(); + plugins.add(DefaultPluginMetadata + .create("key-1") + .setName("plugin-1") + .setVersion("1.1")); + plugins.add(DefaultPluginMetadata + .create("key-2") + .setName("plugin-2") + .setVersion("2.2")); + return plugins; + } + + @Override + public PluginMetadata getMetadata(String pluginKey) { + return null; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SonarQubeMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SonarQubeMonitorTest.java new file mode 100644 index 00000000000..6972f98e6e7 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SonarQubeMonitorTest.java @@ -0,0 +1,46 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.api.platform.Server; +import org.sonar.api.utils.DateUtils; +import org.sonar.server.user.SecurityRealmFactory; + +import java.util.LinkedHashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SonarQubeMonitorTest { + + @Test + public void getServerId() throws Exception { + Settings settings = new Settings(); + Server server = mock(Server.class); + when(server.getStartedAt()).thenReturn(DateUtils.parseDate("2015-01-01")); + SonarQubeMonitor monitor = new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server); + + LinkedHashMap<String, Object> attributes = monitor.attributes(); + assertThat(attributes).containsKeys("Started at", "Server ID"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemMonitorTest.java new file mode 100644 index 00000000000..ba7064ca217 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemMonitorTest.java @@ -0,0 +1,43 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.monitoring; + +import org.junit.Test; + +import java.util.LinkedHashMap; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SystemMonitorTest { + + SystemMonitor sut = new SystemMonitor(); + + @Test + public void name() throws Exception { + assertThat(sut.name()).isEqualTo("System"); + } + + @Test + public void system_properties() throws Exception { + LinkedHashMap<String, Object> attributes = sut.attributes(); + + assertThat(attributes).containsKeys("System Date", "Processors"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemInfoWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemInfoWsActionTest.java new file mode 100644 index 00000000000..f42f3dc09c8 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemInfoWsActionTest.java @@ -0,0 +1,71 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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 org.apache.commons.lang.StringUtils; +import org.assertj.core.api.Condition; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.platform.monitoring.Monitor; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class SystemInfoWsActionTest { + + @ClassRule + public static ServerTester serverTester = new ServerTester(); + + private SystemInfoWsAction sut; + + @Test + public void all_monitors_have_a_name() throws Exception { + sut = serverTester.get(SystemInfoWsAction.class); + + assertThat(sut.getMonitors()).areNot(withEmptyName()); + } + + @Test(expected = ForbiddenException.class) + public void should_fail_when_does_not_have_admin_right() throws Exception { + sut = serverTester.get(SystemInfoWsAction.class); + MockUserSession.set() + .setLogin("login") + .setName("name") + .setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + + sut.handle(mock(Request.class), mock(Response.class)); + } + + private Condition<Monitor> withEmptyName() { + return new Condition<Monitor>() { + @Override + public boolean matches(Monitor m) { + return StringUtils.isEmpty(m.name()); + } + }; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemRestartWsActionTest.java index 832f80f2585..4329ff9a5d5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/ws/RestartHandlerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemRestartWsActionTest.java @@ -21,63 +21,33 @@ package org.sonar.server.platform.ws; import org.junit.Test; import org.sonar.api.config.Settings; -import org.sonar.api.utils.System2; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.platform.Platform; import org.sonar.server.ws.WsTester; import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; -public class RestartHandlerTest { +public class SystemRestartWsActionTest { - System2 system = mock(System2.class); + Settings settings = new Settings(); + Platform platform = mock(Platform.class); + SystemRestartWsAction sut = new SystemRestartWsAction(settings, platform); @Test public void restart_if_dev_mode() throws Exception { - Platform platform = mock(Platform.class); - Settings settings = new Settings(); settings.setProperty("sonar.web.dev", true); - when(system.isOsWindows()).thenReturn(false); - RestartHandler restartHandler = new RestartHandler(settings, platform, system); - SystemWs ws = new SystemWs(restartHandler); + SystemWs ws = new SystemWs(sut); WsTester tester = new WsTester(ws); tester.newPostRequest("api/system", "restart").execute(); - verify(platform).restart(); } @Test public void fail_if_production_mode() throws Exception { - Platform platform = mock(Platform.class); - Settings settings = new Settings(); - RestartHandler restartHandler = new RestartHandler(settings, platform, system); - SystemWs ws = new SystemWs(restartHandler); - - WsTester tester = new WsTester(ws); - try { - tester.newPostRequest("api/system", "restart").execute(); - fail(); - } catch (ForbiddenException e) { - verifyZeroInteractions(platform); - } - } - - @Test - public void fail_if_windows_java_6() throws Exception { - Platform platform = mock(Platform.class); - Settings settings = new Settings(); - settings.setProperty("sonar.web.dev", true); - when(system.isOsWindows()).thenReturn(true); - when(system.isJavaAtLeast17()).thenReturn(false); - - RestartHandler restartHandler = new RestartHandler(settings, platform, system); - SystemWs ws = new SystemWs(restartHandler); + SystemWs ws = new SystemWs(sut); WsTester tester = new WsTester(ws); try { 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 132f77d4f35..1c535200e66 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 @@ -22,7 +22,6 @@ package org.sonar.server.platform.ws; import org.junit.Test; import org.sonar.api.config.Settings; import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.System2; import org.sonar.server.platform.Platform; import static org.assertj.core.api.Assertions.assertThat; @@ -32,18 +31,15 @@ public class SystemWsTest { @Test public void define() throws Exception { - Platform platform = mock(Platform.class); - Settings settings = new Settings(); - RestartHandler restartHandler = new RestartHandler(settings, platform, mock(System2.class)); - SystemWs ws = new SystemWs(restartHandler); + SystemRestartWsAction action1 = new SystemRestartWsAction(mock(Settings.class), mock(Platform.class)); + SystemInfoWsAction action2 = new SystemInfoWsAction(); + SystemWs ws = new SystemWs(action1, action2); WebService.Context context = new WebService.Context(); ws.define(context); assertThat(context.controllers()).hasSize(1); - assertThat(context.controller("api/system")).isNotNull(); - assertThat(context.controller("api/system").description()).isNotEmpty(); - assertThat(context.controller("api/system").since()).isEqualTo("4.3"); - assertThat(context.controller("api/system").actions()).isNotEmpty(); + assertThat(context.controller("api/system").actions()).hasSize(2); + assertThat(context.controller("api/system").action("info")).isNotNull(); } } diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb index 34de3ab3b16..91ff7fd875e 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb @@ -45,24 +45,6 @@ class Api::ServerController < Api::ApiController end end - def system - access_denied unless has_role?(:admin) - @server=Server.new - json=[ - {:system_info => server_properties_to_json(@server.system_info)}, - {:system_statistics => server_properties_to_json(@server.system_statistics)}, - {:sonar_info => server_properties_to_json(@server.sonar_info)}, - {:sonar_plugins => server_properties_to_json(@server.sonar_plugins)}, - {:system_properties => server_properties_to_json(@server.system_properties)}, - ] - - respond_to do |format| - format.json{ render :json => jsonp(json) } - format.xml { render :xml => xml_not_supported } - format.text { render :text => text_not_supported} - end - end - def setup verify_post_request manager=DatabaseMigrationManager.instance diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb index 1c86a0f75b5..9bb32af6f5a 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/system_controller.rb @@ -17,7 +17,6 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -require 'fastercsv' class SystemController < ApplicationController SECTION=Navigation::SECTION_CONFIGURATION @@ -32,19 +31,7 @@ class SystemController < ApplicationController respond_to do |format| format.html - format.csv { - send_data(to_csv, :type => 'text/csv; charset=utf-8', :disposition => "attachment; filename=#{filename}.csv") - } - end - end - - private - - def to_csv - FasterCSV.generate do |csv| - @server.info.each do |property| - csv << property - end end end + end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb index b611f37aed8..2da765f923f 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/models/server.rb @@ -31,20 +31,30 @@ class Server add_property(system_info, 'JVM Vendor') { java.lang.management.ManagementFactory.getRuntimeMXBean().getVmVendor() } add_property(system_info, 'JVM Name') { java.lang.management.ManagementFactory.getRuntimeMXBean().getVmName() } add_property(system_info, 'JVM Version') { java.lang.management.ManagementFactory.getRuntimeMXBean().getVmVersion() } - add_property(system_info, 'Java Version') { java_property('java.runtime.version') } - add_property(system_info, 'Java Home') { java_property('java.home') } - add_property(system_info, 'JIT Compiler') { java_property('java.compiler') } - add_property(system_info, 'Application Server Container') { $servlet_context.getServerInfo() } - add_property(system_info, 'User Name') { java_property('user.name') } - add_property(system_info, 'User TimeZone') { java_property('user.timezone') } - add_property(system_info, 'OS') { "#{java_property('os.name')} / #{java_property('os.arch')} / #{java_property('os.version')}" } + #add_property(system_info, 'Java Version') { java_property('java.runtime.version') } + #add_property(system_info, 'Java Home') { java_property('java.home') } + #add_property(system_info, 'JIT Compiler') { java_property('java.compiler') } + #add_property(system_info, 'Application Server Container') { $servlet_context.getServerInfo() } + #add_property(system_info, 'User Name') { java_property('user.name') } + #add_property(system_info, 'User TimeZone') { java_property('user.timezone') } + #add_property(system_info, 'OS') { "#{java_property('os.name')} / #{java_property('os.arch')} / #{java_property('os.version')}" } add_property(system_info, 'Processors') { java.lang.Runtime.getRuntime().availableProcessors() } add_property(system_info, 'System Classpath') { java.lang.management.ManagementFactory.getRuntimeMXBean().getClassPath() } add_property(system_info, 'Boot Classpath') { java.lang.management.ManagementFactory.getRuntimeMXBean().getBootClassPath() } add_property(system_info, 'Library Path') { java.lang.management.ManagementFactory.getRuntimeMXBean().getLibraryPath() } + add_property(system_statistics, 'Total Memory') { "#{java.lang.Runtime.getRuntime().totalMemory() / 1000000} MB" } + add_property(system_statistics, 'Free Memory') { "#{java.lang.Runtime.getRuntime().freeMemory() / 1000000} MB" } + add_property(system_statistics, 'Max Memory') { "#{java.lang.Runtime.getRuntime().maxMemory() / 1000000} MB" } + add_property(system_statistics, 'Heap') { "#{java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()}" } + add_property(system_statistics, 'Non Heap') { "#{java.lang.management.ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()}" } + add_property(system_statistics, 'System Load Average (last minute)') { system_load_average() } + add_property(system_statistics, 'Loaded Classes (currently/total/unloaded)') { "#{java.lang.management.ManagementFactory.getClassLoadingMXBean().getLoadedClassCount()} / #{java.lang.management.ManagementFactory.getClassLoadingMXBean().getTotalLoadedClassCount()} / #{java.lang.management.ManagementFactory.getClassLoadingMXBean().getUnloadedClassCount()}" } + add_property(system_statistics, 'Start Time') { "#{format_date(java.util.Date.new(java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime()))}" } + add_property(system_statistics, 'Threads (total/peak/daemon)') { "#{java.lang.management.ManagementFactory.getThreadMXBean().getThreadCount()} / #{java.lang.management.ManagementFactory.getThreadMXBean().getPeakThreadCount()} / #{java.lang.management.ManagementFactory.getThreadMXBean().getDaemonThreadCount() }" } system_info end + #TODO to remove def system_statistics system_statistics=[] add_property(system_statistics, 'Total Memory') { "#{java.lang.Runtime.getRuntime().totalMemory() / 1000000} MB" } @@ -64,13 +74,13 @@ class Server add_property(sonar_info, 'Server ID') { sonar_property(ServerIdConfigurationController::PROPERTY_SERVER_ID) } add_property(sonar_info, 'Version') { org.sonar.server.platform.Platform.getServer().getVersion() } add_property(sonar_info, 'Started at') { org.sonar.server.platform.Platform.getServer().getStartedAt() } - add_property(sonar_info, 'Database') { "#{jdbc_metadata.getDatabaseProductName()} #{jdbc_metadata.getDatabaseProductVersion()}" } - add_property(sonar_info, 'Database URL') { sonar_property('sonar.jdbc.url') } - add_property(sonar_info, 'Database Login') { sonar_property('sonar.jdbc.username') } - add_property(sonar_info, 'Database Driver') { "#{jdbc_metadata.getDriverName()} #{jdbc_metadata.getDriverVersion()}" } - add_property(sonar_info, 'Database Active Connections') { "#{Java::OrgSonarServerUi::JRubyFacade.getInstance().getDatabase().getDataSource().getNumActive()}" } - add_property(sonar_info, 'Database Max. Active Connections') { sonar_property('sonar.jdbc.maxActive') } - add_property(sonar_info, 'Database Max. Pool Wait') { sonar_property('sonar.jdbc.maxWait') } + #add_property(sonar_info, 'Database') { "#{jdbc_metadata.getDatabaseProductName()} #{jdbc_metadata.getDatabaseProductVersion()}" } + #add_property(sonar_info, 'Database URL') { sonar_property('sonar.jdbc.url') } + #add_property(sonar_info, 'Database Login') { sonar_property('sonar.jdbc.username') } + #add_property(sonar_info, 'Database Driver') { "#{jdbc_metadata.getDriverName()} #{jdbc_metadata.getDriverVersion()}" } + #add_property(sonar_info, 'Database Active Connections') { "#{Java::OrgSonarServerUi::JRubyFacade.getInstance().getDatabase().getDataSource().getNumActive()}" } + #add_property(sonar_info, 'Database Max. Active Connections') { sonar_property('sonar.jdbc.maxActive') } + #add_property(sonar_info, 'Database Max. Pool Wait') { sonar_property('sonar.jdbc.maxWait') } add_property(sonar_info, 'External User Authentication') { realm_name } add_property(sonar_info, 'Automatic User Creation') { sonar_property(org.sonar.api.CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS) } add_property(sonar_info, 'Allow Users to Sign Up') { sonar_property(org.sonar.api.CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY) } @@ -124,6 +134,7 @@ class Server nodes_info end + #TODO should be removed def sonar_plugins sonar_plugins=[] Java::OrgSonarServerUi::JRubyFacade.getInstance().getPluginsMetadata().to_a.select { |plugin| !plugin.isCore() }.sort.each do |plugin| diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb index 56b92a5fc50..f237a396468 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/system/index.html.erb @@ -2,8 +2,8 @@ <header class="page-header"> <h1 class="page-title"><%= message('system_info.page') -%></h1> <div class="page-actions"> - <div class="csv"> - <a href="<%= ApplicationController.root_context -%>/system/index?format=csv" id="download-link">Download</a> + <div> + <a href="<%= ApplicationController.root_context -%>/api/system/info" id="download-link">Download</a> </div> </div> <p class="page-description"><%= message('system_info.page.description') -%></p> @@ -19,35 +19,11 @@ <% @server.sonar_info.each do |data| %> <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'sonar'} %> <% end %> - <tbody> + </tbody> </table> <br/> - <table class="data width100" id="plugins"> - <thead> - <tr> - <th colspan="2"><h2>SonarQube Plugins</h2></th> - </tr> - </thead> - <tbody> - <% - user_plugins=@server.sonar_plugins - if user_plugins.empty? - %> - <tr> - <td colspan="2" class="even">None</td> - </tr> - <% else %> - <% user_plugins.each do |data| %> - <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'plugins'} %> - <% end %> - <% end %> - <tbody> - </table> - - <br/> - <table class="data width100" id="system_info"> <thead> <tr> @@ -58,7 +34,7 @@ <% @server.system_info.each do |data| %> <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'system'} %> <% end %> - <tbody> + </tbody> </table> <br/> @@ -66,14 +42,14 @@ <table class="data width100" id="cluster_info"> <thead> <tr> - <th colspan="2"><h2>Search Info - Cluster</h2></th> + <th colspan="2"><h2>ElasticSearch - Cluster</h2></th> </tr> </thead> <tbody> <% @server.cluster_info.each do |data| %> <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'cluster'} %> <% end %> - <tbody> + </tbody> </table> <br/> @@ -82,35 +58,19 @@ <table class="data width100" id="cluster_info<%= node_info[0][1] -%>"> <thead> <tr> - <th colspan="2"><h2>Search Info - <%= node_info[0][1] -%></h2></th> + <th colspan="2"><h2>ElasticSearch - <%= node_info[0][1] -%></h2></th> </tr> </thead> <tbody> <% node_info.drop(1).each do |data| %> <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'node'} %> <% end %> - <tbody> + </tbody> </table> <br/> <% end -%> - <table class="data width100" id="memory"> - <thead> - <tr> - <th colspan="2"><h2>Java VM Statistics</h2></th> - </tr> - </thead> - <tbody> - <% @server.system_statistics.each do |data| %> - <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'memory'} %> - <% end %> - <tbody> - </table> - - <br/> - - <table class="data width100" id="system_properties"> <thead> <tr> @@ -121,6 +81,6 @@ <% @server.system_properties.each do |data| %> <%= render :partial => 'row', :locals => {:title => data[0], :value => data[1], :name => 'system_properties'} %> <% end %> - <tbody> + </tbody> </table> </div> |