/*******************************************************************************
* Copyright 2014 Miami-Dade County
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.sharegov.cirm.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import mjson.Json;
/**
*
* Class that uses jmx remote monitoring and polls a host:port
* to gather cpu and memory utilization.
*
* @author SABBAS
*
*/
public class ServerMonitorClient extends Thread{
private String hostname;
private String port;
private MBeanServerConnection connection;
private JMXConnector connector;
private Json stats;
private Long pollPeriod = 3000l;
private Long samples = 10l;
private File statsFile;
private File csvFile;
public ServerMonitorClient() {
this.hostname = "s0020284";
this.port = "9010";
this.statsFile = new File("C:/Work/cirmservices/server-stats.json");
try
{
this.init();
}catch (Exception e) {
e.printStackTrace(System.err);
}
}
public ServerMonitorClient(String hostname, String port, String file) throws Exception {
this.hostname = hostname;
this.port = port;
this.statsFile = new File(file);
this.init();
}
public ServerMonitorClient(String hostname, String port, String file, String csvFile) throws Exception {
this(hostname, port, file);
this.csvFile = new File(csvFile);
}
public ServerMonitorClient(String hostname, String port, String file,
String csvFile, long pollPeriod, long samples) throws Exception{
this(hostname, port, file,csvFile);
this.pollPeriod = pollPeriod;
this.samples = samples;
}
public void init()
throws IOException {
Integer portInteger = Integer.valueOf(port);
JMXServiceURL address = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://" + hostname + ":" + port
+ "/jmxrmi");
connector = JMXConnectorFactory.connect(address, null);
connection = connector.getMBeanServerConnection();
}
public void remotegc() throws Exception {
ObjectName memoryMXBean = new ObjectName("java.lang:type=Memory");
connection.invoke(memoryMXBean, "gc", null, null);
}
private Json getOperatingSystemDetails() throws Exception {
Json result = Json.object();
result.set("host", hostname);
ObjectName operatingSystemMXBean = new ObjectName(
"java.lang:type=OperatingSystem");
AttributeList list = connection.getAttributes(operatingSystemMXBean, new String[]{
"SystemLoadAverage",
"FreePhysicalMemorySize",
"ProcessCpuTime",
"CommittedVirtualMemorySize",
"FreeSwapSpaceSize",
"TotalPhysicalMemorySize",
"TotalSwapSpaceSize",
"Name",
"Version",
"Arch",
"AvailableProcessors"
});
for(Attribute a : list.asList())
{
result.set(a.getName(), a.getValue());
}
return result;
}
private Json getHeapMemoryUsage() throws Exception {
Json result = Json.object();
ObjectName memoryMXBean = new ObjectName("java.lang:type=Memory");
CompositeDataSupport dataSenders = (CompositeDataSupport) connection
.getAttribute(memoryMXBean, "HeapMemoryUsage");
if (dataSenders != null) {
Long committed = (Long) dataSenders.get("committed");
Long init = (Long) dataSenders.get("init");
Long max = (Long) dataSenders.get("max");
Long used = (Long) dataSenders.get("used");
Long percentage = ((used * 100) / max);
result.set("committed", committed)
.set("init", init)
.set("max", max)
.set("used", used)
.set("percentage", percentage);
}
return result;
}
private Json getNonHeapMemoryUsage() throws Exception {
Json result = Json.object();
ObjectName memoryMXBean = new ObjectName("java.lang:type=Memory");
CompositeDataSupport dataSenders = (CompositeDataSupport) connection
.getAttribute(memoryMXBean, "NonHeapMemoryUsage");
if (dataSenders != null) {
Long committed = (Long) dataSenders.get("committed");
Long init = (Long) dataSenders.get("init");
Long max = (Long) dataSenders.get("max");
Long used = (Long) dataSenders.get("used");
Long percentage = ((used * 100) / max);
result.set("committed", committed)
.set("init", init)
.set("max", max)
.set("used", used)
.set("percentage", percentage);
}
return result;
}
private Json getStats() throws Exception
{
return Json.object()
.set("OperatingSystem", getOperatingSystemDetails())
.set("HeapMemoryUsage", getHeapMemoryUsage())
.set("NonHeapMemoryUsage", getNonHeapMemoryUsage());
}
public void run()
{
System.out.println("ServerMonitorClient is running.");
int sampleCount = 0;
boolean interrupted = isInterrupted();
while(!interrupted)
{
try
{
if(sampleCount >= samples)
{
interrupt();
}
Long nanoBefore = System.nanoTime();
Json statsBefore;
if(stats == null)
{
stats = Json.array();
statsBefore = getStats();
stats.add(statsBefore.set("time", System.currentTimeMillis()).set("CpuLoad", 0.0));
}else
{
statsBefore = stats.at(stats.asList().size() - 1);
}
sleep(pollPeriod);
Json statsAfter = getStats();
Long nanoAfter = System.nanoTime();
statsAfter.set("time", System.currentTimeMillis());
statsAfter.set("CpuLoad",
calculateCpuLoad(nanoBefore, nanoAfter
, statsBefore.at("OperatingSystem").at("ProcessCpuTime").asLong()
, statsAfter.at("OperatingSystem").at("ProcessCpuTime").asLong()));
writeCSVRow(statsAfter, sampleCount == 0);
stats.add(statsAfter);
sampleCount++;
}catch(InterruptedException e)
{
interrupted = true;
}catch(Exception e)
{
e.printStackTrace(System.err);
}
}
}
private Double calculateCpuLoad(Long nanoBefore, Long nanoAfter, Long cpuBefore, Long cpuAfter)
{
// System.out.println("------------------------------");
// System.out.println("nanoBefore" + nanoBefore);
// System.out.println("nanoAfter" + nanoAfter);
// System.out.println("cpuBefore" + cpuBefore);
// System.out.println("cpuAfter" + cpuAfter);
// System.out.println("(cpuAfter - cpuBefore)" + (cpuAfter - cpuBefore));
// System.out.println("(nanoAfter - nanoBefore)" + (nanoAfter - nanoBefore));
double x = (cpuAfter - cpuBefore);
double y = (nanoAfter - nanoBefore);
double r = .0;
if(y > 0)
r = x/y;
// System.out.println("ratio" + r);
return (r>1)?1.0d:r;
}
private void write() {
if(statsFile != null && stats != null)
{
FileWriter writer;
try {
writer = new FileWriter(statsFile);
writer.write(stats.toString());
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void writeCSV(){
if(csvFile != null && stats != null)
{
try {
boolean headersWritten = false;
FileWriter writer = new FileWriter(csvFile);
for(Json stat: stats.asJsonList())
{
StringBuilder csvRow = new StringBuilder();
StringBuilder header = new StringBuilder();
for(Map.Entry<String, Json> properties : stat.asJsonMap().entrySet())
{
String key = properties.getKey();
Json value = properties.getValue();
if(value.isObject())
{
for(Map.Entry<String, Json> prop : value.asJsonMap().entrySet())
{
String k = prop.getKey();
String v = prop.getValue().toString();
csvRow.append("\"").append(v.toString().replaceAll(",", "\\,").replaceAll("\"", "")).append("\"").append(",");
if(!headersWritten)
header.append("\"").append(key).append("-").append(k.toString().replaceAll(",", "\\,")).append("\"").append(",");
}
}else{
if(!headersWritten)
header.append("\"").append(key.toString().replaceAll(",", "\\,")).append("\"").append(",");
csvRow.append("\"").append(value.toString().replaceAll(",", "\\,")).append("\"").append(",");
}
}
csvRow.deleteCharAt(csvRow.length() - 1);
csvRow.append("\n");
if(!headersWritten)
{
header.deleteCharAt(header.length() - 1);
header.append("\n");
writer.write(header.toString());
headersWritten = true;
}
writer.write(csvRow.toString());
}
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
private void writeCSVRow(Json row, boolean writeHeader){
if(row != null)
{
try {
FileWriter writer = new FileWriter(csvFile, !writeHeader);
StringBuilder csvRow = new StringBuilder();
StringBuilder header = new StringBuilder();
for(Map.Entry<String, Json> properties : row.asJsonMap().entrySet())
{
String key = properties.getKey();
Json value = properties.getValue();
if(value.isObject())
{
for(Map.Entry<String, Json> prop : value.asJsonMap().entrySet())
{
String k = prop.getKey();
String v = prop.getValue().toString();
csvRow.append("\"").append(v.toString().replaceAll(",", "\\,").replaceAll("\"", "")).append("\"").append(",");
if(writeHeader)
header.append("\"").append(key).append("-").append(k.toString().replaceAll(",", "\\,")).append("\"").append(",");
}
}else{
if(writeHeader)
header.append("\"").append(key.toString().replaceAll(",", "\\,")).append("\"").append(",");
csvRow.append("\"").append(value.toString().replaceAll(",", "\\,")).append("\"").append(",");
}
}
csvRow.deleteCharAt(csvRow.length() - 1);
csvRow.append("\n");
if(writeHeader)
{
header.deleteCharAt(header.length() - 1);
header.append("\n");
writer.write(header.toString());
}
writer.write(csvRow.toString());
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
protected void stopClient() {
interrupt();
//write();
//writeCSV();
}
/**
*
* java -classpath .\classes;.\lib\mjson-1.2.jar org.sharegov.cirm.utils.ServerMonitorClient s0020269 9010 "c:\work\cirmservices\server-stats.json" "c:\work\cirmservices\server-stats.csv"
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
if(args.length < 6)
{
System.out.println("Usage:");
System.out.println("java -classpath .\\classes;.\\lib\\mjson-1.2.jar org.sharegov.cirm.utils.ServerMonitorClient arg[0] arg[1] arg[2] arg[3] arg[4] arg[5]");
System.out.println("\t\t arg[0] - the server hostname to be monitored - i.e. s0020269");
System.out.println("\t\t arg[1] - the server port which jmx is running");
System.out.println("\t\t arg[2] - the fullpath where to store the monitor json file");
System.out.println("\t\t arg[3] - the fullpath where to store the monitor csv file");
System.out.println("\t\t arg[4] - the poll period, how often to poll the server in seconds");
System.out.println("\t\t arg[5] - how many total samples to take.");
System.exit(0);
}
String hostname = args[0];
String port = args[1];
String file = args[2];
String cFile = args[3];
String pollPeriod = args[4];
String samples = args[5];
final ServerMonitorClient client = new ServerMonitorClient(hostname, port, file, cFile, Long.parseLong(pollPeriod) * 1000, Long.parseLong(samples));
//client.setDaemon(true);
client.start();
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run(){
client.stopClient();
}});
}
}