/*
* -----------------------------------------------------------------------\
* PerfCake
*
* Copyright (C) 2010 - 2016 the original author or authors.
*
* 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.perfcake.agent;
import com.sun.management.HotSpotDiagnosticMXBean;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/**
* Implements the PerfCake agent.
*
* @author <a href="mailto:pavel.macik@gmail.com">Pavel Macík</a>
*/
public class AgentThread implements Runnable {
/**
* Agent's arguments.
*/
private final String agentArgs;
/**
* Creates a new agent thread.
*
* @param agentArgs
* Arguments passed to the agent.
*/
public AgentThread(final String agentArgs) {
super();
this.agentArgs = agentArgs;
}
@Override
public void run() {
InetAddress host;
int port = PerfCakeAgent.DEFAULT_PORT;
ServerSocket serverSocket = null;
Socket socket;
InputStream is = null;
try {
// parse agent properties
final Properties props = new Properties();
if (!"".equals(agentArgs) && agentArgs != null) {
final String[] args = agentArgs.split(",");
for (final String arg : args) {
final String[] keyValuePair = arg.split("=");
if (keyValuePair.length == 2) {
props.put(keyValuePair[0], keyValuePair[1].trim());
} else {
err("Invalid agent argument \"" + arg + "\" - ignoring");
}
}
}
if (props.get("port") != null) {
port = Integer.parseInt((String) props.get("port"));
}
if (props.get("hostname") != null) {
host = InetAddress.getByName(props.getProperty("hostname"));
} else {
host = InetAddress.getLocalHost();
}
serverSocket = new ServerSocket(port, 1, host);
while (!Thread.currentThread().isInterrupted()) {
log("Listening at " + serverSocket.getInetAddress().getHostAddress() + " on port " + serverSocket.getLocalPort());
socket = serverSocket.accept();
log("Client connected from " + socket.getInetAddress().getHostAddress());
is = socket.getInputStream();
String command;
final BufferedReader br = new BufferedReader(new InputStreamReader(is, PerfCakeAgent.DEFAULT_ENCODING));
final PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), PerfCakeAgent.DEFAULT_ENCODING), true);
while ((command = br.readLine()) != null) {
String response = "Unrecognized command!";
final Runtime rt = Runtime.getRuntime();
try {
if (AgentCommand.FREE.name().equals(command)) {
response = String.valueOf(rt.freeMemory());
} else if (AgentCommand.MAX.name().equals(command)) {
response = String.valueOf(rt.maxMemory());
} else if (AgentCommand.TOTAL.name().equals(command)) {
response = String.valueOf(rt.totalMemory());
} else if (AgentCommand.USED.name().equals(command)) {
response = String.valueOf(rt.totalMemory() - rt.freeMemory());
} else if (command.startsWith(AgentCommand.DUMP.name())) {
final int colonIndex = command.indexOf(":");
String dumpFileName;
if (colonIndex >= 0) {
dumpFileName = command.substring(colonIndex + 1);
} else {
dumpFileName = "dump-" + System.currentTimeMillis() + ".bin";
}
int dumpNameIndex = 0;
File dumpFile = new File(dumpFileName);
while (dumpFile.exists()) {
log("WARNING: File " + dumpFileName + " already exists. Trying another file name.");
dumpFile = new File(dumpFileName + "." + (dumpNameIndex++));
}
log("Saving a heap dump to " + dumpFile.getAbsolutePath());
try {
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class).dumpHeap(dumpFile.getAbsolutePath(), true);
log("Heap dump saved to " + dumpFile.getAbsolutePath());
response = "0";
} catch (final IOException ioe) {
log("Error saving heap dump!");
ioe.printStackTrace();
response = "-1";
}
} else if (AgentCommand.GC.name().equals(command)) {
System.gc();
response = "0";
}
} catch (final IllegalArgumentException iae) {
err(iae.getLocalizedMessage());
response = "-1";
}
pw.println(response);
}
log("Client disconnected");
}
} catch (final Throwable e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Logs a message to standard output.
*
* @param message
* Message to be logged.
*/
private static void log(final String message) {
System.out.println(PerfCakeAgent.class.getSimpleName() + " > " + message);
}
/**
* Logs a message to error output.
*
* @param message
* Message to be logged.
*/
private static void err(final String message) {
System.err.println(PerfCakeAgent.class.getSimpleName() + " > ERROR: " + message);
}
}