@@ -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> |
@@ -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> |
@@ -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 |
@@ -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 |
@@ -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> |
@@ -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 |
@@ -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 |
@@ -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; |
@@ -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 |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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(); |
@@ -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()); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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)"); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 { | |||
@@ -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 |
@@ -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 { | |||
@@ -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> | |||
@@ -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, |
@@ -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(); | |||
@@ -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(); | |||
} | |||
} |
@@ -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 { | |||
@@ -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(); | |||
} | |||
} |
@@ -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} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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) { |
@@ -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() { |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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() { |
@@ -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, |
@@ -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(); | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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. |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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" |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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")) |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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"); | |||
} |
@@ -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); |
@@ -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(); | |||
@@ -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> |