Javadoc, imports, and (potentially) some indentation. Maybe it won't do anything.... who knows?

(shendaras)
This commit is contained in:
shendaras
2004-04-10 09:12:59 +00:00
committed by zzz
parent e3a86bb150
commit 99790695a2
6 changed files with 468 additions and 152 deletions

View File

@ -1,14 +1,14 @@
package net.i2p.heartbeat; package net.i2p.heartbeat;
import net.i2p.data.Destination;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/** /**
* Define the configuration for testing against one particular peer as a client * Define the configuration for testing against one particular peer as a client
* *
@ -26,17 +26,63 @@ public class ClientConfig {
private String _comment; private String _comment;
private int _averagePeriods[]; private int _averagePeriods[];
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_PREFIX = "peer."; public static final String PROP_PREFIX = "peer.";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_PEER = ".peer"; public static final String PROP_PEER = ".peer";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_STATFILE = ".statFile"; public static final String PROP_STATFILE = ".statFile";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_STATDURATION = ".statDuration"; public static final String PROP_STATDURATION = ".statDuration";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_STATFREQUENCY = ".statFrequency"; public static final String PROP_STATFREQUENCY = ".statFrequency";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_SENDFREQUENCY = ".sendFrequency"; public static final String PROP_SENDFREQUENCY = ".sendFrequency";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_SENDSIZE = ".sendSize"; public static final String PROP_SENDSIZE = ".sendSize";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_COMMENT = ".comment"; public static final String PROP_COMMENT = ".comment";
/**
* @seeRoutine ClientConfig#load
* @seeRoutine ClientConfig#store
*/
public static final String PROP_AVERAGEPERIODS = ".averagePeriods"; public static final String PROP_AVERAGEPERIODS = ".averagePeriods";
/**
* Default constructor...
*/
public ClientConfig() { public ClientConfig() {
this(null, null, null, -1, -1, -1, -1, 0, null, null); this(null, null, null, -1, -1, -1, -1, 0, null, null);
} }
@ -65,64 +111,135 @@ public class ClientConfig {
_averagePeriods = averagePeriods; _averagePeriods = averagePeriods;
} }
/** peer to test against */ /** Retrieves the peer to test against
* @return the Destination (peer)
*/
public Destination getPeer() { return _peer; } public Destination getPeer() { return _peer; }
/**
* Sets the peer to test against
* @param peer the Destination (peer)
*/
public void setPeer(Destination peer) { _peer = peer; } public void setPeer(Destination peer) { _peer = peer; }
/** who we are when we test */
/**
* Retrieves who we are when we test
* @return the Destination (us)
*/
public Destination getUs() { return _us; } public Destination getUs() { return _us; }
/**
* Sets who we are when we test
* @param us the Destination (us)
*/
public void setUs(Destination us) { _us = us; } public void setUs(Destination us) { _us = us; }
/** location to write the current stats to */ /**
* Retrieves the location to write the current stats to
* @return the name of the file
*/
public String getStatFile() { return _statFile; } public String getStatFile() { return _statFile; }
/**
* Sets the name of the location we write the current stats to
* @param statFile the name of the file
*/
public void setStatFile(String statFile) { _statFile = statFile; } public void setStatFile(String statFile) { _statFile = statFile; }
/** how many minutes of statistics should be maintained within the window for this client? */ /**
* Retrieves how many minutes of statistics should be maintained within the window for this client
* @return the number of minutes
*/
public int getStatDuration() { return _statDuration; } public int getStatDuration() { return _statDuration; }
/**
* Sets how many minutes of statistics should be maintained within the window for this client
* @param durationMinutes the number of minutes
*/
public void setStatDuration(int durationMinutes) { _statDuration = durationMinutes; } public void setStatDuration(int durationMinutes) { _statDuration = durationMinutes; }
/** how frequenty the stats are written out (in seconds) */ /**
* Retrieves how frequently the stats are written out (in seconds)
* @return the frequency in seconds
*/
public int getStatFrequency() { return _statFrequency; } public int getStatFrequency() { return _statFrequency; }
/**
* Sets how frequently the stats are written out (in seconds)
* @param freqSeconds the frequency in seconds
*/
public void setStatFrequency(int freqSeconds) { _statFrequency = freqSeconds; } public void setStatFrequency(int freqSeconds) { _statFrequency = freqSeconds; }
/** how frequenty we send messages to the peer (in seconds) */ /**
* Retrieves how frequenty we send messages to the peer (in seconds)
* @return the frequency in seconds
*/
public int getSendFrequency() { return _sendFrequency; } public int getSendFrequency() { return _sendFrequency; }
/**
* Sets how frequenty we send messages to the peer (in seconds)
* @param freqSeconds the frequency in seconds
*/
public void setSendFrequency(int freqSeconds) { _sendFrequency = freqSeconds; } public void setSendFrequency(int freqSeconds) { _sendFrequency = freqSeconds; }
/** /**
* How many bytes should the ping messages be (min values ~700, max ~32KB)? * Retrieves how many bytes the ping messages should be
* * (min values ~700, max ~32KB)
* @return the size in bytes
*/ */
public int getSendSize() { return _sendSize; } public int getSendSize() { return _sendSize; }
/**
* Sets how many bytes the ping messages should be
* (min values ~700, max ~32KB)
* @param numBytes the size in bytes
*/
public void setSendSize(int numBytes) { _sendSize = numBytes; } public void setSendSize(int numBytes) { _sendSize = numBytes; }
/** /**
* Brief 1 line description of the test. Useful comments are along the lines * Retrieves the brief, 1 line description of the test.
* of "The peer is located on a fast router and connection with 2 hop tunnels". * Useful comments are along the lines of "The peer is located on a fast router and connection with 2 hop tunnels".
* *
* @return the brief comment
*/ */
public String getComment() { return _comment; } public String getComment() { return _comment; }
/**
* Sets a brief, 1 line description (comment) of the test.
* @param comment the brief comment
*/
public void setComment(String comment) { _comment = comment; } public void setComment(String comment) { _comment = comment; }
/** /**
* Periods that the client's tests should be averaged over. * Retrieves the periods that the client's tests should be averaged over.
*
* @return list of periods (in minutes) that the data should be averaged over, or null * @return list of periods (in minutes) that the data should be averaged over, or null
*/ */
public int[] getAveragePeriods() { return _averagePeriods; } public int[] getAveragePeriods() { return _averagePeriods; }
/**
* Sets the periods that the client's tests should be averaged over.
* @param periods the list of periods (in minutes) that the data should be averaged over, or null
*/
public void setAveragePeriods(int periods[]) { _averagePeriods = periods; } public void setAveragePeriods(int periods[]) { _averagePeriods = periods; }
/** /**
* How many hops is this test engine configured to use for its outbound and inbound tunnels? * Retrieves how many hops this test engine is configured to use for its outbound and inbound tunnels
* * @return the number of hops
*/ */
public int getNumHops() { return _numHops; } public int getNumHops() { return _numHops; }
/**
* Sets how many hops this test engine is configured to use for its outbound and inbound tunnels
* @param numHops the number of hops
*/
public void setNumHops(int numHops) { _numHops = numHops; } public void setNumHops(int numHops) { _numHops = numHops; }
/** /**
* Load the client config from the properties specified, deriving the current * Load the client config from the properties specified, deriving the current
* config entry from the peer number. * config entry from the peer number.
* *
* @param clientConfig the properties to load from
* @param peerNum the number associated with the peer
* @return true if it was loaded correctly, false if there were errors * @return true if it was loaded correctly, false if there were errors
*/ */
public boolean load(Properties clientConfig, int peerNum) { public boolean load(Properties clientConfig, int peerNum) {
@ -138,8 +255,9 @@ public class ClientConfig {
if ( (peerVal == null) || (statFileVal == null) || (statDurationVal == null) || if ( (peerVal == null) || (statFileVal == null) || (statDurationVal == null) ||
(statFrequencyVal == null) || (sendFrequencyVal == null) || (sendSizeVal == null) ) { (statFrequencyVal == null) || (sendFrequencyVal == null) || (sendSizeVal == null) ) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Peer number "+ peerNum + " does not exist"); _log.debug("Peer number "+ peerNum + " does not exist");
}
return false; return false;
} }
@ -151,24 +269,28 @@ public class ClientConfig {
int sendSize = getInt(sendSizeVal); int sendSize = getInt(sendSizeVal);
if ( (duration <= 0) || (statFreq <= 0) || (sendFreq <= 0) || (sendSize <= 0) ) { if ( (duration <= 0) || (statFreq <= 0) || (sendFreq <= 0) || (sendSize <= 0) ) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN)) {
_log.warn("Invalid client config: duration [" + statDurationVal + "] stat frequency [" + statFrequencyVal + _log.warn("Invalid client config: duration [" + statDurationVal + "] stat frequency [" + statFrequencyVal +
"] send frequency [" + sendFrequencyVal + "] send size [" + sendSizeVal + "]"); "] send frequency [" + sendFrequencyVal + "] send size [" + sendSizeVal + "]");
}
return false; return false;
} }
statFileVal = statFileVal.trim(); statFileVal = statFileVal.trim();
if (statFileVal.length() <= 0) { if (statFileVal.length() <= 0) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN)) {
_log.warn("Stat file is blank for peer " + peerNum); _log.warn("Stat file is blank for peer " + peerNum);
}
return false; return false;
} }
Destination d = new Destination(); Destination d = new Destination();
d.fromBase64(peerVal); d.fromBase64(peerVal);
if (commentVal == null) if (commentVal == null) {
commentVal = ""; commentVal = "";
}
commentVal = commentVal.trim(); commentVal = commentVal.trim();
commentVal = commentVal.replace('\n', '_'); commentVal = commentVal.replace('\n', '_');
@ -178,13 +300,15 @@ public class ClientConfig {
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
String periodVal = tok.nextToken(); String periodVal = tok.nextToken();
int minutes = getInt(periodVal); int minutes = getInt(periodVal);
if (minutes > 0) if (minutes > 0) {
periods.add(new Integer(minutes)); periods.add(new Integer(minutes));
} }
} }
}
int avgPeriods[] = new int[periods.size()]; int avgPeriods[] = new int[periods.size()];
for (int i = 0; i < periods.size(); i++) for (int i = 0; i < periods.size(); i++) {
avgPeriods[i] = ((Integer)periods.get(i)).intValue(); avgPeriods[i] = ((Integer)periods.get(i)).intValue();
}
_comment = commentVal; _comment = commentVal;
_statDuration = duration; _statDuration = duration;
@ -205,6 +329,8 @@ public class ClientConfig {
* Store the client config to the properties specified, deriving the current * Store the client config to the properties specified, deriving the current
* config entry from the peer number. * config entry from the peer number.
* *
* @param clientConfig the properties to store to
* @param peerNum the number associated with the peer
* @return true if it was stored correctly, false if there were errors * @return true if it was stored correctly, false if there were errors
*/ */
public boolean store(Properties clientConfig, int peerNum) { public boolean store(Properties clientConfig, int peerNum) {
@ -214,8 +340,10 @@ public class ClientConfig {
} }
String comment = _comment; String comment = _comment;
if (comment == null) if (comment == null) {
comment = ""; comment = "";
}
comment = comment.trim(); comment = comment.trim();
comment = comment.replace('\n', '_'); comment = comment.replace('\n', '_');
@ -243,8 +371,9 @@ public class ClientConfig {
int i = Integer.parseInt(val); int i = Integer.parseInt(val);
return i; return i;
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Value [" + val + "] is not a valid integer"); _log.debug("Value [" + val + "] is not a valid integer");
}
return -1; return -1;
} }
} }

View File

@ -2,8 +2,8 @@ package net.i2p.heartbeat;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/** /**
* Responsible for actually conducting the tests, coordinating the storing of the * Responsible for actually conducting the tests, coordinating the storing of the
@ -13,7 +13,7 @@ import net.i2p.util.I2PThread;
*/ */
class ClientEngine { class ClientEngine {
private static final Log _log = new Log(ClientEngine.class); private static final Log _log = new Log(ClientEngine.class);
/** who can send our pings/ */ /** who can send our pings? */
private Heartbeat _heartbeat; private Heartbeat _heartbeat;
/** actual test state */ /** actual test state */
private PeerData _data; private PeerData _data;
@ -28,7 +28,8 @@ class ClientEngine {
/** /**
* Create a new engine that will send its pings through the given heartbeat * Create a new engine that will send its pings through the given heartbeat
* system, and will coordinate the test according to the configuration specified. * system, and will coordinate the test according to the configuration specified.
* * @param heartbeat the Heartbeat to send pings through
* @param config the Configuration to load configuration from =p
*/ */
public ClientEngine(Heartbeat heartbeat, ClientConfig config) { public ClientEngine(Heartbeat heartbeat, ClientConfig config) {
_heartbeat = heartbeat; _heartbeat = heartbeat;
@ -50,10 +51,18 @@ class ClientEngine {
t.setName("HeartbeatClient " + _id); t.setName("HeartbeatClient " + _id);
t.start(); t.start();
} }
/** who are we testing? */ /**
* Who are we testing?
* @return the Destination (peer) we're testing
*/
public Destination getPeer() { return _data.getConfig().getPeer(); } public Destination getPeer() { return _data.getConfig().getPeer(); }
/** what is our series identifier (used to locally identify a test) */
/**
* What is our series identifier (used to locally identify a test)
* @return the series identifier
*/
public int getSeriesNum() { return _id; } public int getSeriesNum() { return _id; }
/** /**
* receive notification from the heartbeat system that a pong was received in * receive notification from the heartbeat system that a pong was received in
* reply to a ping we have sent. * reply to a ping we have sent.
@ -74,6 +83,10 @@ class ClientEngine {
/** our actual heartbeat pumper - this drives the test */ /** our actual heartbeat pumper - this drives the test */
private class ClientRunner implements Runnable { private class ClientRunner implements Runnable {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() { public void run() {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Starting engine talking to peer " + _data.getConfig().getPeer().calculateHash().toBase64()); _log.info("Starting engine talking to peer " + _data.getConfig().getPeer().calculateHash().toBase64());

View File

@ -1,18 +1,16 @@
package net.i2p.heartbeat; package net.i2p.heartbeat;
import net.i2p.util.Log;
import net.i2p.util.Clock;
import net.i2p.data.Destination;
import java.io.IOException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Iterator;
import java.util.Date; import net.i2p.data.Destination;
import net.i2p.util.Log;
/** /**
* Main driver for the heartbeat engine, loading 0 or more tests, firing * Main driver for the heartbeat engine, loading 0 or more tests, firing
@ -73,7 +71,10 @@ public class Heartbeat {
/** if there are no command line arguments, load the config from "heartbeat.config" */ /** if there are no command line arguments, load the config from "heartbeat.config" */
public static final String CONFIG_FILE_DEFAULT = "heartbeat.config"; public static final String CONFIG_FILE_DEFAULT = "heartbeat.config";
/** build up a new heartbeat manager, but don't actually do anything */ /**
* build up a new heartbeat manager, but don't actually do anything
* @param configFile the name of the configuration file
*/
public Heartbeat(String configFile) { public Heartbeat(String configFile) {
_configFile = configFile; _configFile = configFile;
_clientConfigs = new HashMap(); _clientConfigs = new HashMap();
@ -94,8 +95,9 @@ public class Heartbeat {
fin = new FileInputStream(_configFile); fin = new FileInputStream(_configFile);
props.load(fin); props.load(fin);
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error reading the config data", ioe); _log.error("Error reading the config data", ioe);
}
} finally { } finally {
if (fin != null) try { fin.close(); } catch (IOException ioe) {} if (fin != null) try { fin.close(); } catch (IOException ioe) {}
} }
@ -119,18 +121,25 @@ public class Heartbeat {
_adapter.sendPing(peer, seriesNum, now, size); _adapter.sendPing(peer, seriesNum, now, size);
} }
/** load up the base data (I2CP config, etc) */ /**
* load up the base data (I2CP config, etc)
* @param props the properties to load from
*/
private void loadBaseConfig(Properties props) { private void loadBaseConfig(Properties props) {
_adapter.loadConfig(props); _adapter.loadConfig(props);
} }
/** load up all of the test config data */ /**
* load up all of the test config data
* @param props the properties to load from
* */
private void loadClientConfigs(Properties props) { private void loadClientConfigs(Properties props) {
int i = 0; int i = 0;
while (true) { while (true) {
ClientConfig config = new ClientConfig(); ClientConfig config = new ClientConfig();
if (!config.load(props, i)) if (!config.load(props, i)) {
break; break;
}
_clientConfigs.put(new Integer(i), config); _clientConfigs.put(new Integer(i), config);
i++; i++;
} }
@ -174,14 +183,17 @@ public class Heartbeat {
* running any tests. <p /> * running any tests. <p />
* *
* <code> <b>Usage: </b> Heartbeat [<i>configFileName</i>]</code> <p /> * <code> <b>Usage: </b> Heartbeat [<i>configFileName</i>]</code> <p />
* @param args the list of args passed to the program from the command-line
*/ */
public static void main(String args[]) { public static void main(String args[]) {
String configFile = CONFIG_FILE_DEFAULT; String configFile = CONFIG_FILE_DEFAULT;
if (args.length == 1) if (args.length == 1) {
configFile = args[0]; configFile = args[0];
}
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Starting up with config file " + configFile); _log.info("Starting up with config file " + configFile);
}
Heartbeat heartbeat = new Heartbeat(configFile); Heartbeat heartbeat = new Heartbeat(configFile);
heartbeat.loadConfig(); heartbeat.loadConfig();
heartbeat.connect(); heartbeat.connect();
@ -210,9 +222,10 @@ public class Heartbeat {
* @param data arbitrary payload data * @param data arbitrary payload data
*/ */
public void receivePing(Destination from, int seriesNum, Date sentOn, byte[] data) { public void receivePing(Destination from, int seriesNum, Date sentOn, byte[] data) {
if (_adapter.getIsConnected()) if (_adapter.getIsConnected()) {
_adapter.sendPong(from, seriesNum, sentOn, data); _adapter.sendPong(from, seriesNum, sentOn, data);
} }
}
/** /**
* We received a pong, so find the right client engine and tell it about the pong. * We received a pong, so find the right client engine and tell it about the pong.
@ -225,9 +238,10 @@ public class Heartbeat {
*/ */
public void receivePong(Destination from, int seriesNum, Date sentOn, Date replyOn, byte[] data) { public void receivePong(Destination from, int seriesNum, Date sentOn, Date replyOn, byte[] data) {
ClientEngine engine = (ClientEngine)_clientEngines.get(new Integer(seriesNum)); ClientEngine engine = (ClientEngine)_clientEngines.get(new Integer(seriesNum));
if (engine.getPeer().equals(from)) if (engine.getPeer().equals(from)) {
engine.receivePong(sentOn.getTime(), replyOn.getTime()); engine.receivePong(sentOn.getTime(), replyOn.getTime());
} }
} }
}
} }

View File

@ -1,28 +1,26 @@
package net.i2p.heartbeat; package net.i2p.heartbeat;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Properties;
import java.util.Date;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.Properties;
import net.i2p.data.DataFormatException; import net.i2p.I2PException;
import net.i2p.data.DataHelper;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PClient; import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession; import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException; import net.i2p.client.I2PSessionException;
import net.i2p.client.I2PSessionListener; import net.i2p.client.I2PSessionListener;
import net.i2p.I2PException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Tie-in to the I2P SDK for the Heartbeat system, talking to the I2PSession and * Tie-in to the I2P SDK for the Heartbeat system, talking to the I2PSession and
@ -69,6 +67,9 @@ class I2PAdapter {
/** by default, use 2 hop tunnels */ /** by default, use 2 hop tunnels */
public static final int NUMHOPS_DEFAULT = 2; public static final int NUMHOPS_DEFAULT = 2;
/**
* Constructs an I2PAdapter . . .
*/
public I2PAdapter() { public I2PAdapter() {
_privateDestFile = null; _privateDestFile = null;
_i2cpHost = null; _i2cpHost = null;
@ -79,22 +80,40 @@ class I2PAdapter {
_numHops = 0; _numHops = 0;
} }
/** who are we? */ /**
* who are we?
* @return the destination (us)
*/
public Destination getLocalDestination() { return _localDest; } public Destination getLocalDestination() { return _localDest; }
/** who gets notified when we receive a ping or a pong? */ /**
* who gets notified when we receive a ping or a pong?
* @return the event listener who gets notified
*/
public PingPongEventListener getListener() { return _listener; } public PingPongEventListener getListener() { return _listener; }
/**
* Sets who gets notified when we receive a ping or a pong
* @param listener the event listener to get notified
*/
public void setListener(PingPongEventListener listener) { _listener = listener; } public void setListener(PingPongEventListener listener) { _listener = listener; }
/** how many hops do we want in our tunnels? */ /**
* how many hops do we want in our tunnels?
* @return the number of hops
*/
public int getNumHops() { return _numHops; } public int getNumHops() { return _numHops; }
/** are we connected? */ /**
* are we connected?
* @return true or false . . .
*/
public boolean getIsConnected() { return _session != null; } public boolean getIsConnected() { return _session != null; }
/** /**
* Read in all of the config data * Read in all of the config data
* * @param props the properties to load from
*/ */
void loadConfig(Properties props) { void loadConfig(Properties props) {
String privDestFile = props.getProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT); String privDestFile = props.getProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT);
@ -106,16 +125,18 @@ class I2PAdapter {
try { try {
portNum = Integer.parseInt(port); portNum = Integer.parseInt(port);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN)) {
_log.warn("Invalid I2CP port specified [" + port + "]"); _log.warn("Invalid I2CP port specified [" + port + "]");
}
portNum = I2CP_PORT_DEFAULT; portNum = I2CP_PORT_DEFAULT;
} }
int hops = -1; int hops = -1;
try { try {
hops = Integer.parseInt(numHops); hops = Integer.parseInt(numHops);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN)) {
_log.warn("Invalid # hops specified [" + numHops + "]"); _log.warn("Invalid # hops specified [" + numHops + "]");
}
hops = NUMHOPS_DEFAULT; hops = NUMHOPS_DEFAULT;
} }
@ -125,20 +146,29 @@ class I2PAdapter {
_i2cpPort = portNum; _i2cpPort = portNum;
} }
/** write out the config to the props */ /**
* write out the config to the props
* @param props the properties to write to
*/
void storeConfig(Properties props) { void storeConfig(Properties props) {
if (_privateDestFile != null) if (_privateDestFile != null) {
props.setProperty(DEST_FILE_PROP, _privateDestFile); props.setProperty(DEST_FILE_PROP, _privateDestFile);
else } else {
props.setProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT); props.setProperty(DEST_FILE_PROP, DEST_FILE_DEFAULT);
if (_i2cpHost != null) }
if (_i2cpHost != null) {
props.setProperty(I2CP_HOST_PROP, _i2cpHost); props.setProperty(I2CP_HOST_PROP, _i2cpHost);
else } else {
props.setProperty(I2CP_HOST_PROP, I2CP_HOST_DEFAULT); props.setProperty(I2CP_HOST_PROP, I2CP_HOST_DEFAULT);
if (_i2cpPort > 0) }
if (_i2cpPort > 0) {
props.setProperty(I2CP_PORT_PROP, ""+_i2cpPort); props.setProperty(I2CP_PORT_PROP, ""+_i2cpPort);
else } else {
props.setProperty(I2CP_PORT_PROP, ""+I2CP_PORT_DEFAULT); props.setProperty(I2CP_PORT_PROP, ""+I2CP_PORT_DEFAULT);
}
props.setProperty(NUMHOPS_PROP, ""+_numHops); props.setProperty(NUMHOPS_PROP, ""+_numHops);
} }
@ -170,23 +200,28 @@ class I2PAdapter {
baos.write(paddingData); baos.write(paddingData);
boolean sent = _session.sendMessage(peer, baos.toByteArray()); boolean sent = _session.sendMessage(peer, baos.toByteArray());
if (!sent) { if (!sent) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error sending the ping to " + peer.calculateHash().toBase64() + " for series " + seriesNum); _log.error("Error sending the ping to " + peer.calculateHash().toBase64() + " for series " + seriesNum);
}
} else { } else {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Ping sent to " + peer.calculateHash().toBase64() + " for series " + seriesNum); _log.info("Ping sent to " + peer.calculateHash().toBase64() + " for series " + seriesNum);
} }
}
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error sending the ping", ioe); _log.error("Error sending the ping", ioe);
}
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error writing out the ping message", dfe); _log.error("Error writing out the ping message", dfe);
}
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error writing out the ping message", ise); _log.error("Error writing out the ping message", ise);
} }
} }
}
/** /**
* send a pong message to the peer * send a pong message to the peer
@ -211,27 +246,33 @@ class I2PAdapter {
baos.write(data); baos.write(data);
boolean sent = _session.sendMessage(peer, baos.toByteArray()); boolean sent = _session.sendMessage(peer, baos.toByteArray());
if (!sent) { if (!sent) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error sending the pong to " + peer.calculateHash().toBase64() + " for series " + seriesNum + " which was sent on " + sentOn); _log.error("Error sending the pong to " + peer.calculateHash().toBase64() + " for series " + seriesNum + " which was sent on " + sentOn);
}
} else { } else {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Pong sent to " + peer.calculateHash().toBase64() + " for series " + seriesNum + " which was sent on " + sentOn); _log.info("Pong sent to " + peer.calculateHash().toBase64() + " for series " + seriesNum + " which was sent on " + sentOn);
} }
}
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error sending the ping", ioe); _log.error("Error sending the ping", ioe);
}
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error writing out the pong message", dfe); _log.error("Error writing out the pong message", dfe);
}
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error writing out the pong message", ise); _log.error("Error writing out the pong message", ise);
} }
} }
}
/** /**
* We've received this data from I2P - parse it into a ping or a pong * We've received this data from I2P - parse it into a ping or a pong
* and notify accordingly * and notify accordingly
* @param data the data to handle
*/ */
private void handleMessage(byte data[]) { private void handleMessage(byte data[]) {
ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayInputStream bais = new ByteArrayInputStream(data);
@ -248,35 +289,41 @@ class I2PAdapter {
int size = (int)DataHelper.readLong(bais, 2); int size = (int)DataHelper.readLong(bais, 2);
byte payload[] = new byte[size]; byte payload[] = new byte[size];
int read = DataHelper.read(bais, payload); int read = DataHelper.read(bais, payload);
if (read != size) if (read != size) {
throw new IOException("Malformed payload - read " + read + " instead of " + size); throw new IOException("Malformed payload - read " + read + " instead of " + size);
}
if (_listener == null) { if (_listener == null) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Listener isn't set, but we received a valid message of type " + type + " sent from " + from.calculateHash().toBase64()); _log.error("Listener isn't set, but we received a valid message of type " + type + " sent from " + from.calculateHash().toBase64());
}
return; return;
} }
if (type == TYPE_PING) { if (type == TYPE_PING) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Ping received from " + from.calculateHash().toBase64() + " on series " + series + " sent on " + sentOn + " containing " + size + " bytes"); _log.info("Ping received from " + from.calculateHash().toBase64() + " on series " + series + " sent on " + sentOn + " containing " + size + " bytes");
}
_listener.receivePing(from, series, sentOn, payload); _listener.receivePing(from, series, sentOn, payload);
} else if (type == TYPE_PONG) { } else if (type == TYPE_PONG) {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Pong received from " + from.calculateHash().toBase64() + " on series " + series + " sent on " + sentOn + " with pong sent on " + receivedOn + " containing " + size + " bytes"); _log.info("Pong received from " + from.calculateHash().toBase64() + " on series " + series + " sent on " + sentOn + " with pong sent on " + receivedOn + " containing " + size + " bytes");
}
_listener.receivePong(from, series, sentOn, receivedOn, payload); _listener.receivePong(from, series, sentOn, receivedOn, payload);
} else { } else {
throw new IOException("Invalid message type " + type); throw new IOException("Invalid message type " + type);
} }
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error handling the message", ioe); _log.error("Error handling the message", ioe);
}
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error parsing the message", dfe); _log.error("Error parsing the message", dfe);
} }
} }
}
/** /**
@ -303,17 +350,20 @@ class I2PAdapter {
session.setSessionListener(lsnr); session.setSessionListener(lsnr);
session.connect(); session.connect();
_localDest = session.getMyDestination(); _localDest = session.getMyDestination();
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("I2CP Session created and connected as " + _localDest.calculateHash().toBase64()); _log.info("I2CP Session created and connected as " + _localDest.calculateHash().toBase64());
}
_session = session; _session = session;
_i2pListener = lsnr; _i2pListener = lsnr;
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error connecting", ise); _log.error("Error connecting", ise);
}
return false; return false;
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error loading the destionation", ioe); _log.error("Error loading the destionation", ioe);
}
return false; return false;
} finally { } finally {
if (fin != null) try { fin.close(); } catch (IOException ioe) {} if (fin != null) try { fin.close(); } catch (IOException ioe) {}
@ -325,6 +375,8 @@ class I2PAdapter {
/** /**
* load, verify, or create a destination * load, verify, or create a destination
* *
* @param client the client
* @param destFile the file holding the destination
* @return the destination loaded, or null if there was an error * @return the destination loaded, or null if there was an error
*/ */
private Destination verifyDestination(I2PClient client, File destFile) { private Destination verifyDestination(I2PClient client, File destFile) {
@ -335,8 +387,9 @@ class I2PAdapter {
fin = new FileInputStream(destFile); fin = new FileInputStream(destFile);
us = new Destination(); us = new Destination();
us.readBytes(fin); us.readBytes(fin);
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("Existing destination loaded: [" + us.toBase64() + "]"); _log.info("Existing destination loaded: [" + us.toBase64() + "]");
}
} catch (IOException ioe) { } catch (IOException ioe) {
if (fin != null) try { fin.close(); } catch (IOException ioe2) {} if (fin != null) try { fin.close(); } catch (IOException ioe2) {}
fin = null; fin = null;
@ -359,15 +412,18 @@ class I2PAdapter {
try { try {
fos = new FileOutputStream(destFile); fos = new FileOutputStream(destFile);
us = client.createDestination(fos); us = client.createDestination(fos);
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO)) {
_log.info("New destination created: [" + us.toBase64() + "]"); _log.info("New destination created: [" + us.toBase64() + "]");
}
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error writing out the destination keys being created", ioe); _log.error("Error writing out the destination keys being created", ioe);
}
return null; return null;
} catch (I2PException ie) { } catch (I2PException ie) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error creating the destination", ie); _log.error("Error creating the destination", ie);
}
return null; return null;
} finally { } finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {} if (fos != null) try { fos.close(); } catch (IOException ioe) {}
@ -378,6 +434,7 @@ class I2PAdapter {
/** /**
* I2PSession connect options * I2PSession connect options
* @return the options as Properties
*/ */
private Properties getOptions() { private Properties getOptions() {
Properties props = new Properties(); Properties props = new Properties();
@ -395,8 +452,9 @@ class I2PAdapter {
try { try {
_session.destroySession(); _session.destroySession();
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Error destroying the session", ise); _log.error("Error destroying the session", ise);
}
} }
_session = null; _session = null;
} }
@ -434,20 +492,34 @@ class I2PAdapter {
* *
*/ */
private class I2PListener implements I2PSessionListener { private class I2PListener implements I2PSessionListener {
/* (non-Javadoc)
* @see net.i2p.client.I2PSessionListener#disconnected(net.i2p.client.I2PSession)
*/
public void disconnected(I2PSession session) { public void disconnected(I2PSession session) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR)) {
_log.error("Session disconnected"); _log.error("Session disconnected");
}
disconnect(); disconnect();
} }
/* (non-Javadoc)
* @see net.i2p.client.I2PSessionListener#errorOccurred(net.i2p.client.I2PSession, java.lang.String, java.lang.Throwable)
*/
public void errorOccurred(I2PSession session, String message, Throwable error) { public void errorOccurred(I2PSession session, String message, Throwable error) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
_log.error("Error occurred", error); _log.error("Error occurred", error);
} }
/* (non-Javadoc)
* @see net.i2p.client.I2PSessionListener#reportAbuse(net.i2p.client.I2PSession, int)
*/
public void reportAbuse(I2PSession session, int severity) { public void reportAbuse(I2PSession session, int severity) {
if (_log.shouldLog(Log.ERROR)) if (_log.shouldLog(Log.ERROR))
_log.error("Abuse reported"); _log.error("Abuse reported");
} }
/* (non-Javadoc)
* @see net.i2p.client.I2PSessionListener#messageAvailable(net.i2p.client.I2PSession, int, long)
*/
public void messageAvailable(I2PSession session, int msgId, long size) { public void messageAvailable(I2PSession session, int msgId, long size) {
try { try {
byte data[] = session.receiveMessage(msgId); byte data[] = session.receiveMessage(msgId);

View File

@ -1,17 +1,17 @@
package net.i2p.heartbeat; package net.i2p.heartbeat;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Contain the current window of data for a particular series of ping/pong stats * Contain the current window of data for a particular series of ping/pong stats
* sent to a peer. This should be periodically kept clean by calling cleanup() * sent to a peer. This should be periodically kept clean by calling cleanup()
@ -42,6 +42,10 @@ public class PeerData {
/** synchronize on this when updating _dataPoints or _pendingPings */ /** synchronize on this when updating _dataPoints or _pendingPings */
private Object _updateLock = new Object(); private Object _updateLock = new Object();
/**
* Creates a PeerData . . .
* @param config configuration to load from
*/
public PeerData(ClientConfig config) { public PeerData(ClientConfig config) {
_peer = config; _peer = config;
_dataPoints = new TreeMap(); _dataPoints = new TreeMap();
@ -54,7 +58,11 @@ public class PeerData {
_lostRate = new RateStat("lostRate", "How frequently we lose messages", "peer", getPeriods(config.getAveragePeriods())); _lostRate = new RateStat("lostRate", "How frequently we lose messages", "peer", getPeriods(config.getAveragePeriods()));
} }
/** turn the periods (# minutes) into rate periods (# milliseconds) */ /**
* turn the periods (# minutes) into rate periods (# milliseconds)
* @param periods (in minutes)
* @return an array of periods (in milliseconds)
*/
private static long[] getPeriods(int periods[]) { private static long[] getPeriods(int periods[]) {
long rv[] = null; long rv[] = null;
if (periods == null) periods = new int[0]; if (periods == null) periods = new int[0];
@ -65,22 +73,48 @@ public class PeerData {
return rv; return rv;
} }
/** how many pings are still outstanding? */ /**
* how many pings are still outstanding?
* @return the number of pings outstanding
*/
public int getPendingCount() { synchronized (_updateLock) { return _pendingPings.size(); } } public int getPendingCount() { synchronized (_updateLock) { return _pendingPings.size(); } }
/** how many data points are available in the current window? */
/**
* how many data points are available in the current window?
* @return the number of datapoints available
*/
public int getDataPointCount() { synchronized (_updateLock) { return _dataPoints.size(); } } public int getDataPointCount() { synchronized (_updateLock) { return _dataPoints.size(); } }
/** when did this test begin? */
/**
* when did this test begin?
* @return when the test began
*/
public long getSessionStart() { return _sessionStart; } public long getSessionStart() { return _sessionStart; }
/** how many pings have we sent for this test? */
/**
* how many pings have we sent for this test?
* @return the number of pings sent
*/
public long getLifetimeSent() { return _lifetimeSent; } public long getLifetimeSent() { return _lifetimeSent; }
/** how many pongs have we received for this test? */
/**
* how many pongs have we received for this test?
* @return the number of pings received
*/
public long getLifetimeReceived() { return _lifetimeReceived; } public long getLifetimeReceived() { return _lifetimeReceived; }
/**
* @return the client configuration
*/
public ClientConfig getConfig() { return _peer; } public ClientConfig getConfig() { return _peer; }
/** /**
* What periods are we averaging the data over (in minutes)? * What periods are we averaging the data over (in minutes)?
* @return the periods as an array of ints (in minutes)
*/ */
public int[] getAveragePeriods() { return (_peer.getAveragePeriods() != null ? _peer.getAveragePeriods() : new int[0]); } public int[] getAveragePeriods() { return (_peer.getAveragePeriods() != null ? _peer.getAveragePeriods() : new int[0]); }
/** /**
* average time to send over the given period. * average time to send over the given period.
* *
@ -88,6 +122,7 @@ public class PeerData {
* @return milliseconds average, or -1 if we dont track that period * @return milliseconds average, or -1 if we dont track that period
*/ */
public double getAverageSendTime(int period) { return getAverage(_sendRate, period); } public double getAverageSendTime(int period) { return getAverage(_sendRate, period); }
/** /**
* average time to receive over the given period. * average time to receive over the given period.
* *
@ -95,6 +130,7 @@ public class PeerData {
* @return milliseconds average, or -1 if we dont track that period * @return milliseconds average, or -1 if we dont track that period
*/ */
public double getAverageReceiveTime(int period) { return getAverage(_receiveRate, period); } public double getAverageReceiveTime(int period) { return getAverage(_receiveRate, period); }
/** /**
* number of lost messages over the given period. * number of lost messages over the given period.
* *
@ -129,7 +165,7 @@ public class PeerData {
/** /**
* We have sent the peer a ping on this series (using the send time as given) * We have sent the peer a ping on this series (using the send time as given)
* * @param dateSent when the ping was sent
*/ */
public void addPing(long dateSent) { public void addPing(long dateSent) {
EventDataPoint sent = new EventDataPoint(dateSent); EventDataPoint sent = new EventDataPoint(dateSent);
@ -239,9 +275,17 @@ public class PeerData {
private long _pongSent; private long _pongSent;
private long _pongReceived; private long _pongReceived;
/**
* Creates an EventDataPoint
*/
public EventDataPoint() { public EventDataPoint() {
this(-1); this(-1);
} }
/**
* Creates an EventDataPoint with pingtime associated with it =)
* @param pingSentOn the time a ping was sent
*/
public EventDataPoint(long pingSentOn) { public EventDataPoint(long pingSentOn) {
_wasPonged = false; _wasPonged = false;
_pingSent = pingSentOn; _pingSent = pingSentOn;
@ -249,20 +293,51 @@ public class PeerData {
_pongReceived = -1; _pongReceived = -1;
} }
/** when did we send this ping? */ /**
* when did we send this ping?
* @return the time the ping was sent
*/
public long getPingSent() { return _pingSent; } public long getPingSent() { return _pingSent; }
/**
* Set the time the ping was sent
* @param when time to set
*/
public void setPingSent(long when) { _pingSent = when; } public void setPingSent(long when) { _pingSent = when; }
/** when did the peer receive the ping? */ /**
* when did the peer receive the ping?
* @return the time the ping was receieved
*/
public long getPongSent() { return _pongSent; } public long getPongSent() { return _pongSent; }
/**
* Set the time the peer received the ping
* @param when the time to set
*/
public void setPongSent(long when) { _pongSent = when; } public void setPongSent(long when) { _pongSent = when; }
/** when did we receive the peer's pong? */ /**
* when did we receive the peer's pong?
* @return the time we receieved the pong
*/
public long getPongReceived() { return _pongReceived; } public long getPongReceived() { return _pongReceived; }
/**
* Set the time the peer's pong was receieved
* @param when the time to set
*/
public void setPongReceived(long when) { _pongReceived = when; } public void setPongReceived(long when) { _pongReceived = when; }
/** did the peer reply in time? */ /**
* did the peer reply in time?
* @return true or false, whether we got a reply in time */
public boolean getWasPonged() { return _wasPonged; } public boolean getWasPonged() { return _wasPonged; }
/**
* Set whether we receieved the peer's reply in time
* @param pong true or false
*/
public void setWasPonged(boolean pong) { _wasPonged = pong; } public void setWasPonged(boolean pong) { _wasPonged = pong; }
} }
} }

View File

@ -1,18 +1,17 @@
package net.i2p.heartbeat; package net.i2p.heartbeat;
import net.i2p.util.Log;
import net.i2p.util.Clock;
import java.io.IOException;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.Locale; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.Locale;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Actually write out the stats for peer test * Actually write out the stats for peer test
@ -24,6 +23,7 @@ class PeerDataWriter {
/** /**
* persist the peer state to the location specified in the peer config * persist the peer state to the location specified in the peer config
* *
* @param data the peer data to persist
* @return true if it was persisted correctly, false on error * @return true if it was persisted correctly, false on error
*/ */
public boolean persist(PeerData data) { public boolean persist(PeerData data) {
@ -93,12 +93,25 @@ class PeerDataWriter {
} }
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd.HH:mm:ss.SSS", Locale.UK); 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) { public String getTime(long when) {
synchronized (_fmt) { synchronized (_fmt) {
return _fmt.format(new Date(when)); return _fmt.format(new Date(when));
} }
} }
private static final DecimalFormat _numFmt = new DecimalFormat("#0", new DecimalFormatSymbols(Locale.UK)); private static final DecimalFormat _numFmt = new DecimalFormat("#0", new DecimalFormatSymbols(Locale.UK));
/**
* Converts a number (double) to text
* @param val the number to convert
* @return the textual representation
*/
public String getNum(double val) { public String getNum(double val) {
synchronized (_numFmt) { synchronized (_numFmt) {
return _numFmt.format(val); return _numFmt.format(val);