initial impl (public domain, yadda yadda yadda)
This commit is contained in:
77
apps/netmonitor/java/build.xml
Normal file
77
apps/netmonitor/java/build.xml
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project basedir="." default="all" name="netmonitor">
|
||||||
|
<target name="all" depends="clean, build" />
|
||||||
|
<target name="build" depends="builddep, jar" />
|
||||||
|
<target name="builddep">
|
||||||
|
<ant dir="../../../core/java/" target="build" />
|
||||||
|
</target>
|
||||||
|
<target name="fetchJfreechart">
|
||||||
|
<mkdir dir="./lib" />
|
||||||
|
<get src="http://www.jfree.org/jfreechart/jfreechart-0.9.17.zip" verbose="true" dest="lib/jfreechart-0.9.17.zip" />
|
||||||
|
<unzip src="lib/jfreechart-0.9.17.zip" dest="lib/" />
|
||||||
|
</target>
|
||||||
|
<target name="buildGUI" depends="build, jfreechart, jarGUI" />
|
||||||
|
<target name="compile">
|
||||||
|
<mkdir dir="./build" />
|
||||||
|
<mkdir dir="./build/obj" />
|
||||||
|
<javac srcdir="./src" debug="true" destdir="./build/obj" includes="net/**/*.java" excludes="net/i2p/netmonitor/gui/**" classpath="../../../core/java/build/i2p.jar" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compileGUI" depends="builddep">
|
||||||
|
<mkdir dir="./build" />
|
||||||
|
<mkdir dir="./build/obj" />
|
||||||
|
<javac debug="true" destdir="./build/obj">
|
||||||
|
<src path="src/" />
|
||||||
|
<classpath path="../../../core/java/build/i2p.jar" />
|
||||||
|
<classpath path="lib/jfreechart-0.9.17/lib/jcommon-0.9.2.jar" />
|
||||||
|
<classpath path="lib/jfreechart-0.9.17/lib/log4j-1.2.8.jar" />
|
||||||
|
<classpath path="lib/jfreechart-0.9.17/jfreechart-0.9.17.jar" />
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
<target name="jfreechart">
|
||||||
|
<ant dir="./lib/jfreechart-0.9.17/" antfile="ant/build.xml" target="compile" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="jarGUI" depends="compileGUI">
|
||||||
|
<copy file="lib/jfreechart-0.9.17/jfreechart-0.9.17.jar" todir="build/" />
|
||||||
|
<copy file="lib/jfreechart-0.9.17/lib/log4j-1.2.8.jar" todir="build/" />
|
||||||
|
<copy file="lib/jfreechart-0.9.17/lib/jcommon-0.9.2.jar" todir="build/" />
|
||||||
|
<jar destfile="./build/netviewer.jar" basedir="./build/obj" includes="**">
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="net.i2p.netmonitor.gui.NetViewer" />
|
||||||
|
<attribute name="Class-Path" value="log4j-1.2.8.jar jcommon-0.9.2.jar jfreechart-0.9.17.jar netviewer.jar i2p.jar" />
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
<echo message="You will need to copy the log4j, jcommon, and jfreechart jar files from build/ into your lib dir" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="jar" depends="compile">
|
||||||
|
<jar destfile="./build/netmonitor.jar" basedir="./build/obj" includes="**/*.class">
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="net.i2p.netmonitor.NetMonitor" />
|
||||||
|
<attribute name="Class-Path" value="i2p.jar netmonitor.jar" />
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
<target name="javadoc">
|
||||||
|
<mkdir dir="./build" />
|
||||||
|
<mkdir dir="./build/javadoc" />
|
||||||
|
<javadoc
|
||||||
|
sourcepath="./src:../../../core/java/src:../../../core/java/test" destdir="./build/javadoc"
|
||||||
|
packagenames="*"
|
||||||
|
use="true"
|
||||||
|
access="package"
|
||||||
|
splitindex="true"
|
||||||
|
windowtitle="I2P netmonitor" />
|
||||||
|
</target>
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="./build" />
|
||||||
|
</target>
|
||||||
|
<target name="cleandep" depends="clean">
|
||||||
|
<ant dir="../../../core/java/" target="cleandep" />
|
||||||
|
</target>
|
||||||
|
<target name="distclean" depends="clean">
|
||||||
|
<ant dir="../../../core/java/" target="distclean" />
|
||||||
|
<delete dir="./lib/" />
|
||||||
|
</target>
|
||||||
|
</project>
|
264
apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java
Normal file
264
apps/netmonitor/java/src/net/i2p/netmonitor/DataHarvester.java
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import net.i2p.data.RouterInfo;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.Clock;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.text.ParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull out important data from the published routerInfo and stash it away
|
||||||
|
* in the netMonitor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DataHarvester {
|
||||||
|
private static final Log _log = new Log(DataHarvester.class);
|
||||||
|
private static final DataHarvester _instance = new DataHarvester();
|
||||||
|
public static final DataHarvester getInstance() { return _instance; }
|
||||||
|
|
||||||
|
protected DataHarvester() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Harvest all of the data from the peers and store it in the monitor.
|
||||||
|
*
|
||||||
|
* @param peers list of RouterInfo structures to harvest from
|
||||||
|
*/
|
||||||
|
public void harvestData(NetMonitor monitor, List peers) {
|
||||||
|
for (int i = 0; i < peers.size(); i++) {
|
||||||
|
harvestData(monitor, (RouterInfo)peers.get(i), peers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull out all the data we can for the specified peer
|
||||||
|
*
|
||||||
|
* @param peer who are we focusing on in this pass
|
||||||
|
* @param peers everyone on the network, to co
|
||||||
|
*/
|
||||||
|
private void harvestData(NetMonitor monitor, RouterInfo peer, List peers) {
|
||||||
|
_log.info("Harvest the data from " + peer.getIdentity().getHash().toBase64());
|
||||||
|
harvestRank(monitor, peer, peers);
|
||||||
|
harvestRankAs(monitor, peer);
|
||||||
|
harvestEncryptionTime(monitor, peer);
|
||||||
|
harvestDroppedJobs(monitor, peer);
|
||||||
|
harvestProcessingTime(monitor, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How does the peer rank other routers? Stored in the peer summary as
|
||||||
|
* "rankAs", containing 4 longs (numFast, numReliable, numNotFailing, numFailing)
|
||||||
|
*
|
||||||
|
* @param peer who is doing the ranking
|
||||||
|
*/
|
||||||
|
private void harvestRankAs(NetMonitor monitor, RouterInfo peer) {
|
||||||
|
int numFast = 0;
|
||||||
|
int numReliable = 0;
|
||||||
|
int numNotFailing = 0;
|
||||||
|
int numFailing = 0;
|
||||||
|
|
||||||
|
Properties props = peer.getOptions();
|
||||||
|
for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
String key = (String)iter.next();
|
||||||
|
if (key.startsWith("profile.")) {
|
||||||
|
String val = (String)props.get(key);
|
||||||
|
if (val.indexOf("fastReliable") != -1)
|
||||||
|
numFast++;
|
||||||
|
else if (val.indexOf("reliable") != -1)
|
||||||
|
numReliable++;
|
||||||
|
else if (val.indexOf("notFailing") != -1)
|
||||||
|
numNotFailing++;
|
||||||
|
else if (val.indexOf("failing") != -1)
|
||||||
|
numFailing++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long rankAs[] = new long[4];
|
||||||
|
rankAs[0] = numFast;
|
||||||
|
rankAs[1] = numReliable;
|
||||||
|
rankAs[2] = numNotFailing;
|
||||||
|
rankAs[3] = numFailing;
|
||||||
|
String description = "how we rank peers";
|
||||||
|
String valDescr[] = new String[4];
|
||||||
|
valDescr[0] = "# peers we rank as fast";
|
||||||
|
valDescr[1] = "# peers we rank as reliable";
|
||||||
|
valDescr[2] = "# peers we rank as not failing";
|
||||||
|
valDescr[3] = "# peers we rank as failing";
|
||||||
|
monitor.addData(peer.getIdentity().getHash().toBase64(), "rankAs", description, valDescr, peer.getPublished(), rankAs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How do other peers rank the current peer? Stored in the peer summary as
|
||||||
|
* "rank", containing 4 longs (numFast, numReliable, numNotFailing, numFailing)
|
||||||
|
*
|
||||||
|
* @param peer who do we want to check the network's perception of
|
||||||
|
* @param peers peers whose rankings we will use
|
||||||
|
*/
|
||||||
|
private void harvestRank(NetMonitor monitor, RouterInfo peer, List peers) {
|
||||||
|
int numFast = 0;
|
||||||
|
int numReliable = 0;
|
||||||
|
int numNotFailing = 0;
|
||||||
|
int numFailing = 0;
|
||||||
|
|
||||||
|
// now count 'em
|
||||||
|
for (int i = 0; i < peers.size(); i++) {
|
||||||
|
RouterInfo cur = (RouterInfo)peers.get(i);
|
||||||
|
if (peer == cur) continue;
|
||||||
|
String prop = "profile." + peer.getIdentity().getHash().toBase64().replace('=', '_');
|
||||||
|
String val = cur.getOptions().getProperty(prop);
|
||||||
|
if ( (val == null) || (val.length() <= 0) ) continue;
|
||||||
|
if (val.indexOf("fastReliable") != -1)
|
||||||
|
numFast++;
|
||||||
|
else if (val.indexOf("reliable") != -1)
|
||||||
|
numReliable++;
|
||||||
|
else if (val.indexOf("notFailing") != -1)
|
||||||
|
numNotFailing++;
|
||||||
|
else if (val.indexOf("failing") != -1)
|
||||||
|
numFailing++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long rank[] = new long[4];
|
||||||
|
rank[0] = numFast;
|
||||||
|
rank[1] = numReliable;
|
||||||
|
rank[2] = numNotFailing;
|
||||||
|
rank[3] = numFailing;
|
||||||
|
String description = "how peers rank us";
|
||||||
|
String valDescr[] = new String[4];
|
||||||
|
valDescr[0] = "# peers ranking us as fast";
|
||||||
|
valDescr[1] = "# peers ranking us as reliable";
|
||||||
|
valDescr[2] = "# peers ranking us as not failing";
|
||||||
|
valDescr[3] = "# peers ranking us as failing";
|
||||||
|
// we use the current date, not the published date, since this sample doesnt come from them
|
||||||
|
monitor.addData(peer.getIdentity().getHash().toBase64(), "rank", description, valDescr, Clock.getInstance().now(), rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long does it take the peer to perform an elGamal encryption? Stored in
|
||||||
|
* the peer summary as "encryptTime", containing 4 doubles (numMs for 1 minute,
|
||||||
|
* quantity in the last minute, numMs for 1 hour, quantity in the last hour)
|
||||||
|
*
|
||||||
|
* @param peer who are we checking the encryption time of
|
||||||
|
*/
|
||||||
|
private void harvestEncryptionTime(NetMonitor monitor, RouterInfo peer) {
|
||||||
|
double minuteMs = getDouble(peer, "stat_crypto.elGamal.encrypt.60s", 0);
|
||||||
|
double hourMs = getDouble(peer, "stat_crypto.elGamal.encrypt.60m", 0);
|
||||||
|
double minuteQuantity = getDouble(peer, "stat_crypto.elGamal.encrypt.60s", 7);
|
||||||
|
double hourQuantity = getDouble(peer, "stat_crypto.elGamal.encrypt.60m", 7);
|
||||||
|
if ( (minuteMs == -1) || (hourMs == -1) || (minuteQuantity == -1) || (hourQuantity == -1) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
double times[] = new double[4];
|
||||||
|
times[0] = minuteMs;
|
||||||
|
times[1] = minuteQuantity;
|
||||||
|
times[2] = hourMs;
|
||||||
|
times[3] = hourQuantity;
|
||||||
|
|
||||||
|
String description = "how long it takes to do an ElGamal encryption";
|
||||||
|
String valDescr[] = new String[4];
|
||||||
|
valDescr[0] = "encryption time avg ms (minute)";
|
||||||
|
valDescr[1] = "# encryptions (minute)";
|
||||||
|
valDescr[2] = "encryption time avg ms (hour)";
|
||||||
|
valDescr[3] = "# encryptions (hour)";
|
||||||
|
monitor.addData(peer.getIdentity().getHash().toBase64(), "encryptTime", description, valDescr, peer.getPublished(), times);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How jobs has the peer dropped in the last minute / hour? Stored in
|
||||||
|
* the peer summary as "droppedJobs", containing 2 doubles (num jobs for 1 minute,
|
||||||
|
* num jobs for 1 hour)
|
||||||
|
*
|
||||||
|
* @param peer who are we checking the frequency of dropping jobs for
|
||||||
|
*/
|
||||||
|
private void harvestDroppedJobs(NetMonitor monitor, RouterInfo peer) {
|
||||||
|
double minute = getDouble(peer, "stat_jobQueue.droppedJobs.60s", 0);
|
||||||
|
double hour = getDouble(peer, "stat_jobQueue.droppedJobs.60m", 0);
|
||||||
|
double quantity[] = new double[2];
|
||||||
|
quantity[0] = minute;
|
||||||
|
quantity[1] = hour;
|
||||||
|
if ( (minute == -1) || (hour == -1) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
String valDescr[] = new String[2];
|
||||||
|
valDescr[0] = "# dropped jobs (minute)";
|
||||||
|
valDescr[1] = "# dropped jobs (hour)";
|
||||||
|
String description = "how many dropped jobs";
|
||||||
|
monitor.addData(peer.getIdentity().getHash().toBase64(), "droppedJobs", description, valDescr, peer.getPublished(), quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long does it take to process an outbound message? Stored in
|
||||||
|
* the peer summary as "processingTime", containing 4 doubles (avg ms for 1 minute,
|
||||||
|
* num messages for 1 minute, avg ms for 1 hour, num messages for 1 hour)
|
||||||
|
*
|
||||||
|
* @param peer who are we checking the frequency of dropping jobs for
|
||||||
|
*/
|
||||||
|
private void harvestProcessingTime(NetMonitor monitor, RouterInfo peer) {
|
||||||
|
double minuteMs = getDouble(peer, "stat_transport.sendProcessingTime.60s", 0);
|
||||||
|
double minuteFreq = getDouble(peer, "stat_transport.sendProcessingTime.60s", 7);
|
||||||
|
double hourMs = getDouble(peer, "stat_transport.sendProcessingTime.60m", 0);
|
||||||
|
double hourFreq = getDouble(peer, "stat_transport.sendProcessingTime.60m", 7);
|
||||||
|
if ( (minuteMs == -1) || (hourMs == -1) || (minuteFreq == -1) || (hourFreq == -1) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
double times[] = new double[4];
|
||||||
|
times[0] = minuteMs;
|
||||||
|
times[1] = minuteFreq;
|
||||||
|
times[2] = hourMs;
|
||||||
|
times[3] = hourFreq;
|
||||||
|
|
||||||
|
String valDescr[] = new String[4];
|
||||||
|
valDescr[0] = "process time avg ms (minute)";
|
||||||
|
valDescr[1] = "process events (minute)";
|
||||||
|
valDescr[2] = "process time avg ms (hour)";
|
||||||
|
valDescr[3] = "process events (hour)";
|
||||||
|
String description = "how long does it take to process a message";
|
||||||
|
monitor.addData(peer.getIdentity().getHash().toBase64(), "processingTime", description, valDescr, peer.getPublished(), times);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull a value from the peer's option as a double, assuming the standard semicolon
|
||||||
|
* delimited formatting
|
||||||
|
*
|
||||||
|
* @param peer peer to query
|
||||||
|
* @param key peer option to check
|
||||||
|
* @param index 0-based index into the semicolon delimited values to pull out
|
||||||
|
* @return value, or -1 if there was an error
|
||||||
|
*/
|
||||||
|
private static final double getDouble(RouterInfo peer, String key, int index) {
|
||||||
|
String val = peer.getOptions().getProperty(key);
|
||||||
|
if (val == null) return -1;
|
||||||
|
StringTokenizer tok = new StringTokenizer(val, ";");
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
if (!tok.hasMoreTokens()) return -1;
|
||||||
|
tok.nextToken(); // ignore
|
||||||
|
}
|
||||||
|
if (!tok.hasMoreTokens()) return -1;
|
||||||
|
String cur = tok.nextToken();
|
||||||
|
try {
|
||||||
|
return getDoubleValue(cur);
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
_log.warn("Unable to parse out the double from field " + index + " out of " + val + " for " + key, pe);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** this mimics the format used in the router's StatisticsManager */
|
||||||
|
private static final DecimalFormat _numFmt = new DecimalFormat("###,###,###,###,##0.00", new DecimalFormatSymbols(Locale.UK));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a number (double) to text
|
||||||
|
* @param val the number to convert
|
||||||
|
* @return the textual representation
|
||||||
|
*/
|
||||||
|
private static final double getDoubleValue(String val) throws ParseException {
|
||||||
|
synchronized (_numFmt) {
|
||||||
|
Number n = _numFmt.parse(val);
|
||||||
|
return n.doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
212
apps/netmonitor/java/src/net/i2p/netmonitor/NetMonitor.java
Normal file
212
apps/netmonitor/java/src/net/i2p/netmonitor/NetMonitor.java
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main driver for the app that harvests data about the performance of the network,
|
||||||
|
* building summaries for each peer that change over time. <p />
|
||||||
|
*
|
||||||
|
* Usage: <code>NetMonitor [configFilename]</code> <p />
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class NetMonitor {
|
||||||
|
private static final Log _log = new Log(NetMonitor.class);
|
||||||
|
public static final String CONFIG_LOCATION_DEFAULT = "netmonitor.config";
|
||||||
|
public static final String HARVEST_DELAY_PROP = "harvestDelaySeconds";
|
||||||
|
public static final int HARVEST_DELAY_DEFAULT = 60;
|
||||||
|
public static final String EXPORT_DELAY_PROP = "exportDelaySeconds";
|
||||||
|
public static final int EXPORT_DELAY_DEFAULT = 120;
|
||||||
|
public static final String SUMMARY_DURATION_PROP = "summaryDurationHours";
|
||||||
|
public static final int SUMMARY_DURATION_DEFAULT = 72;
|
||||||
|
public static final String NETDB_DIR_PROP = "netDbDir";
|
||||||
|
public static final String NETDB_DIR_DEFAULT = "netDb";
|
||||||
|
public static final String EXPORT_DIR_PROP = "exportDir";
|
||||||
|
public static final String EXPORT_DIR_DEFAULT = "monitorData";
|
||||||
|
private String _configLocation;
|
||||||
|
private int _harvestDelay;
|
||||||
|
private int _exportDelay;
|
||||||
|
private String _exportDir;
|
||||||
|
private String _netDbDir;
|
||||||
|
private int _summaryDurationHours;
|
||||||
|
private boolean _isRunning;
|
||||||
|
private Map _peerSummaries;
|
||||||
|
|
||||||
|
public NetMonitor() {
|
||||||
|
this(CONFIG_LOCATION_DEFAULT);
|
||||||
|
}
|
||||||
|
public NetMonitor(String configLocation) {
|
||||||
|
_configLocation = configLocation;
|
||||||
|
_peerSummaries = new HashMap(32);
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** read and call parse */
|
||||||
|
private void loadConfig() {
|
||||||
|
Properties props = new Properties();
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(_configLocation);
|
||||||
|
props.load(fis);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.warn("Error loading the net monitor config", ioe);
|
||||||
|
} finally {
|
||||||
|
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
parseConfig(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** interpret the config elements and shove 'em in the vars */
|
||||||
|
private void parseConfig(Properties props) {
|
||||||
|
String val = props.getProperty(HARVEST_DELAY_PROP, ""+HARVEST_DELAY_DEFAULT);
|
||||||
|
try {
|
||||||
|
_harvestDelay = Integer.parseInt(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.warn("Error parsing the harvest delay [" + val + "]", nfe);
|
||||||
|
_harvestDelay = HARVEST_DELAY_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = props.getProperty(EXPORT_DELAY_PROP, ""+EXPORT_DELAY_DEFAULT);
|
||||||
|
try {
|
||||||
|
_exportDelay = Integer.parseInt(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.warn("Error parsing the export delay [" + val + "]", nfe);
|
||||||
|
_exportDelay = EXPORT_DELAY_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = props.getProperty(SUMMARY_DURATION_PROP, ""+SUMMARY_DURATION_DEFAULT);
|
||||||
|
try {
|
||||||
|
_summaryDurationHours = Integer.parseInt(val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.warn("Error parsing the summary duration [" + val + "]", nfe);
|
||||||
|
_summaryDurationHours = SUMMARY_DURATION_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
_netDbDir = props.getProperty(NETDB_DIR_PROP, NETDB_DIR_DEFAULT);
|
||||||
|
_exportDir = props.getProperty(EXPORT_DIR_PROP, EXPORT_DIR_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startMonitor() {
|
||||||
|
_isRunning = true;
|
||||||
|
I2PThread t = new I2PThread(new NetMonitorRunner(this));
|
||||||
|
t.setName("DataHarvester");
|
||||||
|
t.setPriority(I2PThread.MIN_PRIORITY);
|
||||||
|
t.setDaemon(false);
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopMonitor() { _isRunning = false; }
|
||||||
|
public boolean isRunning() { return _isRunning; }
|
||||||
|
/** how many seconds should we wait between harvestings? */
|
||||||
|
public int getHarvestDelay() { return _harvestDelay; }
|
||||||
|
/** how many seconds should we wait between exporting the data? */
|
||||||
|
public int getExportDelay() { return _exportDelay; }
|
||||||
|
/** where should we export the data? */
|
||||||
|
public String getExportDir() { return _exportDir; }
|
||||||
|
public void setExportDir(String dir) { _exportDir = dir; }
|
||||||
|
public int getSummaryDurationHours() { return _summaryDurationHours; }
|
||||||
|
/** where should we read the data from? */
|
||||||
|
public String getNetDbDir() { return _netDbDir; }
|
||||||
|
/**
|
||||||
|
* what peers are we keeping track of?
|
||||||
|
*
|
||||||
|
* @return list of peer names (H(routerIdentity).toBase64())
|
||||||
|
*/
|
||||||
|
public List getPeers() {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
return new ArrayList(_peerSummaries.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** what data do we have for the peer? */
|
||||||
|
public PeerSummary getSummary(String peer) {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
return (PeerSummary)_peerSummaries.get(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** keep track of the given stat on the given peer */
|
||||||
|
public void addData(String peer, String stat, String descr, String valDescr[], long sampleDate, double val[]) {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
if (!_peerSummaries.containsKey(peer))
|
||||||
|
_peerSummaries.put(peer, new PeerSummary(peer));
|
||||||
|
PeerSummary summary = (PeerSummary)_peerSummaries.get(peer);
|
||||||
|
summary.addData(stat, descr, valDescr, sampleDate, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** keep track of the given stat on the given peer */
|
||||||
|
public void addData(String peer, String stat, String descr, String valDescr[], long sampleDate, long val[]) {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
if (!_peerSummaries.containsKey(peer))
|
||||||
|
_peerSummaries.put(peer, new PeerSummary(peer));
|
||||||
|
PeerSummary summary = (PeerSummary)_peerSummaries.get(peer);
|
||||||
|
summary.addData(stat, descr, valDescr, sampleDate, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** keep track of the loaded summary, overwriting any existing summary for the specified peer */
|
||||||
|
public void addSummary(PeerSummary summary) {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
Object rv = _peerSummaries.put(summary.getPeer(), summary);
|
||||||
|
if (rv != summary) _log.error("Updating the peer summary changed objects! old = " + rv + " new = " + summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void importData() {
|
||||||
|
_log.debug("Running import");
|
||||||
|
File dataDir = new File(getExportDir());
|
||||||
|
if (!dataDir.exists()) return;
|
||||||
|
File dataFiles[] = dataDir.listFiles(new FilenameFilter() {
|
||||||
|
public boolean accept(File f, String name) {
|
||||||
|
return name.endsWith(".txt");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (dataFiles == null) return;
|
||||||
|
for (int i = 0; i < dataFiles.length; i++) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
boolean delete = false;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(dataFiles[i]);
|
||||||
|
PeerSummaryReader.getInstance().read(this, fis);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("Error reading the data file " + dataFiles[i].getAbsolutePath(), ioe);
|
||||||
|
delete = true;
|
||||||
|
} finally {
|
||||||
|
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||||
|
if (delete) dataFiles[i].delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_log.debug(dataFiles.length + " summaries imported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** drop all the old summary data */
|
||||||
|
public void coallesceData() {
|
||||||
|
synchronized (_peerSummaries) {
|
||||||
|
for (Iterator iter = _peerSummaries.values().iterator(); iter.hasNext(); ) {
|
||||||
|
PeerSummary summary = (PeerSummary)iter.next();
|
||||||
|
summary.coallesceData(_summaryDurationHours * 60*60*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void main(String args[]) {
|
||||||
|
if (args.length == 1)
|
||||||
|
new NetMonitor(args[0]).startMonitor();
|
||||||
|
else
|
||||||
|
new NetMonitor(CONFIG_LOCATION_DEFAULT).startMonitor();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
|
||||||
|
import net.i2p.util.Clock;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.RouterInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active process that drives the monitoring by periodically rading the
|
||||||
|
* netDb dir, pumping the router data throug the data harvester, updating
|
||||||
|
* the state, and coordinating the export.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class NetMonitorRunner implements Runnable {
|
||||||
|
private static final Log _log = new Log(NetMonitorRunner.class);
|
||||||
|
private NetMonitor _monitor;
|
||||||
|
/**
|
||||||
|
* @param monitor who do we give our data to?
|
||||||
|
*/
|
||||||
|
public NetMonitorRunner(NetMonitor monitor) {
|
||||||
|
_monitor = monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
runImport();
|
||||||
|
long now = Clock.getInstance().now();
|
||||||
|
long nextHarvest = now;
|
||||||
|
long nextExport = now + _monitor.getExportDelay() * 1000;
|
||||||
|
while (_monitor.isRunning()) {
|
||||||
|
now = Clock.getInstance().now();
|
||||||
|
_monitor.coallesceData();
|
||||||
|
if (now >= nextHarvest) {
|
||||||
|
runHarvest();
|
||||||
|
nextHarvest = now + _monitor.getHarvestDelay() * 1000;
|
||||||
|
}
|
||||||
|
if (now >= nextExport) {
|
||||||
|
runExport();
|
||||||
|
nextExport = now + _monitor.getExportDelay() * 1000;
|
||||||
|
}
|
||||||
|
pauseHarvesting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runHarvest() {
|
||||||
|
List routers = getRouters();
|
||||||
|
DataHarvester.getInstance().harvestData(_monitor, routers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all of the available RouterInfo structures
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private List getRouters() {
|
||||||
|
File routers[] = listRouters();
|
||||||
|
List rv = new ArrayList(64);
|
||||||
|
if (routers != null) {
|
||||||
|
for (int i = 0; i < routers.length; i++) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(routers[i]);
|
||||||
|
RouterInfo ri = new RouterInfo();
|
||||||
|
ri.readBytes(fis);
|
||||||
|
rv.add(ri);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
_log.warn("Unable to parse the routerInfo from " + routers[i].getAbsolutePath(), dfe);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.warn("Unable to read the routerInfo from " + routers[i].getAbsolutePath(), ioe);
|
||||||
|
} finally {
|
||||||
|
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dump the data to the filesystem
|
||||||
|
*/
|
||||||
|
private void runExport() {
|
||||||
|
_log.info("Export");
|
||||||
|
List peers = _monitor.getPeers();
|
||||||
|
File exportDir = new File(_monitor.getExportDir());
|
||||||
|
if (!exportDir.exists())
|
||||||
|
exportDir.mkdirs();
|
||||||
|
for (int i = 0; i < peers.size(); i++) {
|
||||||
|
String peerName = (String)peers.get(i);
|
||||||
|
PeerSummary summary = (PeerSummary)_monitor.getSummary(peerName);
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
File summaryFile = new File(exportDir, peerName + ".txt");
|
||||||
|
fos = new FileOutputStream(summaryFile);
|
||||||
|
PeerSummaryWriter.getInstance().write(summary, fos);
|
||||||
|
_log.debug("Peer summary written to " + summaryFile.getAbsolutePath());
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
_log.error("Error exporting the peer summary for " + peerName, ioe);
|
||||||
|
} finally {
|
||||||
|
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in all the peer summaries we had previously exported, overwriting any
|
||||||
|
* existing ones in memory
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void runImport() {
|
||||||
|
_monitor.importData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all of the routers to load
|
||||||
|
*
|
||||||
|
* @return list of File objects pointing at the routers around
|
||||||
|
*/
|
||||||
|
private File[] listRouters() {
|
||||||
|
File dbDir = new File(_monitor.getNetDbDir());
|
||||||
|
File files[] = dbDir.listFiles(new FilenameFilter() {
|
||||||
|
public boolean accept(File f, String name) {
|
||||||
|
return name.startsWith("routerInfo-");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait the correct amount of time before harvesting again
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void pauseHarvesting() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(_monitor.getHarvestDelay());
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
46
apps/netmonitor/java/src/net/i2p/netmonitor/PeerStat.java
Normal file
46
apps/netmonitor/java/src/net/i2p/netmonitor/PeerStat.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual data point (though its not fully normalized, but KISS)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PeerStat {
|
||||||
|
private String _statName;
|
||||||
|
private String _description;
|
||||||
|
private String _valueDescriptions[];
|
||||||
|
private long _sampleDate;
|
||||||
|
private long _lvalues[];
|
||||||
|
private double _dvalues[];
|
||||||
|
|
||||||
|
public PeerStat(String name, String description, String valueDescriptions[], long sampleDate, double values[]) {
|
||||||
|
this(name, description, valueDescriptions, sampleDate, null, values);
|
||||||
|
}
|
||||||
|
public PeerStat(String name, String description, String valueDescriptions[], long sampleDate, long values[]) {
|
||||||
|
this(name, description, valueDescriptions, sampleDate, values, null);
|
||||||
|
}
|
||||||
|
private PeerStat(String name, String description, String valueDescriptions[], long sampleDate, long lvalues[], double dvalues[]) {
|
||||||
|
_statName = name;
|
||||||
|
_description = description;
|
||||||
|
_valueDescriptions = valueDescriptions;
|
||||||
|
_sampleDate = sampleDate;
|
||||||
|
_lvalues = lvalues;
|
||||||
|
_dvalues = dvalues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** unique name of the stat */
|
||||||
|
public String getStatName() { return _statName; }
|
||||||
|
/** one line summary of the stat */
|
||||||
|
public String getDescription() { return _description; }
|
||||||
|
/** description of each value */
|
||||||
|
public String getValueDescription(int index) { return _valueDescriptions[index]; }
|
||||||
|
/** description of all values */
|
||||||
|
public String[] getValueDescriptions() { return _valueDescriptions; }
|
||||||
|
/** when did the router publish the info being sampled? */
|
||||||
|
public long getSampleDate() { return _sampleDate; }
|
||||||
|
/** if the values are integers, this contains their values */
|
||||||
|
public long[] getLongValues() { return _lvalues; }
|
||||||
|
/** if the values are floating point numbers, this contains their values */
|
||||||
|
public double[] getDoubleValues() { return _dvalues; }
|
||||||
|
/** are the values floating poing numbers? */
|
||||||
|
public boolean getIsDouble() { return _dvalues != null; }
|
||||||
|
}
|
119
apps/netmonitor/java/src/net/i2p/netmonitor/PeerSummary.java
Normal file
119
apps/netmonitor/java/src/net/i2p/netmonitor/PeerSummary.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import net.i2p.util.Clock;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* coordinate the data points summarizing the performance of a particular peer
|
||||||
|
* within the network
|
||||||
|
*/
|
||||||
|
public class PeerSummary {
|
||||||
|
private static final Log _log = new Log(PeerSummary.class);
|
||||||
|
private String _peer;
|
||||||
|
/** statName to a List of PeerStat elements (sorted by sample date, earliest first) */
|
||||||
|
private Map _stats;
|
||||||
|
/** lock on this when accessing stat data */
|
||||||
|
private Object _coallesceLock = new Object();
|
||||||
|
|
||||||
|
public PeerSummary(String peer) {
|
||||||
|
_peer = peer;
|
||||||
|
_stats = new HashMap(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track a data point
|
||||||
|
*
|
||||||
|
* @param stat what data are we tracking?
|
||||||
|
* @param description what does this data mean? (and what are the values?)
|
||||||
|
* @param when what data set is this sample based off?
|
||||||
|
* @param val actual data harvested
|
||||||
|
*/
|
||||||
|
public void addData(String stat, String description, String valueDescriptions[], long when, double val[]) {
|
||||||
|
synchronized (_coallesceLock) {
|
||||||
|
TreeMap stats = locked_getData(stat);
|
||||||
|
stats.put(new Long(when), new PeerStat(stat, description, valueDescriptions, when, val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track a data point
|
||||||
|
*
|
||||||
|
* @param stat what data are we tracking?
|
||||||
|
* @param description what does this data mean? (and what are the values?)
|
||||||
|
* @param when what data set is this sample based off?
|
||||||
|
* @param val actual data harvested
|
||||||
|
*/
|
||||||
|
public void addData(String stat, String description, String valueDescriptions[], long when, long val[]) {
|
||||||
|
synchronized (_coallesceLock) {
|
||||||
|
TreeMap stats = locked_getData(stat);
|
||||||
|
stats.put(new Long(when), new PeerStat(stat, description, valueDescriptions, when, val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the peer's name (H(routerIdentity).toBase64()) */
|
||||||
|
public String getPeer() { return _peer; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fetch the ordered list of PeerStat objects for the given stat (or null if
|
||||||
|
* isn't being tracked or has no data)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public List getData(String statName) {
|
||||||
|
synchronized (_coallesceLock) {
|
||||||
|
return new ArrayList(((TreeMap)_stats.get(statName)).values());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the names of all of the stats that are being tracked
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Set getStatNames() {
|
||||||
|
synchronized (_coallesceLock) {
|
||||||
|
return new HashSet(_stats.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** drop old data points */
|
||||||
|
public void coallesceData(long summaryDurationMs) {
|
||||||
|
long earliest = Clock.getInstance().now() - summaryDurationMs;
|
||||||
|
synchronized (_coallesceLock) {
|
||||||
|
locked_coallesce(earliest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** go through all the stats and remove ones from before the given date */
|
||||||
|
private void locked_coallesce(long earliestSampleDate) {
|
||||||
|
if (true) return;
|
||||||
|
for (Iterator iter = _stats.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
String statName = (String)iter.next();
|
||||||
|
TreeMap stats = (TreeMap)_stats.get(statName);
|
||||||
|
while (stats.size() > 0) {
|
||||||
|
Long when = (Long)stats.keySet().iterator().next();
|
||||||
|
if (when.longValue() < earliestSampleDate) {
|
||||||
|
stats.remove(when);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PeerStat elements, ordered by sample date (earliest first)
|
||||||
|
*/
|
||||||
|
private TreeMap locked_getData(String statName) {
|
||||||
|
if (!_stats.containsKey(statName))
|
||||||
|
_stats.put(statName, new TreeMap());
|
||||||
|
return (TreeMap)_stats.get(statName);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load up the peer summary
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class PeerSummaryReader {
|
||||||
|
private static final Log _log = new Log(PeerSummaryReader.class);
|
||||||
|
private static final PeerSummaryReader _instance = new PeerSummaryReader();
|
||||||
|
public static final PeerSummaryReader getInstance() { return _instance; }
|
||||||
|
private PeerSummaryReader() {}
|
||||||
|
|
||||||
|
/** */
|
||||||
|
public void read(NetMonitor monitor, InputStream in) throws IOException {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
String line = null;
|
||||||
|
PeerSummary summary = null;
|
||||||
|
String curDescription = null;
|
||||||
|
List curArgs = null;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.startsWith("peer\t")) {
|
||||||
|
String name = line.substring("peer\t".length()).trim();
|
||||||
|
summary = monitor.getSummary(name);
|
||||||
|
if (summary == null)
|
||||||
|
summary = new PeerSummary(name);
|
||||||
|
} else if (line.startsWith("## ")) {
|
||||||
|
curDescription = line.substring("## ".length()).trim();
|
||||||
|
curArgs = new ArrayList(4);
|
||||||
|
} else if (line.startsWith("# param ")) {
|
||||||
|
int start = line.indexOf(':');
|
||||||
|
String arg = line.substring(start+1).trim();
|
||||||
|
curArgs.add(arg);
|
||||||
|
} else {
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
String name = tok.nextToken();
|
||||||
|
try {
|
||||||
|
long when = getTime(tok.nextToken());
|
||||||
|
boolean isDouble = false;
|
||||||
|
List argVals = new ArrayList(curArgs.size());
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
String val = (String)tok.nextToken();
|
||||||
|
if (val.indexOf('.') >= 0) {
|
||||||
|
argVals.add(new Double(val));
|
||||||
|
isDouble = true;
|
||||||
|
} else {
|
||||||
|
argVals.add(new Long(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String valDescriptions[] = new String[curArgs.size()];
|
||||||
|
for (int i = 0; i < curArgs.size(); i++)
|
||||||
|
valDescriptions[i] = (String)curArgs.get(i);
|
||||||
|
if (isDouble) {
|
||||||
|
double values[] = new double[argVals.size()];
|
||||||
|
for (int i = 0; i < argVals.size(); i++)
|
||||||
|
values[i] = ((Double)argVals.get(i)).doubleValue();
|
||||||
|
summary.addData(name, curDescription, valDescriptions, when, values);
|
||||||
|
} else {
|
||||||
|
long values[] = new long[argVals.size()];
|
||||||
|
for (int i = 0; i < argVals.size(); i++)
|
||||||
|
values[i] = ((Long)argVals.get(i)).longValue();
|
||||||
|
summary.addData(name, curDescription, valDescriptions, when, values);
|
||||||
|
}
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
_log.error("Error parsing the data line [" + line + "]", pe);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
_log.error("Error parsing the data line [" + line + "]", nfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.coallesceData(monitor.getSummaryDurationHours() * 60*60*1000);
|
||||||
|
monitor.addSummary(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time (long) to text
|
||||||
|
* @param when the time to convert
|
||||||
|
* @return the textual representation
|
||||||
|
*/
|
||||||
|
public long getTime(String when) throws ParseException {
|
||||||
|
synchronized (_fmt) {
|
||||||
|
return _fmt.parse(when).getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package net.i2p.netmonitor;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump various peer summaries to disk (so external apps (or good ol' vi) can
|
||||||
|
* peek into what we're harvesting
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class PeerSummaryWriter {
|
||||||
|
private static final Log _log = new Log(PeerSummaryWriter.class);
|
||||||
|
private static final PeerSummaryWriter _instance = new PeerSummaryWriter();
|
||||||
|
public static final PeerSummaryWriter getInstance() { return _instance; }
|
||||||
|
private PeerSummaryWriter() {}
|
||||||
|
|
||||||
|
/** write out the peer summary to the stream specified */
|
||||||
|
public void write(PeerSummary summary, OutputStream out) throws IOException {
|
||||||
|
StringBuffer buf = new StringBuffer(4*1024);
|
||||||
|
buf.append("peer\t").append(summary.getPeer()).append('\n');
|
||||||
|
TreeSet names = new TreeSet(summary.getStatNames());
|
||||||
|
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
|
||||||
|
String statName = (String)iter.next();
|
||||||
|
List stats = summary.getData(statName);
|
||||||
|
for (int i = 0; i < stats.size(); i++) {
|
||||||
|
PeerStat stat = (PeerStat)stats.get(i);
|
||||||
|
if (i == 0) {
|
||||||
|
buf.append("## ").append(stat.getDescription()).append('\n');
|
||||||
|
String descr[] = stat.getValueDescriptions();
|
||||||
|
if (descr != null) {
|
||||||
|
for (int j = 0; j < descr.length; j++)
|
||||||
|
buf.append("# param ").append(j).append(": ").append(descr[j]).append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append(statName).append('\t');
|
||||||
|
buf.append(getTime(stat.getSampleDate())).append('\t');
|
||||||
|
|
||||||
|
if (stat.getIsDouble()) {
|
||||||
|
double vals[] = stat.getDoubleValues();
|
||||||
|
if (vals != null) {
|
||||||
|
for (int j = 0; j < vals.length; j++)
|
||||||
|
buf.append(vals[j]).append('\t');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
long vals[] = stat.getLongValues();
|
||||||
|
if (vals != null) {
|
||||||
|
for (int j = 0; j < vals.length; j++)
|
||||||
|
buf.append(vals[j]).append('\t');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String data = buf.toString();
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Stat: \n" + data);
|
||||||
|
out.write(data.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time (long) to text
|
||||||
|
* @param when the time to convert
|
||||||
|
* @return the textual representation
|
||||||
|
*/
|
||||||
|
public String getTime(long when) {
|
||||||
|
synchronized (_fmt) {
|
||||||
|
return _fmt.format(new Date(when));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import net.i2p.netmonitor.PeerSummary;
|
||||||
|
import net.i2p.netmonitor.PeerStat;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
import org.jfree.data.XYSeries;
|
||||||
|
import org.jfree.data.XYSeriesCollection;
|
||||||
|
import org.jfree.data.MovingAverage;
|
||||||
|
import org.jfree.chart.JFreeChart;
|
||||||
|
import org.jfree.chart.ChartPanel;
|
||||||
|
import org.jfree.chart.plot.Plot;
|
||||||
|
import org.jfree.chart.plot.XYPlot;
|
||||||
|
import org.jfree.chart.axis.DateAxis;
|
||||||
|
import org.jfree.chart.axis.NumberAxis;
|
||||||
|
import org.jfree.chart.renderer.XYLineAndShapeRenderer;
|
||||||
|
import org.jfree.chart.renderer.XYItemRenderer;
|
||||||
|
import org.jfree.chart.renderer.XYDotRenderer;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
class JFreeChartAdapter {
|
||||||
|
private final static Log _log = new Log(JFreeChartAdapter.class);
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
|
||||||
|
ChartPanel createPanel(NetViewer state) {
|
||||||
|
ChartPanel panel = new ChartPanel(createChart(state));
|
||||||
|
panel.setDisplayToolTips(true);
|
||||||
|
panel.setEnforceFileExtensions(true);
|
||||||
|
panel.setHorizontalZoom(true);
|
||||||
|
panel.setVerticalZoom(true);
|
||||||
|
panel.setMouseZoomable(true, true);
|
||||||
|
panel.getChart().setBackgroundPaint(WHITE);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
JFreeChart createChart(NetViewer state) {
|
||||||
|
Plot plot = createPlot(state);
|
||||||
|
JFreeChart chart = new JFreeChart("I2P network performance", Font.getFont("arial"), plot, true);
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateChart(ChartPanel panel, NetViewer state) {
|
||||||
|
XYPlot plot = (XYPlot)panel.getChart().getPlot();
|
||||||
|
plot.setDataset(getCollection(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
Plot createPlot(NetViewer state) {
|
||||||
|
XYItemRenderer renderer = new XYLineAndShapeRenderer(); // new XYDotRenderer(); //
|
||||||
|
XYPlot plot = new XYPlot(getCollection(state), new DateAxis(), new NumberAxis("val"), renderer);
|
||||||
|
return plot;
|
||||||
|
}
|
||||||
|
|
||||||
|
XYSeriesCollection getCollection(NetViewer state) {
|
||||||
|
XYSeriesCollection col = new XYSeriesCollection();
|
||||||
|
if (state != null) {
|
||||||
|
List names = state.getConfigNames();
|
||||||
|
for (int i = 0; i < names.size(); i++) {
|
||||||
|
addPeer(col, state.getConfig((String)names.get(i)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XYSeries series = new XYSeries("latency", false, false);
|
||||||
|
series.add(System.currentTimeMillis(), 0);
|
||||||
|
col.addSeries(series);
|
||||||
|
}
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPeer(XYSeriesCollection col, PeerPlotConfig config) {
|
||||||
|
Set statNames = config.getSummary().getStatNames();
|
||||||
|
for (Iterator iter = statNames.iterator(); iter.hasNext(); ) {
|
||||||
|
String statName = (String)iter.next();
|
||||||
|
List statData = config.getSummary().getData(statName);
|
||||||
|
if (statData.size() > 0) {
|
||||||
|
PeerStat data = (PeerStat)statData.get(0);
|
||||||
|
String args[] = data.getValueDescriptions();
|
||||||
|
if (args != null) {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (config.getSeriesConfig().getShouldPlotValue(statName, args[i], false))
|
||||||
|
addLine(col, config.getPeerName(), statName, args[i], statData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
void addLine(XYSeriesCollection col, String peer, String statName, String argName, List dataPoints) {
|
||||||
|
XYSeries series = new XYSeries(peer.substring(0, 4) + ": " + argName, false, false);
|
||||||
|
for (int i = 0; i < dataPoints.size(); i++) {
|
||||||
|
PeerStat data = (PeerStat)dataPoints.get(i);
|
||||||
|
String argNames[] = data.getValueDescriptions();
|
||||||
|
int argIndex = -1;
|
||||||
|
for (int j = 0; j < argNames.length; j++) {
|
||||||
|
if (argNames[j].equals(argName)) {
|
||||||
|
argIndex = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.getIsDouble())
|
||||||
|
series.add(data.getSampleDate(), data.getDoubleValues()[argIndex]);
|
||||||
|
else
|
||||||
|
series.add(data.getSampleDate(), data.getLongValues()[argIndex]);
|
||||||
|
}
|
||||||
|
col.addSeries(series);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
import org.jfree.chart.ChartPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the graph and legend
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class JFreeChartHeartbeatPlotPane extends NetViewerPlotPane {
|
||||||
|
private final static Log _log = new Log(JFreeChartHeartbeatPlotPane.class);
|
||||||
|
private ChartPanel _panel;
|
||||||
|
private JFreeChartAdapter _adapter;
|
||||||
|
|
||||||
|
public JFreeChartHeartbeatPlotPane(NetViewerGUI gui) {
|
||||||
|
super(gui);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stateUpdated() {
|
||||||
|
if (_panel == null) {
|
||||||
|
remove(0); // remove the dummy
|
||||||
|
|
||||||
|
_adapter = new JFreeChartAdapter();
|
||||||
|
_panel = _adapter.createPanel(_gui.getViewer());
|
||||||
|
_panel.setBackground(_gui.getBackground());
|
||||||
|
JScrollPane pane = new JScrollPane(_panel);
|
||||||
|
pane.setBackground(_gui.getBackground());
|
||||||
|
add(pane, BorderLayout.CENTER);
|
||||||
|
} else {
|
||||||
|
_adapter.updateChart(_panel, _gui.getViewer());
|
||||||
|
//_gui.pack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeComponents() {
|
||||||
|
// noop
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
add(new JLabel(), BorderLayout.CENTER);
|
||||||
|
//dummy.setBackground(_gui.getBackground());
|
||||||
|
//dummy.setPreferredSize(new Dimension(800,600));
|
||||||
|
//add(dummy);
|
||||||
|
|
||||||
|
//add(_panel);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import net.i2p.netmonitor.NetMonitor;
|
||||||
|
import net.i2p.netmonitor.PeerSummary;
|
||||||
|
import net.i2p.netmonitor.PeerStat;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.I2PThread;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coordinate the visualization of the network monitor. <p />
|
||||||
|
*
|
||||||
|
* <b>Usage: <code>NetViewer [exportDir]</code></b> <br />
|
||||||
|
* (exportDir is where the NetMonitor exports its state, "monitorData" by default)
|
||||||
|
*/
|
||||||
|
public class NetViewer {
|
||||||
|
private static final Log _log = new Log(NetViewer.class);
|
||||||
|
private NetMonitor _monitor;
|
||||||
|
private NetViewerGUI _gui;
|
||||||
|
private Map _plotConfigs;
|
||||||
|
private boolean _isClosed;
|
||||||
|
|
||||||
|
public NetViewer() {
|
||||||
|
this(NetMonitor.EXPORT_DIR_DEFAULT);
|
||||||
|
}
|
||||||
|
public NetViewer(String dataDir) {
|
||||||
|
_monitor = new NetMonitor();
|
||||||
|
_monitor.setExportDir(dataDir);
|
||||||
|
_isClosed = false;
|
||||||
|
_gui = new NetViewerGUI(this);
|
||||||
|
_plotConfigs = new HashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runViewer() {
|
||||||
|
I2PThread t = new I2PThread(new NetViewerRunner(this));
|
||||||
|
t.setName("NetViewer");
|
||||||
|
t.setDaemon(false);
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshGUI() {
|
||||||
|
_gui.stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reloadData() {
|
||||||
|
_log.debug("Reloading data");
|
||||||
|
_monitor.importData();
|
||||||
|
refreshGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addConfig(String peerName, PeerPlotConfig config) {
|
||||||
|
synchronized (_plotConfigs) {
|
||||||
|
_plotConfigs.put(peerName, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PeerPlotConfig getConfig(String peerName) {
|
||||||
|
synchronized (_plotConfigs) {
|
||||||
|
return (PeerPlotConfig)_plotConfigs.get(peerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List getConfigNames() {
|
||||||
|
synchronized (_plotConfigs) {
|
||||||
|
return new ArrayList(_plotConfigs.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetMonitor getMonitor() { return _monitor; }
|
||||||
|
|
||||||
|
long getDataLoadDelay() { return _monitor.getExportDelay(); }
|
||||||
|
|
||||||
|
/** has the viewer been closed? */
|
||||||
|
public boolean getIsClosed() { return _isClosed; }
|
||||||
|
public void setIsClosed(boolean closed) { _isClosed = closed; }
|
||||||
|
|
||||||
|
public static final void main(String args[]) {
|
||||||
|
if (args.length == 1)
|
||||||
|
new NetViewer(args[0]).runViewer();
|
||||||
|
else
|
||||||
|
new NetViewer().runViewer();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.ItemListener;
|
||||||
|
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
class NetViewerCommandBar extends JPanel {
|
||||||
|
private NetViewerGUI _gui;
|
||||||
|
private JComboBox _refreshRate;
|
||||||
|
private JTextField _location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a command bar onto the gui
|
||||||
|
* @param gui the gui the command bar is associated with
|
||||||
|
*/
|
||||||
|
public NetViewerCommandBar(NetViewerGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshChanged(ItemEvent evt) {}
|
||||||
|
private void loadCalled() {
|
||||||
|
//_gui.getMonitor().load(_location.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void browseCalled() {
|
||||||
|
JFileChooser chooser = new JFileChooser(_location.getText());
|
||||||
|
chooser.setBackground(_gui.getBackground());
|
||||||
|
chooser.setMultiSelectionEnabled(false);
|
||||||
|
int rv = chooser.showDialog(this, "Load");
|
||||||
|
//if (rv == JFileChooser.APPROVE_OPTION)
|
||||||
|
// _gui.getMonitor().load(chooser.getSelectedFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
_refreshRate = new JComboBox(new DefaultComboBoxModel(new Object[] {"10 second refresh", "30 second refresh", "1 minute refresh", "5 minute refresh"}));
|
||||||
|
_refreshRate.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { refreshChanged(evt); } });
|
||||||
|
_refreshRate.setEnabled(false);
|
||||||
|
_refreshRate.setBackground(_gui.getBackground());
|
||||||
|
//add(_refreshRate);
|
||||||
|
JLabel loadLabel = new JLabel("Load from: ");
|
||||||
|
loadLabel.setBackground(_gui.getBackground());
|
||||||
|
add(loadLabel);
|
||||||
|
_location = new JTextField(20);
|
||||||
|
_location.setToolTipText("Either specify a local filename or a fully qualified URL");
|
||||||
|
_location.setBackground(_gui.getBackground());
|
||||||
|
_location.setEnabled(false);
|
||||||
|
add(_location);
|
||||||
|
JButton browse = new JButton("Browse...");
|
||||||
|
browse.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { browseCalled(); } });
|
||||||
|
browse.setBackground(_gui.getBackground());
|
||||||
|
browse.setEnabled(false);
|
||||||
|
add(browse);
|
||||||
|
JButton load = new JButton("Load");
|
||||||
|
load.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadCalled(); } });
|
||||||
|
load.setBackground(_gui.getBackground());
|
||||||
|
load.setEnabled(false);
|
||||||
|
add(load);
|
||||||
|
setBackground(_gui.getBackground());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the control widgets (refresh/load/snapshot and the
|
||||||
|
* tabbed panel with the plot config data)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class NetViewerControlPane extends JPanel {
|
||||||
|
private final static Log _log = new Log(NetViewerControlPane.class);
|
||||||
|
private NetViewerGUI _gui;
|
||||||
|
private JTabbedPane _configPane;
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private final static Color LIGHT_BLUE = new Color(180, 180, 255);
|
||||||
|
private final static Color BLACK = new Color(0, 0, 0);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
private Color _foreground = BLACK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a control panel onto the gui
|
||||||
|
* @param gui the gui the panel is associated with
|
||||||
|
*/
|
||||||
|
public NetViewerControlPane(NetViewerGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the settings have changed - revise */
|
||||||
|
void refreshView() {
|
||||||
|
_gui.refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback: when tests have changed
|
||||||
|
*/
|
||||||
|
public synchronized void stateUpdated() {
|
||||||
|
List knownNames = new ArrayList(8);
|
||||||
|
List peers = _gui.getViewer().getMonitor().getPeers();
|
||||||
|
_log.debug("GUI updated with peers: " + peers);
|
||||||
|
for (int i = 0; i < peers.size(); i++) {
|
||||||
|
String name = (String)peers.get(i);
|
||||||
|
String shortName = name.substring(0,4);
|
||||||
|
knownNames.add(shortName);
|
||||||
|
if (_configPane.indexOfTab(shortName) >= 0) {
|
||||||
|
JScrollPane pane = (JScrollPane)_configPane.getComponentAt(_configPane.indexOfTab(shortName));
|
||||||
|
PeerPlotConfigPane cfgPane = (PeerPlotConfigPane)pane.getViewport().getView();
|
||||||
|
cfgPane.stateUpdated();
|
||||||
|
_log.debug("We already know about [" + name + "]");
|
||||||
|
} else {
|
||||||
|
_log.info("The peer [" + name + "] is new to us");
|
||||||
|
PeerPlotConfig cfg = new PeerPlotConfig(_gui.getViewer().getMonitor().getSummary(name));
|
||||||
|
_gui.getViewer().addConfig(name, cfg);
|
||||||
|
PeerPlotConfigPane pane = new PeerPlotConfigPane(cfg, this);
|
||||||
|
JScrollPane p = new JScrollPane(pane);
|
||||||
|
p.setBackground(_background);
|
||||||
|
_configPane.addTab(shortName, null, p, "Peer " + name);
|
||||||
|
_configPane.setBackgroundAt(_configPane.getTabCount()-1, _background);
|
||||||
|
_configPane.setForegroundAt(_configPane.getTabCount()-1, _foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List toRemove = new ArrayList(4);
|
||||||
|
for (int i = 0; i < _configPane.getTabCount(); i++) {
|
||||||
|
if (knownNames.contains(_configPane.getTitleAt(i))) {
|
||||||
|
// noop
|
||||||
|
} else {
|
||||||
|
toRemove.add(_configPane.getTitleAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < toRemove.size(); i++) {
|
||||||
|
String title = (String)toRemove.get(i);
|
||||||
|
_log.info("Removing peer [" + title + "]");
|
||||||
|
_configPane.removeTabAt(_configPane.indexOfTab(title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
if (_gui != null)
|
||||||
|
setBackground(_gui.getBackground());
|
||||||
|
else
|
||||||
|
setBackground(_background);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
NetViewerCommandBar bar = new NetViewerCommandBar(_gui);
|
||||||
|
bar.setBackground(getBackground());
|
||||||
|
add(bar, BorderLayout.NORTH);
|
||||||
|
_configPane = new JTabbedPane(JTabbedPane.LEFT);
|
||||||
|
_configPane.setBackground(_background);
|
||||||
|
JScrollPane pane = new JScrollPane(_configPane);
|
||||||
|
pane.setBackground(_background);
|
||||||
|
add(pane, BorderLayout.CENTER);
|
||||||
|
//setPreferredSize(new Dimension(800, 400));
|
||||||
|
//setMinimumSize(new Dimension(800, 400));
|
||||||
|
//setMaximumSize(new Dimension(800, 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
NetViewer getViewer() { return _gui.getViewer(); }
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
|
|
||||||
|
class NetViewerGUI extends JFrame {
|
||||||
|
private NetViewer _viewer;
|
||||||
|
private NetViewerPlotPane _plotPane;
|
||||||
|
private NetViewerControlPane _controlPane;
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the GUI for all youz who be too shoopid for text based shitz
|
||||||
|
* @param monitor the monitor the gui operates over
|
||||||
|
*/
|
||||||
|
public NetViewerGUI(NetViewer viewer) {
|
||||||
|
super("Network Viewer");
|
||||||
|
_viewer = viewer;
|
||||||
|
initializeComponents();
|
||||||
|
pack();
|
||||||
|
//setResizable(false);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetViewer getViewer() { return _viewer; }
|
||||||
|
|
||||||
|
/** build up all our widgets */
|
||||||
|
private void initializeComponents() {
|
||||||
|
getContentPane().setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
setBackground(_background);
|
||||||
|
|
||||||
|
_plotPane = new JFreeChartHeartbeatPlotPane(this); // // new NetViewerPlotPane(this); //
|
||||||
|
_plotPane.setBackground(_background);
|
||||||
|
JScrollPane pane = new JScrollPane(_plotPane);
|
||||||
|
pane.setBackground(_background);
|
||||||
|
//getContentPane().add(pane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
_controlPane = new NetViewerControlPane(this);
|
||||||
|
_controlPane.setBackground(_background);
|
||||||
|
//getContentPane().add(_controlPane, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pane, new JScrollPane(_controlPane));
|
||||||
|
getContentPane().add(split, BorderLayout.CENTER);
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
initializeMenus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback: when the state of the world changes . . .
|
||||||
|
*/
|
||||||
|
public void stateUpdated() {
|
||||||
|
_controlPane.stateUpdated();
|
||||||
|
_plotPane.stateUpdated();
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshView() {
|
||||||
|
_plotPane.stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exitCalled() {
|
||||||
|
_viewer.setIsClosed(true);
|
||||||
|
setVisible(false);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
private void loadConfigCalled() {}
|
||||||
|
private void saveConfigCalled() {}
|
||||||
|
private void loadSnapshotCalled() {}
|
||||||
|
private void saveSnapshotCalled() {}
|
||||||
|
|
||||||
|
private void initializeMenus() {
|
||||||
|
JMenuBar bar = new JMenuBar();
|
||||||
|
JMenu fileMenu = new JMenu("File");
|
||||||
|
JMenuItem loadConfig = new JMenuItem("Load config");
|
||||||
|
loadConfig.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadConfigCalled(); } });
|
||||||
|
JMenuItem saveConfig = new JMenuItem("Save config");
|
||||||
|
saveConfig.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveConfigCalled(); } });
|
||||||
|
JMenuItem saveSnapshot = new JMenuItem("Save snapshot");
|
||||||
|
saveSnapshot.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { saveSnapshotCalled(); } });
|
||||||
|
JMenuItem loadSnapshot = new JMenuItem("Load snapshot");
|
||||||
|
loadSnapshot.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { loadSnapshotCalled(); } });
|
||||||
|
JMenuItem exit = new JMenuItem("Exit");
|
||||||
|
exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exitCalled(); } });
|
||||||
|
|
||||||
|
fileMenu.add(loadConfig);
|
||||||
|
fileMenu.add(saveConfig);
|
||||||
|
fileMenu.add(loadSnapshot);
|
||||||
|
fileMenu.add(saveSnapshot);
|
||||||
|
fileMenu.add(exit);
|
||||||
|
bar.add(fileMenu);
|
||||||
|
setJMenuBar(bar);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the graph and legend
|
||||||
|
*/
|
||||||
|
class NetViewerPlotPane extends JPanel {
|
||||||
|
private final static Log _log = new Log(NetViewerPlotPane.class);
|
||||||
|
protected NetViewerGUI _gui;
|
||||||
|
private JTextArea _text;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the plot pane
|
||||||
|
* @param gui the gui the pane is attached to
|
||||||
|
*/
|
||||||
|
public NetViewerPlotPane(NetViewerGUI gui) {
|
||||||
|
_gui = gui;
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback: when things change . . .
|
||||||
|
*/
|
||||||
|
public void stateUpdated() {
|
||||||
|
StringBuffer buf = new StringBuffer(32*1024);
|
||||||
|
buf.append("moo");
|
||||||
|
_text.setText(buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeComponents() {
|
||||||
|
setBackground(_gui.getBackground());
|
||||||
|
//Dimension size = new Dimension(800, 600);
|
||||||
|
_text = new JTextArea("",30,80); // 16, 60);
|
||||||
|
_text.setAutoscrolls(true);
|
||||||
|
_text.setEditable(false);
|
||||||
|
// _text.setLineWrap(true);
|
||||||
|
// add(new JScrollPane(_text));
|
||||||
|
add(_text);
|
||||||
|
// add(new JScrollPane(_text, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS));
|
||||||
|
// setPreferredSize(size);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
class NetViewerRunner implements Runnable {
|
||||||
|
private NetViewer _viewer;
|
||||||
|
|
||||||
|
public NetViewerRunner(NetViewer viewer) {
|
||||||
|
_viewer = viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
while (!_viewer.getIsClosed()) {
|
||||||
|
_viewer.reloadData();
|
||||||
|
try { Thread.sleep(_viewer.getDataLoadDelay()*1000); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,218 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.netmonitor.PeerSummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure how we want to render a particular peerSummary in the GUI
|
||||||
|
*/
|
||||||
|
class PeerPlotConfig {
|
||||||
|
private final static Log _log = new Log(PeerPlotConfig.class);
|
||||||
|
/** where can we find the current state/data (either as a filename or a URL)? */
|
||||||
|
private String _location;
|
||||||
|
/** what test are we defining the plot data for? */
|
||||||
|
private PeerSummary _summary;
|
||||||
|
/** how should we render the current data set? */
|
||||||
|
private PlotSeriesConfig _seriesConfig;
|
||||||
|
private Set _listeners;
|
||||||
|
private boolean _disabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegating constructor . . .
|
||||||
|
* @param location the name of the file/URL to get the data from
|
||||||
|
*/
|
||||||
|
public PeerPlotConfig(String location) {
|
||||||
|
this(location, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegating constructor . . .
|
||||||
|
* @param location the name of the file/URL to get the data from
|
||||||
|
*/
|
||||||
|
public PeerPlotConfig(PeerSummary summary) {
|
||||||
|
this(null, summary, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a config =)
|
||||||
|
* @param location the location of the file/URL to get the data from
|
||||||
|
* @param config the client's configuration
|
||||||
|
* @param seriesConfig the series config
|
||||||
|
*/
|
||||||
|
public PeerPlotConfig(String location, PeerSummary summary, PlotSeriesConfig seriesConfig) {
|
||||||
|
_location = location;
|
||||||
|
_summary = summary;
|
||||||
|
if (seriesConfig != null)
|
||||||
|
_seriesConfig = seriesConfig;
|
||||||
|
else
|
||||||
|
_seriesConfig = new PlotSeriesConfig();
|
||||||
|
|
||||||
|
_listeners = Collections.synchronizedSet(new HashSet(2));
|
||||||
|
_disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where is the current state data supposed to be found? This must either be a
|
||||||
|
* local file path or a URL
|
||||||
|
* @return the current location
|
||||||
|
*/
|
||||||
|
public String getLocation() { return _location; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location the current state data is supposed to be found. This must either be
|
||||||
|
* a local file path or a URL
|
||||||
|
* @param location the location
|
||||||
|
*/
|
||||||
|
public void setLocation(String location) {
|
||||||
|
_location = location;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What are we configuring?
|
||||||
|
* @return the client configuration
|
||||||
|
*/
|
||||||
|
public PeerSummary getSummary() { return _summary; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets what we are currently configuring
|
||||||
|
* @param config the new config
|
||||||
|
*/
|
||||||
|
public void setPeerSummary(PeerSummary summary) {
|
||||||
|
_summary = summary;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How do we want to render the current data set?
|
||||||
|
* @return the way we currently render the data
|
||||||
|
*/
|
||||||
|
public PlotSeriesConfig getSeriesConfig() { return _seriesConfig; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets how we want to render the current data set.
|
||||||
|
* @param config the new config
|
||||||
|
*/
|
||||||
|
public void setSeriesConfig(PlotSeriesConfig config) {
|
||||||
|
_seriesConfig = config;
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* four char description of the peer
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
public String getPeerName() { return _summary.getPeer(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* we've got someone who wants to be notified of changes to the plot config
|
||||||
|
* @param lsnr the listener to be added
|
||||||
|
*/
|
||||||
|
public void addListener(UpdateListener lsnr) { _listeners.add(lsnr); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove a listener
|
||||||
|
* @param lsnr the listener to remove
|
||||||
|
*/
|
||||||
|
public void removeListener(UpdateListener lsnr) { _listeners.remove(lsnr); }
|
||||||
|
|
||||||
|
void fireUpdate() {
|
||||||
|
if (_disabled) return;
|
||||||
|
for (Iterator iter = _listeners.iterator(); iter.hasNext(); ) {
|
||||||
|
((UpdateListener)iter.next()).configUpdated(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables notification of events listeners
|
||||||
|
* @see PeerPlotConfig#fireUpdate()
|
||||||
|
*/
|
||||||
|
public void disableEvents() { _disabled = true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables notification of events listeners
|
||||||
|
* @see PeerPlotConfig#fireUpdate()
|
||||||
|
*/
|
||||||
|
public void enableEvents() { _disabled = false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How do we want to render a particular dataset (either the current or the averaged values)?
|
||||||
|
*/
|
||||||
|
public class PlotSeriesConfig {
|
||||||
|
private Map _shouldPlot;
|
||||||
|
private Map _plotColors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a config for the rendering of a particular dataset)
|
||||||
|
* @param plotLost do we plot lost packets?
|
||||||
|
* @param plotColor in what color?
|
||||||
|
*/
|
||||||
|
public PlotSeriesConfig() {
|
||||||
|
_shouldPlot = new HashMap(16);
|
||||||
|
_plotColors = new HashMap(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the plot config this plot series config is a part of
|
||||||
|
* @return the plot config
|
||||||
|
*/
|
||||||
|
public PeerPlotConfig getPlotConfig() { return PeerPlotConfig.this; }
|
||||||
|
|
||||||
|
public boolean getShouldPlotValue(String statName, String argName, boolean defaultVal) {
|
||||||
|
Boolean val = (Boolean)_shouldPlot.get(statName + argName);
|
||||||
|
if (val == null)
|
||||||
|
return defaultVal;
|
||||||
|
else
|
||||||
|
return val.booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShouldPlotValue(String statName, String argName, boolean shouldPlot) {
|
||||||
|
_shouldPlot.put(statName + argName, new Boolean(shouldPlot));
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What color should we plot the data with?
|
||||||
|
* @return the color
|
||||||
|
*/
|
||||||
|
public Color getPlotLineColor(String statName, String argName) {
|
||||||
|
return (Color)_plotColors.get(statName + argName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the color we should plot the data with
|
||||||
|
* @param color the color to use
|
||||||
|
*/
|
||||||
|
public void setPlotLineColor(String statName, String argName, Color color) {
|
||||||
|
if (color == null)
|
||||||
|
_plotColors.remove(statName + argName);
|
||||||
|
else
|
||||||
|
_plotColors.put(statName + argName, color);
|
||||||
|
fireUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for listening to updates . . .
|
||||||
|
*/
|
||||||
|
public interface UpdateListener {
|
||||||
|
/**
|
||||||
|
* @param config the peer plot config that changes
|
||||||
|
* @see PeerPlotConfig#fireUpdate()
|
||||||
|
*/
|
||||||
|
void configUpdated(PeerPlotConfig config);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,280 @@
|
|||||||
|
package net.i2p.netmonitor.gui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JColorChooser;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.netmonitor.PeerStat;
|
||||||
|
import net.i2p.netmonitor.PeerSummary;
|
||||||
|
|
||||||
|
class PeerPlotConfigPane extends JPanel implements PeerPlotConfig.UpdateListener {
|
||||||
|
private final static Log _log = new Log(PeerPlotConfigPane.class);
|
||||||
|
private PeerPlotConfig _config;
|
||||||
|
private NetViewerControlPane _parent;
|
||||||
|
private JLabel _peer;
|
||||||
|
private JButton _toggleAll;
|
||||||
|
private OptionGroup _options[];
|
||||||
|
private Random _rnd = new Random();
|
||||||
|
private final static Color WHITE = new Color(255, 255, 255);
|
||||||
|
private Color _background = WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a pane
|
||||||
|
* @param config the plot config it represents
|
||||||
|
* @param pane the pane this one is attached to
|
||||||
|
*/
|
||||||
|
public PeerPlotConfigPane(PeerPlotConfig config, NetViewerControlPane pane) {
|
||||||
|
_config = config;
|
||||||
|
_parent = pane;
|
||||||
|
if (_parent != null)
|
||||||
|
_background = _parent.getBackground();
|
||||||
|
_config.addListener(this);
|
||||||
|
initializeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeComponents() {
|
||||||
|
buildComponents();
|
||||||
|
placeComponents(this);
|
||||||
|
refreshView();
|
||||||
|
//setBorder(new BevelBorder(BevelBorder.RAISED));
|
||||||
|
setBackground(_background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* place all the gui components onto the given panel
|
||||||
|
* @param body the panel to place the components on
|
||||||
|
*/
|
||||||
|
private void placeComponents(JPanel body) {
|
||||||
|
body.setLayout(new GridBagLayout());
|
||||||
|
GridBagConstraints cts = new GridBagConstraints();
|
||||||
|
|
||||||
|
// row 0: peer name + toggle All
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = 0;
|
||||||
|
cts.gridwidth = 2;
|
||||||
|
cts.anchor = GridBagConstraints.WEST;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_peer, cts);
|
||||||
|
cts.gridx = 2;
|
||||||
|
cts.gridy = 0;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.anchor = GridBagConstraints.EAST;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(_toggleAll, cts);
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
for (int i = 0; i < _options.length; i++) {
|
||||||
|
row++;
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = row;
|
||||||
|
cts.gridwidth = 3;
|
||||||
|
cts.weightx = 5;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
cts.anchor = GridBagConstraints.WEST;
|
||||||
|
body.add(_options[i]._statName, cts);
|
||||||
|
|
||||||
|
for (int j = 0; j < _options[i]._statAttributes.size(); j++) {
|
||||||
|
row++;
|
||||||
|
cts.gridx = 0;
|
||||||
|
cts.gridy = row;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.weightx = 1;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
body.add(new JLabel(" "), cts);
|
||||||
|
cts.gridx = 1;
|
||||||
|
cts.gridy = row;
|
||||||
|
cts.gridwidth = 1;
|
||||||
|
cts.weightx = 5;
|
||||||
|
cts.fill = GridBagConstraints.BOTH;
|
||||||
|
JCheckBox box = (JCheckBox)_options[i]._statAttributes.get(j);
|
||||||
|
box.setBackground(_background);
|
||||||
|
body.add(box, cts);
|
||||||
|
cts.gridx = 2;
|
||||||
|
cts.fill = GridBagConstraints.NONE;
|
||||||
|
cts.anchor = GridBagConstraints.EAST;
|
||||||
|
JButton toggleAttr = new JButton("Toggle all");
|
||||||
|
toggleAttr.setBackground(_background);
|
||||||
|
toggleAttr.addActionListener(new ToggleAttribute(_options[i].getStatName(), box.getText(), box));
|
||||||
|
body.add(toggleAttr, cts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ToggleAttribute implements ActionListener {
|
||||||
|
private String _statName;
|
||||||
|
private String _attrName;
|
||||||
|
private JCheckBox _box;
|
||||||
|
public ToggleAttribute(String statName, String attrName, JCheckBox box) {
|
||||||
|
_statName = statName;
|
||||||
|
_attrName = attrName;
|
||||||
|
_box = box;
|
||||||
|
}
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
boolean setActive = true;
|
||||||
|
if (_box.isSelected()) {
|
||||||
|
setActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List names = _parent.getViewer().getConfigNames();
|
||||||
|
for (int i = 0; i < names.size(); i++) {
|
||||||
|
String name = (String)names.get(i);
|
||||||
|
PeerPlotConfig cfg = _parent.getViewer().getConfig(name);
|
||||||
|
cfg.getSeriesConfig().setShouldPlotValue(_statName, _attrName, setActive);
|
||||||
|
_log.debug("Setting " + _statName + "." + _attrName + " to " + setActive + " for " + name);
|
||||||
|
}
|
||||||
|
_parent.stateUpdated();
|
||||||
|
_parent.refreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** build all of the gui components */
|
||||||
|
private void buildComponents() {
|
||||||
|
_peer = new JLabel("Router: " + _config.getPeerName());
|
||||||
|
|
||||||
|
_toggleAll = new JButton("Show all");
|
||||||
|
_toggleAll.setBackground(_background);
|
||||||
|
_toggleAll.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
_config.disableEvents();
|
||||||
|
for (int i = 0; i < _options.length; i++) {
|
||||||
|
for (int j = 0; j < _options[i]._statAttributes.size(); j++) {
|
||||||
|
JCheckBox box = (JCheckBox)_options[i]._statAttributes.get(j);
|
||||||
|
String statName = _options[i].getStatName();
|
||||||
|
String attrName = box.getText();
|
||||||
|
if (_toggleAll.getText().equals("Show all")) {
|
||||||
|
box.setSelected(true);
|
||||||
|
_config.getSeriesConfig().setShouldPlotValue(statName, attrName, true);
|
||||||
|
} else {
|
||||||
|
box.setSelected(false);
|
||||||
|
_config.getSeriesConfig().setShouldPlotValue(statName, attrName, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_toggleAll.getText().equals("Show all"))
|
||||||
|
_toggleAll.setText("Hide all");
|
||||||
|
else
|
||||||
|
_toggleAll.setText("Show all");
|
||||||
|
_config.enableEvents();
|
||||||
|
_config.fireUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Set statNames = _config.getSummary().getStatNames();
|
||||||
|
List options = new ArrayList(statNames.size());
|
||||||
|
for (Iterator iter = statNames.iterator(); iter.hasNext(); ) {
|
||||||
|
String statName = (String)iter.next();
|
||||||
|
List data = _config.getSummary().getData(statName);
|
||||||
|
if ( (data != null) && (data.size() > 0) ) {
|
||||||
|
PeerStat stat = (PeerStat)data.get(0);
|
||||||
|
String attributes[] = stat.getValueDescriptions();
|
||||||
|
OptionGroup group = new OptionGroup(statName, attributes);
|
||||||
|
options.add(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TreeMap orderedOptions = new TreeMap();
|
||||||
|
for (int i = 0; i < options.size(); i++) {
|
||||||
|
OptionGroup grp = (OptionGroup)options.get(i);
|
||||||
|
orderedOptions.put(grp.getStatName(), grp);
|
||||||
|
}
|
||||||
|
_options = new OptionGroup[options.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Iterator iter = orderedOptions.values().iterator(); iter.hasNext(); ) {
|
||||||
|
OptionGroup grp = (OptionGroup)iter.next();
|
||||||
|
_options[i] = grp;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** the settings have changed - revise */
|
||||||
|
private void refreshView() {
|
||||||
|
_parent.refreshView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* notified that the config has been updated
|
||||||
|
* @param config the config that was been updated
|
||||||
|
*/
|
||||||
|
public void configUpdated(PeerPlotConfig config) { refreshView(); }
|
||||||
|
|
||||||
|
public void stateUpdated() {
|
||||||
|
for (int i = 0; i < _options.length; i++) {
|
||||||
|
for (int j = 0; j < _options[i]._statAttributes.size(); j++) {
|
||||||
|
JCheckBox box = (JCheckBox)_options[i]._statAttributes.get(j);
|
||||||
|
if (_config.getSeriesConfig().getShouldPlotValue(_options[i].getStatName(), box.getText(), false))
|
||||||
|
box.setSelected(true);
|
||||||
|
else
|
||||||
|
box.setSelected(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OptionGroup {
|
||||||
|
JTextField _statName;
|
||||||
|
List _statAttributes;
|
||||||
|
|
||||||
|
public String getStatName() { return _statName.getText(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an OptionLine.
|
||||||
|
* @param statName statistic group in question
|
||||||
|
* @param statAttributes list of attributes we keep about this stat
|
||||||
|
*/
|
||||||
|
public OptionGroup(String statName, String statAttributes[]) {
|
||||||
|
_statName = new JTextField(statName);
|
||||||
|
_statName.setEditable(false);
|
||||||
|
_statName.setBackground(_background);
|
||||||
|
_statAttributes = new ArrayList(4);
|
||||||
|
if (statAttributes != null) {
|
||||||
|
for (int i = 0; i < statAttributes.length; i++) {
|
||||||
|
JCheckBox box = new JCheckBox(statAttributes[i]);
|
||||||
|
box.addActionListener(new UpdateListener(OptionGroup.this));
|
||||||
|
_statAttributes.add(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpdateListener implements ActionListener {
|
||||||
|
private OptionGroup _group;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Listener constructor . . .
|
||||||
|
* @param group the group of stats to watch
|
||||||
|
*/
|
||||||
|
public UpdateListener(OptionGroup group) {
|
||||||
|
_group = group;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
|
||||||
|
*/
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
_config.disableEvents();
|
||||||
|
|
||||||
|
for (int i = 0; i < _group._statAttributes.size(); i++) {
|
||||||
|
JCheckBox box = (JCheckBox)_group._statAttributes.get(i);
|
||||||
|
_config.getSeriesConfig().setShouldPlotValue(_group.getStatName(), box.getText(), box.isSelected());
|
||||||
|
}
|
||||||
|
_config.enableEvents();
|
||||||
|
_config.fireUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user