/**
* Written by Fedor Burdun of Azul Systems, and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*
* @author Fedor Burdun
*/
package org.jrt.impl;
import org.jrt.socket.api.JRTHic;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.LatencyUtils.LatencyStats;
import org.jrt.socket.api.CodeWriter;
import org.jrt.socket.api.Transformer;
import org.jrt.socket.nio.NioSocketCodeWrapper;
import org.jrt.socket.regular.JavaNetSocketCodeWrapper;
public class JRT {
public static volatile boolean initialized = false;
public static volatile boolean finishByError = false;
public static long jrtInstances = 0;
private static final String title = "";
public long startTime;
public LatencyStats i2oLS;
public LatencyStats o2iLS;
public boolean isAlive = true;
public Configuration configuration = new Configuration();
public JRTStatistic jrtStat;
public Map<Object, JRTHic> sockRTs = new ConcurrentHashMap(new WeakHashMap<Object, JRTHic>());
public static void main(String[] args) {
System.out.println("jRT.jar doesn't have now functional main method. Please rerun your application as:\n\t"
+ "java -javaagent:jRT.jar -jar yourapp.jar");
System.exit(1);
}
private static String printKeys(String[] keys) {
StringBuilder sb = new StringBuilder();
for (String s : keys) {
if (sb.length() > 0) {
sb.append(" | ");
}
sb.append(s);
}
return sb.toString();
}
private static String printKeys(String[] keys, int align) {
String st = printKeys(keys);
return st + String.format("%0" + Math.max(1, align - st.length()) + "d", 0).replace('0', ' ');
}
public static void printHelpAndExit() {
System.out.println("Usage:");
System.out.println("\tjava -jar jRT.jar[=<args>] -jar yourapp.jar\n");
printHelpParameters();
System.out.println("\n");
System.out.println("Please rerun application with proper CLI options.\n");
finishByError = true;
System.exit(1);
}
public static void printHelpParameters() {
System.out.println("\t\twhere <args> is an comma separated list of arguments like arg1,arg2=val2 e.t.c\n");
System.out.println("\t\tARGUMENTS:");
System.out.println("\t\t " + printKeys(help, 40) + " to print help");
System.out.println("\t\t " + printKeys(remoteaddr, 40) + " to add filter by remote address");
System.out.println("\t\t " + printKeys(remoteport, 40) + " to add filter by remote port");
System.out.println("\t\t " + printKeys(localport, 40) + " to add filter by local port");
System.out.println("\t\t " + printKeys(filterentry, 40) + " to add filter by entry: <Local port>:<Remote address>:<Remote port> any part can be empty");
System.out.println("\t\t " + printKeys(loginterval, 40) + " to set log sampling interval");
System.out.println("\t\t " + printKeys(startdelaying, 40) + " to specify time delay to start jRT");
System.out.println("\t\t " + printKeys(workingtime, 40) + " to specify how long jRT will work");
System.out.println("\t\t " + printKeys(logprefix, 40) + " to specify jRT log prefix");
System.out.println("\t\t " + printKeys(uuid, 40) + " to specify jRT inner ID (take <string>)");
System.out.println("\t\t " + printKeys(ioMode, 40) + " to specify jRT mode. Expects one of i2o, o2i, both. Both by default");
// System.out.println("\t\t " + printKeys(i2oenabling, 40) + " to calculate latency (take <boolean>)");
// System.out.println("\t\t " + printKeys(o2ienabling, 40) + " to calculate latency (take <boolean>)");
}
public static ConcurrentHashMap<String, JRT> jRTWorkers = new ConcurrentHashMap<String, JRT>();
public void premain(String agentArgument, Instrumentation instrumentation) {
jrtStat = new JRTStatistic();
startTime = System.currentTimeMillis();
parseArguments(agentArgument);
jRTWorkers.put(configuration.uuid, this);
if (configuration.i2oEnabled) i2oLS = new LatencyStats();
if (configuration.o2iEnabled) o2iLS = new LatencyStats();
instrument(agentArgument, instrumentation);
//Some temporary place to print collected statistic.
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
synchronized (JRT.title) {
if (finishByError) {
return;
}
//TODO move/remove/improve
System.out.println("");
System.out.println("***************************************************************");
System.out.println("jRT configuration: ");
System.out.println("jRT uid " + configuration.uuid);
System.out.println("log files " + configuration.logPrefix + ".*");
System.out.println("---------------------------------------------------------------");
System.out.println("jRT Statistic: ");
System.out.println(" " + jrtStat.processedSocket + " sockets was processed");
System.out.println("***************************************************************");
System.out.flush();
}
}
});
LogWriter jRTLogWriter = new LogWriter(this);
jRTLogWriter.start();
}
public void parseArguments(String agentArgument) throws NumberFormatException {
if (null != agentArgument) {
for (String v : agentArgument.split(",")) {
String[] vArr = v.split("=");
if (vArr.length > 2) {
System.out.println("Wrong format jRT arguments.\n");
printHelpAndExit();
}
if (hasKey(help, vArr[0])) {
printHelpAndExit();
}
if (hasKey(remoteaddr, vArr[0])) {
configuration.filterEntries.add(
new Configuration.IOFilterEntry(null, vArr[1], null));
}
if (hasKey(localport, vArr[0])) {
configuration.filterEntries.add(
new Configuration.IOFilterEntry(vArr[1], null, null));
}
if (hasKey(remoteport, vArr[0])) {
configuration.filterEntries.add(
new Configuration.IOFilterEntry(null, null, vArr[1]));
}
if (hasKey(filterentry, vArr[0]) && vArr.length == 2) {
boolean isCorrect = true;
String localPort = null;
String remoteAddr = null;
String remotePort = null;
String[] ports = vArr[1].split(":");
if (ports.length > 0 && ports[0].length() > 0) {
localPort = ports[0];
}
if (ports.length > 1 && ports[1].length() > 0) {
remoteAddr = ports[1];
}
if (ports.length > 2 && ports[2].length() > 0) {
remotePort = ports[2];
}
if (ports.length < 2 || ports.length > 3) {
isCorrect = false;
}
//System.out.println("'" + localPort + "'" + remoteAddr + "'" + remotePort + "'");
if (!isCorrect) {
System.err.println("Wrong " + printKeys(filterentry) + " format\n\n");
printHelpAndExit();
}
configuration.filterEntries.add(
new Configuration.IOFilterEntry(localPort, remoteAddr, remotePort));
}
if (hasKey(loginterval, vArr[0])) {
configuration.logWriterInterval = Long.valueOf(vArr[1]);
}
if (hasKey(startdelaying, vArr[0])) {
configuration.startDelaying = Long.valueOf(vArr[1]);
}
if (hasKey(workingtime, vArr[0])) {
configuration.workingTime = Long.valueOf(vArr[1]);
}
if (hasKey(logprefix, vArr[0])) {
configuration.setLogNamePattern(vArr[1]);
}
if (hasKey(uuid, vArr[0])) {
configuration.uuid = (vArr[1]);
}
if (hasKey(i2oenabling, vArr[0])) {
configuration.i2oEnabled = Boolean.valueOf(vArr[1]);
}
if (hasKey(o2ienabling, vArr[0])) {
configuration.o2iEnabled = Boolean.valueOf(vArr[1]);
}
if (hasKey(ioMode, vArr[0])) {
if ("i2o".equals(vArr[1])) {
configuration.i2oEnabled = true;
configuration.o2iEnabled = false;
} else if ("o2i".equals(vArr[1])) {
configuration.i2oEnabled = false;
configuration.o2iEnabled = true;
} else if ("both".equals(vArr[1])) {
configuration.i2oEnabled = true;
configuration.o2iEnabled = true;
} else {
System.err.println("Parameter " + vArr[0] +
" expects one of i2o, o2i, both argument. But " + vArr[1] + " has been got.");
printHelpAndExit();
}
}
}
}
}
public void instrument(String agentArgument, Instrumentation instrumentation) {
instrumentation.addTransformer(new Transformer(this, new JavaNetSocketCodeWrapper()), true);
instrumentation.addTransformer(new Transformer(this, new NioSocketCodeWrapper()), true);
/*
untested code to support attaching to existing java process
*/
redeclare(instrumentation, new JavaNetSocketCodeWrapper());
redeclare(instrumentation, new NioSocketCodeWrapper());
}
/*
untested code to support attaching to existing java process
*/
private void redeclare(Instrumentation instrumentation, CodeWriter cw) {
ArrayList<Class> ac = new ArrayList<Class>();
for (Class c : instrumentation.getAllLoadedClasses()) {
final String className = c.getName().replace(".", "/");
if (cw.needInstrument(className)) {
ac.add(c);
try {
instrumentation.retransformClasses(c);
} catch (UnmodifiableClassException ex) {
ex.printStackTrace();
}
}
}
}
public static JRT premain0(String agentArgument, Instrumentation instrumentation) {
//Check here another instances and exit if then!
if (initialized) {
System.out.println("WARNING: multiple instances of jRT was ran. (It's not well tested yet)");
//System.err.println("\nTrying to run multiple instances of jRT simultaneously.\n"
// + "\nPlease run only one at the same time.\n\n");
//finishByError = true;
//System.exit(1);
}
JRT jRT = new JRT();
jRT.premain(agentArgument, instrumentation);
initialized = true;
return jRT;
}
private static final String[] remoteaddr = {"-raddr", "remote-addr"};
private static final String[] loginterval = {"-si", "sample-interval"};
private static final String[] remoteport = {"-rport", "remote-port"};
private static final String[] localport = {"-lport", "local-port"};
private static final String[] filterentry = {"-f", "filter-entry"};
private static final String[] help = {"-h", "--help", "help", "h"};
private static final String[] startdelaying = {"-start", "start"};
private static final String[] workingtime = {"-fin", "finish-after"};
private static final String[] logprefix = {"-lp", "log-prefix"};
private static final String[] uuid = {"-id", "uuid"};
private static final String[] ioMode = {"-mode"};
private static final String[] i2oenabling = {"-i2o"};
private static final String[] o2ienabling = {"-o2i"};
private static boolean hasKey(String[] list, String key) {
for (String s : list) {
if (s.equals(key)) {
return true;
}
}
return false;
}
}