Browse Source

SONAR-7436 replace JMX/RMI by HTTP

tags/5.5-M14
Simon Brandhof 8 years ago
parent
commit
975158a5a7
66 changed files with 1689 additions and 2013 deletions
  1. 5
    0
      pom.xml
  2. 10
    1
      server/sonar-ce/pom.xml
  3. 0
    2
      server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java
  4. 7
    3
      server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
  5. 17
    1
      server/sonar-process/pom.xml
  6. 14
    14
      server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java
  7. 4
    4
      server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java
  8. 1
    1
      server/sonar-process/src/main/java/org/sonar/process/Jmx.java
  9. 2
    2
      server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java
  10. 0
    14
      server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java
  11. 0
    99
      server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java
  12. 65
    0
      server/sonar-process/src/main/java/org/sonar/process/systeminfo/ProcessStateSystemInfo.java
  13. 104
    0
      server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoHttpServer.java
  14. 4
    26
      server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoSection.java
  15. 1
    1
      server/sonar-process/src/main/java/org/sonar/process/systeminfo/package-info.java
  16. 43
    0
      server/sonar-process/src/main/protobuf/process_system_info.proto
  17. 2
    2
      server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java
  18. 11
    1
      server/sonar-process/src/test/java/org/sonar/process/JmxTest.java
  19. 0
    91
      server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionFactoryTest.java
  20. 0
    98
      server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java
  21. 0
    71
      server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTestServer.java
  22. 58
    0
      server/sonar-process/src/test/java/org/sonar/process/systeminfo/ProcessStateSystemInfoTest.java
  23. 89
    0
      server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoHttpServerTest.java
  24. 0
    1
      server/sonar-search/src/main/java/org/sonar/search/EsSettings.java
  25. 1
    1
      server/sonar-search/src/main/java/org/sonar/search/EsSettingsMBean.java
  26. 1
    2
      server/sonar-search/src/main/java/org/sonar/search/SearchServer.java
  27. 9
    0
      server/sonar-server/pom.xml
  28. 4
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java
  29. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBean.java
  30. 19
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java
  31. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBean.java
  32. 17
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBeanImpl.java
  33. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/BaseMonitorMBean.java
  34. 0
    65
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java
  35. 0
    50
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java
  36. 0
    62
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java
  37. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitor.java
  38. 3
    19
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java
  39. 0
    46
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProvider.java
  40. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropsMonitor.java
  41. 1
    7
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/Monitor.java
  42. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java
  43. 24
    30
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/ProcessSystemInfoClient.java
  44. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitor.java
  45. 2
    3
      server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemMonitor.java
  46. 2
    10
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  47. 38
    7
      server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java
  48. 70
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImplTest.java
  49. 8
    1
      server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeTasksMBeanImplTest.java
  50. 0
    61
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java
  51. 0
    61
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java
  52. 0
    61
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java
  53. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/DatabaseMonitorTest.java
  54. 4
    18
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java
  55. 0
    60
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java
  56. 2
    3
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitor.java
  57. 0
    56
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java
  58. 2
    3
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropsMonitorTest.java
  59. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java
  60. 99
    0
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/ProcessSystemInfoClientTest.java
  61. 8
    9
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SonarQubeMonitorTest.java
  62. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemMonitorTest.java
  63. 7
    8
      server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java
  64. 4
    1
      server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java
  65. 2
    2
      sonar-application/assembly.xml
  66. 909
    909
      tests/upgrade/projects/struts-1.3.9-diet/core/src/main/resources/org/apache/struts/resources/web-app_2_3.dtd

+ 5
- 0
pom.xml View File

@@ -673,6 +673,11 @@
<artifactId>mybatis</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.picocontainer</groupId>
<artifactId>picocontainer</artifactId>

+ 10
- 1
server/sonar-ce/pom.xml View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonarsource.sonarqube</groupId>
@@ -17,6 +18,14 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.findbugs</groupId>

+ 0
- 2
server/sonar-ce/src/main/java/org/sonar/ce/container/ComputeEngineContainerImpl.java View File

@@ -104,7 +104,6 @@ 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.JmxConnectionFactoryProvider;
import org.sonar.server.plugins.InstalledPluginReferentialFactory;
import org.sonar.server.plugins.ServerExtensionInstaller;
import org.sonar.server.plugins.privileged.PrivilegedPluginsBootstraper;
@@ -137,7 +136,6 @@ public class ComputeEngineContainerImpl implements ComputeEngineContainer {
private static final Object[] LEVEL_1_COMPONENTS = new Object[] {
ComputeEngineSettings.class,
SonarQubeVersionFactory.create(System2.INSTANCE),
new JmxConnectionFactoryProvider(),
ServerImpl.class,
UuidFactoryImpl.INSTANCE,
// no EmbeddedDatabaseFactory.class, creating H2 DB if responsibility of WebServer

+ 7
- 3
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java View File

@@ -31,9 +31,12 @@ import org.picocontainer.MutablePicoContainer;
import org.sonar.api.database.DatabaseProperties;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;

import static java.lang.String.valueOf;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
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;
@@ -62,10 +65,11 @@ public class ComputeEngineContainerImplTest {
File homeDir = tempFolder.newFolder();
File dataDir = new File(homeDir, "data");
File tmpDir = new File(homeDir, "tmp");
properties.setProperty(STARTED_AT, String.valueOf(new Date().getTime()));
properties.setProperty(STARTED_AT, valueOf(new Date().getTime()));
properties.setProperty(PATH_HOME, homeDir.getAbsolutePath());
properties.setProperty(PATH_DATA, dataDir.getAbsolutePath());
properties.setProperty(PATH_TEMP, tmpDir.getAbsolutePath());
properties.setProperty(PROPERTY_PROCESS_INDEX, valueOf(ProcessId.COMPUTE_ENGINE.getIpcIndex()));
properties.setProperty(PROPERTY_SHARED_PATH, tmpDir.getAbsolutePath());
String url = ((BasicDataSource) dbTester.database().getDataSource()).getUrl();
properties.setProperty(DatabaseProperties.PROP_URL, url);
@@ -80,7 +84,7 @@ public class ComputeEngineContainerImplTest {
.hasSize(
CONTAINER_ITSELF
+ 75 // level 4
+ 5 // content of CeModule
+ 7 // content of CeModule
+ 7 // content of CeQueueModule
+ 4 // content of ReportProcessingModule
+ 4 // content of CeTaskProcessorModule
@@ -95,7 +99,7 @@ public class ComputeEngineContainerImplTest {
);
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
+ 23 // level 1
+ 22 // level 1
+ 45 // content of DaoModule
+ 1 // content of EsSearchModule
+ 56 // content of CorePropertyDefinitions

+ 17
- 1
server/sonar-process/pom.xml View File

@@ -29,12 +29,23 @@
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<!-- only if org.sonar.process.systeminfo HTTP server is being used -->
<groupId>org.nanohttpd</groupId>
<artifactId>nanohttpd</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<!-- only if org.sonar.process.systeminfo.protobuf classes are used -->
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
@@ -56,6 +67,11 @@
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

+ 14
- 14
server/sonar-process/src/main/java/org/sonar/process/AllProcessesCommands.java View File

@@ -69,11 +69,11 @@ public class AllProcessesCommands implements AutoCloseable {
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 SYSTEM_INFO_URL_BYTE_OFFSET = PING_BYTE_OFFSET + 8;

private static final int JMX_URL_SIZE_IN_BYTES = 500;
private static final int SYSTEM_INFO_URL_SIZE_IN_BYTES = 500;

private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8 + JMX_URL_SIZE_IN_BYTES;
private static final int BYTE_LENGTH_FOR_ONE_PROCESS = 1 + 1 + 1 + 1 + 8 + SYSTEM_INFO_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;
@@ -148,17 +148,17 @@ public class AllProcessesCommands implements AutoCloseable {
return readLong(processNumber, PING_BYTE_OFFSET);
}

String getJmxUrl(int processNumber) {
byte[] urlBytes = readBytes(processNumber, JMX_URL_BYTE_OFFSET, JMX_URL_SIZE_IN_BYTES);
String getSystemInfoUrl(int processNumber) {
byte[] urlBytes = readBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, SYSTEM_INFO_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));
void setSystemInfoUrl(int processNumber, String url) {
byte[] urlBytes = rightPad(url, SYSTEM_INFO_URL_SIZE_IN_BYTES).getBytes(StandardCharsets.US_ASCII);
if (urlBytes.length > SYSTEM_INFO_URL_SIZE_IN_BYTES) {
throw new IllegalArgumentException(format("System Info URL is too long. Max is %d bytes. Got: %s", SYSTEM_INFO_URL_SIZE_IN_BYTES, url));
}
writeBytes(processNumber, JMX_URL_BYTE_OFFSET, urlBytes);
writeBytes(processNumber, SYSTEM_INFO_URL_BYTE_OFFSET, urlBytes);
}

/**
@@ -277,13 +277,13 @@ public class AllProcessesCommands implements AutoCloseable {
}

@Override
public void setJmxUrl(String s) {
AllProcessesCommands.this.setJmxUrl(processNumber, s);
public void setSystemInfoUrl(String s) {
AllProcessesCommands.this.setSystemInfoUrl(processNumber, s);
}

@Override
public String getJmxUrl() {
return AllProcessesCommands.this.getJmxUrl(processNumber);
public String getSystemInfoUrl() {
return AllProcessesCommands.this.getSystemInfoUrl(processNumber);
}

@Override

+ 4
- 4
server/sonar-process/src/main/java/org/sonar/process/DefaultProcessCommands.java View File

@@ -83,13 +83,13 @@ public class DefaultProcessCommands implements ProcessCommands {
}

@Override
public void setJmxUrl(String s) {
delegate.setJmxUrl(s);
public void setSystemInfoUrl(String s) {
delegate.setSystemInfoUrl(s);
}

@Override
public String getJmxUrl() {
return delegate.getJmxUrl();
public String getSystemInfoUrl() {
return delegate.getSystemInfoUrl();
}

@Override

server/sonar-process/src/main/java/org/sonar/process/jmx/Jmx.java → server/sonar-process/src/main/java/org/sonar/process/Jmx.java View File

@@ -17,7 +17,7 @@
* 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;
package org.sonar.process;

import java.lang.management.ManagementFactory;
import javax.management.InstanceAlreadyExistsException;

+ 2
- 2
server/sonar-process/src/main/java/org/sonar/process/ProcessCommands.java View File

@@ -55,9 +55,9 @@ public interface ProcessCommands extends AutoCloseable {

long getLastPing();

void setJmxUrl(String s);
void setSystemInfoUrl(String s);

String getJmxUrl();
String getSystemInfoUrl();

/**
* To be executed by monitor process to ask for child process termination

+ 0
- 14
server/sonar-process/src/main/java/org/sonar/process/ProcessEntryPoint.java View File

@@ -22,7 +22,6 @@ package org.sonar.process;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.VMSupport;

public class ProcessEntryPoint implements Stoppable {

@@ -107,10 +106,6 @@ public class ProcessEntryPoint implements Stoppable {
Thread.sleep(20L);
}

String jmxUrl = guessJmxUrl();
logger.debug("JMX URL is {}", jmxUrl);
commands.setJmxUrl(jmxUrl);

// notify monitor that process is ready
commands.setUp();

@@ -125,15 +120,6 @@ 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;
}

+ 0
- 99
server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnection.java View File

@@ -1,99 +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.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.LinkedHashMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;

import static java.lang.String.format;

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> M getMBean(String mBeanName, Class<M> 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(format("Fail to connect to MBean [%s]", mBeanName), e);
}
}

public Map<String, Object> getSystemState() {
Map<String, Object> props = new LinkedHashMap<>();
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);
}
}
}

+ 65
- 0
server/sonar-process/src/main/java/org/sonar/process/systeminfo/ProcessStateSystemInfo.java View File

@@ -0,0 +1,65 @@
/*
* 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.systeminfo;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadMXBean;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

public class ProcessStateSystemInfo implements SystemInfoSection {
private static final long MEGABYTE = 1024L * 1024L;
private final String name;

public ProcessStateSystemInfo(String name) {
this.name = name;
}

@Override
public ProtobufSystemInfo.Section toProtobuf() {
return toProtobuf(ManagementFactory.getMemoryMXBean());
}

// Visible for testing
ProtobufSystemInfo.Section toProtobuf(MemoryMXBean memoryBean) {
ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder();
builder.setName(name);
MemoryUsage heap = memoryBean.getHeapMemoryUsage();
addAttributeInMb(builder, "Heap Committed (MB)", heap.getCommitted());
addAttributeInMb(builder, "Heap Init (MB)", heap.getInit());
addAttributeInMb(builder, "Heap Max (MB)", heap.getMax());
addAttributeInMb(builder, "Heap Used (MB)", heap.getUsed());
MemoryUsage nonHeap = memoryBean.getNonHeapMemoryUsage();
addAttributeInMb(builder, "Non Heap Committed (MB)", nonHeap.getCommitted());
addAttributeInMb(builder, "Non Heap Init (MB)", nonHeap.getInit());
addAttributeInMb(builder, "Non Heap Max (MB)", nonHeap.getMax());
addAttributeInMb(builder, "Non Heap Used (MB)", nonHeap.getUsed());
ThreadMXBean thread = ManagementFactory.getThreadMXBean();
builder.addAttributesBuilder().setKey("Thread Count").setLongValue(thread.getThreadCount()).build();
return builder.build();
}

private void addAttributeInMb(ProtobufSystemInfo.Section.Builder builder, String key, long valueInBytes) {
if (valueInBytes >= 0L) {
builder.addAttributesBuilder().setKey(key).setLongValue(valueInBytes / MEGABYTE).build();
}
}
}

+ 104
- 0
server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoHttpServer.java View File

@@ -0,0 +1,104 @@
/*
* 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.systeminfo;

import fi.iki.elonen.NanoHTTPD;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Properties;
import org.slf4j.LoggerFactory;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static java.lang.Integer.parseInt;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;

/**
* This HTTP server exports data required for display of System Info page (and the related web service).
* It listens on loopback address only, so it does not need to be secure (no HTTPS, no authentication).
*/
public class SystemInfoHttpServer {

private static final String PROTOBUF_MIME_TYPE = "application/x-protobuf";

private final Properties processProps;
private final List<SystemInfoSection> sectionProviders;
private final SystemInfoNanoHttpd nanoHttpd;

public SystemInfoHttpServer(Properties processProps, List<SystemInfoSection> sectionProviders) throws UnknownHostException {
this.processProps = processProps;
this.sectionProviders = sectionProviders;
InetAddress loopbackAddress = InetAddress.getByName(null);
this.nanoHttpd = new SystemInfoNanoHttpd(loopbackAddress.getHostAddress(), 0);
}

// do not rename. This naming convention is required for picocontainer.
public void start() {
try {
nanoHttpd.start();
registerHttpUrl();
} catch (IOException e) {
throw new IllegalStateException("Can not start local HTTP server for System Info monitoring", e);
}
}

private void registerHttpUrl() {
int processNumber = parseInt(processProps.getProperty(PROPERTY_PROCESS_INDEX));
File shareDir = new File(processProps.getProperty(PROPERTY_SHARED_PATH));
try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(shareDir, processNumber)) {
String url = getUrl();
commands.setSystemInfoUrl(url);
LoggerFactory.getLogger(getClass()).debug("System Info HTTP server listening at {}", url);
}
}

// do not rename. This naming convention is required for picocontainer.
public void stop() {
nanoHttpd.stop();
}

// visible for testing
String getUrl() {
return "http://" + nanoHttpd.getHostname() + ":" + nanoHttpd.getListeningPort();
}

private class SystemInfoNanoHttpd extends NanoHTTPD {

SystemInfoNanoHttpd(String hostname, int port) {
super(hostname, port);
}

@Override
public Response serve(IHTTPSession session) {
ProtobufSystemInfo.SystemInfo.Builder infoBuilder = ProtobufSystemInfo.SystemInfo.newBuilder();
for (SystemInfoSection sectionProvider : sectionProviders) {
ProtobufSystemInfo.Section section = sectionProvider.toProtobuf();
infoBuilder.addSections(section);
}
byte[] bytes = infoBuilder.build().toByteArray();
return newFixedLengthResponse(Response.Status.OK, PROTOBUF_MIME_TYPE, new ByteArrayInputStream(bytes), bytes.length);
}
}
}

server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsStateMonitor.java → server/sonar-process/src/main/java/org/sonar/process/systeminfo/SystemInfoSection.java View File

@@ -17,34 +17,12 @@
* 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;
package org.sonar.process.systeminfo;

import com.google.common.base.Optional;
import java.util.Map;
import org.sonar.process.ProcessId;
import org.sonar.process.jmx.JmxConnection;
import org.sonar.process.jmx.JmxConnectionFactory;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

public class EsStateMonitor implements Monitor {
public interface SystemInfoSection {

private final JmxConnectionFactory jmxConnectionFactory;
ProtobufSystemInfo.Section toProtobuf();

public EsStateMonitor(JmxConnectionFactory jmxConnectionFactory) {
this.jmxConnectionFactory = jmxConnectionFactory;
}

@Override
public String name() {
return "Elasticsearch State";
}

@Override
public Optional<Map<String, Object>> attributes() {
try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.ELASTICSEARCH)) {
if (connection == null) {
return Optional.absent();
}
return Optional.of(connection.getSystemState());
}
}
}

server/sonar-process/src/main/java/org/sonar/process/jmx/package-info.java → server/sonar-process/src/main/java/org/sonar/process/systeminfo/package-info.java View File

@@ -18,6 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@ParametersAreNonnullByDefault
package org.sonar.process.jmx;
package org.sonar.process.systeminfo;

import javax.annotation.ParametersAreNonnullByDefault;

+ 43
- 0
server/sonar-process/src/main/protobuf/process_system_info.proto View File

@@ -0,0 +1,43 @@
// SonarQube, open source software quality management tool.
// Copyright (C) 2008-2016 SonarSource
// mailto:contact AT sonarsource DOT com
//
// SonarQube is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// SonarQube is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

syntax = "proto3";

option java_package = "org.sonar.process.systeminfo.protobuf";
option java_outer_classname = "ProtobufSystemInfo";

option optimize_for = SPEED;

message SystemInfo {
repeated Section sections = 1;
}

message Section {
string name = 1;
repeated Attribute attributes = 2;
}

message Attribute {
string key = 1;
oneof value {
bool boolean_value = 2;
int64 long_value = 3;
double double_value = 4;
string string_value = 5;
}
}

+ 2
- 2
server/sonar-process/src/test/java/org/sonar/process/AllProcessesCommandsTest.java View File

@@ -105,9 +105,9 @@ public class AllProcessesCommandsTest {
assertThat(readByte(commands, offset + i)).isEqualTo(EMPTY);
}

commands.setJmxUrl(PROCESS_NUMBER, "jmx:foo");
commands.setSystemInfoUrl(PROCESS_NUMBER, "jmx:foo");
assertThat(readByte(commands, offset)).isNotEqualTo(EMPTY);
assertThat(commands.getJmxUrl(PROCESS_NUMBER)).isEqualTo("jmx:foo");
assertThat(commands.getSystemInfoUrl(PROCESS_NUMBER)).isEqualTo("jmx:foo");
}

@Test

server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTest.java → server/sonar-process/src/test/java/org/sonar/process/JmxTest.java View File

@@ -17,7 +17,7 @@
* 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;
package org.sonar.process;

import java.lang.management.ManagementFactory;
import javax.annotation.CheckForNull;
@@ -27,6 +27,8 @@ import javax.management.ObjectName;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.process.jmx.Fake;
import org.sonar.process.jmx.FakeMBean;

import static org.assertj.core.api.Assertions.assertThat;

@@ -64,6 +66,14 @@ public class JmxTest {
Jmx.register(FAKE_NAME, "not a mbean");
}

@Test
public void register_fails_if_name_is_not_valid() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Can not register MBean [/]");

Jmx.register("/", new Fake());
}

@Test
public void support_implementation_in_different_package_than_interface() throws Exception {
assertThat(lookupMBean()).isNull();

+ 0
- 91
server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionFactoryTest.java View File

@@ -1,91 +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.process.jmx;

import java.io.File;
import java.util.Properties;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;

import static org.assertj.core.api.Assertions.assertThat;

public class JmxConnectionFactoryTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule
public JmxTestServer jmxServer = new JmxTestServer();

@Test
public void create_returns_null_if_process_is_down() throws Exception {
File ipcSharedDir = temp.newFolder();
JmxConnectionFactory underTest = new JmxConnectionFactory(ipcSharedDir);

JmxConnection connection = underTest.create(ProcessId.COMPUTE_ENGINE);

assertThat(connection).isNull();
}

@Test
public void create_connection_if_process_is_up() throws Exception {
File ipcSharedDir = temp.newFolder();
try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(ipcSharedDir, ProcessId.COMPUTE_ENGINE.getIpcIndex())) {
processCommands.setUp();
processCommands.setJmxUrl(jmxServer.getAddress().toString());
}

JmxConnection connection = new JmxConnectionFactory(ipcSharedDir).create(ProcessId.COMPUTE_ENGINE);
assertThat(connection).isNotNull();
}

@Test
public void create_throws_ISE_if_fails_to_connect_to_process() throws Exception {
File ipcSharedDir = temp.newFolder();
try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(ipcSharedDir, ProcessId.COMPUTE_ENGINE.getIpcIndex())) {
// process is up but does not have any JMX URL
processCommands.setUp();
}

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Can not connect to process " + ProcessId.COMPUTE_ENGINE.toString());
new JmxConnectionFactory(ipcSharedDir).create(ProcessId.COMPUTE_ENGINE);
}

@Test
public void load_ipc_shared_dir_from_props() throws Exception {
File ipcSharedDir = temp.newFolder();
Properties props = new Properties();
props.setProperty("process.sharedDir", ipcSharedDir.getAbsolutePath());

JmxConnectionFactory underTest = new JmxConnectionFactory(new Props(props));

assertThat(underTest.getIpcSharedDir().getCanonicalPath()).isEqualTo(ipcSharedDir.getCanonicalPath());
}

}

+ 0
- 98
server/sonar-process/src/test/java/org/sonar/process/jmx/JmxConnectionTest.java View File

@@ -1,98 +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.process.jmx;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.Map;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.assertj.core.api.Assertions.assertThat;

public class JmxConnectionTest {

private static final String CUSTOM_OBJECT_NAME = "Test:name=test";

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule
public JmxTestServer jmxServer = new JmxTestServer();

JmxConnection underTest;

@Before
public void setUp() throws Exception {
jmxServer.getMBeanServer().registerMBean(new Fake(), new ObjectName(CUSTOM_OBJECT_NAME));
JMXConnector jmxConnector = JMXConnectorFactory.newJMXConnector(jmxServer.getAddress(), null);
jmxConnector.connect();
underTest = new JmxConnection(jmxConnector);
}

@After
public void tearDown() throws Exception {
underTest.close();
jmxServer.getMBeanServer().unregisterMBean(new ObjectName(CUSTOM_OBJECT_NAME));
}

@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);
}

@Test
public void get_platform_mbean() throws Exception {
RuntimeMXBean runtimeMBean = underTest.getMBean(ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class);

assertThat(runtimeMBean).isNotNull();
}

@Test
public void get_custom_mbean() throws Exception {
FakeMBean mbean = underTest.getMBean(CUSTOM_OBJECT_NAME, FakeMBean.class);

assertThat(mbean).isNotNull();
}

@Test
public void getMBean_fails_if_does_not_exist() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Fail to connect to MBean [unknown]");
underTest.getMBean("unknown", FakeMBean.class);
}

@Test
public void getSystemState() throws Exception {
Map<String, Object> state = underTest.getSystemState();
assertThat(state).containsKey("Heap Max (MB)");
assertThat((int) state.get("Thread Count")).isGreaterThan(0);
}
}

+ 0
- 71
server/sonar-process/src/test/java/org/sonar/process/jmx/JmxTestServer.java View File

@@ -1,71 +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.process.jmx;

import com.google.common.base.Throwables;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import javax.management.MBeanServer;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import org.junit.rules.ExternalResource;
import org.sonar.process.NetworkUtils;

public class JmxTestServer extends ExternalResource {

private final int jmxPort = NetworkUtils.freePort();
private JMXConnectorServer jmxServer;

@Override
protected void before() throws Throwable {
LocateRegistry.createRegistry(jmxPort);
JMXServiceURL serviceUrl = new JMXServiceURL("service:jmx:rmi://localhost:" + jmxPort + "/jndi/rmi://localhost:" + jmxPort + "/jmxrmi");
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
jmxServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, new HashMap<String, Object>(), mbeanServer);
jmxServer.start();

}

@Override
protected void after() {
if (jmxServer != null) {
try {
jmxServer.stop();
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
}

public int getPort() {
return jmxPort;
}

public MBeanServer getMBeanServer() {
return jmxServer.getMBeanServer();
}

public JMXServiceURL getAddress() {
return jmxServer.getAddress();
}
}

+ 58
- 0
server/sonar-process/src/test/java/org/sonar/process/systeminfo/ProcessStateSystemInfoTest.java View File

@@ -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.process.systeminfo;

import java.lang.management.MemoryMXBean;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ProcessStateSystemInfoTest {

public static final String PROCESS_NAME = "the process name";

@Test
public void toSystemInfoSection() {
ProcessStateSystemInfo underTest = new ProcessStateSystemInfo(PROCESS_NAME);
ProtobufSystemInfo.Section section = underTest.toProtobuf();

assertThat(section.getName()).isEqualTo(PROCESS_NAME);
assertThat(section.getAttributesCount()).isEqualTo(9);
assertThat(section.getAttributes(0).getKey()).isEqualTo("Heap Committed (MB)");
assertThat(section.getAttributes(0).getLongValue()).isGreaterThan(1L);
assertThat(section.getAttributes(8).getKey()).isEqualTo("Thread Count");
assertThat(section.getAttributes(8).getLongValue()).isGreaterThan(1L);
}

@Test
public void should_hide_attributes_without_values() {
MemoryMXBean memoryBean = mock(MemoryMXBean.class, Mockito.RETURNS_DEEP_STUBS);
when(memoryBean.getHeapMemoryUsage().getCommitted()).thenReturn(-1L);

ProcessStateSystemInfo underTest = new ProcessStateSystemInfo(PROCESS_NAME);
ProtobufSystemInfo.Section section = underTest.toProtobuf(memoryBean);

assertThat(section.getAttributesList()).extracting("key").doesNotContain("Heap Committed (MB)");
}
}

+ 89
- 0
server/sonar-process/src/test/java/org/sonar/process/systeminfo/SystemInfoHttpServerTest.java View File

@@ -0,0 +1,89 @@
/*
* 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.systeminfo;

import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.net.ConnectException;
import java.util.Arrays;
import java.util.Properties;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_PROCESS_INDEX;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;

public class SystemInfoHttpServerTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Rule
public ExpectedException expectedException = ExpectedException.none();

SystemInfoSection stateProvider1 = new ProcessStateSystemInfo("state1");
SystemInfoSection stateProvider2 = new ProcessStateSystemInfo("state2");
SystemInfoHttpServer underTest;

@Before
public void setUp() throws Exception {
Properties properties = new Properties();
properties.setProperty(PROPERTY_PROCESS_INDEX, "1");
properties.setProperty(PROPERTY_SHARED_PATH, temp.newFolder().getAbsolutePath());
underTest = new SystemInfoHttpServer(properties, Arrays.asList(stateProvider1, stateProvider2));
}

@After
public void tearDown() {
underTest.stop();
}

@Test
public void start_starts_http_server_and_publishes_URL_in_IPC() throws Exception {
underTest.start();
Response response = call(underTest.getUrl());
assertThat(response.code()).isEqualTo(200);
ProtobufSystemInfo.SystemInfo systemInfo = ProtobufSystemInfo.SystemInfo.parseFrom(response.body().bytes());
assertThat(systemInfo.getSectionsCount()).isEqualTo(2);
assertThat(systemInfo.getSections(0).getName()).isEqualTo("state1");
assertThat(systemInfo.getSections(1).getName()).isEqualTo("state2");
}

@Test
public void stop_stops_http_server() throws Exception {
underTest.start();
underTest.stop();
expectedException.expect(ConnectException.class);
call(underTest.getUrl());
}

private static Response call(String url) throws IOException {
Request request = new Request.Builder().get().url(url).build();
return new OkHttpClient().newCall(request).execute();
}
}

+ 0
- 1
server/sonar-search/src/main/java/org/sonar/search/EsSettings.java View File

@@ -33,7 +33,6 @@ 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;

public class EsSettings implements EsSettingsMBean {


server/sonar-process/src/main/java/org/sonar/process/jmx/EsSettingsMBean.java → server/sonar-search/src/main/java/org/sonar/search/EsSettingsMBean.java View File

@@ -17,7 +17,7 @@
* 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;
package org.sonar.search;

/**
* MBean registered in the Elasticsearch process

+ 1
- 2
server/sonar-search/src/main/java/org/sonar/search/SearchServer.java View File

@@ -22,12 +22,11 @@ package org.sonar.search;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.internal.InternalNode;
import org.sonar.process.Jmx;
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 {


+ 9
- 0
server/sonar-server/pom.xml View File

@@ -159,6 +159,10 @@
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sonar-ws</artifactId>
@@ -223,6 +227,11 @@
<artifactId>subethasmtp</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>

</dependencies>


+ 4
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/CeModule.java View File

@@ -22,6 +22,8 @@ package org.sonar.server.computation;
import org.sonar.ce.log.CeLogging;
import org.sonar.core.platform.Module;
import org.sonar.db.purge.period.DefaultPeriodCleaner;
import org.sonar.process.systeminfo.ProcessStateSystemInfo;
import org.sonar.process.systeminfo.SystemInfoHttpServer;
import org.sonar.server.computation.configuration.CeConfigurationImpl;
import org.sonar.server.computation.dbcleaner.IndexPurgeListener;
import org.sonar.server.computation.dbcleaner.ProjectCleaner;
@@ -37,6 +39,8 @@ public class CeModule extends Module {
CeConfigurationImpl.class,
CeLogging.class,
CeDatabaseMBeanImpl.class,
SystemInfoHttpServer.class,
new ProcessStateSystemInfo("Compute Engine State"),

DefaultPeriodCleaner.class,
ProjectCleaner.class,

server/sonar-process/src/main/java/org/sonar/process/jmx/CeDatabaseMBean.java → server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBean.java View File

@@ -17,11 +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.process.jmx;
package org.sonar.server.computation.monitoring;

public interface CeDatabaseMBean {

String OBJECT_NAME = "SonarQube:name=ComputeEngineDatabase";
String OBJECT_NAME = "SonarQube:name=ComputeEngineDatabaseConnection";

int getPoolActiveConnections();


+ 19
- 3
server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImpl.java View File

@@ -22,10 +22,11 @@ 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;
import org.sonar.process.Jmx;
import org.sonar.process.systeminfo.SystemInfoSection;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

public class CeDatabaseMBeanImpl implements CeDatabaseMBean, Startable {
public class CeDatabaseMBeanImpl implements CeDatabaseMBean, Startable, SystemInfoSection {
private final DbClient dbClient;

public CeDatabaseMBeanImpl(DbClient dbClient) {
@@ -94,4 +95,19 @@ public class CeDatabaseMBeanImpl implements CeDatabaseMBean, Startable {
return (BasicDataSource) dbClient.getDatabase().getDataSource();
}

@Override
public ProtobufSystemInfo.Section toProtobuf() {
ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder();
builder.setName("Compute Engine Database Connection");
builder.addAttributesBuilder().setKey("Pool Initial Size").setLongValue(getPoolInitialSize()).build();
builder.addAttributesBuilder().setKey("Pool Active Connections").setLongValue(getPoolActiveConnections()).build();
builder.addAttributesBuilder().setKey("Pool Idle Connections").setLongValue(getPoolIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Active Connections").setLongValue(getPoolMaxActiveConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Idle Connections").setLongValue(getPoolMaxIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Min Idle Connections").setLongValue(getPoolMinIdleConnections()).build();
builder.addAttributesBuilder().setKey("Pool Max Wait (ms)").setLongValue(getPoolMaxWaitMillis()).build();
builder.addAttributesBuilder().setKey("Pool Remove Abandoned").setBooleanValue(getPoolRemoveAbandoned()).build();
builder.addAttributesBuilder().setKey("Pool Remove Abandoned Timeout (sec)").setLongValue(getPoolRemoveAbandonedTimeoutSeconds()).build();
return builder.build();
}
}

server/sonar-process/src/main/java/org/sonar/process/jmx/CeTasksMBean.java → server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBean.java View File

@@ -17,7 +17,7 @@
* 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;
package org.sonar.server.computation.monitoring;

public interface CeTasksMBean {


+ 17
- 3
server/sonar-server/src/main/java/org/sonar/server/computation/monitoring/CeTasksMBeanImpl.java View File

@@ -21,11 +21,12 @@ package org.sonar.server.computation.monitoring;

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.process.Jmx;
import org.sonar.process.systeminfo.SystemInfoSection;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.computation.configuration.CeConfiguration;

public class CeTasksMBeanImpl implements CeTasksMBean, Startable {
public class CeTasksMBeanImpl implements CeTasksMBean, Startable, SystemInfoSection {
private final CEQueueStatus queueStatus;
private final CeConfiguration ceConfiguration;

@@ -76,4 +77,17 @@ public class CeTasksMBeanImpl implements CeTasksMBean, Startable {
public int getWorkerCount() {
return ceConfiguration.getWorkerCount();
}

@Override
public ProtobufSystemInfo.Section toProtobuf() {
ProtobufSystemInfo.Section.Builder builder = ProtobufSystemInfo.Section.newBuilder();
builder.setName("Compute Engine Tasks");
builder.addAttributesBuilder().setKey("Pending").setLongValue(getPendingCount()).build();
builder.addAttributesBuilder().setKey("In Progress").setLongValue(getInProgressCount()).build();
builder.addAttributesBuilder().setKey("Processed With Error").setLongValue(getErrorCount()).build();
builder.addAttributesBuilder().setKey("Processed With Success").setLongValue(getSuccessCount()).build();
builder.addAttributesBuilder().setKey("Processing Time (ms)").setLongValue(getProcessingTime()).build();
builder.addAttributesBuilder().setKey("Worker Count").setLongValue(getWorkerCount()).build();
return builder.build();
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/BaseMonitorMBean.java View File

@@ -20,7 +20,7 @@
package org.sonar.server.platform.monitoring;

import org.picocontainer.Startable;
import org.sonar.process.jmx.Jmx;
import org.sonar.process.Jmx;

/**
* Base implementation of a {@link org.sonar.server.platform.monitoring.Monitor}

+ 0
- 65
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeDatabaseMonitor.java View File

@@ -1,65 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
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 static final int NUMBER_OF_ATTRIBUTES = 9;

private final JmxConnectionFactory jmxConnectionFactory;

public CeDatabaseMonitor(JmxConnectionFactory jmxConnectionFactory) {
this.jmxConnectionFactory = jmxConnectionFactory;
}

@Override
public String name() {
return "Compute Engine Database Connection";
}

@Override
public Optional<Map<String, Object>> attributes() {
try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) {
if (connection == null) {
return Optional.absent();
}
Map<String, Object> result = new LinkedHashMap<>(NUMBER_OF_ATTRIBUTES);
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 Optional.of(result);
}
}
}

+ 0
- 50
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeStateMonitor.java View File

@@ -1,50 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
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 Optional<Map<String, Object>> attributes() {
try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) {
if (connection == null) {
return Optional.absent();
}
return Optional.of(connection.getSystemState());
}
}
}

+ 0
- 62
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/CeTasksMonitor.java View File

@@ -1,62 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
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 static final int NUMBER_OF_ATTRIBUTES = 6;

private final JmxConnectionFactory jmxConnectionFactory;

public CeTasksMonitor(JmxConnectionFactory jmxConnectionFactory) {
this.jmxConnectionFactory = jmxConnectionFactory;
}

@Override
public String name() {
return "Compute Engine Tasks";
}

@Override
public Optional<Map<String, Object>> attributes() {
try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)) {
if (connection == null) {
return Optional.absent();
}
Map<String, Object> result = new LinkedHashMap<>(NUMBER_OF_ATTRIBUTES);
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 Optional.of(result);
}
}
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/DatabaseMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
@@ -101,11 +100,11 @@ public class DatabaseMonitor extends BaseMonitorMBean implements DatabaseMonitor
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> attributes = new LinkedHashMap<>();
completeDbAttributes(attributes);
completePoolAttributes(attributes);
return Optional.of(attributes);
return attributes;
}

private void completePoolAttributes(Map<String, Object> attributes) {

+ 3
- 19
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/EsMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
@@ -30,21 +29,15 @@ 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(JmxConnectionFactory jmxConnectionFactory, EsClient esClient) {
this.jmxConnectionFactory = jmxConnectionFactory;
public EsMonitor(EsClient esClient) {
this.esClient = esClient;
}

@@ -72,22 +65,13 @@ public class EsMonitor extends BaseMonitorMBean implements EsMonitorMBean {
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> attributes = new LinkedHashMap<>();

try (JmxConnection connection = jmxConnectionFactory.create(ProcessId.ELASTICSEARCH)) {
if (connection != null) {
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());
attributes.put("Nodes", nodeAttributes());
return Optional.of(attributes);
return attributes;
}

private LinkedHashMap<String, LinkedHashMap<String, Object>> indexAttributes() {

+ 0
- 46
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProvider.java View File

@@ -1,46 +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.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 JmxConnectionFactoryProvider 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);
}
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/JvmPropsMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -31,11 +30,11 @@ public class JvmPropsMonitor implements Monitor {
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> sortedProps = new TreeMap<>();
for (Map.Entry<Object, Object> systemProp : System.getProperties().entrySet()) {
sortedProps.put(Objects.toString(systemProp.getKey()), Objects.toString(systemProp.getValue()));
}
return Optional.of(sortedProps);
return sortedProps;
}
}

+ 1
- 7
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/Monitor.java View File

@@ -19,9 +19,7 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
import org.sonar.server.platform.ws.InfoAction;

@@ -29,7 +27,6 @@ import org.sonar.server.platform.ws.InfoAction;
* Any component that is involved in the information returned by the web service api/system/info
*/
@ServerSide
@ComputeEngineSide
public interface Monitor {
/**
* Name of section in System Info page
@@ -39,9 +36,6 @@ public interface Monitor {
/**
* Type of attribute values must be supported by {@link org.sonar.api.utils.text.JsonWriter#valueObject(Object)}
* because of JSON export by {@link InfoAction}.
*
* @return map of attributes, or Optional.absent() if the monitored component is not up. In the latter case
* nothing is returned in the web service api/system/info.
*/
Optional<Map<String, Object>> attributes();
Map<String, Object> attributes();
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/PluginsMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
import org.sonar.core.platform.PluginInfo;
@@ -42,7 +41,7 @@ public class PluginsMonitor implements Monitor {
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> attributes = new LinkedHashMap<>();
for (PluginInfo plugin : repository.getPluginInfos()) {
LinkedHashMap<String, Object> pluginAttributes = new LinkedHashMap<>();
@@ -53,6 +52,6 @@ public class PluginsMonitor implements Monitor {
}
attributes.put(plugin.getKey(), pluginAttributes);
}
return Optional.of(attributes);
return attributes;
}
}

server/sonar-process/src/main/java/org/sonar/process/jmx/JmxConnectionFactory.java → server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/ProcessSystemInfoClient.java View File

@@ -17,51 +17,45 @@
* 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;
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.io.File;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.net.URI;
import org.apache.commons.io.IOUtils;
import org.sonar.api.config.Settings;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.ProcessEntryPoint;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;

/**
* Connects to JMX of other JVM processes
* Connects to the System Info HTTP server of another JVM process.
*/
@Immutable
public class JmxConnectionFactory {
private final File ipcSharedDir;
public class ProcessSystemInfoClient {

public JmxConnectionFactory(File ipcSharedDir) {
this.ipcSharedDir = ipcSharedDir;
}
private final File ipcSharedDir;

public JmxConnectionFactory(Props props) {
this.ipcSharedDir = props.nonNullValueAsFile(ProcessEntryPoint.PROPERTY_SHARED_PATH);
public ProcessSystemInfoClient(Settings props) {
this.ipcSharedDir = new File(props.getString(PROPERTY_SHARED_PATH));
}

@CheckForNull
public JmxConnection create(ProcessId processId) {
/**
* Connects to the specified JVM process and requests system information.
* @return the system info, or absent if the process is not up or if its HTTP URL
* is not registered into IPC.
*/
public Optional<ProtobufSystemInfo.SystemInfo> connect(ProcessId processId) {
try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(ipcSharedDir, processId.getIpcIndex())) {
if (commands.isUp()) {
String url = commands.getJmxUrl();
JMXConnector jmxConnector = JMXConnectorFactory.newJMXConnector(new JMXServiceURL(url), null);
jmxConnector.connect();
return new JmxConnection(jmxConnector);
String url = commands.getSystemInfoUrl();
byte[] protobuf = IOUtils.toByteArray(new URI(url));
return Optional.of(ProtobufSystemInfo.SystemInfo.parseFrom(protobuf));
}
return null;
return Optional.absent();
} catch (Exception e) {
throw new IllegalStateException("Can not connect to process " + processId, e);
throw new IllegalStateException("Can not get system info of process " + processId, e);
}
}

// visible for testing
File getIpcSharedDir() {
return ipcSharedDir;
}
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SonarQubeMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -97,7 +96,7 @@ public class SonarQubeMonitor extends BaseMonitorMBean implements SonarQubeMonit
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("Server ID", getServerId());
attributes.put("Version", getVersion());
@@ -111,7 +110,7 @@ public class SonarQubeMonitor extends BaseMonitorMBean implements SonarQubeMonit
attributes.put("Temp Dir", settings.getString(ProcessProperties.PATH_TEMP));
attributes.put("Logs Dir", settings.getString(ProcessProperties.PATH_LOGS));
attributes.put("Logs Level", getLogLevel());
return Optional.of(attributes);
return attributes;

}
}

+ 2
- 3
server/sonar-server/src/main/java/org/sonar/server/platform/monitoring/SystemMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
@@ -53,7 +52,7 @@ public class SystemMonitor implements Monitor {
}

@Override
public Optional<Map<String, Object>> attributes() {
public Map<String, Object> attributes() {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("System Date", formatDateTime(new Date(system.now())));
attributes.put("Start Time", formatDateTime(new Date(runtimeMXBean().getStartTime())));
@@ -76,7 +75,7 @@ public class SystemMonitor implements Monitor {
attributes.put("Threads", threadMXBean().getThreadCount());
attributes.put("Threads Peak", threadMXBean().getPeakThreadCount());
attributes.put("Daemon Thread", threadMXBean().getDaemonThreadCount());
return Optional.of(attributes);
return attributes;
}

private static RuntimeMXBean runtimeMXBean() {

+ 2
- 10
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java View File

@@ -149,15 +149,11 @@ 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.EsStateMonitor;
import org.sonar.server.platform.monitoring.JmxConnectionFactoryProvider;
import org.sonar.server.platform.monitoring.JvmPropsMonitor;
import org.sonar.server.platform.monitoring.PluginsMonitor;
import org.sonar.server.platform.monitoring.ProcessSystemInfoClient;
import org.sonar.server.platform.monitoring.SonarQubeMonitor;
import org.sonar.server.platform.monitoring.SystemMonitor;
import org.sonar.server.platform.ws.ChangeLogLevelAction;
@@ -635,7 +631,7 @@ public class PlatformLevel4 extends PlatformLevel {
TypeValidationModule.class,

// System
new JmxConnectionFactoryProvider(),
ProcessSystemInfoClient.class,
ServerLogging.class,
RestartAction.class,
InfoAction.class,
@@ -648,10 +644,6 @@ public class PlatformLevel4 extends PlatformLevel {
PluginsMonitor.class,
JvmPropsMonitor.class,
DatabaseMonitor.class,
EsStateMonitor.class,
CeStateMonitor.class,
CeTasksMonitor.class,
CeDatabaseMonitor.class,
MigrateDbAction.class,
LogsAction.class,
ChangeLogLevelAction.class,

+ 38
- 7
server/sonar-server/src/main/java/org/sonar/server/platform/ws/InfoAction.java View File

@@ -26,7 +26,10 @@ import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.process.ProcessId;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.platform.monitoring.Monitor;
import org.sonar.server.platform.monitoring.ProcessSystemInfoClient;
import org.sonar.server.user.UserSession;

/**
@@ -34,11 +37,13 @@ import org.sonar.server.user.UserSession;
*/
public class InfoAction implements SystemWsAction {

private final Monitor[] monitors;
private final UserSession userSession;
private final ProcessSystemInfoClient processSystemInfoClient;
private final Monitor[] monitors;

public InfoAction(UserSession userSession, Monitor... monitors) {
public InfoAction(UserSession userSession, ProcessSystemInfoClient processSystemInfoClient, Monitor... monitors) {
this.userSession = userSession;
this.processSystemInfoClient = processSystemInfoClient;
this.monitors = monitors;
}

@@ -64,12 +69,38 @@ public class InfoAction implements SystemWsAction {
private void writeJson(JsonWriter json) {
json.beginObject();
for (Monitor monitor : monitors) {
Optional<Map<String, Object>> attributes = monitor.attributes();
if (attributes.isPresent()) {
json.name(monitor.name());
Map<String, Object> attributes = monitor.attributes();
json.name(monitor.name());
json.beginObject();
for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
json.name(attribute.getKey()).valueObject(attribute.getValue());
}
json.endObject();
}
Optional<ProtobufSystemInfo.SystemInfo> ceSysInfo = processSystemInfoClient.connect(ProcessId.COMPUTE_ENGINE);
if (ceSysInfo.isPresent()) {
for (ProtobufSystemInfo.Section section : ceSysInfo.get().getSectionsList()) {
json.name(section.getName());
json.beginObject();
for (Map.Entry<String, Object> attribute : attributes.get().entrySet()) {
json.name(attribute.getKey()).valueObject(attribute.getValue());
for (ProtobufSystemInfo.Attribute attribute : section.getAttributesList()) {
switch (attribute.getValueCase()) {
case BOOLEAN_VALUE:
json.name(attribute.getKey()).valueObject(attribute.getBooleanValue());
break;
case LONG_VALUE:
json.name(attribute.getKey()).valueObject(attribute.getLongValue());
break;
case DOUBLE_VALUE:
json.name(attribute.getKey()).valueObject(attribute.getDoubleValue());
break;
case STRING_VALUE:
json.name(attribute.getKey()).valueObject(attribute.getStringValue());
break;
case VALUE_NOT_SET:
break;
default:
throw new IllegalArgumentException("Unsupported type: " + attribute.getValueCase());
}
}
json.endObject();
}

+ 70
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeDatabaseMBeanImplTest.java View File

@@ -0,0 +1,70 @@
/*
* 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 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.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static org.assertj.core.api.Assertions.assertThat;

public class CeDatabaseMBeanImplTest {

@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);

CeDatabaseMBeanImpl underTest = new CeDatabaseMBeanImpl(dbTester.getDbClient());

@Test
public void register_and_unregister() throws Exception {
assertThat(getMBean()).isNull();

underTest.start();
assertThat(getMBean()).isNotNull();

underTest.stop();
assertThat(getMBean()).isNull();
}

@Test
public void export_system_info() {
ProtobufSystemInfo.Section section = underTest.toProtobuf();
assertThat(section.getName()).isEqualTo("Compute Engine Database Connection");
assertThat(section.getAttributesCount()).isEqualTo(9);
assertThat(section.getAttributes(0).getKey()).isEqualTo("Pool Initial Size");
assertThat(section.getAttributes(0).getLongValue()).isGreaterThanOrEqualTo(0);
}

@CheckForNull
private ObjectInstance getMBean() throws Exception {
try {
return ManagementFactory.getPlatformMBeanServer().getObjectInstance(new ObjectName(CeDatabaseMBean.OBJECT_NAME));
} catch (InstanceNotFoundException e) {
return null;
}
}
}

+ 8
- 1
server/sonar-server/src/test/java/org/sonar/server/computation/monitoring/CeTasksMBeanImplTest.java View File

@@ -26,7 +26,7 @@ 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.process.systeminfo.protobuf.ProtobufSystemInfo;
import org.sonar.server.computation.configuration.CeConfiguration;

import static org.assertj.core.api.Assertions.assertThat;
@@ -66,6 +66,13 @@ public class CeTasksMBeanImplTest {
assertThat(underTest.getWorkerCount()).isEqualTo(WORKER_COUNT);
}

@Test
public void export_system_info() {
ProtobufSystemInfo.Section section = underTest.toProtobuf();
assertThat(section.getName()).isEqualTo("Compute Engine Tasks");
assertThat(section.getAttributesCount()).isEqualTo(6);
}

/**
* Dumb implementation of CEQueueStatus which returns constant values for get methods and throws UnsupportedOperationException
* for other methods.

+ 0
- 61
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeDatabaseMonitorTest.java View File

@@ -1,61 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
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 attributes() {
CeDatabaseMBean mbean = mock(CeDatabaseMBean.class, Mockito.RETURNS_DEFAULTS);

when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE).getMBean(CeDatabaseMBean.OBJECT_NAME, CeDatabaseMBean.class))
.thenReturn(mbean);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.get()).containsKeys("Pool Initial Size", "Pool Active Connections");
assertThat(attributes.get()).hasSize(9);
}

@Test
public void absent_attributes_if_CE_is_down() {
when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)).thenReturn(null);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.isPresent()).isFalse();
}
}

+ 0
- 61
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeStateMonitorTest.java View File

@@ -1,61 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSortedMap;
import java.util.Map;
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.<String, Object>of(
"foo", "foo_val", "bar", "bar_val"));
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.get()).containsExactly(
MapEntry.entry("bar", "bar_val"),
MapEntry.entry("foo", "foo_val"));
}

@Test
public void absent_attributes_if_CE_is_down() {
when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)).thenReturn(null);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.isPresent()).isFalse();
}
}

+ 0
- 61
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/CeTasksMonitorTest.java View File

@@ -1,61 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
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);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.get()).containsKeys("Pending");
assertThat(attributes.get()).hasSize(6);
}

@Test
public void absent_attributes_if_CE_is_down() {
when(jmxConnectionFactory.create(ProcessId.COMPUTE_ENGINE)).thenReturn(null);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.isPresent()).isFalse();
}
}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/DatabaseMonitorTest.java View File

@@ -49,7 +49,7 @@ public class DatabaseMonitorTest {

@Test
public void db_info() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();
assertThat(attributes.get("Database")).isEqualTo("H2");
assertThat(attributes.get("Database Version").toString()).startsWith("1.");
assertThat(attributes.get("Username")).isEqualTo("SONAR");
@@ -58,7 +58,7 @@ public class DatabaseMonitorTest {

@Test
public void pool_info() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();
assertThat((int) attributes.get("Pool Max Connections")).isGreaterThan(0);
}
}

+ 4
- 18
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsMonitorTest.java View File

@@ -21,35 +21,21 @@ package org.sonar.server.platform.monitoring;

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);
}
EsMonitor underTest = new EsMonitor(esTester.client());

@Test
public void name() {
@@ -58,7 +44,7 @@ public class EsMonitorTest {

@Test
public void cluster_attributes() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> 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);
@@ -66,7 +52,7 @@ public class EsMonitorTest {

@Test
public void node_attributes() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();
Map nodesAttributes = (Map) attributes.get("Nodes");

// one node
@@ -78,7 +64,7 @@ public class EsMonitorTest {

@Test
public void index_attributes() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();
Map indicesAttributes = (Map) attributes.get("Indices");

// one index "issues"

+ 0
- 60
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/EsStateMonitorTest.java View File

@@ -1,60 +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.server.platform.monitoring;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSortedMap;
import java.util.Map;
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.<String, Object>of(
"foo", "foo_val", "bar", "bar_val"));
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.get()).containsExactly(
MapEntry.entry("bar", "bar_val"),
MapEntry.entry("foo", "foo_val"));
}

@Test
public void absent_attributes_if_CE_is_down() {
when(jmxConnectionFactory.create(ProcessId.ELASTICSEARCH)).thenReturn(null);
Optional<Map<String, Object>> attributes = underTest.attributes();
assertThat(attributes.isPresent()).isFalse();
}
}

+ 2
- 3
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/FakeMonitor.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Collections;
import java.util.Map;

@@ -36,7 +35,7 @@ public class FakeMonitor extends BaseMonitorMBean implements FakeMonitorMBean {
}

@Override
public Optional<Map<String, Object>> attributes() {
return Optional.of(Collections.<String, Object>emptyMap());
public Map<String, Object> attributes() {
return Collections.emptyMap();
}
}

+ 0
- 56
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JmxConnectionFactoryProviderTest.java View File

@@ -1,56 +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.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();
JmxConnectionFactoryProvider underTest = new JmxConnectionFactoryProvider();

@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);
}
}

+ 2
- 3
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/JvmPropsMonitorTest.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.util.Map;
import org.junit.Test;

@@ -36,8 +35,8 @@ public class JvmPropsMonitorTest {

@Test
public void attributes() {
Optional<Map<String, Object>> attributes = underTest.attributes();
Map<String, Object> attributes = underTest.attributes();

assertThat(attributes.get()).containsKeys("java.vm.vendor", "os.name");
assertThat(attributes).containsKeys("java.vm.vendor", "os.name");
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/PluginsMonitorTest.java View File

@@ -52,7 +52,7 @@ public class PluginsMonitorTest {
new PluginInfo("no-version")
.setName("No Version")));

Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();

assertThat(attributes).containsKeys("key-1", "key-2");
assertThat((Map) attributes.get("key-1"))

+ 99
- 0
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/ProcessSystemInfoClientTest.java View File

@@ -0,0 +1,99 @@
/*
* 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.base.Optional;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import java.io.File;
import okio.Buffer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.config.Settings;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.ProcessEntryPoint;
import org.sonar.process.ProcessId;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;

import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;

public class ProcessSystemInfoClientTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Rule
public MockWebServer server = new MockWebServer();

File ipcSharedDir;
ProcessSystemInfoClient underTest;

@Before
public void setUp() throws Exception {
ipcSharedDir = temp.newFolder();
Settings settings = new Settings();
settings.setProperty(ProcessEntryPoint.PROPERTY_SHARED_PATH, ipcSharedDir.getAbsolutePath());
underTest = new ProcessSystemInfoClient(settings);
}

@Test
public void connect_returns_absent_if_process_is_down() throws Exception {
Optional<ProtobufSystemInfo.SystemInfo> info = underTest.connect(ProcessId.COMPUTE_ENGINE);

assertThat(info.isPresent()).isFalse();
}

@Test
public void get_information_if_process_is_up() throws Exception {
Buffer response = new Buffer();
response.read(ProtobufSystemInfo.Section.newBuilder().build().toByteArray());
server.enqueue(new MockResponse().setBody(response));

// initialize registration of process
try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(ipcSharedDir, ProcessId.COMPUTE_ENGINE.getIpcIndex())) {
processCommands.setUp();
processCommands.setSystemInfoUrl(format("http://%s:%d", server.getHostName(), server.getPort()));
}

Optional<ProtobufSystemInfo.SystemInfo> info = underTest.connect(ProcessId.COMPUTE_ENGINE);
assertThat(info.get().getSectionsCount()).isEqualTo(0);
}

@Test
public void throws_ISE_if_http_error() throws Exception {
server.enqueue(new MockResponse().setResponseCode(500));

// initialize registration of process
try (DefaultProcessCommands processCommands = DefaultProcessCommands.secondary(ipcSharedDir, ProcessId.COMPUTE_ENGINE.getIpcIndex())) {
processCommands.setUp();
processCommands.setSystemInfoUrl(format("http://%s:%d", server.getHostName(), server.getPort()));
}

expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Can not get system info of process " + ProcessId.COMPUTE_ENGINE);
underTest.connect(ProcessId.COMPUTE_ENGINE);
}
}

+ 8
- 9
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SonarQubeMonitorTest.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.platform.monitoring;

import com.google.common.base.Optional;
import java.io.File;
import java.util.Map;
import org.apache.commons.io.FileUtils;
@@ -62,8 +61,8 @@ public class SonarQubeMonitorTest {
when(server.getStartedAt()).thenReturn(DateUtils.parseDate("2015-01-01"));
SonarQubeMonitor monitor = new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server, serverLogging);

Optional<Map<String, Object>> attributes = monitor.attributes();
assertThat(attributes.get()).containsKeys("Server ID", "Version");
Map<String, Object> attributes = monitor.attributes();
assertThat(attributes).containsKeys("Server ID", "Version");
}

@Test
@@ -74,8 +73,8 @@ public class SonarQubeMonitorTest {
when(server.getRootDir()).thenReturn(rootDir);
SonarQubeMonitor monitor = new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server, serverLogging);

Optional<Map<String, Object>> attributes = monitor.attributes();
assertThat(attributes.get()).containsEntry("Official Distribution", Boolean.TRUE);
Map<String, Object> attributes = monitor.attributes();
assertThat(attributes).containsEntry("Official Distribution", Boolean.TRUE);
}

@Test
@@ -85,15 +84,15 @@ public class SonarQubeMonitorTest {
when(server.getRootDir()).thenReturn(rootDir);
SonarQubeMonitor monitor = new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server, serverLogging);

Optional<Map<String, Object>> attributes = monitor.attributes();
assertThat(attributes.get()).containsEntry("Official Distribution", Boolean.FALSE);
Map<String, Object> attributes = monitor.attributes();
assertThat(attributes).containsEntry("Official Distribution", Boolean.FALSE);
}

@Test
public void get_log_level() throws Exception {
SonarQubeMonitor monitor = new SonarQubeMonitor(settings, new SecurityRealmFactory(settings), server, serverLogging);

Optional<Map<String, Object>> attributes = monitor.attributes();
assertThat(attributes.get()).containsEntry("Logs Level", "DEBUG");
Map<String, Object> attributes = monitor.attributes();
assertThat(attributes).containsEntry("Logs Level", "DEBUG");
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/platform/monitoring/SystemMonitorTest.java View File

@@ -35,7 +35,7 @@ public class SystemMonitorTest {

@Test
public void system_properties() {
Map<String, Object> attributes = underTest.attributes().get();
Map<String, Object> attributes = underTest.attributes();

assertThat(attributes).containsKeys("System Date", "Processors");
}

+ 7
- 8
server/sonar-server/src/test/java/org/sonar/server/platform/ws/InfoActionTest.java View File

@@ -19,17 +19,18 @@
*/
package org.sonar.server.platform.ws;

import com.google.common.base.Optional;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.internal.SimpleGetRequest;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.platform.monitoring.Monitor;
import org.sonar.server.platform.monitoring.ProcessSystemInfoClient;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsTester;

@@ -40,12 +41,12 @@ import static org.mockito.Mockito.when;
public class InfoActionTest {
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone().login("login")
.setName("name");
.setName("name");

Monitor monitor1 = mock(Monitor.class);
Monitor monitor2 = mock(Monitor.class);
Monitor monitor3 = mock(Monitor.class);
InfoAction underTest = new InfoAction(userSessionRule, monitor1, monitor2, monitor3);
ProcessSystemInfoClient processSystemInfoClient = mock(ProcessSystemInfoClient.class, Mockito.RETURNS_MOCKS);
InfoAction underTest = new InfoAction(userSessionRule, processSystemInfoClient, monitor1, monitor2);

@Test(expected = ForbiddenException.class)
public void should_fail_when_does_not_have_admin_right() {
@@ -64,11 +65,9 @@ public class InfoActionTest {
attributes2.put("one", 1);
attributes2.put("two", 2);
when(monitor1.name()).thenReturn("Monitor One");
when(monitor1.attributes()).thenReturn(Optional.of(attributes1));
when(monitor1.attributes()).thenReturn(attributes1);
when(monitor2.name()).thenReturn("Monitor Two");
when(monitor2.attributes()).thenReturn(Optional.of(attributes2));
when(monitor3.name()).thenReturn("Monitor Three");
when(monitor3.attributes()).thenReturn(Optional.<Map<String, Object>>absent());
when(monitor2.attributes()).thenReturn(attributes2);

WsTester.TestResponse response = new WsTester.TestResponse();
underTest.handle(new SimpleGetRequest(), response);

+ 4
- 1
server/sonar-server/src/test/java/org/sonar/server/platform/ws/SystemWsTest.java View File

@@ -24,6 +24,7 @@ import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.WebService;
import org.sonar.server.app.ProcessCommandWrapper;
import org.sonar.server.platform.Platform;
import org.sonar.server.platform.monitoring.ProcessSystemInfoClient;
import org.sonar.server.tester.AnonymousMockUserSession;
import org.sonar.server.user.UserSession;

@@ -32,10 +33,12 @@ import static org.mockito.Mockito.mock;

public class SystemWsTest {

ProcessSystemInfoClient processSystemInfoClient = mock(ProcessSystemInfoClient.class);

@Test
public void define() {
RestartAction action1 = new RestartAction(mock(UserSession.class), mock(Settings.class), mock(Platform.class), mock(ProcessCommandWrapper.class));
InfoAction action2 = new InfoAction(new AnonymousMockUserSession());
InfoAction action2 = new InfoAction(new AnonymousMockUserSession(), processSystemInfoClient);
SystemWs ws = new SystemWs(action1, action2);
WebService.Context context = new WebService.Context();


+ 2
- 2
sonar-application/assembly.xml View File

@@ -71,8 +71,8 @@
<dependencySet>
<outputDirectory>lib/ce</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
<useTransitiveDependencies>false</useTransitiveDependencies>
<useTransitiveFiltering>false</useTransitiveFiltering>
<useTransitiveDependencies>true</useTransitiveDependencies>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>org.sonarsource.sonarqube:sonar-ce</include>
</includes>

+ 909
- 909
tests/upgrade/projects/struts-1.3.9-diet/core/src/main/resources/org/apache/struts/resources/web-app_2_3.dtd
File diff suppressed because it is too large
View File


Loading…
Cancel
Save