From 6bc55b17ebd87802cb9b55d5b6625e1a935f3909 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 16 Mar 2016 23:01:11 +0100 Subject: [PATCH] SONAR-7436 Monitoring of CE process in system WS and console --- .../it/serverSystem/ServerSystemTest.java | 19 +++ .../sonar/ce/app/WebServerWatcherImpl.java | 4 +- .../container/ComputeEngineContainerImpl.java | 4 +- .../ce/app/WebServerWatcherImplTest.java | 4 +- .../ComputeEngineContainerImplTest.java | 6 +- .../sonar/process/monitor/JavaCommand.java | 24 +-- .../process/monitor/JavaProcessLauncher.java | 40 +++-- .../process/monitor/JavaCommandTest.java | 6 +- .../monitor/JavaProcessLauncherTest.java | 5 +- .../sonar/process/monitor/MonitorTest.java | 65 +++++---- server/sonar-process/pom.xml | 14 +- .../sonar/process/AllProcessesCommands.java | 52 ++++++- .../sonar/process/DefaultProcessCommands.java | 10 ++ .../org/sonar/process/ProcessCommands.java | 6 +- .../org/sonar/process/ProcessEntryPoint.java | 12 ++ .../java/org/sonar/process/ProcessId.java | 53 +++++++ .../sonar/process/jmx/CeDatabaseMBean.java | 43 ++++++ .../org/sonar/process/jmx/CeTasksMBean.java} | 11 +- .../sonar/process/jmx/EsSettingsMBean.java | 38 +++++ .../main/java/org/sonar/process/jmx/Jmx.java | 86 +++++++++++ .../org/sonar/process/jmx/JmxConnection.java | 97 ++++++++++++ .../process/jmx/JmxConnectionFactory.java | 57 ++++++++ .../org/sonar/process/jmx/package-info.java | 23 +++ .../process/AllProcessesCommandsTest.java | 14 ++ .../sonar/process/ProcessEntryPointTest.java | 12 +- .../java/org/sonar/process/ProcessIdTest.java | 47 ++++++ .../test/java/org/sonar/process/jmx/Fake.java | 27 ++++ .../java/org/sonar/process/jmx/FakeMBean.java | 24 +++ .../sonar/process/jmx/JmxConnectionTest.java | 36 +++++ .../java/org/sonar/process/jmx/JmxTest.java | 87 +++++++++++ .../java/org/sonar/process/jmx/test/Fake.java | 30 ++++ .../{SearchSettings.java => EsSettings.java} | 35 +++-- .../java/org/sonar/search/SearchServer.java | 8 +- ...hSettingsTest.java => EsSettingsTest.java} | 30 ++-- .../org/sonar/search/SearchServerTest.java | 7 +- .../src/main/java/org/sonar/ce/CeModule.java | 4 - .../sonar/ce/monitoring/CEQueueStatus.java | 52 +------ .../ce/monitoring/DummyCEQueueStatusImpl.java | 93 ------------ .../java/org/sonar/ce/queue/CeQueueImpl.java | 11 +- .../server/app/ProcessCommandWrapper.java | 1 + .../server/app/ProcessCommandWrapperImpl.java | 24 +-- .../sonar/server/computation/CeModule.java | 2 + .../monitoring/CEQueueStatusImpl.java | 65 ++------- .../monitoring/CeDatabaseMBeanImpl.java | 97 ++++++++++++ ...ueueMonitor.java => CeTasksMBeanImpl.java} | 39 ++--- .../computation/queue/CeQueueInitializer.java | 10 +- .../computation/queue/CeQueueModule.java | 6 +- .../queue/InternalCeQueueImpl.java | 12 +- .../platform/monitoring/BaseMonitorMBean.java | 29 +--- .../monitoring/CeDatabaseMonitor.java | 58 ++++++++ .../platform/monitoring/CeStateMonitor.java | 46 ++++++ .../platform/monitoring/CeTasksMonitor.java | 55 +++++++ .../platform/monitoring/DatabaseMonitor.java | 13 +- .../server/platform/monitoring/EsMonitor.java | 25 +++- .../platform/monitoring/EsStateMonitor.java | 46 ++++++ .../monitoring/JmxConnectorProvider.java | 46 ++++++ ...rtiesMonitor.java => JvmPropsMonitor.java} | 3 +- .../platformlevel/PlatformLevel4.java | 14 +- .../CEQueueStatusImplConcurrentTest.java | 19 +-- .../org/sonar/ce/queue/CeQueueImplTest.java | 41 +----- .../app/ProcessCommandWrapperImplTest.java | 12 +- .../monitoring/CEQueueStatusImplTest.java | 138 +++--------------- ...torTest.java => CeTasksMBeanImplTest.java} | 64 +++----- .../queue/CeQueueInitializerTest.java | 56 +------ .../queue/InternalCeQueueImplTest.java | 32 +--- .../org/sonar/server/es/EsServerHolder.java | 7 +- .../monitoring/BaseMonitorMBeanTest.java | 8 +- .../monitoring/CeDatabaseMonitorTest.java | 53 +++++++ .../monitoring/CeStateMonitorTest.java | 53 +++++++ .../monitoring/CeTasksMonitorTest.java | 53 +++++++ .../monitoring/DatabaseMonitorTest.java | 5 + .../platform/monitoring/EsMonitorTest.java | 39 +++-- .../monitoring/EsStateMonitorTest.java | 52 +++++++ .../JmxConnectionFactoryProviderTest.java | 56 +++++++ ...itorTest.java => JvmPropsMonitorTest.java} | 13 +- .../monitoring/SonarQubeMonitorTest.java | 5 + .../sonar-web/src/main/js/apps/system/main.js | 4 +- .../main/java/org/sonar/application/App.java | 16 +- .../java/org/sonar/application/AppTest.java | 7 +- 79 files changed, 1757 insertions(+), 763 deletions(-) create mode 100644 server/sonar-process/src/main/java/org/sonar/process/ProcessId.java create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/CeDatabaseMBean.java rename server/{sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorMBean.java => sonar-process/src/main/java/org/sonar/process/jmx/CeTasksMBean.java} (85%) create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/EsSettingsMBean.java create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/Jmx.java create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnectionFactory.java create mode 100644 server/sonar-process/src/main/java/org/sonar/process/jmx/package-info.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/ProcessIdTest.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/jmx/Fake.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/jmx/FakeMBean.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTest.java create mode 100644 server/sonar-process/src/test/java/org/sonar/process/jmx/test/Fake.java rename server/sonar-search/src/main/java/org/sonar/search/{SearchSettings.java => EsSettings.java} (88%) rename server/sonar-search/src/test/java/org/sonar/search/{SearchSettingsTest.java => EsSettingsTest.java} (87%) delete mode 100644 server/sonar-server/src/main/java/org/sonar/ce/monitoring/DummyCEQueueStatusImpl.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java rename server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/{ComputeEngineQueueMonitor.java => CeTasksMBeanImpl.java} (59%) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStateMonitor.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectorProvider.java rename server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/{JvmPropertiesMonitor.java => JvmPropsMonitor.java} (96%) rename server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/{ComputeEngineQueueMonitorTest.java => CeTasksMBeanImplTest.java} (70%) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java rename server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/{JvmPropertiesMonitorTest.java => JvmPropsMonitorTest.java} (86%) diff --git a/it/it-tests/src/test/java/it/serverSystem/ServerSystemTest.java b/it/it-tests/src/test/java/it/serverSystem/ServerSystemTest.java index 64c72d3faec..51192aac4bc 100644 --- a/it/it-tests/src/test/java/it/serverSystem/ServerSystemTest.java +++ b/it/it-tests/src/test/java/it/serverSystem/ServerSystemTest.java @@ -40,12 +40,15 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.sonar.wsclient.services.Server; import org.sonar.wsclient.services.ServerQuery; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.WsResponse; import util.ItUtils; import util.QaOnly; import util.selenium.SeleneseTest; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; +import static util.ItUtils.newAdminWsClient; @Category(QaOnly.class) public class ServerSystemTest { @@ -89,6 +92,22 @@ public class ServerSystemTest { new SeleneseTest(selenese).runOn(orchestrator); } + /** + * SONAR-7436 + */ + @Test + public void monitor_compute_engine_and_elasticsearch_processes() throws Exception { + WsResponse response = newAdminWsClient(orchestrator).wsConnector().call( + new GetRequest("api/system/info")); + assertThat(response.code()).isEqualTo(200); + + assertThat(response.content()).containsSequence("\"Compute Engine Database\":", "\"Pool Active Connections\""); + assertThat(response.content()).containsSequence("\"Compute Engine State\":", "\"Heap Used\""); + assertThat(response.content()).containsSequence("\"Compute Engine Tasks\":", "\"Pending\"", "\"In Progress\""); + + assertThat(response.content()).containsSequence("\"Elasticsearch\":", "\"State\":\"GREEN\""); + } + /** * See http://jira.codehaus.org/browse/SONAR-2727 */ diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/app/WebServerWatcherImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/app/WebServerWatcherImpl.java index 4ebc8f80030..73bde24aa0e 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/app/WebServerWatcherImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/app/WebServerWatcherImpl.java @@ -23,10 +23,10 @@ import java.io.File; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.ProcessId; public class WebServerWatcherImpl implements WebServerWatcher { private static final Logger LOG = Loggers.get(WebServerWatcherImpl.class); - private static final int WEB_SERVER_PROCESS_NUMBER = 2; private static final int POLL_DELAY = 200; // accounting only every 5 log calls so that only one every second (because delay is 200ms) is taken into account private static final int CALL_RATIO = 5; @@ -39,7 +39,7 @@ public class WebServerWatcherImpl implements WebServerWatcher { @Override public boolean waitForOperational() { - try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(sharedDir, WEB_SERVER_PROCESS_NUMBER)) { + try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(sharedDir, ProcessId.WEB_SERVER.getIpcIndex())) { if (processCommands.isOperational()) { return true; } diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java index 42527bf4e8b..be0bda6e858 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java @@ -108,6 +108,7 @@ import org.sonar.server.platform.ServerImpl; import org.sonar.server.platform.ServerLifecycleNotifier; import org.sonar.server.platform.ServerLogging; import org.sonar.server.platform.TempFolderProvider; +import org.sonar.server.platform.monitoring.JmxConnectorProvider; import org.sonar.server.plugins.InstalledPluginReferentialFactory; import org.sonar.server.plugins.ServerExtensionInstaller; import org.sonar.server.properties.ProjectSettingsFactory; @@ -138,6 +139,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { private static final Object[] LEVEL_1_COMPONENTS = new Object[] { ComputeEngineSettings.class, new SonarQubeVersionProvider(), + new JmxConnectorProvider(), ServerImpl.class, UuidFactoryImpl.INSTANCE, // no EmbeddedDatabaseFactory.class, creating H2 DB if responsibility of WebServer @@ -521,7 +523,7 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer { // SonarQubeMonitor.class, no Monitor in CE, responsibility of Web Server // EsMonitor.class, no Monitor in CE, responsibility of Web Server // PluginsMonitor.class, no Monitor in CE, responsibility of Web Server - // JvmPropertiesMonitor.class, no Monitor in CE, responsibility of Web Server + // JvmPropsMonitor.class, no Monitor in CE, responsibility of Web Server // DatabaseMonitor.class, no Monitor in CE, responsibility of Web Server // MigrateDbAction.class, no Web Service in CE // LogsAction.class, no Web Service in CE diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/app/WebServerWatcherImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/app/WebServerWatcherImplTest.java index 32007dc60f1..d4a9ae285a4 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/app/WebServerWatcherImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/app/WebServerWatcherImplTest.java @@ -30,12 +30,12 @@ import org.junit.rules.Timeout; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.ProcessId; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; public class WebServerWatcherImplTest { - private static final int WEB_SERVER_PROCESS_NUMBER = 2; @Rule public Timeout timeout = Timeout.seconds(1); @@ -102,7 +102,7 @@ public class WebServerWatcherImplTest { } private void setWebServerOperational() { - try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(sharedDir, WEB_SERVER_PROCESS_NUMBER)) { + try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(sharedDir, ProcessId.WEB_SERVER.getIpcIndex())) { processCommands.setOperational(); } } diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index b56bd13daf1..291093f2b45 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -34,6 +34,7 @@ import org.sonar.db.DbTester; import org.sonar.process.Props; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH; import static org.sonar.process.ProcessProperties.PATH_DATA; import static org.sonar.process.ProcessProperties.PATH_HOME; import static org.sonar.process.ProcessProperties.PATH_TEMP; @@ -65,6 +66,7 @@ public class ComputeEngineContainerImplTest { properties.setProperty(PATH_HOME, homeDir.getAbsolutePath()); properties.setProperty(PATH_DATA, dataDir.getAbsolutePath()); properties.setProperty(PATH_TEMP, tmpDir.getAbsolutePath()); + properties.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath()); String url = ((BasicDataSource) dbTester.database().getDataSource()).getUrl(); properties.setProperty(DatabaseProperties.PROP_URL, url); properties.setProperty(DatabaseProperties.PROP_USER, "sonar"); @@ -77,7 +79,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getComponentAdapters()) .hasSize( CONTAINER_ITSELF - + 77 // level 4 + + 78 // level 4 + 5 // content of CeModule + 7 // content of CeQueueModule + 4 // content of ReportProcessingModule @@ -93,7 +95,7 @@ public class ComputeEngineContainerImplTest { ); assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION - + 22 // level 1 + + 23 // level 1 + 47 // content of DaoModule + 1 // content of EsSearchModule + 56 // content of CorePropertyDefinitions diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java index 6a4f77607d9..98caa87d620 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaCommand.java @@ -19,10 +19,6 @@ */ package org.sonar.process.monitor; -import org.apache.commons.lang.StringUtils; - -import javax.annotation.Nullable; - import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -30,11 +26,14 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.process.ProcessId; public class JavaCommand { // unique key among the group of commands to launch - private final String key; + private final ProcessId id; private File workDir; @@ -52,19 +51,12 @@ public class JavaCommand { private final Map envVariables = new HashMap<>(System.getenv()); - private final int processIndex; - - public JavaCommand(String key, int processIndex) { - this.key = key; - this.processIndex = processIndex; - } - - public String getKey() { - return key; + public JavaCommand(ProcessId id) { + this.id = id; } - public int getProcessIndex() { - return processIndex; + public ProcessId getProcessId() { + return id; } public File getWorkDir() { diff --git a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java index 7a5869abb38..c586d7c9df0 100644 --- a/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java +++ b/server/sonar-process-monitor/src/main/java/org/sonar/process/monitor/JavaProcessLauncher.java @@ -33,18 +33,24 @@ import org.sonar.process.ProcessCommands; import org.sonar.process.ProcessEntryPoint; import org.sonar.process.ProcessUtils; -public class JavaProcessLauncher { +import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX; +import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_KEY; +import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH; +import static org.sonar.process.ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT; + +class JavaProcessLauncher implements AutoCloseable { private final Timeouts timeouts; private final File tempDir; private final AllProcessesCommands allProcessesCommands; - public JavaProcessLauncher(Timeouts timeouts, File tempDir) { + JavaProcessLauncher(Timeouts timeouts, File tempDir) { this.timeouts = timeouts; this.tempDir = tempDir; this.allProcessesCommands = new AllProcessesCommands(tempDir); } + @Override public void close() { allProcessesCommands.close(); } @@ -52,21 +58,21 @@ public class JavaProcessLauncher { ProcessRef launch(JavaCommand command) { Process process = null; try { - ProcessCommands commands = allProcessesCommands.createAfterClean(command.getProcessIndex()); + ProcessCommands commands = allProcessesCommands.createAfterClean(command.getProcessId().getIpcIndex()); ProcessBuilder processBuilder = create(command); LoggerFactory.getLogger(getClass()).info("Launch process[{}]: {}", - command.getKey(), StringUtils.join(processBuilder.command(), " ")); + command.getProcessId().getKey(), StringUtils.join(processBuilder.command(), " ")); process = processBuilder.start(); - StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), command.getKey()); + StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), command.getProcessId().getKey()); inputGobbler.start(); - return new ProcessRef(command.getKey(), commands, process, inputGobbler); + return new ProcessRef(command.getProcessId().getKey(), commands, process, inputGobbler); } catch (Exception e) { // just in case ProcessUtils.sendKillSignal(process); - throw new IllegalStateException("Fail to launch " + command.getKey(), e); + throw new IllegalStateException("Fail to launch [" + command.getProcessId().getKey() + "]", e); } } @@ -76,6 +82,7 @@ public class JavaProcessLauncher { commands.addAll(javaCommand.getJavaOptions()); // TODO warning - does it work if temp dir contains a whitespace ? commands.add(String.format("-Djava.io.tmpdir=%s", tempDir.getAbsolutePath())); + commands.add(getJmxAgentCommand()); commands.addAll(buildClasspath(javaCommand)); commands.add(javaCommand.getClassName()); commands.add(buildPropertiesFile(javaCommand).getAbsolutePath()); @@ -88,6 +95,15 @@ public class JavaProcessLauncher { return processBuilder; } + /** + * JVM option to enable the agent that allows inter-process communication through JMX without + * opening new ports. The agent is available in JRE of OpenJDK/OracleJDK only. + * @see ProcessEntryPoint + */ + private static String getJmxAgentCommand() { + return "-javaagent:" + System.getProperty("java.home") + File.separator + "lib" + File.separator + "management-agent.jar"; + } + private String buildJavaPath() { String separator = System.getProperty("file.separator"); return new File(new File(System.getProperty("java.home")), @@ -104,12 +120,12 @@ public class JavaProcessLauncher { propertiesFile = File.createTempFile("sq-process", "properties"); Properties props = new Properties(); props.putAll(javaCommand.getArguments()); - props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_KEY, javaCommand.getKey()); - props.setProperty(ProcessEntryPoint.PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessIndex())); - props.setProperty(ProcessEntryPoint.PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout())); - props.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, tempDir.getAbsolutePath()); + props.setProperty(PROPERTY_PROCESS_KEY, javaCommand.getProcessId().getKey()); + props.setProperty(PROPERTY_PROCESS_INDEX, Integer.toString(javaCommand.getProcessId().getIpcIndex())); + props.setProperty(PROPERTY_TERMINATION_TIMEOUT, String.valueOf(timeouts.getTerminationTimeout())); + props.setProperty(PROPERTY_SHARED_PATH, tempDir.getAbsolutePath()); OutputStream out = new FileOutputStream(propertiesFile); - props.store(out, String.format("Temporary properties file for command [%s]", javaCommand.getKey())); + props.store(out, String.format("Temporary properties file for command [%s]", javaCommand.getProcessId().getKey())); out.close(); return propertiesFile; } catch (Exception e) { diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java index e53c564cdac..c0ddbce5709 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaCommandTest.java @@ -25,6 +25,7 @@ import org.junit.rules.TemporaryFolder; import java.io.File; import java.util.Properties; +import org.sonar.process.ProcessId; import static org.assertj.core.api.Assertions.assertThat; @@ -32,11 +33,10 @@ public class JavaCommandTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - private int processIndex = 0; @Test public void test_parameters() throws Exception { - JavaCommand command = new JavaCommand("es", processIndex++); + JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH); command.setArgument("first_arg", "val1"); Properties args = new Properties(); @@ -64,7 +64,7 @@ public class JavaCommandTest { @Test public void add_java_options() { - JavaCommand command = new JavaCommand("foo", processIndex++); + JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH); assertThat(command.getJavaOptions()).isEmpty(); command.addJavaOptions(""); diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java index 499b22a04bd..df9ae973d17 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/JavaProcessLauncherTest.java @@ -23,6 +23,7 @@ import java.io.File; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.process.ProcessId; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -35,7 +36,7 @@ public class JavaProcessLauncherTest { @Test public void fail_to_launch() throws Exception { File tempDir = temp.newFolder(); - JavaCommand command = new JavaCommand("test", 0); + JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH); JavaProcessLauncher launcher = new JavaProcessLauncher(new Timeouts(), tempDir); try { // command is not correct (missing options), java.lang.ProcessBuilder#start() @@ -43,7 +44,7 @@ public class JavaProcessLauncherTest { launcher.launch(command); fail(); } catch (IllegalStateException e) { - assertThat(e).hasMessage("Fail to launch test"); + assertThat(e).hasMessage("Fail to launch [es]"); } } } diff --git a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java index f278e4978bb..475fe2c0867 100644 --- a/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java +++ b/server/sonar-process-monitor/src/test/java/org/sonar/process/monitor/MonitorTest.java @@ -42,6 +42,7 @@ import org.junit.rules.TestRule; import org.junit.rules.Timeout; import org.sonar.process.Lifecycle.State; import org.sonar.process.NetworkUtils; +import org.sonar.process.ProcessId; import org.sonar.process.SystemExit; import static java.util.Collections.singletonList; @@ -147,7 +148,7 @@ public class MonitorTest { @Test public void start_then_stop_gracefully() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient client = new HttpProcessClient(tempDir, "test"); + HttpProcessClient client = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); // blocks until started underTest.start(singletonList(client.newCommand())); @@ -157,7 +158,7 @@ public class MonitorTest { // blocks until stopped underTest.stop(); assertThat(client) - .isNotReady() + .isNotUp() .wasGracefullyTerminated(); assertThat(underTest.getState()).isEqualTo(State.STOPPED); verify(fileSystem).reset(); @@ -166,8 +167,8 @@ public class MonitorTest { @Test public void start_then_stop_sequence_of_commands() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); - HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2"); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); + HttpProcessClient p2 = new HttpProcessClient(tempDir, ProcessId.WEB_SERVER); underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand())); // start p2 when p1 is fully started (ready) @@ -181,10 +182,10 @@ public class MonitorTest { // stop in inverse order assertThat(p1) - .isNotReady() + .isNotUp() .wasGracefullyTerminated(); assertThat(p2) - .isNotReady() + .isNotUp() .wasGracefullyTerminatedBefore(p1); verify(fileSystem).reset(); } @@ -192,8 +193,8 @@ public class MonitorTest { @Test public void stop_all_processes_if_monitor_shutdowns() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); - HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2"); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); + HttpProcessClient p2 = new HttpProcessClient(tempDir, ProcessId.WEB_SERVER); underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand())); assertThat(p1).isUp(); assertThat(p2).isUp(); @@ -211,8 +212,8 @@ public class MonitorTest { @Test public void restart_all_processes_if_one_asks_for_restart() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); - HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2"); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); + HttpProcessClient p2 = new HttpProcessClient(tempDir, ProcessId.WEB_SERVER); underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand())); assertThat(p1).isUp(); @@ -244,8 +245,8 @@ public class MonitorTest { @Test public void stop_all_processes_if_one_shutdowns() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); - HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2"); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); + HttpProcessClient p2 = new HttpProcessClient(tempDir, ProcessId.WEB_SERVER); underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand())); assertThat(p1.isUp()).isTrue(); assertThat(p2.isUp()).isTrue(); @@ -255,10 +256,10 @@ public class MonitorTest { underTest.awaitTermination(); assertThat(p1) - .isNotReady() + .isNotUp() .wasNotGracefullyTerminated(); assertThat(p2) - .isNotReady() + .isNotUp() .wasGracefullyTerminated(); verify(fileSystem).reset(); @@ -267,8 +268,8 @@ public class MonitorTest { @Test public void stop_all_processes_if_one_fails_to_start() throws Exception { underTest = newDefaultMonitor(tempDir); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); - HttpProcessClient p2 = new HttpProcessClient(tempDir, "p2", -1); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.ELASTICSEARCH); + HttpProcessClient p2 = new HttpProcessClient(tempDir, ProcessId.WEB_SERVER, -1); try { underTest.start(Arrays.asList(p1.newCommand(), p2.newCommand())); fail(); @@ -286,7 +287,7 @@ public class MonitorTest { @Test public void fail_to_start_if_bad_class_name() throws Exception { underTest = newDefaultMonitor(tempDir); - JavaCommand command = new JavaCommand("test", processIndex++) + JavaCommand command = new JavaCommand(ProcessId.ELASTICSEARCH) .addClasspath(testJar.getAbsolutePath()) .setClassName("org.sonar.process.test.Unknown"); @@ -304,7 +305,7 @@ public class MonitorTest { underTest = newDefaultMonitor(tempDir, true); assertThat(underTest.hardStopWatcher).isNull(); - HttpProcessClient p1 = new HttpProcessClient(tempDir, "p1"); + HttpProcessClient p1 = new HttpProcessClient(tempDir, ProcessId.COMPUTE_ENGINE); underTest.start(singletonList(p1.newCommand())); assertThat(underTest.hardStopWatcher).isNotNull(); @@ -330,24 +331,24 @@ public class MonitorTest { */ private class HttpProcessClient { private final int httpPort; - private final String commandKey; + private final ProcessId processId; private final File tempDir; - private HttpProcessClient(File tempDir, String commandKey) throws IOException { - this(tempDir, commandKey, NetworkUtils.freePort()); + private HttpProcessClient(File tempDir, ProcessId processId) throws IOException { + this(tempDir, processId, NetworkUtils.freePort()); } /** * Use httpPort=-1 to make server fail to start */ - private HttpProcessClient(File tempDir, String commandKey, int httpPort) throws IOException { + private HttpProcessClient(File tempDir, ProcessId processId, int httpPort) throws IOException { this.tempDir = tempDir; - this.commandKey = commandKey; + this.processId = processId; this.httpPort = httpPort; } JavaCommand newCommand() { - return new JavaCommand(commandKey, processIndex++) + return new JavaCommand(processId) .addClasspath(testJar.getAbsolutePath()) .setClassName("org.sonar.process.test.HttpProcess") .setArgument("httpPort", String.valueOf(httpPort)); @@ -490,7 +491,7 @@ public class MonitorTest { isNotNull(); if (!actual.wasGracefullyTerminated()) { - failWithMessage("HttpClient %s should have been gracefully terminated", actual.commandKey); + failWithMessage("HttpClient %s should have been gracefully terminated", actual.processId.getKey()); } return this; @@ -500,7 +501,7 @@ public class MonitorTest { isNotNull(); if (actual.wasGracefullyTerminated()) { - failWithMessage("HttpClient %s should not have been gracefully terminated", actual.commandKey); + failWithMessage("HttpClient %s should not have been gracefully terminated", actual.processId.getKey()); } return this; @@ -530,17 +531,17 @@ public class MonitorTest { // check condition if (!actual.isUp()) { - failWithMessage("HttpClient %s should be up", actual.commandKey); + failWithMessage("HttpClient %s should be up", actual.processId.getKey()); } return this; } - public HttpProcessClientAssert isNotReady() { + public HttpProcessClientAssert isNotUp() { isNotNull(); if (actual.isUp()) { - failWithMessage("HttpClient %s should not be ready", actual.commandKey); + failWithMessage("HttpClient %s should not be up", actual.processId.getKey()); } return this; @@ -551,7 +552,7 @@ public class MonitorTest { // check condition if (!actual.wasReady()) { - failWithMessage("HttpClient %s should been ready at least once", actual.commandKey); + failWithMessage("HttpClient %s should been ready at least once", actual.processId.getKey()); } return this; @@ -562,7 +563,7 @@ public class MonitorTest { // check condition if (actual.wasReady()) { - failWithMessage("HttpClient %s should never been ready", actual.commandKey); + failWithMessage("HttpClient %s should never been ready", actual.processId.getKey()); } return this; @@ -570,7 +571,7 @@ public class MonitorTest { } private JavaCommand newStandardProcessCommand() throws IOException { - return new JavaCommand("standard", processIndex++) + return new JavaCommand(ProcessId.ELASTICSEARCH) .addClasspath(testJar.getAbsolutePath()) .setClassName("org.sonar.process.test.StandardProcess"); } diff --git a/server/sonar-process/pom.xml b/server/sonar-process/pom.xml index dad1c57192b..df90101c3c0 100644 --- a/server/sonar-process/pom.xml +++ b/server/sonar-process/pom.xml @@ -93,7 +93,19 @@ ${skipServerTests} - + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + sun.misc.VMSupport + + + + + diff --git a/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java b/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java index a3b0b8364c9..9652aca6795 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java +++ b/server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java @@ -24,8 +24,11 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; +import static java.lang.String.format; +import static org.apache.commons.lang.StringUtils.rightPad; import static org.sonar.process.ProcessCommands.MAX_PROCESSES; /** @@ -60,14 +63,17 @@ import static org.sonar.process.ProcessCommands.MAX_PROCESSES; * *

*/ -public class AllProcessesCommands { +public class AllProcessesCommands implements AutoCloseable { private static final int UP_BYTE_OFFSET = 0; private static final int STOP_BYTE_OFFSET = 1; private static final int RESTART_BYTE_OFFSET = 2; private static final int OPERATIONAL_BYTE_OFFSET = 3; private static final int PING_BYTE_OFFSET = 4; + private static final int JMX_URL_BYTE_OFFSET = PING_BYTE_OFFSET + 8; - private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8; + private static final int JMX_URL_SIZE_IN_BYTES = 500; + + private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8 + JMX_URL_SIZE_IN_BYTES; // With this shared memory we can handle up to MAX_PROCESSES processes private static final int MAX_SHARED_MEMORY = BYTE_LENGTH_FOR_ONE_PROCESS * MAX_PROCESSES; @@ -142,6 +148,19 @@ public class AllProcessesCommands { return readLong(processNumber, PING_BYTE_OFFSET); } + String getJmxUrl(int processNumber) { + byte[] urlBytes = readBytes(processNumber, JMX_URL_BYTE_OFFSET, JMX_URL_SIZE_IN_BYTES); + return new String(urlBytes, StandardCharsets.US_ASCII).trim(); + } + + void setJmxUrl(int processNumber, String jmxUrl) { + byte[] urlBytes = rightPad(jmxUrl, JMX_URL_SIZE_IN_BYTES).getBytes(StandardCharsets.US_ASCII); + if (urlBytes.length > JMX_URL_SIZE_IN_BYTES) { + throw new IllegalArgumentException(format("JMX URL is too long. Max is %d bytes. Got: %s", JMX_URL_SIZE_IN_BYTES, jmxUrl)); + } + writeBytes(processNumber, JMX_URL_BYTE_OFFSET, urlBytes); + } + /** * To be executed by monitor process to ask for child process termination */ @@ -165,13 +184,14 @@ public class AllProcessesCommands { writeByte(processNumber, RESTART_BYTE_OFFSET, EMPTY); } + @Override public void close() { IOUtils.closeQuietly(sharedMemory); } public void checkProcessNumber(int processNumber) { if (processNumber < 0 || processNumber >= MAX_PROCESSES) { - throw new IllegalArgumentException(String.format("Process number %s is not valid", processNumber)); + throw new IllegalArgumentException(format("Process number %s is not valid", processNumber)); } } @@ -185,10 +205,26 @@ public class AllProcessesCommands { mappedByteBuffer.put(offset(processNumber) + offset, value); } + private void writeBytes(int processNumber, int offset, byte[] value) { + int bufferOffset = offset(processNumber) + offset; + for (int i = 0; i < value.length; i++) { + mappedByteBuffer.put(bufferOffset + i, value[i]); + } + } + private byte readByte(int processNumber, int offset) { return mappedByteBuffer.get(offset(processNumber) + offset); } + private byte[] readBytes(int processNumber, int offset, int length) { + int bufferOffset = offset(processNumber) + offset; + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) { + bytes[i] = mappedByteBuffer.get(bufferOffset + i); + } + return bytes; + } + private void writeLong(int processNumber, int offset, long value) { mappedByteBuffer.putLong(offset(processNumber) + offset, value); } @@ -240,6 +276,16 @@ public class AllProcessesCommands { return AllProcessesCommands.this.getLastPing(processNumber); } + @Override + public void setJmxUrl(String s) { + AllProcessesCommands.this.setJmxUrl(processNumber, s); + } + + @Override + public String getJmxUrl() { + return AllProcessesCommands.this.getJmxUrl(processNumber); + } + @Override public void askForStop() { AllProcessesCommands.this.askForStop(processNumber); diff --git a/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java index 04a608fef06..d8c9975a4ba 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java +++ b/server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java @@ -82,6 +82,16 @@ public class DefaultProcessCommands implements ProcessCommands { return delegate.getLastPing(); } + @Override + public void setJmxUrl(String s) { + delegate.setJmxUrl(s); + } + + @Override + public String getJmxUrl() { + return delegate.getJmxUrl(); + } + @Override public void askForStop() { delegate.askForStop(); diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java index 482029b81d8..01ece9a7060 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java +++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java @@ -33,7 +33,7 @@ import java.io.File; */ public interface ProcessCommands extends AutoCloseable { - int MAX_PROCESSES = 50; + int MAX_PROCESSES = 5; boolean isUp(); @@ -55,6 +55,10 @@ public interface ProcessCommands extends AutoCloseable { long getLastPing(); + void setJmxUrl(String s); + + String getJmxUrl(); + /** * To be executed by monitor process to ask for child process termination */ diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java index 10930daf679..98190b78e2c 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java +++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java @@ -21,6 +21,7 @@ package org.sonar.process; import java.io.File; import org.slf4j.LoggerFactory; +import sun.misc.VMSupport; public class ProcessEntryPoint implements Stoppable { @@ -104,6 +105,8 @@ public class ProcessEntryPoint implements Stoppable { Thread.sleep(20L); } + commands.setJmxUrl(guessJmxUrl()); + // notify monitor that process is ready commands.setUp(); @@ -118,6 +121,15 @@ public class ProcessEntryPoint implements Stoppable { } } + private static String guessJmxUrl() { + // this property is set by the agent management-agent.jar enabled by org.sonar.process.monitor.JavaProcessLauncher. + String jmxUrl = VMSupport.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); + if (jmxUrl == null) { + throw new IllegalStateException("Fail to load the JMX URL of JVM " + System.getProperty("java.vm.name")); + } + return jmxUrl; + } + boolean isStarted() { return lifecycle.getState() == Lifecycle.State.STARTED; } diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessId.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessId.java new file mode 100644 index 00000000000..9189ada6cd2 --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessId.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process; + +public enum ProcessId { + + APP("app", 0), ELASTICSEARCH("es", 1), WEB_SERVER("web", 2), COMPUTE_ENGINE("ce", 3); + + private final String key; + private final int ipcIndex; + + ProcessId(String key, int ipcIndex) { + this.key = key; + this.ipcIndex = ipcIndex; + } + + public String getKey() { + return key; + } + + /** + * Index used for inter-process communication + */ + public int getIpcIndex() { + return ipcIndex; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + sb.append("key='").append(key).append('\''); + sb.append(", ipcIndex=").append(ipcIndex); + sb.append(']'); + return sb.toString(); + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/CeDatabaseMBean.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/CeDatabaseMBean.java new file mode 100644 index 00000000000..354ce0db734 --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/CeDatabaseMBean.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +public interface CeDatabaseMBean { + + String OBJECT_NAME = "SonarQube:name=ComputeEngineDatabase"; + + int getPoolActiveConnections(); + + int getPoolMaxActiveConnections(); + + int getPoolIdleConnections(); + + int getPoolMaxIdleConnections(); + + int getPoolMinIdleConnections(); + + int getPoolInitialSize(); + + long getPoolMaxWaitMillis(); + + boolean getPoolRemoveAbandoned(); + + int getPoolRemoveAbandonedTimeoutSeconds(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorMBean.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/CeTasksMBean.java similarity index 85% rename from server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorMBean.java rename to server/sonar-process/src/main/java/org/sonar/process/jmx/CeTasksMBean.java index f1d694ded59..968dabc52b2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorMBean.java +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/CeTasksMBean.java @@ -17,14 +17,11 @@ * 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.computation.monitoring; +package org.sonar.process.jmx; -public interface ComputeEngineQueueMonitorMBean { +public interface CeTasksMBean { - /** - * Count of received batch reports since instance startup - */ - long getReceivedCount(); + String OBJECT_NAME = "SonarQube:name=ComputeEngineTasks"; /** * Count of batch reports waiting for processing since startup, including reports received before instance startup. @@ -47,7 +44,7 @@ public interface ComputeEngineQueueMonitorMBean { long getSuccessCount(); /** - * Time spent processing reports since startup. + * Time spent processing reports since startup, in milliseconds. */ long getProcessingTime(); diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/EsSettingsMBean.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/EsSettingsMBean.java new file mode 100644 index 00000000000..197dc35051c --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/EsSettingsMBean.java @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +/** + * MBean registered in the Elasticsearch process + */ +public interface EsSettingsMBean { + + String OBJECT_NAME = "SonarQube:name=ElasticsearchSettings"; + + /** + * @return the enabled HTTP port, -1 if disabled + */ + int getHttpPort(); + + String getClusterName(); + + String getNodeName(); + +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/Jmx.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/Jmx.java new file mode 100644 index 00000000000..62375a32e83 --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/Jmx.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +import java.lang.management.ManagementFactory; +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.StandardMBean; +import org.slf4j.LoggerFactory; + +/** + * JMX utilities to register MBeans to JMX server + */ +public class Jmx { + + private Jmx() { + // only statics + } + + /** + * Register a MBean to JMX server + */ + public static void register(String name, Object instance) { + try { + Class mbeanInterface = guessMBeanInterface(instance); + ManagementFactory.getPlatformMBeanServer().registerMBean(new StandardMBean(instance, mbeanInterface), new ObjectName(name)); + + } catch (MalformedObjectNameException | NotCompliantMBeanException | InstanceAlreadyExistsException | MBeanRegistrationException e) { + throw new IllegalStateException("Can not register MBean [" + name + "]", e); + } + } + + /** + * MBeans have multiple conventions, including: + * 1. name of interface is suffixed by "MBean" + * 2. name of implementation is the name of the interface without "MBean" + * 3. implementation and interface must be in the same package + * To avoid the last convention, we wrap the mbean within a StandardMBean. That + * requires to find the related interface. + */ + private static Class guessMBeanInterface(Object instance) { + Class mbeanInterface = null; + Class[] interfaces = instance.getClass().getInterfaces(); + for (Class anInterface : interfaces) { + if (anInterface.getName().endsWith("MBean")) { + mbeanInterface = anInterface; + break; + } + } + if (mbeanInterface == null) { + throw new IllegalArgumentException("Can not find the MBean interface of class " + instance.getClass().getName()); + } + return mbeanInterface; + } + + /** + * Unregister a MBean from JMX server. Errors are ignored and logged as warnings. + */ + public static void unregister(String name) { + try { + ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(name)); + } catch (Exception e) { + LoggerFactory.getLogger(Jmx.class).warn("Can not unregister MBean [" + name + "]", e); + } + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java new file mode 100644 index 00000000000..378c135af03 --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java @@ -0,0 +1,97 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.management.ThreadMXBean; +import java.util.SortedMap; +import java.util.TreeMap; +import javax.annotation.CheckForNull; +import javax.management.JMX; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; + +public class JmxConnection implements AutoCloseable { + + private static final long MEGABYTE = 1024L * 1024L; + private final JMXConnector jmxConnector; + + JmxConnection(JMXConnector jmxConnector) { + this.jmxConnector = jmxConnector; + } + + /** + * Get a MBean from a remote JMX server. + * @throws IllegalStateException if a valid + * connection to remote server cannot be created, for instance because the connection to has + * not yet been established (with {@link JMXConnector#connect()}), or it has been closed/broken. + */ + public M getMBean(String mBeanName, Class mBeanInterfaceClass) { + try { + MBeanServerConnection connection = jmxConnector.getMBeanServerConnection(); + if (mBeanName.startsWith("java.lang")) { + return ManagementFactory.newPlatformMXBeanProxy(connection, mBeanName, mBeanInterfaceClass); + } + return JMX.newMBeanProxy(connection, new ObjectName(mBeanName), mBeanInterfaceClass); + } catch (Exception e) { + throw new IllegalStateException("Fail to connect to MBean " + mBeanName, e); + } + } + + public SortedMap getSystemState() { + SortedMap props = new TreeMap<>(); + MemoryMXBean memory = getMBean(ManagementFactory.MEMORY_MXBEAN_NAME, MemoryMXBean.class); + MemoryUsage heap = memory.getHeapMemoryUsage(); + props.put("Heap Committed (MB)", toMegaBytes(heap.getCommitted())); + props.put("Heap Init (MB)", toMegaBytes(heap.getInit())); + props.put("Heap Max (MB)", toMegaBytes(heap.getMax())); + props.put("Heap Used (MB)", toMegaBytes(heap.getUsed())); + MemoryUsage nonHeap = memory.getNonHeapMemoryUsage(); + props.put("Non Heap Committed (MB)", toMegaBytes(nonHeap.getCommitted())); + props.put("Non Heap Init (MB)", toMegaBytes(nonHeap.getInit())); + props.put("Non Heap Max (MB)", toMegaBytes(nonHeap.getMax())); + props.put("Non Heap Used (MB)", toMegaBytes(nonHeap.getUsed())); + ThreadMXBean thread = getMBean(ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class); + props.put("Thread Count", thread.getThreadCount()); + return props; + } + + // visible for testing + @CheckForNull + static Long toMegaBytes(long bytes) { + if (bytes < 0L) { + return null; + } + return bytes / MEGABYTE; + } + + @Override + public void close() { + try { + jmxConnector.close(); + } catch (IOException e) { + throw new IllegalStateException("Can not close JMX connector", e); + } + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnectionFactory.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnectionFactory.java new file mode 100644 index 00000000000..ca906b9540d --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnectionFactory.java @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +import java.io.File; +import javax.annotation.concurrent.Immutable; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import org.sonar.process.DefaultProcessCommands; +import org.sonar.process.ProcessEntryPoint; +import org.sonar.process.ProcessId; +import org.sonar.process.Props; + +/** + * Connects to JMX of other JVM processes + */ +@Immutable +public class JmxConnectionFactory { + private final File ipcSharedDir; + + public JmxConnectionFactory(File ipcSharedDir) { + this.ipcSharedDir = ipcSharedDir; + } + + public JmxConnectionFactory(Props props) { + this.ipcSharedDir = props.nonNullValueAsFile(ProcessEntryPoint.PROPERTY_SHARED_PATH); + } + + public JmxConnection create(ProcessId processId) { + try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(ipcSharedDir, processId.getIpcIndex())) { + String url = commands.getJmxUrl(); + JMXConnector jmxConnector = JMXConnectorFactory.newJMXConnector(new JMXServiceURL(url), null); + jmxConnector.connect(); + return new JmxConnection(jmxConnector); + } catch (Exception e) { + throw new IllegalStateException("Can not connect to process " + processId, e); + } + } +} diff --git a/server/sonar-process/src/main/java/org/sonar/process/jmx/package-info.java b/server/sonar-process/src/main/java/org/sonar/process/jmx/package-info.java new file mode 100644 index 00000000000..bb4fb97207f --- /dev/null +++ b/server/sonar-process/src/main/java/org/sonar/process/jmx/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.process.jmx; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java b/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java index bda42cf07e7..e834fce905f 100644 --- a/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java +++ b/server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java @@ -96,6 +96,20 @@ public class AllProcessesCommandsTest { assertThat(readLong(commands, offset)).isGreaterThanOrEqualTo(currentTime); } + @Test + public void write_and_read_jmx_url() throws IOException { + AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder()); + + int offset = 12; + for (int i = 0; i < 500; i++) { + assertThat(readByte(commands, offset + i)).isEqualTo(EMPTY); + } + + commands.setJmxUrl(PROCESS_NUMBER, "jmx:foo"); + assertThat(readByte(commands, offset)).isNotEqualTo(EMPTY); + assertThat(commands.getJmxUrl(PROCESS_NUMBER)).isEqualTo("jmx:foo"); + } + @Test public void ask_for_stop() throws Exception { AllProcessesCommands commands = new AllProcessesCommands(temp.newFolder()); diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java index 6acf205b52e..f6e2bfd916f 100644 --- a/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java +++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessEntryPointTest.java @@ -54,6 +54,8 @@ public class ProcessEntryPointTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); + ProcessCommands commands = mock(ProcessCommands.class); + @Test public void load_properties_from_file() throws Exception { File propsFile = temp.newFile(); @@ -67,7 +69,7 @@ public class ProcessEntryPointTest { @Test public void test_initial_state() throws Exception { Props props = createProps(); - ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class)); + ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, commands); assertThat(entryPoint.getProps()).isSameAs(props); assertThat(entryPoint.isStarted()).isFalse(); @@ -77,7 +79,7 @@ public class ProcessEntryPointTest { @Test public void fail_to_launch_multiple_times() throws IOException { Props props = createProps(); - ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class)); + ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, commands); entryPoint.launch(new NoopProcess()); try { @@ -91,7 +93,7 @@ public class ProcessEntryPointTest { @Test public void launch_then_request_graceful_stop() throws Exception { Props props = createProps(); - final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class)); + final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, commands); final StandardProcess process = new StandardProcess(); Thread runner = new Thread() { @@ -117,7 +119,7 @@ public class ProcessEntryPointTest { @Test public void terminate_if_unexpected_shutdown() throws Exception { Props props = createProps(); - final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class)); + final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, commands); final StandardProcess process = new StandardProcess(); Thread runner = new Thread() { @@ -147,7 +149,7 @@ public class ProcessEntryPointTest { @Test public void terminate_if_startup_error() throws IOException { Props props = createProps(); - final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, mock(ProcessCommands.class)); + final ProcessEntryPoint entryPoint = new ProcessEntryPoint(props, exit, commands); final Monitored process = new StartupErrorProcess(); entryPoint.launch(process); diff --git a/server/sonar-process/src/test/java/org/sonar/process/ProcessIdTest.java b/server/sonar-process/src/test/java/org/sonar/process/ProcessIdTest.java new file mode 100644 index 00000000000..4eebf2cd124 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/ProcessIdTest.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process; + +import java.util.HashSet; +import java.util.Set; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProcessIdTest { + + @Test + public void test_constants() { + assertThat(ProcessId.COMPUTE_ENGINE.getKey()).isEqualTo("ce"); + assertThat(ProcessId.COMPUTE_ENGINE.getIpcIndex()).isEqualTo(3); + } + + @Test + public void all_values_are_unique() { + Set ipcIndices = new HashSet<>(); + Set keys = new HashSet<>(); + for (ProcessId processId : ProcessId.values()) { + ipcIndices.add(processId.getIpcIndex()); + keys.add(processId.getKey()); + } + assertThat(ipcIndices).hasSize(ProcessId.values().length); + assertThat(keys).hasSize(ProcessId.values().length); + } +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/jmx/Fake.java b/server/sonar-process/src/test/java/org/sonar/process/jmx/Fake.java new file mode 100644 index 00000000000..e7bf9912c78 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/jmx/Fake.java @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +public class Fake implements FakeMBean { + @Override + public void foo() { + + } +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/jmx/FakeMBean.java b/server/sonar-process/src/test/java/org/sonar/process/jmx/FakeMBean.java new file mode 100644 index 00000000000..b5cc660511c --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/jmx/FakeMBean.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +public interface FakeMBean { + void foo(); +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java b/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java new file mode 100644 index 00000000000..c8aa351524d --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JmxConnectionTest { + + @Test + public void toMegaBytes() { + assertThat(JmxConnection.toMegaBytes(-1)).isNull(); + assertThat(JmxConnection.toMegaBytes(0L)).isEqualTo(0L); + assertThat(JmxConnection.toMegaBytes(500L)).isEqualTo(0L); + assertThat(JmxConnection.toMegaBytes(500_000L)).isEqualTo(0L); + assertThat(JmxConnection.toMegaBytes(500_000_000L)).isEqualTo(476L); + } +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTest.java b/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTest.java new file mode 100644 index 00000000000..9b241accac3 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTest.java @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx; + +import java.lang.management.ManagementFactory; +import javax.annotation.CheckForNull; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JmxTest { + + static final String FAKE_NAME = "SonarQube:name=Fake"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + FakeMBean mbean = new Fake(); + + @Test + public void register_and_unregister() throws Exception { + assertThat(lookupMBean()).isNull(); + + Jmx.register(FAKE_NAME, mbean); + assertThat(lookupMBean()).isNotNull(); + + Jmx.unregister(FAKE_NAME); + assertThat(lookupMBean()).isNull(); + } + + @Test + public void do_not_fail_when_unregistering_a_non_registered_bean() throws Exception { + Jmx.unregister(FAKE_NAME); + assertThat(lookupMBean()).isNull(); + } + + @Test + public void register_fails_if_mbean_interface_can_not_be_found() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Can not find the MBean interface of class java.lang.String"); + + Jmx.register(FAKE_NAME, "not a mbean"); + } + + @Test + public void support_implementation_in_different_package_than_interface() throws Exception { + assertThat(lookupMBean()).isNull(); + + Jmx.register(FAKE_NAME, new org.sonar.process.jmx.test.Fake()); + assertThat(lookupMBean()).isNotNull(); + + Jmx.unregister(FAKE_NAME); + assertThat(lookupMBean()).isNull(); + } + + @CheckForNull + private ObjectInstance lookupMBean() throws Exception { + try { + return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(FAKE_NAME)); + } catch (InstanceNotFoundException e) { + return null; + } + } + +} diff --git a/server/sonar-process/src/test/java/org/sonar/process/jmx/test/Fake.java b/server/sonar-process/src/test/java/org/sonar/process/jmx/test/Fake.java new file mode 100644 index 00000000000..d5e15ab0ca9 --- /dev/null +++ b/server/sonar-process/src/test/java/org/sonar/process/jmx/test/Fake.java @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.process.jmx.test; + +import org.sonar.process.jmx.FakeMBean; + +// implementation is in a different package than interface +public class Fake implements FakeMBean { + @Override + public void foo() { + + } +} diff --git a/server/sonar-search/src/main/java/org/sonar/search/SearchSettings.java b/server/sonar-search/src/main/java/org/sonar/search/EsSettings.java similarity index 88% rename from server/sonar-search/src/main/java/org/sonar/search/SearchSettings.java rename to server/sonar-search/src/main/java/org/sonar/search/EsSettings.java index db634f5c587..2f0986e1e45 100644 --- a/server/sonar-search/src/main/java/org/sonar/search/SearchSettings.java +++ b/server/sonar-search/src/main/java/org/sonar/search/EsSettings.java @@ -33,17 +33,18 @@ import org.slf4j.LoggerFactory; import org.sonar.process.MessageException; import org.sonar.process.ProcessProperties; import org.sonar.process.Props; +import org.sonar.process.jmx.EsSettingsMBean; -class SearchSettings { +public class EsSettings implements EsSettingsMBean { - private static final Logger LOGGER = LoggerFactory.getLogger(SearchSettings.class); + private static final Logger LOGGER = LoggerFactory.getLogger(EsSettings.class); public static final String PROP_MARVEL_HOSTS = "sonar.search.marvelHosts"; private final Props props; private final Set masterHosts = new LinkedHashSet<>(); - SearchSettings(Props props) { + EsSettings(Props props) { this.props = props; masterHosts.addAll(Arrays.asList(StringUtils.split(props.value(ProcessProperties.CLUSTER_MASTER_HOST, ""), ","))); } @@ -56,6 +57,21 @@ class SearchSettings { return props.valueAsBoolean(ProcessProperties.CLUSTER_MASTER, false); } + @Override + public int getHttpPort() { + return props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT, -1); + } + + @Override + public String getClusterName() { + return props.value(ProcessProperties.CLUSTER_NAME); + } + + @Override + public String getNodeName() { + return props.value(ProcessProperties.CLUSTER_NODE_NAME); + } + Settings build() { ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder(); configureFileSystem(builder); @@ -115,8 +131,8 @@ class SearchSettings { // Elasticsearch sets the default value of TCP reuse address to true only on non-MSWindows machines, but why ? builder.put("network.tcp.reuse_address", true); - Integer httpPort = props.valueAsInt(ProcessProperties.SEARCH_HTTP_PORT); - if (httpPort == null || httpPort < 0) { + int httpPort = getHttpPort(); + if (httpPort < 0) { // standard configuration builder.put("http.enabled", false); } else { @@ -130,7 +146,7 @@ class SearchSettings { } } - private void configureIndexDefaults(ImmutableSettings.Builder builder) { + private static void configureIndexDefaults(ImmutableSettings.Builder builder) { builder .put("index.number_of_shards", "1") .put("index.refresh_interval", "30s") @@ -156,10 +172,11 @@ class SearchSettings { } } builder.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, replicationFactor); - builder.put("cluster.name", props.value(ProcessProperties.CLUSTER_NAME)); + builder.put("cluster.name", getClusterName()); builder.put("cluster.routing.allocation.awareness.attributes", "rack_id"); - builder.put("node.rack_id", props.value(ProcessProperties.CLUSTER_NODE_NAME, "unknown")); - builder.put("node.name", props.value(ProcessProperties.CLUSTER_NODE_NAME)); + String nodeName = getNodeName(); + builder.put("node.rack_id", nodeName); + builder.put("node.name", nodeName); } private void configureMarvel(ImmutableSettings.Builder builder) { diff --git a/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java b/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java index 0a7939abedf..31541ad259f 100644 --- a/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java +++ b/server/sonar-search/src/main/java/org/sonar/search/SearchServer.java @@ -26,14 +26,16 @@ import org.sonar.process.MinimumViableSystem; import org.sonar.process.Monitored; import org.sonar.process.ProcessEntryPoint; import org.sonar.process.Props; +import org.sonar.process.jmx.EsSettingsMBean; +import org.sonar.process.jmx.Jmx; public class SearchServer implements Monitored { - private final SearchSettings settings; + private final EsSettings settings; private InternalNode node; public SearchServer(Props props) { - this.settings = new SearchSettings(props); + this.settings = new EsSettings(props); new MinimumViableSystem() .checkJavaVersion() .checkWritableTempDir(); @@ -41,6 +43,7 @@ public class SearchServer implements Monitored { @Override public void start() { + Jmx.register(EsSettingsMBean.OBJECT_NAME, settings); node = new InternalNode(settings.build(), false); node.start(); } @@ -70,6 +73,7 @@ public class SearchServer implements Monitored { if (node != null && !node.isClosed()) { node.close(); } + Jmx.unregister(EsSettingsMBean.OBJECT_NAME); } public static void main(String... args) { diff --git a/server/sonar-search/src/test/java/org/sonar/search/SearchSettingsTest.java b/server/sonar-search/src/test/java/org/sonar/search/EsSettingsTest.java similarity index 87% rename from server/sonar-search/src/test/java/org/sonar/search/SearchSettingsTest.java rename to server/sonar-search/src/test/java/org/sonar/search/EsSettingsTest.java index 607eab9d92e..f8f12f25272 100644 --- a/server/sonar-search/src/test/java/org/sonar/search/SearchSettingsTest.java +++ b/server/sonar-search/src/test/java/org/sonar/search/EsSettingsTest.java @@ -34,7 +34,7 @@ import java.util.Properties; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; -public class SearchSettingsTest { +public class EsSettingsTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -49,10 +49,10 @@ public class SearchSettingsTest { props.set(ProcessProperties.CLUSTER_NAME, "tests"); props.set(ProcessProperties.CLUSTER_NODE_NAME, "test"); - SearchSettings searchSettings = new SearchSettings(props); - assertThat(searchSettings.inCluster()).isFalse(); + EsSettings esSettings = new EsSettings(props); + assertThat(esSettings.inCluster()).isFalse(); - Settings generated = searchSettings.build(); + Settings generated = esSettings.build(); assertThat(generated.get("transport.tcp.port")).isEqualTo("1234"); assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1"); assertThat(generated.get("cluster.name")).isEqualTo("tests"); @@ -74,10 +74,10 @@ public class SearchSettingsTest { public void test_default_hosts() throws Exception { Props props = minProps(); - SearchSettings searchSettings = new SearchSettings(props); - assertThat(searchSettings.inCluster()).isFalse(); + EsSettings esSettings = new EsSettings(props); + assertThat(esSettings.inCluster()).isFalse(); - Settings generated = searchSettings.build(); + Settings generated = esSettings.build(); assertThat(generated.get("transport.tcp.port")).isEqualTo("9001"); assertThat(generated.get("transport.host")).isEqualTo("127.0.0.1"); assertThat(generated.get("cluster.name")).isEqualTo("sonarqube"); @@ -94,7 +94,7 @@ public class SearchSettingsTest { props.set(ProcessProperties.PATH_LOGS, logDir.getAbsolutePath()); props.set(ProcessProperties.PATH_TEMP, tempDir.getAbsolutePath()); - Settings settings = new SearchSettings(props).build(); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("path.data")).isEqualTo(new File(dataDir, "es").getAbsolutePath()); assertThat(settings.get("path.logs")).isEqualTo(logDir.getAbsolutePath()); @@ -106,7 +106,7 @@ public class SearchSettingsTest { Props props = minProps(); props.set(ProcessProperties.CLUSTER_ACTIVATE, "true"); props.set(ProcessProperties.CLUSTER_MASTER, "true"); - Settings settings = new SearchSettings(props).build(); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("index.number_of_replicas")).isEqualTo("1"); assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isNull(); @@ -118,7 +118,7 @@ public class SearchSettingsTest { Props props = minProps(); props.set(ProcessProperties.CLUSTER_ACTIVATE, "true"); props.set(ProcessProperties.CLUSTER_MASTER_HOST, "127.0.0.2,127.0.0.3"); - Settings settings = new SearchSettings(props).build(); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("discovery.zen.ping.unicast.hosts")).isEqualTo("127.0.0.2,127.0.0.3"); assertThat(settings.get("node.master")).isEqualTo("false"); @@ -129,7 +129,7 @@ public class SearchSettingsTest { Props props = minProps(); props.set(ProcessProperties.CLUSTER_ACTIVATE, "true"); try { - new SearchSettings(props).build(); + new EsSettings(props).build(); fail(); } catch (MessageException ignored) { // expected @@ -139,8 +139,8 @@ public class SearchSettingsTest { @Test public void enable_marvel() throws Exception { Props props = minProps(); - props.set(SearchSettings.PROP_MARVEL_HOSTS, "127.0.0.2,127.0.0.3"); - Settings settings = new SearchSettings(props).build(); + props.set(EsSettings.PROP_MARVEL_HOSTS, "127.0.0.2,127.0.0.3"); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("marvel.agent.exporter.es.hosts")).isEqualTo("127.0.0.2,127.0.0.3"); } @@ -149,7 +149,7 @@ public class SearchSettingsTest { public void enable_http_connector() throws Exception { Props props = minProps(); props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010"); - Settings settings = new SearchSettings(props).build(); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("http.port")).isEqualTo("9010"); assertThat(settings.get("http.host")).isEqualTo("127.0.0.1"); @@ -161,7 +161,7 @@ public class SearchSettingsTest { Props props = minProps(); props.set(ProcessProperties.SEARCH_HTTP_PORT, "9010"); props.set(ProcessProperties.SEARCH_HOST, "127.0.0.2"); - Settings settings = new SearchSettings(props).build(); + Settings settings = new EsSettings(props).build(); assertThat(settings.get("http.port")).isEqualTo("9010"); assertThat(settings.get("http.host")).isEqualTo("127.0.0.2"); diff --git a/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java b/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java index 5ce6cf13a0b..ebf7e106f4b 100644 --- a/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java +++ b/server/sonar-search/src/test/java/org/sonar/search/SearchServerTest.java @@ -19,6 +19,8 @@ */ package org.sonar.search; +import java.net.InetAddress; +import java.util.Properties; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; import org.elasticsearch.client.Client; import org.elasticsearch.client.transport.NoNodeAvailableException; @@ -34,12 +36,10 @@ import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.rules.Timeout; import org.sonar.process.NetworkUtils; +import org.sonar.process.ProcessEntryPoint; import org.sonar.process.ProcessProperties; import org.sonar.process.Props; -import java.net.InetAddress; -import java.util.Properties; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -78,6 +78,7 @@ public class SearchServerTest { props.set(ProcessProperties.CLUSTER_NAME, CLUSTER_NAME); props.set(ProcessProperties.CLUSTER_NODE_NAME, "test"); props.set(ProcessProperties.PATH_HOME, temp.newFolder().getAbsolutePath()); + props.set(ProcessEntryPoint.PROPERTY_SHARED_PATH, temp.newFolder().getAbsolutePath()); searchServer = new SearchServer(props); searchServer.start(); diff --git a/server/sonar-server/src/main/java/org/sonar/ce/CeModule.java b/server/sonar-server/src/main/java/org/sonar/ce/CeModule.java index f4f05b14fba..1f4bb4b7f35 100644 --- a/server/sonar-server/src/main/java/org/sonar/ce/CeModule.java +++ b/server/sonar-server/src/main/java/org/sonar/ce/CeModule.java @@ -20,7 +20,6 @@ package org.sonar.ce; import org.sonar.ce.log.CeLogging; -import org.sonar.ce.monitoring.DummyCEQueueStatusImpl; import org.sonar.ce.queue.CeQueueImpl; import org.sonar.ce.queue.report.ReportFiles; import org.sonar.ce.queue.report.ReportSubmitter; @@ -32,9 +31,6 @@ public class CeModule extends Module { protected void configureModule() { add(CeLogging.class, - // queue monitoring - DummyCEQueueStatusImpl.class, - // Queue CeQueueImpl.class, ReportSubmitter.class, diff --git a/server/sonar-server/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java b/server/sonar-server/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java index 1ffac1e5ffd..695f50c2d47 100644 --- a/server/sonar-server/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java +++ b/server/sonar-server/src/main/java/org/sonar/ce/monitoring/CEQueueStatus.java @@ -21,48 +21,6 @@ package org.sonar.ce.monitoring; public interface CEQueueStatus { - /** - * Sets the count of reports waiting for processing at startup. This method can be called only once. - * - * @param initialPendingCount the count of reports, must be {@literal >=} 0 - * - * @return the new count of batch reports waiting for processing (which is the same as the argument) - * - * @throws IllegalStateException if this method has already been called or is called after {@link #getPendingCount()} - * @throws IllegalArgumentException if the argument is {@literal <} 0 - */ - long initPendingCount(long initialPendingCount); - - /** - * Adds 1 to the count of received batch reports and 1 to the count of batch reports waiting for processing. - *

- * Calling this method is equivalent to calling {@link #addReceived(long)} with {@code 1} as argument but will - * trigger no parameter check. So, it can be faster. - *

- * - * @return the new count of received batch reports - * - * @see #getReceivedCount() - * @see #getPendingCount() - * - * @throws IllegalStateException if {@link #initPendingCount(long)} has not been called yet - */ - long addReceived(); - - /** - * Adds {@code numberOfReceived} to the count of received batch reports and {@code numberOfReceived} to the count of - * batch reports waiting for processing. - * - * @return the new count of received batch reports - * - * @see #getReceivedCount() - * @see #getPendingCount() - * - * @throws IllegalStateException if {@link #initPendingCount(long)} has not been called yet - * @throws IllegalArgumentException if {@code numberOfReceived} is less or equal to 0 - */ - long addReceived(long numberOfReceived); - /** * Adds 1 to the count of batch reports under processing and removes 1 from the count of batch reports waiting for * processing. @@ -70,9 +28,6 @@ public interface CEQueueStatus { * @return the new count of batch reports under processing * * @see #getInProgressCount() - * @see #getPendingCount() - * - * @throws IllegalStateException if {@link #initPendingCount(long)} has not been called yet */ long addInProgress(); @@ -106,11 +61,6 @@ public interface CEQueueStatus { */ long addError(long processingTime); - /** - * Count of received batch reports since instance startup - */ - long getReceivedCount(); - /** * Count of batch reports waiting for processing since startup, including reports received before instance startup. */ @@ -132,7 +82,7 @@ public interface CEQueueStatus { long getSuccessCount(); /** - * Time spent processing batch reports since startup. + * Time spent processing batch reports since startup, in milliseconds. */ long getProcessingTime(); } diff --git a/server/sonar-server/src/main/java/org/sonar/ce/monitoring/DummyCEQueueStatusImpl.java b/server/sonar-server/src/main/java/org/sonar/ce/monitoring/DummyCEQueueStatusImpl.java deleted file mode 100644 index 9ed4fcc721a..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/ce/monitoring/DummyCEQueueStatusImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.ce.monitoring; - -import java.util.concurrent.atomic.AtomicLong; - -/** - * FIXME fix this dummy CEQueueStatus implementation, probably by removing its use from - */ -public class DummyCEQueueStatusImpl implements CEQueueStatus { - private final AtomicLong received = new AtomicLong(0); - - @Override - public long initPendingCount(long initialPendingCount) { - return notImplemented(); - } - - @Override - public long addReceived() { - return received.incrementAndGet(); - } - - @Override - public long addReceived(long numberOfReceived) { - return received.addAndGet(numberOfReceived); - } - - @Override - public long addInProgress() { - return notImplemented(); - } - - @Override - public long addSuccess(long processingTime) { - return notImplemented(); - } - - @Override - public long addError(long processingTime) { - return notImplemented(); - } - - @Override - public long getReceivedCount() { - return received.get(); - } - - @Override - public long getPendingCount() { - return notImplemented(); - } - - @Override - public long getInProgressCount() { - return notImplemented(); - } - - @Override - public long getErrorCount() { - return notImplemented(); - } - - @Override - public long getSuccessCount() { - return notImplemented(); - } - - @Override - public long getProcessingTime() { - return notImplemented(); - } - - private static long notImplemented() { - throw new UnsupportedOperationException("Not implemented!"); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/ce/queue/CeQueueImpl.java b/server/sonar-server/src/main/java/org/sonar/ce/queue/CeQueueImpl.java index a6aff101fc0..7aa61ad53ad 100644 --- a/server/sonar-server/src/main/java/org/sonar/ce/queue/CeQueueImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/ce/queue/CeQueueImpl.java @@ -37,7 +37,6 @@ import org.sonar.db.DbSession; import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; import org.sonar.db.component.ComponentDto; -import org.sonar.ce.monitoring.CEQueueStatus; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.notNull; @@ -49,7 +48,6 @@ public class CeQueueImpl implements CeQueue { private final DbClient dbClient; private final UuidFactory uuidFactory; - private final CEQueueStatus queueStatus; private final CeQueueListener[] listeners; // state @@ -58,14 +56,13 @@ public class CeQueueImpl implements CeQueue { /** * Constructor in case there is no CeQueueListener */ - public CeQueueImpl(DbClient dbClient, UuidFactory uuidFactory, CEQueueStatus queueStatus) { - this(dbClient, uuidFactory, queueStatus, new CeQueueListener[]{}); + public CeQueueImpl(DbClient dbClient, UuidFactory uuidFactory) { + this(dbClient, uuidFactory, new CeQueueListener[] {}); } - public CeQueueImpl(DbClient dbClient, UuidFactory uuidFactory, CEQueueStatus queueStatus, CeQueueListener[] listeners) { + public CeQueueImpl(DbClient dbClient, UuidFactory uuidFactory, CeQueueListener[] listeners) { this.dbClient = dbClient; this.uuidFactory = uuidFactory; - this.queueStatus = queueStatus; this.listeners = listeners; } @@ -83,7 +80,6 @@ public class CeQueueImpl implements CeQueue { CeQueueDto dto = new CeTaskSubmitToInsertedCeQueueDto(dbSession, dbClient).apply(submission); CeTask task = loadTask(dbSession, dto); dbSession.commit(); - queueStatus.addReceived(); return task; } finally { @@ -105,7 +101,6 @@ public class CeQueueImpl implements CeQueue { .toList(); List tasks = loadTasks(dbSession, ceQueueDtos); dbSession.commit(); - queueStatus.addReceived(tasks.size()); return tasks; } finally { diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java index 3310debda32..bb57d8e563e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapper.java @@ -29,4 +29,5 @@ public interface ProcessCommandWrapper { * Notifies any listening process that the WebServer is operational. */ void notifyOperational(); + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java index 9936a9f7e93..de29b5607e7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/app/ProcessCommandWrapperImpl.java @@ -37,37 +37,41 @@ public class ProcessCommandWrapperImpl implements ProcessCommandWrapper { @Override public void requestSQRestart() { - call(VoidMethod.ASK_FOR_RESTART); + call(VoidMethod.ASK_FOR_RESTART, selfProcessNumber()); } @Override public void notifyOperational() { - call(VoidMethod.SET_OPERATIONAL); + call(VoidMethod.SET_OPERATIONAL, selfProcessNumber()); } - private void call(VoidMethod command) { + private int selfProcessNumber() { + return nonNullAsInt(PROPERTY_PROCESS_INDEX); + } + + private T call(VoidMethod command, int processNumber) { File shareDir = nonNullValueAsFile(PROPERTY_SHARED_PATH); - int processNumber = nonNullAsInt(PROPERTY_PROCESS_INDEX); try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) { - command.callOn(commands); + return command.callOn(commands); } } private enum VoidMethod { SET_OPERATIONAL() { @Override - void callOn(ProcessCommands processCommands) { + T callOn(ProcessCommands processCommands) { processCommands.setOperational(); + return null; } }, ASK_FOR_RESTART() { @Override - void callOn(ProcessCommands processCommands) { + T callOn(ProcessCommands processCommands) { processCommands.askForRestart(); + return null; } }; - - abstract void callOn(ProcessCommands processCommands); + abstract T callOn(ProcessCommands processCommands); } private int nonNullAsInt(String key) { @@ -76,7 +80,7 @@ public class ProcessCommandWrapperImpl implements ProcessCommandWrapper { return Integer.parseInt(s); } - public File nonNullValueAsFile(String key) { + private File nonNullValueAsFile(String key) { String s = settings.getString(key); checkArgument(s != null, "Property %s is not set", key); return new File(s); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java index 25c04f2e25b..4cdf80747d4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java @@ -25,6 +25,7 @@ import org.sonar.db.purge.period.DefaultPeriodCleaner; import org.sonar.server.computation.configuration.CeConfigurationImpl; import org.sonar.server.computation.dbcleaner.IndexPurgeListener; import org.sonar.server.computation.dbcleaner.ProjectCleaner; +import org.sonar.server.computation.monitoring.CeDatabaseMBeanImpl; /** * Globally available components in CE @@ -35,6 +36,7 @@ public class CeModule extends Module { add( CeConfigurationImpl.class, CeLogging.class, + CeDatabaseMBeanImpl.class, DefaultPeriodCleaner.class, ProjectCleaner.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CEQueueStatusImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CEQueueStatusImpl.java index d9e61e25e15..90a5074f6c2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CEQueueStatusImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CEQueueStatusImpl.java @@ -21,86 +21,53 @@ package org.sonar.server.computation.monitoring; import java.util.concurrent.atomic.AtomicLong; import org.sonar.ce.monitoring.CEQueueStatus; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeQueueDto; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; public class CEQueueStatusImpl implements CEQueueStatus { - private static final long PENDING_INITIAL_VALUE = Long.MIN_VALUE; - private final AtomicLong received = new AtomicLong(0); - private final AtomicLong pending = new AtomicLong(PENDING_INITIAL_VALUE); + private final DbClient dbClient; private final AtomicLong inProgress = new AtomicLong(0); private final AtomicLong error = new AtomicLong(0); private final AtomicLong success = new AtomicLong(0); private final AtomicLong processingTime = new AtomicLong(0); - @Override - public long initPendingCount(long initialPendingCount) { - checkArgument(initialPendingCount >= 0, "Initial pending count must be >= 0"); - checkState( - pending.compareAndSet(PENDING_INITIAL_VALUE, initialPendingCount), - "Method initPendingCount must be used before any other method and can not be called twice"); - return initialPendingCount; - } - - @Override - public long addReceived() { - ensurePendingInitialized("addReceived"); - - pending.incrementAndGet(); - return received.incrementAndGet(); - } - - @Override - public long addReceived(long numberOfReceived) { - ensurePendingInitialized("addReceived"); - checkArgument(numberOfReceived > 0, "numberOfReceived must be > 0"); - - pending.addAndGet(numberOfReceived); - return received.addAndGet(numberOfReceived); + public CEQueueStatusImpl(DbClient dbClient) { + this.dbClient = dbClient; } @Override public long addInProgress() { - ensurePendingInitialized("addInProgress"); - - pending.decrementAndGet(); return inProgress.incrementAndGet(); } - private void ensurePendingInitialized(String methodName) { - checkState(pending.get() != PENDING_INITIAL_VALUE, "Method initPendingCount must be used before %s can be called", methodName); - } - @Override - public long addError(long processingTime) { - addProcessingTime(processingTime); + public long addError(long processingTimeInMs) { + addProcessingTime(processingTimeInMs); inProgress.decrementAndGet(); return error.incrementAndGet(); } @Override - public long addSuccess(long processingTime) { - addProcessingTime(processingTime); + public long addSuccess(long processingTimeInMs) { + addProcessingTime(processingTimeInMs); inProgress.decrementAndGet(); return success.incrementAndGet(); } - private void addProcessingTime(long time) { - checkArgument(time >= 0, "Processing time can not be < 0"); - processingTime.addAndGet(time); - } - - @Override - public long getReceivedCount() { - return received.get(); + private void addProcessingTime(long ms) { + checkArgument(ms >= 0, "Processing time can not be < 0"); + processingTime.addAndGet(ms); } @Override public long getPendingCount() { - long currentValue = pending.get(); - return currentValue == PENDING_INITIAL_VALUE ? 0 : currentValue; + try (DbSession dbSession = dbClient.openSession(false)) { + return dbClient.ceQueueDao().countByStatus(dbSession, CeQueueDto.Status.PENDING); + } } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java new file mode 100644 index 00000000000..4b7e43a3fa5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java @@ -0,0 +1,97 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.computation.monitoring; + +import org.apache.commons.dbcp.BasicDataSource; +import org.picocontainer.Startable; +import org.sonar.db.DbClient; +import org.sonar.process.jmx.CeDatabaseMBean; +import org.sonar.process.jmx.Jmx; + +public class CeDatabaseMBeanImpl implements CeDatabaseMBean, Startable { + private final DbClient dbClient; + + public CeDatabaseMBeanImpl(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public void start() { + Jmx.register(OBJECT_NAME, this); + } + + /** + * Unregister, if needed + */ + @Override + public void stop() { + Jmx.unregister(OBJECT_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(); + } + + private BasicDataSource commonsDbcp() { + return (BasicDataSource) dbClient.getDatabase().getDataSource(); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBeanImpl.java similarity index 59% rename from server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java rename to server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBeanImpl.java index 94d2322dc1c..57b08e356de 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBeanImpl.java @@ -19,47 +19,32 @@ */ package org.sonar.server.computation.monitoring; -import java.util.LinkedHashMap; +import org.picocontainer.Startable; import org.sonar.ce.monitoring.CEQueueStatus; +import org.sonar.process.jmx.CeTasksMBean; +import org.sonar.process.jmx.Jmx; import org.sonar.server.computation.configuration.CeConfiguration; -import org.sonar.ce.queue.CeQueue; -import org.sonar.server.platform.monitoring.BaseMonitorMBean; -public class ComputeEngineQueueMonitor extends BaseMonitorMBean implements ComputeEngineQueueMonitorMBean { +public class CeTasksMBeanImpl implements CeTasksMBean, Startable { private final CEQueueStatus queueStatus; private final CeConfiguration ceConfiguration; - public ComputeEngineQueueMonitor(CEQueueStatus queueStatus, - // ReportQueue initializes CEQueueStatus and is therefor a dependency of - // ComputeEngineQueueMonitor. - // Do not remove this parameter, it ensures start order of components - CeQueue ceQueue, - CeConfiguration ceConfiguration) { + public CeTasksMBeanImpl(CEQueueStatus queueStatus, CeConfiguration ceConfiguration) { this.queueStatus = queueStatus; this.ceConfiguration = ceConfiguration; } @Override - public String name() { - return "ComputeEngine"; + public void start() { + Jmx.register(OBJECT_NAME, this); } + /** + * Unregister, if needed + */ @Override - public LinkedHashMap attributes() { - LinkedHashMap attributes = new LinkedHashMap<>(); - attributes.put("Received", getReceivedCount()); - attributes.put("Pending", getPendingCount()); - attributes.put("In progress", getInProgressCount()); - attributes.put("Successfully processed", getSuccessCount()); - attributes.put("Processed with error", getErrorCount()); - attributes.put("Processing time", getProcessingTime()); - attributes.put("Worker count", getWorkerCount()); - return attributes; - } - - @Override - public long getReceivedCount() { - return queueStatus.getReceivedCount(); + public void stop() { + Jmx.unregister(OBJECT_NAME); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueInitializer.java b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueInitializer.java index b6f277f8f8f..eaa67a7fc5d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueInitializer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueInitializer.java @@ -24,7 +24,6 @@ import org.sonar.api.platform.ServerStartHandler; import org.sonar.api.server.ServerSide; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.ce.monitoring.CEQueueStatus; import org.sonar.server.computation.taskprocessor.CeProcessingScheduler; /** @@ -36,14 +35,12 @@ import org.sonar.server.computation.taskprocessor.CeProcessingScheduler; public class CeQueueInitializer implements ServerStartHandler { private final DbClient dbClient; - private final CEQueueStatus queueStatus; private final CeQueueCleaner cleaner; private final CeProcessingScheduler scheduler; private boolean done = false; - public CeQueueInitializer(DbClient dbClient, CEQueueStatus queueStatus, CeQueueCleaner cleaner, CeProcessingScheduler scheduler) { + public CeQueueInitializer(DbClient dbClient, CeQueueCleaner cleaner, CeProcessingScheduler scheduler) { this.dbClient = dbClient; - this.queueStatus = queueStatus; this.cleaner = cleaner; this.scheduler = scheduler; } @@ -59,7 +56,6 @@ public class CeQueueInitializer implements ServerStartHandler { private void initCe() { DbSession dbSession = dbClient.openSession(false); try { - initJmxCounters(dbSession); cleaner.clean(dbSession); scheduler.startScheduling(); @@ -67,8 +63,4 @@ public class CeQueueInitializer implements ServerStartHandler { dbClient.closeSession(dbSession); } } - - private void initJmxCounters(DbSession dbSession) { - queueStatus.initPendingCount(dbClient.ceQueueDao().countAll(dbSession)); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueModule.java index 4be44d6936b..77b676f7dfc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/CeQueueModule.java @@ -19,11 +19,11 @@ */ package org.sonar.server.computation.queue; -import org.sonar.server.computation.queue.report.CleanReportQueueListener; import org.sonar.ce.queue.report.ReportFiles; import org.sonar.core.platform.Module; import org.sonar.server.computation.monitoring.CEQueueStatusImpl; -import org.sonar.server.computation.monitoring.ComputeEngineQueueMonitor; +import org.sonar.server.computation.monitoring.CeTasksMBeanImpl; +import org.sonar.server.computation.queue.report.CleanReportQueueListener; public class CeQueueModule extends Module { @Override @@ -34,7 +34,7 @@ public class CeQueueModule extends Module { // queue monitoring CEQueueStatusImpl.class, - ComputeEngineQueueMonitor.class, + CeTasksMBeanImpl.class, // queue cleaning CeQueueCleaner.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/InternalCeQueueImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/InternalCeQueueImpl.java index d8b10f5c5d3..3e0f988270a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/InternalCeQueueImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/InternalCeQueueImpl.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; +import org.sonar.ce.monitoring.CEQueueStatus; import org.sonar.ce.queue.CeQueueImpl; import org.sonar.ce.queue.CeQueueListener; import org.sonar.ce.queue.CeTask; @@ -33,7 +34,6 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; -import org.sonar.ce.monitoring.CEQueueStatus; import static java.lang.String.format; @@ -49,7 +49,7 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue public InternalCeQueueImpl(System2 system2, DbClient dbClient, UuidFactory uuidFactory, CEQueueStatus queueStatus, CeQueueListener[] listeners) { - super(dbClient, uuidFactory, queueStatus, listeners); + super(dbClient, uuidFactory, listeners); this.system2 = system2; this.dbClient = dbClient; this.queueStatus = queueStatus; @@ -114,12 +114,12 @@ public class InternalCeQueueImpl extends CeQueueImpl implements InternalCeQueue return; } activityDto.setExecutedAt(system2.now()); - long executionTime = activityDto.getExecutedAt() - startedAt; - activityDto.setExecutionTimeMs(executionTime); + long executionTimeInMs = activityDto.getExecutedAt() - startedAt; + activityDto.setExecutionTimeMs(executionTimeInMs); if (status == CeActivityDto.Status.SUCCESS) { - queueStatus.addSuccess(executionTime); + queueStatus.addSuccess(executionTimeInMs); } else { - queueStatus.addError(executionTime); + queueStatus.addError(executionTimeInMs); } } 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 index bbc8ef67e6e..b56d9a9346e 100644 --- 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 @@ -20,17 +20,10 @@ 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; +import org.sonar.process.jmx.Jmx; /** - * Base implementation of {@link org.sonar.server.platform.monitoring.Monitor} + * Base implementation of a {@link org.sonar.server.platform.monitoring.Monitor} * that is exported as a JMX bean */ public abstract class BaseMonitorMBean implements Monitor, Startable { @@ -40,11 +33,7 @@ public abstract class BaseMonitorMBean implements Monitor, Startable { */ @Override public void start() { - try { - ManagementFactory.getPlatformMBeanServer().registerMBean(this, objectName()); - } catch (OperationsException | MBeanRegistrationException e) { - throw new IllegalStateException("Fail to register MBean " + name(), e); - } + Jmx.register(objectName(), this); } /** @@ -52,16 +41,10 @@ public abstract class BaseMonitorMBean implements Monitor, Startable { */ @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); - } + Jmx.unregister(objectName()); } - ObjectName objectName() throws MalformedObjectNameException { - return new ObjectName(String.format("SonarQube:name=%s", name())); + String objectName() { + return "SonarQube:name=" + name(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java new file mode 100644 index 00000000000..1b6b548e379 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.CeDatabaseMBean; +import org.sonar.process.jmx.JmxConnection; +import org.sonar.process.jmx.JmxConnectionFactory; + +public class CeDatabaseMonitor implements Monitor { + + private final JmxConnectionFactory jmxConnectionFactory; + + public CeDatabaseMonitor(JmxConnectionFactory jmxConnectionFactory) { + this.jmxConnectionFactory = jmxConnectionFactory; + } + + @Override + public String name() { + return "Compute Engine Database"; + } + + @Override + public LinkedHashMap attributes() { + try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) { + LinkedHashMap result = new LinkedHashMap<>(); + CeDatabaseMBean mbean = connection.getMBean(CeDatabaseMBean.OBJECT_NAME, CeDatabaseMBean.class); + result.put("Pool Initial Size", mbean.getPoolInitialSize()); + result.put("Pool Active Connections", mbean.getPoolActiveConnections()); + result.put("Pool Idle Connections", mbean.getPoolIdleConnections()); + result.put("Pool Max Active Connections", mbean.getPoolMaxActiveConnections()); + result.put("Pool Max Idle Connections", mbean.getPoolMaxIdleConnections()); + result.put("Pool Min Idle Connections", mbean.getPoolMinIdleConnections()); + result.put("Pool Max Wait (ms)", mbean.getPoolMaxWaitMillis()); + result.put("Pool Remove Abandoned", mbean.getPoolRemoveAbandoned()); + result.put("Pool Remove Abandoned Timeout (sec)", mbean.getPoolRemoveAbandonedTimeoutSeconds()); + return result; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java new file mode 100644 index 00000000000..2c834fe20b7 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.JmxConnection; +import org.sonar.process.jmx.JmxConnectionFactory; + +public class CeStateMonitor implements Monitor { + + private final JmxConnectionFactory jmxConnectionFactory; + + public CeStateMonitor(JmxConnectionFactory jmxConnectionFactory) { + this.jmxConnectionFactory = jmxConnectionFactory; + } + + @Override + public String name() { + return "Compute Engine State"; + } + + @Override + public LinkedHashMap attributes() { + try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) { + return new LinkedHashMap<>(connection.getSystemState()); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java new file mode 100644 index 00000000000..77c1af84c53 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.CeTasksMBean; +import org.sonar.process.jmx.JmxConnection; +import org.sonar.process.jmx.JmxConnectionFactory; + +public class CeTasksMonitor implements Monitor { + + private final JmxConnectionFactory jmxConnectionFactory; + + public CeTasksMonitor(JmxConnectionFactory jmxConnectionFactory) { + this.jmxConnectionFactory = jmxConnectionFactory; + } + + @Override + public String name() { + return "Compute Engine Tasks"; + } + + @Override + public LinkedHashMap attributes() { + try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) { + LinkedHashMap result = new LinkedHashMap<>(); + CeTasksMBean ceMBean = connection.getMBean(CeTasksMBean.OBJECT_NAME, CeTasksMBean.class); + result.put("Pending", ceMBean.getPendingCount()); + result.put("In Progress", ceMBean.getInProgressCount()); + result.put("Processed With Success", ceMBean.getSuccessCount()); + result.put("Processed With Error", ceMBean.getErrorCount()); + result.put("Processing Time (ms)", ceMBean.getProcessingTime()); + result.put("Worker Count", ceMBean.getWorkerCount()); + return result; + } + } +} 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 index cdadf46aa1a..ceb0caf6ff0 100644 --- 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 @@ -19,18 +19,17 @@ */ package org.sonar.server.platform.monitoring; -import org.apache.commons.dbcp.BasicDataSource; -import org.apache.commons.dbutils.DbUtils; -import org.sonar.db.version.DatabaseVersion; -import org.sonar.db.DbSession; -import org.sonar.db.MyBatis; -import org.sonar.db.DbClient; - import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.Map; +import org.apache.commons.dbcp.BasicDataSource; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.MyBatis; +import org.sonar.db.version.DatabaseVersion; /** * Information about database and connection pool diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java index 470676bdd76..0317ba2c1b0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java @@ -27,22 +27,29 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse; import org.elasticsearch.action.admin.indices.stats.IndexStats; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; +import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.breaker.CircuitBreaker; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.EsSettingsMBean; +import org.sonar.process.jmx.JmxConnection; +import org.sonar.process.jmx.JmxConnectionFactory; import org.sonar.server.es.EsClient; import static org.apache.commons.io.FileUtils.byteCountToDisplaySize; public class EsMonitor extends BaseMonitorMBean implements EsMonitorMBean { + private final JmxConnectionFactory jmxConnectionFactory; private final EsClient esClient; - public EsMonitor(EsClient esClient) { + public EsMonitor(JmxConnectionFactory jmxConnectionFactory, EsClient esClient) { + this.jmxConnectionFactory = jmxConnectionFactory; this.esClient = esClient; } @Override public String name() { - return "ElasticSearch"; + return "Elasticsearch"; } /** @@ -66,6 +73,13 @@ public class EsMonitor extends BaseMonitorMBean implements EsMonitorMBean { @Override public LinkedHashMap attributes() { LinkedHashMap attributes = new LinkedHashMap<>(); + + try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.ELASTICSEARCH)) { + EsSettingsMBean mbean = connection.getMBean(EsSettingsMBean.OBJECT_NAME, EsSettingsMBean.class); + attributes.put("Cluster Name", mbean.getClusterName()); + attributes.put("Node Name", mbean.getNodeName()); + attributes.put("HTTP Port", mbean.getHttpPort()); + } attributes.put("State", getStateAsEnum()); attributes.put("Indices", indexAttributes()); attributes.put("Number of Nodes", getNumberOfNodes()); @@ -96,10 +110,11 @@ public class EsMonitor extends BaseMonitorMBean implements EsMonitorMBean { for (Map.Entry entry : nodesStats.getNodesMap().entrySet()) { LinkedHashMap nodeAttributes = new LinkedHashMap<>(); - nodes.put(entry.getKey(), nodeAttributes); NodeStats stats = entry.getValue(); - nodeAttributes.put("Address", stats.getNode().getAddress().toString()); - nodeAttributes.put("Type", stats.getNode().isMasterNode() ? "Master" : "Slave"); + DiscoveryNode node = stats.getNode(); + nodes.put(node.getName(), nodeAttributes); + nodeAttributes.put("Address", node.getAddress().toString()); + nodeAttributes.put("Type", node.isMasterNode() ? "Master" : "Slave"); nodeAttributes.put("Disk Available", byteCountToDisplaySize(stats.getFs().getTotal().getAvailable().bytes())); nodeAttributes.put("Store Size", byteCountToDisplaySize(stats.getIndices().getStore().getSizeInBytes())); nodeAttributes.put("Open Files", stats.getProcess().getOpenFileDescriptors()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStateMonitor.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStateMonitor.java new file mode 100644 index 00000000000..0f8d60c4b07 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStateMonitor.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.JmxConnection; +import org.sonar.process.jmx.JmxConnectionFactory; + +public class EsStateMonitor implements Monitor { + + private final JmxConnectionFactory jmxConnectionFactory; + + public EsStateMonitor(JmxConnectionFactory jmxConnectionFactory) { + this.jmxConnectionFactory = jmxConnectionFactory; + } + + @Override + public String name() { + return "Elasticsearch State"; + } + + @Override + public LinkedHashMap attributes() { + try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.ELASTICSEARCH)) { + return new LinkedHashMap<>(connection.getSystemState()); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectorProvider.java b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectorProvider.java new file mode 100644 index 00000000000..2bc9d247607 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectorProvider.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.io.File; +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.config.Settings; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH; + +public class JmxConnectorProvider extends ProviderAdapter { + + private JmxConnectionFactory singleton = null; + + public synchronized JmxConnectionFactory provide(Settings settings) { + if (singleton == null) { + singleton = new JmxConnectionFactory(nonNullValueAsFile(settings, PROPERTY_SHARED_PATH)); + } + return singleton; + } + + private static File nonNullValueAsFile(Settings settings, String key) { + String s = settings.getString(key); + checkArgument(s != null, "Property %s is not set", key); + return new File(s); + } +} 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/JvmPropsMonitor.java similarity index 96% rename from server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitor.java rename to server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropsMonitor.java index 0a0a8f8cb9c..3d4b33e292b 100644 --- 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/JvmPropsMonitor.java @@ -20,13 +20,12 @@ package org.sonar.server.platform.monitoring; import com.google.common.collect.Maps; - import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.SortedMap; -public class JvmPropertiesMonitor implements Monitor { +public class JvmPropsMonitor implements Monitor { @Override public String name() { return "JvmProperties"; diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 84e8156aa3c..7a39ad8281a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -158,9 +158,14 @@ import org.sonar.server.permission.ws.PermissionsWsModule; import org.sonar.server.platform.BackendCleanup; import org.sonar.server.platform.ServerLogging; import org.sonar.server.platform.SettingsChangeNotifier; +import org.sonar.server.platform.monitoring.CeDatabaseMonitor; +import org.sonar.server.platform.monitoring.CeStateMonitor; +import org.sonar.server.platform.monitoring.CeTasksMonitor; import org.sonar.server.platform.monitoring.DatabaseMonitor; import org.sonar.server.platform.monitoring.EsMonitor; -import org.sonar.server.platform.monitoring.JvmPropertiesMonitor; +import org.sonar.server.platform.monitoring.EsStateMonitor; +import org.sonar.server.platform.monitoring.JmxConnectorProvider; +import org.sonar.server.platform.monitoring.JvmPropsMonitor; import org.sonar.server.platform.monitoring.PluginsMonitor; import org.sonar.server.platform.monitoring.SonarQubeMonitor; import org.sonar.server.platform.monitoring.SystemMonitor; @@ -658,6 +663,7 @@ public class PlatformLevel4 extends PlatformLevel { TypeValidationModule.class, // System + new JmxConnectorProvider(), ServerLogging.class, RestartAction.class, InfoAction.class, @@ -668,8 +674,12 @@ public class PlatformLevel4 extends PlatformLevel { SonarQubeMonitor.class, EsMonitor.class, PluginsMonitor.class, - JvmPropertiesMonitor.class, + JvmPropsMonitor.class, DatabaseMonitor.class, + EsStateMonitor.class, + CeStateMonitor.class, + CeTasksMonitor.class, + CeDatabaseMonitor.class, MigrateDbAction.class, LogsAction.class, ChangeLogLevelAction.class, diff --git a/server/sonar-server/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java b/server/sonar-server/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java index edaaf9cd3f8..63c39f6d995 100644 --- a/server/sonar-server/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java +++ b/server/sonar-server/src/test/java/org/sonar/ce/monitoring/CEQueueStatusImplConcurrentTest.java @@ -28,9 +28,11 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Test; +import org.sonar.db.DbClient; import org.sonar.server.computation.monitoring.CEQueueStatusImpl; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; public class CEQueueStatusImplConcurrentTest { private ExecutorService executorService = Executors.newFixedThreadPool(10, new ThreadFactory() { @@ -41,7 +43,7 @@ public class CEQueueStatusImplConcurrentTest { return new Thread(r, CEQueueStatusImplConcurrentTest.class.getSimpleName() + cnt++); } }); - private CEQueueStatusImpl underTest = new CEQueueStatusImpl(); + private CEQueueStatusImpl underTest = new CEQueueStatusImpl(mock(DbClient.class)); @After public void tearDown() throws Exception { @@ -50,17 +52,12 @@ public class CEQueueStatusImplConcurrentTest { @Test public void test_concurrent_modifications_in_any_order() throws InterruptedException { - long initialPendingCount = 9963L; - underTest.initPendingCount(initialPendingCount); - for (Runnable runnable : buildShuffleCallsToUnderTest()) { executorService.submit(runnable); } executorService.awaitTermination(1, TimeUnit.SECONDS); - assertThat(underTest.getReceivedCount()).isEqualTo(100); - assertThat(underTest.getPendingCount()).isEqualTo(initialPendingCount + 2); assertThat(underTest.getInProgressCount()).isEqualTo(1); assertThat(underTest.getErrorCount()).isEqualTo(17); assertThat(underTest.getSuccessCount()).isEqualTo(80); @@ -69,9 +66,6 @@ public class CEQueueStatusImplConcurrentTest { private List buildShuffleCallsToUnderTest() { List res = new ArrayList<>(); - for (int i = 0; i < 100; i++) { - res.add(new AddReceivedRunnable()); - } for (int i = 0; i < 98; i++) { res.add(new AddInProgressRunnable()); } @@ -85,13 +79,6 @@ public class CEQueueStatusImplConcurrentTest { return res; } - private class AddReceivedRunnable implements Runnable { - @Override - public void run() { - underTest.addReceived(); - } - } - private class AddInProgressRunnable implements Runnable { @Override public void run() { diff --git a/server/sonar-server/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java b/server/sonar-server/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java index d18f19edac8..e069e6c296b 100644 --- a/server/sonar-server/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/ce/queue/CeQueueImplTest.java @@ -22,14 +22,11 @@ package org.sonar.ce.queue; import com.google.common.base.Optional; import java.util.List; import javax.annotation.Nullable; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.System2; import org.sonar.api.utils.internal.TestSystem2; -import org.sonar.ce.monitoring.CEQueueStatus; -import org.sonar.server.computation.monitoring.CEQueueStatusImpl; import org.sonar.core.util.UuidFactory; import org.sonar.core.util.UuidFactoryImpl; import org.sonar.db.DbSession; @@ -45,7 +42,6 @@ import static org.hamcrest.Matchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; public class CeQueueImplTest { @@ -59,14 +55,8 @@ public class CeQueueImplTest { DbSession session = dbTester.getSession(); UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE; - CEQueueStatus queueStatus = new CEQueueStatusImpl(); CeQueueListener listener = mock(CeQueueListener.class); - CeQueue underTest = new CeQueueImpl(dbTester.getDbClient(), uuidFactory, queueStatus, new CeQueueListener[] {listener}); - - @Before - public void setUp() throws Exception { - queueStatus.initPendingCount(0); - } + CeQueue underTest = new CeQueueImpl(dbTester.getDbClient(), uuidFactory, new CeQueueListener[] {listener}); @Test public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() { @@ -77,17 +67,6 @@ public class CeQueueImplTest { verifyCeQueueDtoForTaskSubmit(taskSubmit); } - @Test - public void submit_increments_receivedCount_of_QueueStatus() { - underTest.submit(createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob")); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(1L); - - underTest.submit(createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_2", "rob")); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(2L); - } - @Test public void submit_populates_component_name_and_key_of_CeTask_if_component_exists() { ComponentDto componentDto = insertComponent(newComponentDto("PROJECT_1")); @@ -144,18 +123,6 @@ public class CeQueueImplTest { verifyCeTask(taskSubmit2, tasks.get(1), null); } - @Test - public void massSubmit_increments_receivedCount_of_QueueStatus() { - underTest.massSubmit(asList(createTaskSubmit("type 1"), createTaskSubmit("type 2"))); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(2L); - - underTest.massSubmit(asList(createTaskSubmit("a"), createTaskSubmit("a"), createTaskSubmit("b"))); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(5L); - } - - @Test public void cancel_pending() throws Exception { CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); @@ -259,12 +226,6 @@ public class CeQueueImplTest { return submission.build(); } - private CeTaskResult newTaskResult(Long snapshotId) { - CeTaskResult taskResult = mock(CeTaskResult.class); - when(taskResult.getSnapshotId()).thenReturn(snapshotId); - return taskResult; - } - private ComponentDto insertComponent(ComponentDto componentDto) { dbTester.getDbClient().componentDao().insert(session, componentDto); session.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java index 8a394785681..e819ec37398 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/app/ProcessCommandWrapperImplTest.java @@ -43,22 +43,22 @@ public class ProcessCommandWrapperImplTest { private Settings settings = new Settings(); @Test - public void requestSQRestart_throws_IAE_if_process_sharedDir_property_not_set() throws Exception { + public void requestSQRestart_throws_IAE_if_process_index_property_not_set() throws Exception { ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Property process.sharedDir is not set"); + expectedException.expectMessage("Property process.index is not set"); processCommandWrapper.requestSQRestart(); } @Test - public void requestSQRestart_throws_IAE_if_process_index_property_not_set() throws Exception { - settings.setProperty(PROPERTY_SHARED_PATH, temp.newFolder().getAbsolutePath()); + public void requestSQRestart_throws_IAE_if_process_shared_path_property_not_set() throws Exception { + settings.setProperty(PROPERTY_PROCESS_INDEX, 1); ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Property process.index is not set"); + expectedException.expectMessage("Property process.sharedDir is not set"); processCommandWrapper.requestSQRestart(); } @@ -79,6 +79,7 @@ public class ProcessCommandWrapperImplTest { @Test public void notifyOperational_throws_IAE_if_process_sharedDir_property_not_set() throws Exception { + settings.setProperty(PROPERTY_PROCESS_INDEX, 1); ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings); expectedException.expect(IllegalArgumentException.class); @@ -89,7 +90,6 @@ public class ProcessCommandWrapperImplTest { @Test public void notifyOperational_throws_IAE_if_process_index_property_not_set() throws Exception { - settings.setProperty(PROPERTY_SHARED_PATH, temp.newFolder().getAbsolutePath()); ProcessCommandWrapperImpl processCommandWrapper = new ProcessCommandWrapperImpl(settings); expectedException.expect(IllegalArgumentException.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CEQueueStatusImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CEQueueStatusImplTest.java index b1d82c150b4..733383fc65c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CEQueueStatusImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CEQueueStatusImplTest.java @@ -23,23 +23,29 @@ import java.util.Random; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.Mockito; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.ce.CeQueueDto; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class CEQueueStatusImplTest { private static final int SOME_RANDOM_MAX = 96535; private static final int SOME_PROCESSING_TIME = 8723; - private static final long INITIAL_PENDING_COUNT = 996L; @Rule public ExpectedException expectedException = ExpectedException.none(); - private CEQueueStatusImpl underTest = new CEQueueStatusImpl(); + DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS); + private CEQueueStatusImpl underTest = new CEQueueStatusImpl(dbClient); @Test public void verify_just_created_instance_metrics() { - assertThat(underTest.getReceivedCount()).isEqualTo(0); - assertThat(underTest.getPendingCount()).isEqualTo(0); assertThat(underTest.getInProgressCount()).isEqualTo(0); assertThat(underTest.getErrorCount()).isEqualTo(0); assertThat(underTest.getSuccessCount()).isEqualTo(0); @@ -47,117 +53,9 @@ public class CEQueueStatusImplTest { } @Test - public void initPendingCount_sets_value_of_pendingCount() { - underTest.initPendingCount(10); - - assertThat(underTest.getPendingCount()).isEqualTo(10); - } - - @Test - public void initPendingCount_throws_ISE_if_called_twice() { - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage("Method initPendingCount must be used before any other method and can not be called twice"); - - underTest.initPendingCount(10); - underTest.initPendingCount(10); - } - - @Test - public void addReceived_throws_ISE_if_called_before_initPendingCount() { - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage("Method initPendingCount must be used before addReceived can be called"); - - underTest.addReceived(); - } - - @Test - public void addReceived_sets_received_to_1_and_pending_counts_to_initial_value_plus_1() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - underTest.addReceived(); - - assertThat(underTest.getReceivedCount()).isEqualTo(1); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT + 1); - } - - @Test - public void addReceived_any_number_of_call_adds_1_per_call() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - int calls = new Random().nextInt(SOME_RANDOM_MAX); - for (int i = 0; i < calls; i++) { - underTest.addReceived(); - } - - assertThat(underTest.getReceivedCount()).isEqualTo(calls); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT + calls); - } - - @Test - public void addReceived_with_argument_throws_IAE_if_parameter_is_0() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("numberOfReceived must be > 0"); - - underTest.addReceived(0); - } - - @Test - public void addReceived_with_argument_throws_IAE_if_parameter_less_than_0() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("numberOfReceived must be > 0"); - - underTest.addReceived(-1 * (new Random().nextInt(SOME_RANDOM_MAX))); - } - - @Test - public void addReceived_with_argument_sets_received_to_value_and_pending_counts_to_initial_value_plus_value() { - long value = 123; - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - underTest.addReceived(value); - - assertThat(underTest.getReceivedCount()).isEqualTo(value); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT + value); - } - - @Test - public void addReceived_with_argument_any_number_of_call_adds_some_number_per_call() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - - Random random = new Random(); - int calls = random.nextInt(SOME_RANDOM_MAX); - long added = 0; - for (int i = 0; i < calls; i++) { - // adding 1 to random number because value can not be 0 - long numberOfReceived = random.nextInt(SOME_RANDOM_MAX) + 1; - underTest.addReceived(numberOfReceived); - added += numberOfReceived; - } - - assertThat(underTest.getReceivedCount()).isEqualTo(added); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT + added); - } - - @Test - public void addInProgress_throws_ISE_if_called_before_initPendingCount() { - expectedException.expect(IllegalStateException.class); - expectedException.expectMessage("Method initPendingCount must be used before addInProgress can be called"); - + public void addInProgress_increases_InProgress() { underTest.addInProgress(); - } - - @Test - public void addInProgress_increases_InProgress_and_decreases_Pending_by_1_without_check_on_Pending() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - underTest.addInProgress(); - - assertThat(underTest.getReceivedCount()).isEqualTo(0); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT - 1); assertThat(underTest.getInProgressCount()).isEqualTo(1); assertThat(underTest.getErrorCount()).isEqualTo(0); assertThat(underTest.getSuccessCount()).isEqualTo(0); @@ -166,15 +64,12 @@ public class CEQueueStatusImplTest { @Test public void addInProgress_any_number_of_call_change_by_1_per_call() { - underTest.initPendingCount(INITIAL_PENDING_COUNT); - int calls = new Random().nextInt(SOME_RANDOM_MAX); for (int i = 0; i < calls; i++) { underTest.addInProgress(); } assertThat(underTest.getInProgressCount()).isEqualTo(calls); - assertThat(underTest.getPendingCount()).isEqualTo(INITIAL_PENDING_COUNT - calls); assertThat(underTest.getProcessingTime()).isEqualTo(0); } @@ -190,8 +85,6 @@ public class CEQueueStatusImplTest { public void addError_increases_Error_and_decreases_InProgress_by_1_without_check_on_InProgress() { underTest.addError(SOME_PROCESSING_TIME); - assertThat(underTest.getReceivedCount()).isEqualTo(0); - assertThat(underTest.getPendingCount()).isEqualTo(0); assertThat(underTest.getInProgressCount()).isEqualTo(-1); assertThat(underTest.getErrorCount()).isEqualTo(1); assertThat(underTest.getSuccessCount()).isEqualTo(0); @@ -222,8 +115,6 @@ public class CEQueueStatusImplTest { public void addSuccess_increases_Error_and_decreases_InProgress_by_1_without_check_on_InProgress() { underTest.addSuccess(SOME_PROCESSING_TIME); - assertThat(underTest.getReceivedCount()).isEqualTo(0); - assertThat(underTest.getPendingCount()).isEqualTo(0); assertThat(underTest.getInProgressCount()).isEqualTo(-1); assertThat(underTest.getErrorCount()).isEqualTo(0); assertThat(underTest.getSuccessCount()).isEqualTo(1); @@ -241,4 +132,11 @@ public class CEQueueStatusImplTest { assertThat(underTest.getInProgressCount()).isEqualTo(-calls); assertThat(underTest.getProcessingTime()).isEqualTo(calls); } + + @Test + public void count_Pending_from_database() { + when(dbClient.ceQueueDao().countByStatus(any(DbSession.class), eq(CeQueueDto.Status.PENDING))).thenReturn(42); + + assertThat(underTest.getPendingCount()).isEqualTo(42); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeTasksMBeanImplTest.java similarity index 70% rename from server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java rename to server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeTasksMBeanImplTest.java index bca30441ecc..9b5bc7becc8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/ComputeEngineQueueMonitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeTasksMBeanImplTest.java @@ -19,17 +19,19 @@ */ package org.sonar.server.computation.monitoring; +import java.lang.management.ManagementFactory; +import javax.annotation.CheckForNull; +import javax.management.InstanceNotFoundException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; import org.junit.Test; import org.sonar.ce.monitoring.CEQueueStatus; +import org.sonar.process.jmx.CeTasksMBean; import org.sonar.server.computation.configuration.CeConfiguration; -import org.sonar.ce.queue.CeQueueImpl; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; -public class ComputeEngineQueueMonitorTest { - private static final long RECEIVED_COUNT = 30; +public class CeTasksMBeanImplTest { private static final long PENDING_COUNT = 2; private static final long IN_PROGRESS_COUNT = 5; private static final long ERROR_COUNT = 10; @@ -37,28 +39,21 @@ public class ComputeEngineQueueMonitorTest { private static final long PROCESSING_TIME = 987; private static final int WORKER_COUNT = 56; - private ComputeEngineQueueMonitor underTest = new ComputeEngineQueueMonitor(new DumbCEQueueStatus(), mock(CeQueueImpl.class), new DumbCeConfiguration()); + private CeTasksMBeanImpl underTest = new CeTasksMBeanImpl(new DumbCEQueueStatus(), new DumbCeConfiguration()); @Test - public void name_is_ComputeEngine() { - assertThat(underTest.name()).isEqualTo("ComputeEngine"); - } + public void register_and_unregister() throws Exception { + assertThat(getMBean()).isNull(); - @Test - public void attributes_has_entry_for_each_get_method() { - assertThat(underTest.attributes()).containsOnly( - entry("Received", RECEIVED_COUNT), - entry("Pending", PENDING_COUNT), - entry("In progress", IN_PROGRESS_COUNT), - entry("Successfully processed", SUCCESS_COUNT), - entry("Processed with error", ERROR_COUNT), - entry("Processing time", PROCESSING_TIME), - entry("Worker count", WORKER_COUNT)); + underTest.start(); + assertThat(getMBean()).isNotNull(); + + underTest.stop(); + assertThat(getMBean()).isNull(); } @Test public void get_methods_delegate_to_the_CEQueueStatus_instance() { - assertThat(underTest.getReceivedCount()).isEqualTo(RECEIVED_COUNT); assertThat(underTest.getPendingCount()).isEqualTo(PENDING_COUNT); assertThat(underTest.getInProgressCount()).isEqualTo(IN_PROGRESS_COUNT); assertThat(underTest.getErrorCount()).isEqualTo(ERROR_COUNT); @@ -77,26 +72,6 @@ public class ComputeEngineQueueMonitorTest { */ private static class DumbCEQueueStatus implements CEQueueStatus { - @Override - public long addReceived() { - return methodNotImplemented(); - } - - @Override - public long addReceived(long numberOfReceived) { - return methodNotImplemented(); - } - - @Override - public long getReceivedCount() { - return RECEIVED_COUNT; - } - - @Override - public long initPendingCount(long initialPendingCount) { - return methodNotImplemented(); - } - @Override public long getPendingCount() { return PENDING_COUNT; @@ -153,4 +128,13 @@ public class ComputeEngineQueueMonitorTest { throw new UnsupportedOperationException("getQueuePollingDelay is not implemented"); } } + + @CheckForNull + private ObjectInstance getMBean() throws Exception { + try { + return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(CeTasksMBean.OBJECT_NAME)); + } catch (InstanceNotFoundException e) { + return null; + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/CeQueueInitializerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/CeQueueInitializerTest.java index 41f42da6364..473ebc092ed 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/CeQueueInitializerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/CeQueueInitializerTest.java @@ -19,64 +19,31 @@ */ package org.sonar.server.computation.queue; -import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; import org.mockito.InOrder; import org.mockito.Mockito; import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; import org.sonar.db.DbSession; import org.sonar.db.DbTester; -import org.sonar.db.ce.CeQueueDto; -import org.sonar.db.ce.CeTaskTypes; -import org.sonar.ce.monitoring.CEQueueStatus; -import org.sonar.server.computation.monitoring.CEQueueStatusImpl; -import org.sonar.ce.queue.report.ReportFiles; import org.sonar.server.computation.taskprocessor.CeProcessingScheduler; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; public class CeQueueInitializerTest { @Rule public DbTester dbTester = DbTester.create(System2.INSTANCE); - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); - Server server = mock(Server.class); - ReportFiles reportFiles = mock(ReportFiles.class, Mockito.RETURNS_DEEP_STUBS); - CEQueueStatus queueStatus = new CEQueueStatusImpl(); CeQueueCleaner cleaner = mock(CeQueueCleaner.class); CeProcessingScheduler scheduler = mock(CeProcessingScheduler.class); - CeQueueInitializer underTest = new CeQueueInitializer(dbTester.getDbClient(), queueStatus, cleaner, scheduler); - - @Test - public void init_jmx_counters() throws IOException { - insertInQueue("TASK_1", CeQueueDto.Status.PENDING); - insertInQueue("TASK_2", CeQueueDto.Status.PENDING); - // this in-progress task is going to be moved to PENDING - insertInQueue("TASK_3", CeQueueDto.Status.IN_PROGRESS); - - underTest.onServerStart(server); - - assertThat(queueStatus.getPendingCount()).isEqualTo(3); - } - - @Test - public void init_jmx_counters_when_queue_is_empty() { - underTest.onServerStart(server); - - assertThat(queueStatus.getPendingCount()).isEqualTo(0); - } + CeQueueInitializer underTest = new CeQueueInitializer(dbTester.getDbClient(), cleaner, scheduler); @Test public void clean_queue_then_start_scheduler_of_workers() throws IOException { @@ -100,25 +67,4 @@ public class CeQueueInitializerTest { verifyZeroInteractions(cleaner, scheduler); } - - private void insertInQueue(String taskUuid, CeQueueDto.Status status) throws IOException { - insertInQueue(taskUuid, status, true); - } - - private CeQueueDto insertInQueue(String taskUuid, CeQueueDto.Status status, boolean createFile) throws IOException { - CeQueueDto queueDto = new CeQueueDto(); - queueDto.setTaskType(CeTaskTypes.REPORT); - queueDto.setComponentUuid("PROJECT_1"); - queueDto.setUuid(taskUuid); - queueDto.setStatus(status); - dbTester.getDbClient().ceQueueDao().insert(dbTester.getSession(), queueDto); - dbTester.getSession().commit(); - - File file = tempFolder.newFile(); - when(reportFiles.fileForUuid(taskUuid)).thenReturn(file); - if (!createFile) { - file.delete(); - } - return queueDto; - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/InternalCeQueueImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/InternalCeQueueImplTest.java index 8220ef23961..aeedba846ce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/InternalCeQueueImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/InternalCeQueueImplTest.java @@ -22,14 +22,12 @@ package org.sonar.server.computation.queue; import com.google.common.base.Optional; import java.util.List; import javax.annotation.Nullable; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.System2; import org.sonar.api.utils.internal.TestSystem2; import org.sonar.ce.monitoring.CEQueueStatus; -import org.sonar.server.computation.monitoring.CEQueueStatusImpl; import org.sonar.ce.queue.CeQueueListener; import org.sonar.ce.queue.CeTask; import org.sonar.ce.queue.CeTaskResult; @@ -42,6 +40,7 @@ import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.ComponentDto; +import org.sonar.server.computation.monitoring.CEQueueStatusImpl; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -66,15 +65,10 @@ public class InternalCeQueueImplTest { DbSession session = dbTester.getSession(); UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE; - CEQueueStatus queueStatus = new CEQueueStatusImpl(); + CEQueueStatus queueStatus = new CEQueueStatusImpl(dbTester.getDbClient()); CeQueueListener listener = mock(CeQueueListener.class); InternalCeQueue underTest = new InternalCeQueueImpl(system2, dbTester.getDbClient(), uuidFactory, queueStatus, new CeQueueListener[] {listener}); - @Before - public void setUp() throws Exception { - queueStatus.initPendingCount(0); - } - @Test public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() { CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob"); @@ -84,17 +78,6 @@ public class InternalCeQueueImplTest { verifyCeQueueDtoForTaskSubmit(taskSubmit); } - @Test - public void submit_increments_receivedCount_of_QueueStatus() { - underTest.submit(createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob")); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(1L); - - underTest.submit(createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_2", "rob")); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(2L); - } - @Test public void submit_populates_component_name_and_key_of_CeTask_if_component_exists() { ComponentDto componentDto = insertComponent(newComponentDto("PROJECT_1")); @@ -151,17 +134,6 @@ public class InternalCeQueueImplTest { verifyCeTask(taskSubmit2, tasks.get(1), null); } - @Test - public void massSubmit_increments_receivedCount_of_QueueStatus() { - underTest.massSubmit(asList(createTaskSubmit("type 1"), createTaskSubmit("type 2"))); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(2L); - - underTest.massSubmit(asList(createTaskSubmit("a"), createTaskSubmit("a"), createTaskSubmit("b"))); - - assertThat(queueStatus.getReceivedCount()).isEqualTo(5L); - } - @Test public void test_remove() { CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java index 41a5c8b891c..be6fb6a1259 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java @@ -19,20 +19,20 @@ */ package org.sonar.server.es; +import java.io.File; +import java.util.Properties; import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.sonar.process.LoopbackAddress; import org.sonar.process.NetworkUtils; +import org.sonar.process.ProcessEntryPoint; import org.sonar.process.ProcessProperties; import org.sonar.process.Props; import org.sonar.search.SearchServer; import org.sonar.test.TestUtils; -import java.io.File; -import java.util.Properties; - public class EsServerHolder { private static EsServerHolder HOLDER = null; @@ -114,6 +114,7 @@ public class EsServerHolder { properties.setProperty(ProcessProperties.SEARCH_PORT, String.valueOf(port)); properties.setProperty(ProcessProperties.SEARCH_HOST, hostName); properties.setProperty(ProcessProperties.PATH_HOME, homeDir.getAbsolutePath()); + properties.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, homeDir.getAbsolutePath()); SearchServer server = new SearchServer(new Props(properties)); server.start(); HOLDER = new EsServerHolder(server, clusterName, nodeName, port, hostName, homeDir); 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 index 38adaa34662..514bb7fb7c6 100644 --- 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 @@ -19,12 +19,12 @@ */ package org.sonar.server.platform.monitoring; -import org.junit.Test; - +import java.lang.management.ManagementFactory; import javax.annotation.CheckForNull; import javax.management.InstanceNotFoundException; import javax.management.ObjectInstance; -import java.lang.management.ManagementFactory; +import javax.management.ObjectName; +import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -52,7 +52,7 @@ public class BaseMonitorMBeanTest { @CheckForNull private ObjectInstance getMBean() throws Exception { try { - return ManagementFactory.getPlatformMBeanServer().getObjectInstance(underTest.objectName()); + return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(underTest.objectName())); } catch (InstanceNotFoundException e) { return null; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java new file mode 100644 index 00000000000..4c2ce9ed035 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.CeDatabaseMBean; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CeDatabaseMonitorTest { + + JmxConnectionFactory jmxConnectionFactory = mock(JmxConnectionFactory.class, Mockito.RETURNS_DEEP_STUBS); + CeDatabaseMonitor underTest = new CeDatabaseMonitor(jmxConnectionFactory); + + @Test + public void testName() { + assertThat(underTest.name()).isNotEmpty(); + } + + @Test + public void testAttributes() { + CeDatabaseMBean mbean = mock(CeDatabaseMBean.class, Mockito.RETURNS_DEFAULTS); + + when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE).getMBean(CeDatabaseMBean.OBJECT_NAME, CeDatabaseMBean.class)) + .thenReturn(mbean); + LinkedHashMap attributes = underTest.attributes(); + assertThat(attributes).containsKeys("Pool Initial Size", "Pool Active Connections"); + assertThat(attributes).hasSize(9); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java new file mode 100644 index 00000000000..5db51a693fa --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import com.google.common.collect.ImmutableSortedMap; +import java.util.LinkedHashMap; +import org.assertj.core.data.MapEntry; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CeStateMonitorTest { + + JmxConnectionFactory jmxConnectionFactory = mock(JmxConnectionFactory.class, Mockito.RETURNS_DEEP_STUBS); + CeStateMonitor underTest = new CeStateMonitor(jmxConnectionFactory); + + @Test + public void testName() { + assertThat(underTest.name()).isNotEmpty(); + } + + @Test + public void testAttributes() { + when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE).getSystemState()).thenReturn(ImmutableSortedMap.of( + "foo", "foo_val", "bar", "bar_val")); + LinkedHashMap attributes = underTest.attributes(); + assertThat(attributes).containsExactly( + MapEntry.entry("bar", "bar_val"), + MapEntry.entry("foo", "foo_val")); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java new file mode 100644 index 00000000000..cf1902601ae --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import java.util.LinkedHashMap; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.CeTasksMBean; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CeTasksMonitorTest { + + JmxConnectionFactory jmxConnectionFactory = mock(JmxConnectionFactory.class, Mockito.RETURNS_DEEP_STUBS); + CeTasksMonitor underTest = new CeTasksMonitor(jmxConnectionFactory); + + @Test + public void testName() { + assertThat(underTest.name()).isNotEmpty(); + } + + @Test + public void testAttributes() { + CeTasksMBean mbean = mock(CeTasksMBean.class, Mockito.RETURNS_DEFAULTS); + + when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE).getMBean(CeTasksMBean.OBJECT_NAME, CeTasksMBean.class)) + .thenReturn(mbean); + LinkedHashMap attributes = underTest.attributes(); + assertThat(attributes).containsKeys("Pending"); + assertThat(attributes).hasSize(6); + } +} 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 index 82023198119..1e7cb5eccc5 100644 --- 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 @@ -42,6 +42,11 @@ public class DatabaseMonitorTest { underTest = new DatabaseMonitor(dbVersion, dbTester.getDbClient()); } + @Test + public void name_is_not_empty() { + assertThat(underTest.name()).isNotEmpty(); + } + @Test public void db_info() { LinkedHashMap attributes = underTest.attributes(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java index 76f4b1dd1a3..0716a6415a8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java @@ -22,58 +22,69 @@ package org.sonar.server.platform.monitoring; import java.util.LinkedHashMap; import java.util.Map; import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus; +import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.mockito.Mockito; import org.sonar.api.config.Settings; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.EsSettingsMBean; +import org.sonar.process.jmx.JmxConnectionFactory; import org.sonar.server.es.EsTester; import org.sonar.server.es.NewIndex; import org.sonar.server.issue.index.IssueIndexDefinition; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class EsMonitorTest { @ClassRule public static EsTester esTester = new EsTester().addDefinitions(new IssueIndexDefinition(new Settings())); + JmxConnectionFactory jmxConnectionFactory = mock(JmxConnectionFactory.class, Mockito.RETURNS_DEEP_STUBS); + EsSettingsMBean settingsMBean = mock(EsSettingsMBean.class); + EsMonitor underTest = new EsMonitor(jmxConnectionFactory, esTester.client()); + + @Before + public void setUp() throws Exception { + when(jmxConnectionFactory.create(ProcessId.ELASTICSEARCH).getMBean(EsSettingsMBean.OBJECT_NAME, EsSettingsMBean.class)).thenReturn(settingsMBean); + } + @Test public void name() { - EsMonitor monitor = new EsMonitor(esTester.client()); - assertThat(monitor.name()).isEqualTo("ElasticSearch"); + assertThat(underTest.name()).isEqualTo("Elasticsearch"); } - @Test public void cluster_attributes() { - EsMonitor monitor = new EsMonitor(esTester.client()); - LinkedHashMap attributes = monitor.attributes(); - assertThat(monitor.getState()).isEqualTo(ClusterHealthStatus.GREEN.name()); + LinkedHashMap attributes = underTest.attributes(); + assertThat(underTest.getState()).isEqualTo(ClusterHealthStatus.GREEN.name()); assertThat(attributes.get("State")).isEqualTo(ClusterHealthStatus.GREEN); assertThat(attributes.get("Number of Nodes")).isEqualTo(1); } @Test public void node_attributes() { - EsMonitor monitor = new EsMonitor(esTester.client()); - LinkedHashMap attributes = monitor.attributes(); - Map nodesAttributes = (Map)attributes.get("Nodes"); + LinkedHashMap attributes = underTest.attributes(); + Map nodesAttributes = (Map) attributes.get("Nodes"); // one node assertThat(nodesAttributes).hasSize(1); - Map nodeAttributes = (Map)nodesAttributes.values().iterator().next(); + Map nodeAttributes = (Map) nodesAttributes.values().iterator().next(); assertThat(nodeAttributes.get("Type")).isEqualTo("Master"); assertThat(nodeAttributes.get("Store Size")).isNotNull(); } @Test public void index_attributes() { - EsMonitor monitor = new EsMonitor(esTester.client()); - LinkedHashMap attributes = monitor.attributes(); - Map indicesAttributes = (Map)attributes.get("Indices"); + LinkedHashMap attributes = underTest.attributes(); + Map indicesAttributes = (Map) attributes.get("Indices"); // one index "issues" assertThat(indicesAttributes).hasSize(1); - Map indexAttributes = (Map)indicesAttributes.values().iterator().next(); + Map indexAttributes = (Map) indicesAttributes.values().iterator().next(); assertThat(indexAttributes.get("Docs")).isEqualTo(0L); assertThat(indexAttributes.get("Shards")).isEqualTo(NewIndex.DEFAULT_NUMBER_OF_SHARDS); assertThat(indexAttributes.get("Store Size")).isNotNull(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java new file mode 100644 index 00000000000..e06f5c9c111 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import com.google.common.collect.ImmutableSortedMap; +import java.util.LinkedHashMap; +import org.assertj.core.data.MapEntry; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.process.ProcessId; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class EsStateMonitorTest { + JmxConnectionFactory jmxConnectionFactory = mock(JmxConnectionFactory.class, Mockito.RETURNS_DEEP_STUBS); + EsStateMonitor underTest = new EsStateMonitor(jmxConnectionFactory); + + @Test + public void testName() { + assertThat(underTest.name()).isNotEmpty(); + } + + @Test + public void testAttributes() { + when(jmxConnectionFactory.create(ProcessId.ELASTICSEARCH).getSystemState()).thenReturn(ImmutableSortedMap.of( + "foo", "foo_val", "bar", "bar_val")); + LinkedHashMap attributes = underTest.attributes(); + assertThat(attributes).containsExactly( + MapEntry.entry("bar", "bar_val"), + MapEntry.entry("foo", "foo_val")); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java new file mode 100644 index 00000000000..5a461bc21c3 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.monitoring; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.Settings; +import org.sonar.process.ProcessEntryPoint; +import org.sonar.process.jmx.JmxConnectionFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JmxConnectionFactoryProviderTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + Settings settings = new Settings(); + JmxConnectorProvider underTest = new JmxConnectorProvider(); + + @Test + public void provide_JmxConnector() { + settings.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, "path/"); + JmxConnectionFactory connector = underTest.provide(settings); + + assertThat(connector).isNotNull(); + // cache + assertThat(underTest.provide(settings)).isSameAs(connector); + } + + @Test + public void throw_IAE_if_ipc_shared_path_is_not_set() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Property process.sharedDir is not set"); + + underTest.provide(settings); + } +} 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/JvmPropsMonitorTest.java similarity index 86% rename from server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropertiesMonitorTest.java rename to server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropsMonitorTest.java index e1692321e98..48ce163e151 100644 --- 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/JvmPropsMonitorTest.java @@ -19,17 +19,22 @@ */ package org.sonar.server.platform.monitoring; -import org.junit.Test; - import java.util.LinkedHashMap; +import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class JvmPropertiesMonitorTest { +public class JvmPropsMonitorTest { + + JvmPropsMonitor underTest = new JvmPropsMonitor(); + + @Test + public void name_is_not_empty() { + assertThat(underTest.name()).isNotEmpty(); + } @Test public void attributes() { - JvmPropertiesMonitor underTest = new JvmPropertiesMonitor(); LinkedHashMap attributes = underTest.attributes(); assertThat(attributes).containsKeys("java.vm.vendor", "os.name"); 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 index 16889862087..9c9f91999d9 100644 --- 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 @@ -51,6 +51,11 @@ public class SonarQubeMonitorTest { when(serverLogging.getRootLoggerLevel()).thenReturn(LoggerLevel.DEBUG); } + @Test + public void name_is_not_empty() { + assertThat(new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server, serverLogging).name()).isNotEmpty(); + } + @Test public void getServerId() { when(server.getStartedAt()).thenReturn(DateUtils.parseDate("2015-01-01")); diff --git a/server/sonar-web/src/main/js/apps/system/main.js b/server/sonar-web/src/main/js/apps/system/main.js index 0fbabea94bd..ea003bb7de6 100644 --- a/server/sonar-web/src/main/js/apps/system/main.js +++ b/server/sonar-web/src/main/js/apps/system/main.js @@ -24,8 +24,8 @@ import Section from './section'; import { translate } from '../../helpers/l10n'; import RestartModal from '../../components/RestartModal'; -const SECTIONS_ORDER = ['SonarQube', 'Database', 'Plugins', 'System', 'ElasticSearch', 'JvmProperties', - 'ComputeEngine']; +const SECTIONS_ORDER = ['SonarQube', 'Database', 'Plugins', 'System', 'Elasticsearch State', 'Elasticsearch', + 'Compute Engine Tasks', 'Compute Engine State', 'Compute Engine Database', 'JvmProperties']; export default React.createClass({ componentDidMount() { diff --git a/sonar-application/src/main/java/org/sonar/application/App.java b/sonar-application/src/main/java/org/sonar/application/App.java index 35e9f491694..de5db3c03e4 100644 --- a/sonar-application/src/main/java/org/sonar/application/App.java +++ b/sonar-application/src/main/java/org/sonar/application/App.java @@ -27,26 +27,24 @@ import java.util.Properties; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.sonar.process.MinimumViableSystem; +import org.sonar.process.ProcessId; import org.sonar.process.ProcessProperties; import org.sonar.process.Props; import org.sonar.process.Stoppable; import org.sonar.process.monitor.JavaCommand; import org.sonar.process.monitor.Monitor; +import static org.sonar.process.ProcessId.APP; + /** * Entry-point of process that starts and monitors ElasticSearch, the Web Server and the Compute Engine. */ public class App implements Stoppable { - public static final int APP_PROCESS_NUMBER = 0; - public static final int ES_PROCESS_INDEX = 1; - public static final int WEBSERVER_PROCESS_INDEX = 2; - public static final int CESERVER_PROCESS_INDEX = 3; - private final Monitor monitor; public App(AppFileSystem appFileSystem, boolean watchForHardStop) { - this(Monitor.create(APP_PROCESS_NUMBER, appFileSystem, watchForHardStop)); + this(Monitor.create(APP.getIpcIndex(), appFileSystem, watchForHardStop)); } App(Monitor monitor) { @@ -74,7 +72,7 @@ public class App implements Stoppable { } private static JavaCommand createESCommand(Props props, File homeDir) { - JavaCommand elasticsearch = new JavaCommand("search", ES_PROCESS_INDEX); + JavaCommand elasticsearch = new JavaCommand(ProcessId.ELASTICSEARCH); elasticsearch .setWorkDir(homeDir) .addJavaOptions("-Djava.awt.headless=true") @@ -88,7 +86,7 @@ public class App implements Stoppable { } private static JavaCommand createWebServerCommand(Props props, File homeDir) { - JavaCommand webServer = new JavaCommand("web", WEBSERVER_PROCESS_INDEX) + JavaCommand webServer = new JavaCommand(ProcessId.WEB_SERVER) .setWorkDir(homeDir) .addJavaOptions(ProcessProperties.WEB_ENFORCED_JVM_ARGS) .addJavaOptions(props.nonNullValue(ProcessProperties.WEB_JAVA_OPTS)) @@ -107,7 +105,7 @@ public class App implements Stoppable { } private static JavaCommand createCeServerCommand(Props props, File homeDir) { - JavaCommand webServer = new JavaCommand("ce", CESERVER_PROCESS_INDEX) + JavaCommand webServer = new JavaCommand(ProcessId.COMPUTE_ENGINE) .setWorkDir(homeDir) .addJavaOptions(ProcessProperties.CE_ENFORCED_JVM_ARGS) .addJavaOptions(props.nonNullValue(ProcessProperties.CE_JAVA_OPTS)) diff --git a/sonar-application/src/test/java/org/sonar/application/AppTest.java b/sonar-application/src/test/java/org/sonar/application/AppTest.java index 70f694c97dc..8150639c865 100644 --- a/sonar-application/src/test/java/org/sonar/application/AppTest.java +++ b/sonar-application/src/test/java/org/sonar/application/AppTest.java @@ -28,6 +28,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; +import org.sonar.process.ProcessId; import org.sonar.process.ProcessProperties; import org.sonar.process.Props; import org.sonar.process.monitor.JavaCommand; @@ -52,7 +53,7 @@ public class AppTest { } @Test - public void start_elasticsearch_and_tomcat_by_default() throws Exception { + public void start_all_processes_by_default() throws Exception { Monitor monitor = mock(Monitor.class); App app = new App(monitor); Props props = initDefaultProps(); @@ -61,7 +62,7 @@ public class AppTest { ArgumentCaptor> argument = newJavaCommandArgumentCaptor(); verify(monitor).start(argument.capture()); - assertThat(argument.getValue()).extracting("key").containsExactly("search", "web", "ce"); + assertThat(argument.getValue()).extracting("processId").containsExactly(ProcessId.ELASTICSEARCH, ProcessId.WEB_SERVER, ProcessId.COMPUTE_ENGINE); } @Test @@ -75,7 +76,7 @@ public class AppTest { ArgumentCaptor> argument = newJavaCommandArgumentCaptor(); verify(monitor).start(argument.capture()); - assertThat(argument.getValue()).extracting("key").containsOnly("search"); + assertThat(argument.getValue()).extracting("processId").containsOnly(ProcessId.ELASTICSEARCH); } @Test -- 2.39.5