merge of '398a24f487b61ef778a2e849660e953ef7e43b39'

and '598d00efae4c9b675b64fd626bc2eab2b921e0c5'
This commit is contained in:
zzz
2010-11-03 16:04:14 +00:00
42 changed files with 1005 additions and 231 deletions

View File

@ -152,6 +152,9 @@ public class I2PSnarkUtil {
*/
synchronized public boolean connect() {
if (_manager == null) {
// try to find why reconnecting after stop
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connecting to I2P", new Exception("I did it"));
Properties opts = new Properties();
if (_opts != null) {
for (Iterator iter = _opts.keySet().iterator(); iter.hasNext(); ) {
@ -163,6 +166,10 @@ public class I2PSnarkUtil {
opts.setProperty("inbound.nickname", "I2PSnark");
if (opts.getProperty("outbound.nickname") == null)
opts.setProperty("outbound.nickname", "I2PSnark");
// Dont do this for now, it is set in I2PSocketEepGet for announces,
// we don't need fast handshake for peer connections.
//if (opts.getProperty("i2p.streaming.connectDelay") == null)
// opts.setProperty("i2p.streaming.connectDelay", "500");
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
opts.setProperty("i2p.streaming.inactivityTimeout", "240000");
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
@ -186,6 +193,7 @@ public class I2PSnarkUtil {
*/
public void disconnect() {
I2PSocketManager mgr = _manager;
// FIXME this can cause race NPEs elsewhere
_manager = null;
_shitlist.clear();
mgr.destroySocketManager();
@ -197,6 +205,9 @@ public class I2PSnarkUtil {
/** connect to the given destination */
I2PSocket connect(PeerID peer) throws IOException {
I2PSocketManager mgr = _manager;
if (mgr == null)
throw new IOException("No socket manager");
Destination addr = peer.getAddress();
if (addr == null)
throw new IOException("Null address");

View File

@ -56,8 +56,8 @@ public class Peer implements Comparable
private long _id;
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
private long uploaded_old[] = {-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1};
/**
* Creates a disconnected peer given a PeerID, your own id and the
@ -117,10 +117,15 @@ public class Peer implements Comparable
}
/**
* Returns socket (for debug printing)
* @return socket debug string (for debug printing)
*/
public String getSocket()
{
if (state != null) {
String r = state.getRequests();
if (r != null)
return sock.toString() + "<br>Requests: " + r;
}
return sock.toString();
}
@ -387,6 +392,37 @@ public class Peer implements Comparable
s.havePiece(piece);
}
/**
* Tell the other side that we are no longer interested in any of
* the outstanding requests (if any) for this piece.
* @since 0.8.1
*/
void cancel(int piece) {
PeerState s = state;
if (s != null)
s.cancelPiece(piece);
}
/**
* Are we currently requesting the piece?
* @since 0.8.1
*/
boolean isRequesting(int p) {
PeerState s = state;
return s != null && s.isRequesting(p);
}
/**
* Update the request queue.
* Call after adding wanted pieces.
* @since 0.8.1
*/
void request() {
PeerState s = state;
if (s != null)
s.addRequest();
}
/**
* Whether or not the peer is interested in pieces we have. Returns
* false if not connected.
@ -545,17 +581,8 @@ public class Peer implements Comparable
*/
public void setRateHistory(long up, long down)
{
setRate(up, uploaded_old);
setRate(down, downloaded_old);
}
private void setRate(long val, long array[])
{
synchronized(array) {
for (int i = RATE_DEPTH-1; i > 0; i--)
array[i] = array[i-1];
array[0] = val;
}
PeerCoordinator.setRate(up, uploaded_old);
PeerCoordinator.setRate(down, downloaded_old);
}
/**
@ -563,28 +590,11 @@ public class Peer implements Comparable
*/
public long getUploadRate()
{
return getRate(uploaded_old);
return PeerCoordinator.getRate(uploaded_old);
}
public long getDownloadRate()
{
return getRate(downloaded_old);
}
private long getRate(long array[])
{
long rate = 0;
int i = 0;
synchronized(array) {
for ( ; i < RATE_DEPTH; i++){
if (array[i] < 0)
break;
rate += array[i];
return PeerCoordinator.getRate(downloaded_old);
}
}
if (i == 0)
return 0;
return rate / (i * CHECK_PERIOD / 1000);
}
}

View File

@ -57,9 +57,9 @@ public class PeerCoordinator implements PeerListener
private long uploaded;
private long downloaded;
final static int RATE_DEPTH = 6; // make following arrays RATE_DEPTH long
private long uploaded_old[] = {-1,-1,-1,-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1,-1,-1,-1};
final static int RATE_DEPTH = 3; // make following arrays RATE_DEPTH long
private long uploaded_old[] = {-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1};
// synchronize on this when changing peers or downloaders
final List<Peer> peers = new ArrayList();
@ -78,6 +78,7 @@ public class PeerCoordinator implements PeerListener
private final CoordinatorListener listener;
public I2PSnarkUtil _util;
private static final Random _random = I2PAppContext.getGlobalContext().random();
public String trackerProblems = null;
public int trackerSeenPeers = 0;
@ -97,20 +98,29 @@ public class PeerCoordinator implements PeerListener
// Install a timer to check the uploaders.
// Randomize the first start time so multiple tasks are spread out,
// this will help the behavior with global limits
Random r = I2PAppContext.getGlobalContext().random();
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + r.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
timer.schedule(new PeerCheckerTask(_util, this), (CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD), CHECK_PERIOD);
}
// only called externally from Storage after the double-check fails
public void setWantedPieces()
{
// Make a list of pieces
// FIXME synchronize, clear and re-add instead?
// Don't replace something we are synchronizing on.
wantedPieces = new ArrayList();
BitField bitfield = storage.getBitField();
for(int i = 0; i < metainfo.getPieces(); i++)
if (!bitfield.get(i))
wantedPieces.add(new Piece(i));
Collections.shuffle(wantedPieces);
int[] pri = storage.getPiecePriorities();
for(int i = 0; i < metainfo.getPieces(); i++) {
// only add if we don't have and the priority is >= 0
if ((!bitfield.get(i)) &&
(pri == null || pri[i] >= 0)) {
Piece p = new Piece(i);
if (pri != null)
p.setPriority(pri[i]);
wantedPieces.add(p);
}
}
Collections.shuffle(wantedPieces, _random);
}
public Storage getStorage() { return storage; }
@ -183,7 +193,7 @@ public class PeerCoordinator implements PeerListener
setRate(down, downloaded_old);
}
private static void setRate(long val, long array[])
static void setRate(long val, long array[])
{
synchronized(array) {
for (int i = RATE_DEPTH-1; i > 0; i--)
@ -214,20 +224,23 @@ public class PeerCoordinator implements PeerListener
return (r * 1000) / CHECK_PERIOD;
}
private long getRate(long array[])
static long getRate(long array[])
{
long rate = 0;
int i = 0;
int factor = 0;
synchronized(array) {
for ( ; i < RATE_DEPTH; i++) {
if (array[i] < 0)
break;
rate += array[i];
int f = RATE_DEPTH - i;
rate += array[i] * f;
factor += f;
}
}
if (i == 0)
return 0;
return rate / (i * CHECK_PERIOD / 1000);
return rate / (factor * CHECK_PERIOD / 1000);
}
public MetaInfo getMetaInfo()
@ -454,7 +467,7 @@ public class PeerCoordinator implements PeerListener
}
/**
* Returns true if we don't have the given piece yet.
* @return true if we still want the given piece
*/
public boolean gotHave(Peer peer, int piece)
{
@ -499,6 +512,12 @@ public class PeerCoordinator implements PeerListener
*/
private static final int END_GAME_THRESHOLD = 8;
/**
* Max number of peers to get a piece from when in end game
* @since 0.8.1
*/
private static final int MAX_PARALLEL_REQUESTS = 4;
/**
* Returns one of pieces in the given BitField that is still wanted or
* -1 if none of the given pieces are wanted.
@ -520,6 +539,9 @@ public class PeerCoordinator implements PeerListener
while (piece == null && it.hasNext())
{
Piece p = it.next();
// sorted by priority, so when we hit a disabled piece we are done
if (p.isDisabled())
break;
if (havePieces.get(p.getId()) && !p.isRequested())
{
piece = p;
@ -538,13 +560,30 @@ public class PeerCoordinator implements PeerListener
if (wantedPieces.size() > END_GAME_THRESHOLD)
return -1; // nothing to request and not in end game
// let's not all get on the same piece
Collections.shuffle(requested);
// Even better would be to sort by number of requests
Collections.shuffle(requested, _random);
Iterator<Piece> it2 = requested.iterator();
while (piece == null && it2.hasNext())
{
Piece p = it2.next();
if (havePieces.get(p.getId()))
{
if (havePieces.get(p.getId())) {
// limit number of parallel requests
int requestedCount = 0;
synchronized(peers) {
for (Peer pr : peers) {
if (pr.isRequesting(p.getId())) {
if (pr.equals(peer)) {
// don't give it to him again
requestedCount = MAX_PARALLEL_REQUESTS;
break;
}
if (++requestedCount >= MAX_PARALLEL_REQUESTS)
break;
}
}
}
if (requestedCount >= MAX_PARALLEL_REQUESTS)
continue;
piece = p;
}
}
@ -555,7 +594,7 @@ public class PeerCoordinator implements PeerListener
// + " wanted = " + wantedPieces + " peerHas = " + havePieces);
return -1; //If we still can't find a piece we want, so be it.
} else {
// Should be a lot smarter here - limit # of parallel attempts and
// Should be a lot smarter here -
// share blocks rather than starting from 0 with each peer.
// This is where the flaws of the snark data model are really exposed.
// Could also randomize within the duplicate set rather than strict rarest-first
@ -563,11 +602,83 @@ public class PeerCoordinator implements PeerListener
_log.debug("parallel request (end game?) for " + peer + ": piece = " + piece);
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Now requesting: piece " + piece + " priority " + piece.getPriority());
piece.setRequested(true);
return piece.getId();
}
}
/**
* Maps file priorities to piece priorities.
* Call after updating file priorities Storage.setPriority()
* @since 0.8.1
*/
public void updatePiecePriorities() {
int[] pri = storage.getPiecePriorities();
if (pri == null) {
_log.debug("Updated piece priorities called but no priorities to set?");
return;
}
synchronized(wantedPieces) {
// Add incomplete and previously unwanted pieces to the list
// Temp to avoid O(n**2)
BitField want = new BitField(pri.length);
for (Piece p : wantedPieces) {
want.set(p.getId());
}
BitField bitfield = storage.getBitField();
for (int i = 0; i < pri.length; i++) {
if (pri[i] >= 0 && !bitfield.get(i)) {
if (!want.get(i)) {
Piece piece = new Piece(i);
wantedPieces.add(piece);
// As connections are already up, new Pieces will
// not have their PeerID list populated, so do that.
synchronized(peers) {
for (Peer p : peers) {
PeerState s = p.state;
if (s != null) {
BitField bf = s.bitfield;
if (bf != null && bf.get(i))
piece.addPeer(p);
}
}
}
}
}
}
// now set the new priorities and remove newly unwanted pieces
for (Iterator<Piece> iter = wantedPieces.iterator(); iter.hasNext(); ) {
Piece p = iter.next();
int priority = pri[p.getId()];
if (priority >= 0) {
p.setPriority(priority);
} else {
iter.remove();
// cancel all peers
synchronized(peers) {
for (Peer peer : peers) {
peer.cancel(p.getId());
}
}
}
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updated piece priorities, now wanted: " + wantedPieces);
// if we added pieces, they will be in-order unless we shuffle
Collections.shuffle(wantedPieces, _random);
// update request queues, in case we added wanted pieces
// and we were previously uninterested
synchronized(peers) {
for (Peer peer : peers) {
peer.request();
}
}
}
}
/**
* Returns a byte array containing the requested piece or null of
* the piece is unknown.
@ -632,6 +743,9 @@ public class PeerCoordinator implements PeerListener
// No need to announce have piece to peers.
// Assume we got a good piece, we don't really care anymore.
// Well, this could be caused by a change in priorities, so
// only return true if we already have it, otherwise might as well keep it.
if (storage.getBitField().get(piece))
return true;
}
@ -639,6 +753,7 @@ public class PeerCoordinator implements PeerListener
{
if (storage.putPiece(piece, bs))
{
if (_log.shouldLog(Log.INFO))
_log.info("Got valid piece " + piece + "/" + metainfo.getPieces() +" from " + peer + " for " + metainfo.getName());
}
else

View File

@ -55,12 +55,10 @@ class PeerState
final PeerConnectionOut out;
// Outstanding request
private final List outstandingRequests = new ArrayList();
private final List<Request> outstandingRequests = new ArrayList();
/** the tail (NOT the head) of the request queue */
private Request lastRequest = null;
// If we have te resend outstanding requests (true after we got choked).
private boolean resend = false;
private final static int MAX_PIPELINE = 5; // this is for outbound requests
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
public final static int PARTSIZE = 16*1024; // outbound request
@ -91,14 +89,13 @@ class PeerState
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
boolean resend = choked && !choke;
choked = choke;
if (choked)
resend = true;
listener.gotChoke(peer, choke);
if (!choked && interesting)
request();
if (interesting && !choked)
request(resend);
}
void interestedMessage(boolean interest)
@ -278,7 +275,7 @@ class PeerState
synchronized private int getFirstOutstandingRequest(int piece)
{
for (int i = 0; i < outstandingRequests.size(); i++)
if (((Request)outstandingRequests.get(i)).piece == piece)
if (outstandingRequests.get(i).piece == piece)
return i;
return -1;
}
@ -313,12 +310,12 @@ class PeerState
Request req;
synchronized(this)
{
req = (Request)outstandingRequests.get(r);
req = outstandingRequests.get(r);
while (req.piece == piece && req.off != begin
&& r < outstandingRequests.size() - 1)
{
r++;
req = (Request)outstandingRequests.get(r);
req = outstandingRequests.get(r);
}
// Something wrong?
@ -342,7 +339,7 @@ class PeerState
+ ", wanted for peer: " + peer);
for (int i = 0; i < r; i++)
{
Request dropReq = (Request)outstandingRequests.remove(0);
Request dropReq = outstandingRequests.remove(0);
outstandingRequests.add(dropReq);
if (!choked)
out.sendRequest(dropReq);
@ -366,11 +363,11 @@ class PeerState
{
Request req = null;
for (int i = 0; i < outstandingRequests.size(); i++) {
Request r1 = (Request)outstandingRequests.get(i);
Request r1 = outstandingRequests.get(i);
int j = getFirstOutstandingRequest(r1.piece);
if (j == -1)
continue;
Request r2 = (Request)outstandingRequests.get(j);
Request r2 = outstandingRequests.get(j);
if (r2.off > 0 && ((req == null) || (r2.off > req.off)))
req = r2;
}
@ -398,7 +395,7 @@ class PeerState
}
Request req = null;
for (int i = 0; i < size; i++) {
Request r1 = (Request)outstandingRequests.get(i);
Request r1 = outstandingRequests.get(i);
if (pc != r1.piece) {
pc = r1.piece;
arr[pos++] = pc;
@ -423,31 +420,18 @@ class PeerState
+ " length: " + bs.length);
}
/**
* We now have this piece.
* Tell the peer and cancel any requests for the piece.
*/
void havePiece(int piece)
{
if (_log.shouldLog(Log.DEBUG))
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
synchronized(this)
{
// Tell the other side that we are no longer interested in any of
// the outstanding requests for this piece.
if (lastRequest != null && lastRequest.piece == piece)
lastRequest = null;
Iterator it = outstandingRequests.iterator();
while (it.hasNext())
{
Request req = (Request)it.next();
if (req.piece == piece)
{
it.remove();
// Send cancel even when we are choked to make sure that it is
// really never ever send.
out.sendCancel(req);
}
}
}
cancelPiece(piece);
// Tell the other side that we really have this piece.
out.sendHave(piece);
@ -463,8 +447,46 @@ class PeerState
}
}
// Starts or resumes requesting pieces.
private void request()
/**
* Tell the other side that we are no longer interested in any of
* the outstanding requests (if any) for this piece.
* @since 0.8.1
*/
synchronized void cancelPiece(int piece) {
if (lastRequest != null && lastRequest.piece == piece)
lastRequest = null;
Iterator<Request> it = outstandingRequests.iterator();
while (it.hasNext())
{
Request req = it.next();
if (req.piece == piece)
{
it.remove();
// Send cancel even when we are choked to make sure that it is
// really never ever send.
out.sendCancel(req);
}
}
}
/**
* Are we currently requesting the piece?
* @since 0.8.1
*/
synchronized boolean isRequesting(int piece) {
for (Request req : outstandingRequests) {
if (req.piece == piece)
return true;
}
return false;
}
/**
* Starts or resumes requesting pieces.
* @param resend should we resend outstanding requests?
*/
private void request(boolean resend)
{
// Are there outstanding requests that have to be resend?
if (resend)
@ -472,7 +494,6 @@ class PeerState
synchronized (this) {
out.sendRequests(outstandingRequests);
}
resend = false;
}
// Add/Send some more requests if necessary.
@ -481,8 +502,11 @@ class PeerState
/**
* Adds a new request to the outstanding requests list.
* Then send interested if we weren't.
* Then send new requests if not choked.
* If nothing to request, send not interested if we were.
*/
synchronized private void addRequest()
synchronized void addRequest()
{
boolean more_pieces = true;
while (more_pieces)
@ -526,6 +550,7 @@ class PeerState
/**
* Starts requesting first chunk of next piece. Returns true if
* something has been added to the requests, false otherwise.
* Caller should synchronize.
*/
private boolean requestNextPiece()
{
@ -553,11 +578,10 @@ class PeerState
}
}
int nextPiece = listener.wantPiece(peer, bitfield);
if (nextPiece != -1
&& (lastRequest == null || lastRequest.piece != nextPiece)) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " want piece " + nextPiece);
if (nextPiece != -1
&& (lastRequest == null || lastRequest.piece != nextPiece))
{
// Fail safe to make sure we are interested
// When we transition into the end game we may not be interested...
if (!interesting) {
@ -584,9 +608,25 @@ class PeerState
out.sendRequest(req);
lastRequest = req;
return true;
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " no more pieces to request");
}
}
// failsafe
if (outstandingRequests.isEmpty())
lastRequest = null;
// If we are not in the end game, we may run out of things to request
// because we are asking other peers. Set not-interesting now rather than
// wait for those other requests to be satisfied via havePiece()
if (interesting && lastRequest == null) {
interesting = false;
out.sendInterest(false);
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " nothing more to request, now uninteresting");
}
return false;
}
@ -601,7 +641,7 @@ class PeerState
out.sendInterest(interest);
if (interesting && !choked)
request();
request(true); // we shouldnt have any pending requests, but if we do, resend them
}
}
@ -627,4 +667,16 @@ class PeerState
if (interesting && !choked)
out.retransmitRequests(outstandingRequests);
}
/**
* debug
* @return string or null
* @since 0.8.1
*/
synchronized String getRequests() {
if (outstandingRequests.isEmpty())
return null;
else
return outstandingRequests.toString();
}
}

View File

@ -1,22 +1,31 @@
package org.klomp.snark;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import net.i2p.util.ConcurrentHashSet;
public class Piece implements Comparable {
private int id;
private Set peers;
private Set<PeerID> peers;
private boolean requested;
/** @since 0.8.1 */
private int priority;
public Piece(int id) {
this.id = id;
this.peers = Collections.synchronizedSet(new HashSet());
this.requested = false;
this.peers = new ConcurrentHashSet();
}
/**
* Highest priority first,
* then rarest first
*/
public int compareTo(Object o) throws ClassCastException {
int pdiff = ((Piece)o).priority - this.priority; // reverse
if (pdiff != 0)
return pdiff;
return this.peers.size() - ((Piece)o).peers.size();
}
@ -37,12 +46,25 @@ public class Piece implements Comparable {
}
public int getId() { return this.id; }
public Set getPeers() { return this.peers; }
/** @deprecated unused */
public Set<PeerID> getPeers() { return this.peers; }
public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
public boolean isRequested() { return this.requested; }
public void setRequested(boolean requested) { this.requested = requested; }
/** @return default 0 @since 0.8.1 */
public int getPriority() { return this.priority; }
/** @since 0.8.1 */
public void setPriority(int p) { this.priority = p; }
/** @since 0.8.1 */
public boolean isDisabled() { return this.priority < 0; }
/** @since 0.8.1 */
public void setDisabled() { this.priority = -1; }
@Override
public String toString() {
return String.valueOf(id);

View File

@ -54,6 +54,7 @@ public class SnarkManager implements Snark.CompleteListener {
public static final String PROP_DIR = "i2psnark.dir";
public static final String PROP_META_PREFIX = "i2psnark.zmeta.";
public static final String PROP_META_BITFIELD_SUFFIX = ".bitfield";
public static final String PROP_META_PRIORITY_SUFFIX = ".priority";
private static final String CONFIG_FILE = "i2psnark.config";
public static final String PROP_AUTO_START = "i2snark.autoStart"; // oops
@ -510,6 +511,7 @@ public class SnarkManager implements Snark.CompleteListener {
torrent = new Snark(_util, filename, null, -1, null, null, this,
_peerCoordinatorSet, _connectionAcceptor,
false, dataDir.getPath());
loadSavedFilePriorities(torrent);
torrent.completeListener = this;
synchronized (_snarks) {
_snarks.put(filename, torrent);
@ -587,6 +589,33 @@ public class SnarkManager implements Snark.CompleteListener {
return new BitField(bitfield, len);
}
/**
* Get the saved priorities for a torrent from the config file.
* @since 0.8.1
*/
public void loadSavedFilePriorities(Snark snark) {
MetaInfo metainfo = snark.meta;
if (metainfo.getFiles() == null)
return;
byte[] ih = metainfo.getInfoHash();
String infohash = Base64.encode(ih);
infohash = infohash.replace('=', '$');
String pri = _config.getProperty(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
if (pri == null)
return;
int filecount = metainfo.getFiles().size();
int[] rv = new int[filecount];
String[] arr = pri.split(",");
for (int i = 0; i < filecount && i < arr.length; i++) {
if (arr[i].length() > 0) {
try {
rv[i] = Integer.parseInt(arr[i]);
} catch (Throwable t) {}
}
}
snark.storage.setFilePriorities(rv);
}
/**
* Save the completion status of a torrent and the current time in the config file
* in the form "i2psnark.zmeta.$base64infohash=$time,$base64bitfield".
@ -595,8 +624,9 @@ public class SnarkManager implements Snark.CompleteListener {
* The time is a standard long converted to string.
* The status is either a bitfield converted to Base64 or "." for a completed
* torrent to save space in the config file and in memory.
* @param priorities may be null
*/
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield) {
public void saveTorrentStatus(MetaInfo metainfo, BitField bitfield, int[] priorities) {
byte[] ih = metainfo.getInfoHash();
String infohash = Base64.encode(ih);
infohash = infohash.replace('=', '$');
@ -609,6 +639,34 @@ public class SnarkManager implements Snark.CompleteListener {
bfs = Base64.encode(bf);
}
_config.setProperty(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX, now + "," + bfs);
// now the file priorities
String prop = PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX;
if (priorities != null) {
boolean nonzero = false;
for (int i = 0; i < priorities.length; i++) {
if (priorities[i] != 0) {
nonzero = true;
break;
}
}
if (nonzero) {
// generate string like -5,,4,3,,,,,,-2 where no number is zero.
StringBuilder buf = new StringBuilder(2 * priorities.length);
for (int i = 0; i < priorities.length; i++) {
if (priorities[i] != 0)
buf.append(Integer.toString(priorities[i]));
if (i != priorities.length - 1)
buf.append(',');
}
_config.setProperty(prop, buf.toString());
} else {
_config.remove(prop);
}
} else {
_config.remove(prop);
}
saveConfig();
}
@ -621,6 +679,7 @@ public class SnarkManager implements Snark.CompleteListener {
String infohash = Base64.encode(ih);
infohash = infohash.replace('=', '$');
_config.remove(PROP_META_PREFIX + infohash + PROP_META_BITFIELD_SUFFIX);
_config.remove(PROP_META_PREFIX + infohash + PROP_META_PRIORITY_SUFFIX);
saveConfig();
}
@ -742,7 +801,7 @@ public class SnarkManager implements Snark.CompleteListener {
}
public void updateStatus(Snark snark) {
saveTorrentStatus(snark.meta, snark.storage.getBitField());
saveTorrentStatus(snark.meta, snark.storage.getBitField(), snark.storage.getFilePriorities());
}
private void monitorTorrents(File dir) {

View File

@ -42,6 +42,8 @@ public class Storage
private Object[] RAFlock; // lock on RAF access
private long[] RAFtime; // when was RAF last accessed, or 0 if closed
private File[] RAFfile; // File to make it easier to reopen
/** priorities by file; default 0; may be null. @since 0.8.1 */
private int[] priorities;
private final StorageListener listener;
private I2PSnarkUtil _util;
@ -228,6 +230,8 @@ public class Storage
RAFlock = new Object[size];
RAFtime = new long[size];
RAFfile = new File[size];
priorities = new int[size];
int i = 0;
Iterator it = files.iterator();
@ -330,6 +334,102 @@ public class Storage
return -1;
}
/**
* @param file canonical path (non-directory)
* @since 0.8.1
*/
public int getPriority(String file) {
if (complete() || metainfo.getFiles() == null || priorities == null)
return 0;
for (int i = 0; i < rafs.length; i++) {
File f = RAFfile[i];
// use canonical in case snark dir or sub dirs are symlinked
if (f != null) {
try {
String canonical = f.getCanonicalPath();
if (canonical.equals(file))
return priorities[i];
} catch (IOException ioe) {}
}
}
return 0;
}
/**
* Must call setPiecePriorities() after calling this
* @param file canonical path (non-directory)
* @param priority default 0; <0 to disable
* @since 0.8.1
*/
public void setPriority(String file, int pri) {
if (complete() || metainfo.getFiles() == null || priorities == null)
return;
for (int i = 0; i < rafs.length; i++) {
File f = RAFfile[i];
// use canonical in case snark dir or sub dirs are symlinked
if (f != null) {
try {
String canonical = f.getCanonicalPath();
if (canonical.equals(file)) {
priorities[i] = pri;
return;
}
} catch (IOException ioe) {}
}
}
}
/**
* Get the file priorities array.
* @return null on error, if complete, or if only one file
* @since 0.8.1
*/
public int[] getFilePriorities() {
return priorities;
}
/**
* Set the file priorities array.
* Only call this when stopped, but after check()
* @param p may be null
* @since 0.8.1
*/
void setFilePriorities(int[] p) {
priorities = p;
}
/**
* Call setPriority() for all changed files first,
* then call this.
* Set the piece priority to the highest priority
* of all files spanning the piece.
* Caller must pass array to the PeerCoordinator.
* @return null on error, if complete, or if only one file
* @since 0.8.1
*/
public int[] getPiecePriorities() {
if (complete() || metainfo.getFiles() == null || priorities == null)
return null;
int[] rv = new int[metainfo.getPieces()];
int file = 0;
long pcEnd = -1;
long fileEnd = lengths[0] - 1;
int psz = metainfo.getPieceLength(0);
for (int i = 0; i < rv.length; i++) {
pcEnd += psz;
int pri = priorities[file];
while (fileEnd <= pcEnd && file < lengths.length - 1) {
file++;
long oldFileEnd = fileEnd;
fileEnd += lengths[file];
if (priorities[file] > pri && oldFileEnd < pcEnd)
pri = priorities[file];
}
rv[i] = pri;
}
return rv;
}
/**
* The BitField that tells which pieces this storage contains.
* Do not change this since this is the current state of the storage.
@ -436,11 +536,15 @@ public class Storage
changed = true;
checkCreateFiles();
}
if (complete())
if (complete()) {
_util.debug("Torrent is complete", Snark.NOTICE);
else
} else {
// fixme saved priorities
if (files != null)
priorities = new int[files.size()];
_util.debug("Still need " + needed + " out of " + metainfo.getPieces() + " pieces", Snark.NOTICE);
}
}
/**
* Reopen the file descriptors for a restart
@ -565,6 +669,10 @@ public class Storage
changed = true;
synchronized(RAFlock[i]) {
allocateFile(i);
// close as we go so we don't run out of file descriptors
try {
closeRAF(i);
} catch (IOException ioe) {}
}
} else {
_util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
@ -573,8 +681,10 @@ public class Storage
synchronized(RAFlock[i]) {
checkRAF(i);
rafs[i].setLength(lengths[i]);
try {
closeRAF(i);
} catch (IOException ioe) {}
}
// will be closed below
}
}
@ -583,10 +693,25 @@ public class Storage
{
pieces = metainfo.getPieces();
byte[] piece = new byte[metainfo.getPieceLength(0)];
int file = 0;
long fileEnd = lengths[0];
long pieceEnd = 0;
for (int i = 0; i < pieces; i++)
{
int length = getUncheckedPiece(i, piece);
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
// close as we go so we don't run out of file descriptors
pieceEnd += length;
while (fileEnd <= pieceEnd) {
synchronized(RAFlock[file]) {
try {
closeRAF(file);
} catch (IOException ioe) {}
}
if (++file >= rafs.length)
break;
fileEnd += lengths[file];
}
if (correctHash)
{
bitfield.set(i);
@ -601,13 +726,14 @@ public class Storage
_probablyComplete = complete();
// close all the files so we don't end up with a zillion open ones;
// we will reopen as needed
for (int i = 0; i < rafs.length; i++) {
synchronized(RAFlock[i]) {
try {
closeRAF(i);
} catch (IOException ioe) {}
}
}
// Now closed above to avoid running out of file descriptors
//for (int i = 0; i < rafs.length; i++) {
// synchronized(RAFlock[i]) {
// try {
// closeRAF(i);
// } catch (IOException ioe) {}
// }
//}
if (listener != null) {
listener.storageAllChecked(this);
@ -616,6 +742,7 @@ public class Storage
}
}
/** this calls openRAF(); caller must synnchronize and call closeRAF() */
private void allocateFile(int nr) throws IOException
{
// caller synchronized
@ -624,7 +751,12 @@ public class Storage
// the whole file?
listener.storageCreateFile(this, names[nr], lengths[nr]);
final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
byte[] zeros = new byte[ZEROBLOCKSIZE];
byte[] zeros;
try {
zeros = new byte[ZEROBLOCKSIZE];
} catch (OutOfMemoryError oom) {
throw new IOException(oom.toString());
}
int i;
for (i = 0; i < lengths[nr]/ZEROBLOCKSIZE; i++)
{

View File

@ -266,9 +266,9 @@ public class TrackerClient extends I2PAppThread
// we only want to talk to new people if we need things
// from them (duh)
List ordered = new ArrayList(peers);
Collections.shuffle(ordered);
Collections.shuffle(ordered, r);
Iterator it = ordered.iterator();
while (it.hasNext()) {
while ((!stop) && it.hasNext()) {
Peer cur = (Peer)it.next();
// FIXME if id == us || dest == us continue;
// only delay if we actually make an attempt to add peer
@ -357,7 +357,7 @@ public class TrackerClient extends I2PAppThread
+ "&uploaded=" + uploaded
+ "&downloaded=" + downloaded
+ "&left=" + left
+ "&compact"
+ "&compact=1" // NOTE: opentracker will return 400 for &compact alone
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
s += "&numwant=0";

View File

@ -133,6 +133,7 @@ public class I2PSnarkServlet extends Default {
// bypass the horrid Resource.getListHTML()
String pathInfo = req.getPathInfo();
String pathInContext = URI.addPaths(path, pathInfo);
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
Resource resource = getResource(pathInContext);
@ -140,7 +141,7 @@ public class I2PSnarkServlet extends Default {
resp.sendError(HttpResponse.__404_Not_Found);
} else {
String base = URI.addPaths(req.getRequestURI(), "/");
String listing = getListHTML(resource, base, true);
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null);
if (listing != null)
resp.getWriter().write(listing);
else // shouldn't happen
@ -241,7 +242,7 @@ public class I2PSnarkServlet extends Default {
out.write(TABLE_HEADER);
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\"");
out.write(" title=\"");
out.write(_("Torrent Status"));
out.write(_("Status"));
out.write("\">");
out.write(_("Status"));
if (_manager.util().connected() && !snarks.isEmpty()) {
@ -250,14 +251,14 @@ public class I2PSnarkServlet extends Default {
if (peerParam != null) {
out.write("\">");
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/showpeers.png\" title=\"");
out.write(_("Toggle Peer Visibility"));
out.write(_("Hide Peers"));
out.write("\" alt=\"");
out.write(_("Hide Peers"));
out.write("\">");
} else {
out.write("?p=1\">");
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/hidepeers.png\" title=\"");
out.write(_("Toggle Peer Visibility"));
out.write(_("Show Peers"));
out.write("\" alt=\"");
out.write(_("Show Peers"));
out.write("\">");
@ -266,7 +267,7 @@ public class I2PSnarkServlet extends Default {
}
out.write("</th>\n<th align=\"left\">");
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/torrent.png\" title=\"");
out.write(_("Loaded Torrents"));
out.write(_("Torrent"));
out.write("\">");
out.write(_("Torrent"));
out.write("</th>\n<th align=\"center\">");
@ -276,21 +277,21 @@ public class I2PSnarkServlet extends Default {
out.write(_("ETA"));
out.write("</th>\n<th align=\"center\">");
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
out.write(_("Data Downloaded"));
out.write(_("Downloaded"));
out.write("\">");
out.write(_("RX"));
out.write("</th>\n<th align=\"center\">");
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
out.write(_("Data Uploaded"));
out.write(_("Uploaded"));
out.write("\">");
out.write(_("TX"));
out.write("</th>\n<th align=\"center\">");
out.write("<img border=\"0\" src=\"/themes/console/images/inbound.png\" title=\"");
out.write(_("Download Speed"));
out.write(_("Down Rate"));
out.write("\">Rate");
out.write("</th>\n<th align=\"center\">");
out.write("<img border=\"0\" src=\"/themes/console/images/outbound.png\" title=\"");
out.write(_("Upload Speed"));
out.write(_("Up Rate"));
out.write("\">");
out.write(_("Rate"));
out.write("</th>\n");
@ -301,7 +302,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Stop all torrents and the I2P tunnel"));
out.write("\">");
out.write("<img src=\"/themes/snark/ubergine/images/stop_all.png\" title=\"");
out.write(_("Stop All Torrents"));
out.write(_("Stop all torrents and the I2P tunnel"));
out.write("\" alt=\"");
out.write(_("Stop All"));
out.write("\">");
@ -312,7 +313,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Start all torrents and the I2P tunnel"));
out.write("\">");
out.write("<img src=\"/themes/snark/ubergine/images/start_all.png\" title=\"");
out.write(_("Start All Torrents"));
out.write(_("Start all torrents and the I2P tunnel"));
out.write("\" alt=\"Start All\">");
out.write("</a>");
} else {
@ -537,7 +538,7 @@ public class I2PSnarkServlet extends Default {
File torrentFile = new File(baseFile.getParent(), baseFile.getName() + ".torrent");
if (torrentFile.exists())
throw new IOException("Cannot overwrite an existing .torrent file: " + torrentFile.getPath());
_manager.saveTorrentStatus(info, s.getBitField()); // so addTorrent won't recheck
_manager.saveTorrentStatus(info, s.getBitField(), null); // so addTorrent won't recheck
// DirMonitor could grab this first, maybe hold _snarks lock?
FileOutputStream out = new FileOutputStream(torrentFile);
out.write(info.getTorrentData());
@ -564,6 +565,8 @@ public class I2PSnarkServlet extends Default {
_manager.stopTorrent(snark.torrent, false);
}
if (_manager.util().connected()) {
// Give the stopped announces time to get out
try { Thread.sleep(2000); } catch (InterruptedException ie) {}
_manager.util().disconnect();
_manager.addMessage(_("I2P tunnel closed."));
}
@ -718,7 +721,7 @@ public class I2PSnarkServlet extends Default {
curPeers + "/" +
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
else
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Not Seeding");
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/complete.png\" title=\"" + _("Complete") + "\">" + _("Complete");
} else {
if (isRunning && curPeers > 0 && downBps > 0 && !showPeers)
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
@ -727,7 +730,7 @@ public class I2PSnarkServlet extends Default {
ngettext("1 peer", "{0} peers", knownPeers) + "</a>";
else if (isRunning && curPeers > 0 && downBps > 0)
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/downloading.png\" title=\"" + _("Downloading") + "\">" +
" (" + curPeers + "/" +
curPeers + "/" +
ngettext("1 peer", "{0} peers", knownPeers);
else if (isRunning && curPeers > 0 && !showPeers)
statusString = "<img border=\"0\" src=\"/themes/snark/ubergine/images/stalled.png\" title=\"" + _("Stalled") + "\">" +
@ -790,7 +793,7 @@ public class I2PSnarkServlet extends Default {
baseURL = baseURL.substring(e + 1);
out.write("&nbsp;<a href=\"" + baseURL + "details.php?dllist=1&filelist=1&info_hash=");
out.write(TrackerClient.urlencode(snark.meta.getInfoHash()));
out.write("\" title=\"" + name + _("Tracker") + "\" target=\"_blank\">");
out.write("\" title=\"" + name + ' ' + _("Tracker") + "\" target=\"_blank\">");
out.write("<img border=\"0\" src=\"/themes/snark/ubergine/images/details.png\">");
out.write("</a>");
break;
@ -804,7 +807,7 @@ public class I2PSnarkServlet extends Default {
out.write("</td>\n\t");
out.write("<td align=\"right\" class=\"snarkTorrentDownloaded " + rowClass + "\">");
if (remaining > 0)
out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB
out.write(formatSize(total-remaining) + "&thinsp;/&thinsp;" + formatSize(total)); // 18MB/3GB; thin space so it will line break well
else
out.write(formatSize(total)); // 3GB
out.write("</td>\n\t");
@ -828,7 +831,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Stop the torrent"));
out.write("\">");
out.write("<img src=\"/themes/snark/ubergine/images/stop.png\" title=\"");
out.write(_("Stop Torrent"));
out.write(_("Stop"));
out.write("\" alt=\"");
out.write(_("Stop"));
out.write("\">");
@ -840,7 +843,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Start the torrent"));
out.write("\">");
out.write("<img src=\"/themes/snark/ubergine/images/start.png\" title=\"");
out.write(_("Start Torrent"));
out.write(_("Start the torrent"));
out.write("\" alt=\"");
out.write(_("Start"));
out.write("\">");
@ -856,7 +859,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Are you sure you want to delete the file \\''{0}.torrent\\'' (downloaded data will not be deleted) ?", fullFilename));
out.write("')) { return false; }\">");
out.write("<img src=\"/themes/snark/ubergine/images/remove.png\" title=\"");
out.write(_("Remove Torrent"));
out.write(_("Remove"));
out.write("\" alt=\"");
out.write(_("Remove"));
out.write("\">");
@ -871,7 +874,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?", fullFilename));
out.write("')) { return false; }\">");
out.write("<img src=\"/themes/snark/ubergine/images/delete.png\" title=\"");
out.write(_("Delete Torrent + Data"));
out.write(_("Delete"));
out.write("\" alt=\"");
out.write(_("Delete"));
out.write("\">");
@ -1007,7 +1010,7 @@ public class I2PSnarkServlet extends Default {
out.write(_("Add torrent"));
out.write("\" name=\"foo\" ><br>\n");
out.write("<tr><td>&nbsp;<td><span class=\"snarkAddInfo\">");
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath ())) + "</code>";
out.write(_("You can also copy .torrent files to: {0}.", "<code>" + _manager.getDataDir().getAbsolutePath () + "</code>"));
out.write("\n");
out.write(_("Removing a .torrent will cause it to stop."));
out.write("<br></span></table>\n");
@ -1262,19 +1265,21 @@ public class I2PSnarkServlet extends Default {
// rounding makes us look faster :)
private static String formatSize(long bytes) {
if (bytes < 5*1024)
return bytes + " B";
return bytes + "&nbsp;B";
else if (bytes < 5*1024*1024)
return ((bytes + 512)/1024) + " KB";
return ((bytes + 512)/1024) + "&nbsp;KB";
else if (bytes < 10*1024*1024*1024l)
return ((bytes + 512*1024)/(1024*1024)) + " MB";
return ((bytes + 512*1024)/(1024*1024)) + "&nbsp;MB";
else
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + " GB";
return ((bytes + 512*1024*1024)/(1024*1024*1024)) + "&nbsp;GB";
}
/** @since 0.7.14 */
private static String urlify(String s) {
StringBuilder buf = new StringBuilder(256);
buf.append("<a href=\"").append(s).append("\">").append(s).append("</a>");
// browsers seem to work without doing this but let's be strict
String link = s.replace("&", "&amp;");
buf.append("<a href=\"").append(link).append("\">").append(link).append("</a>");
return buf.toString();
}
@ -1313,10 +1318,11 @@ public class I2PSnarkServlet extends Default {
* @param r The Resource
* @param base The base URL
* @param parent True if the parent directory should be included
* @param postParams map of POST parameters or null if not a POST
* @return String of HTML
* @since 0.7.14
*/
private String getListHTML(Resource r, String base, boolean parent)
private String getListHTML(Resource r, String base, boolean parent, Map postParams)
throws IOException
{
if (!r.isDirectory())
@ -1341,6 +1347,10 @@ public class I2PSnarkServlet extends Default {
else
torrentName = title;
Snark snark = _manager.getTorrentByBaseName(torrentName);
if (snark != null && postParams != null)
savePriorities(snark, postParams);
if (title.endsWith("/"))
title = title.substring(0, title.length() - 1);
title = _("Torrent") + ": " + title;
@ -1348,12 +1358,29 @@ public class I2PSnarkServlet extends Default {
buf.append("</TITLE>").append(HEADER).append("<link rel=\"shortcut icon\" href=\"/themes/snark/ubergine/favicon.ico\"></HEAD><BODY>\n<center><div class=\"snarknavbar\"> <a href=\"/i2psnark/\" title=\"Torrents\"");
buf.append(" class=\"snarkRefresh\">I2PSnark</a>").append("</div>");
buf.append("<div class=\"page\"><div class=\"mainsection\">" +
"<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
if (parent)
{
buf.append("\n<br><A HREF=\"");
// corrupts utf-8
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
buf.append(URI.addPaths(base,"../"));
buf.append("\"><img border=\"0\" src=\"/themes/console/images/outbound.png\"> ")
.append(_("Up to higher level directory")).append("</A>\n");
}
buf.append("</div><div class=\"page\"><div class=\"mainsection\">");
boolean showPriority = snark != null && !snark.storage.complete();
if (showPriority)
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n");
buf.append("<TABLE BORDER=0 class=\"snarkTorrents\" cellpadding=\"5px 10px\">" +
"<thead><tr><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/file.png\" title=\"").append(_("File")).append("\" alt=\"").append(_("File")).append("\">&nbsp;").append(title).append("</th><th align=\"right\">").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/size.png\" title=\"").append(_("FileSize")).append("\" alt=\"").append(_("FileSize")).append("\">").append(_("Size"));
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th></tr></thead>");
buf.append("</th><th>").append("<img border=\"0\" src=\"/themes/snark/ubergine/images/status.png\" title=\"").append(_("Download Status")).append("\">").append(_("Status")).append("</th>");
if (showPriority)
buf.append("<th>").append(_("Priority")).append("</th>");
buf.append("</tr></thead>\n");
//DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
// DateFormat.MEDIUM);
boolean showSaveButton = false;
for (int i=0 ; i< ls.length ; i++)
{
String encoded=URI.encodePath(ls[i]);
@ -1391,7 +1418,8 @@ public class I2PSnarkServlet extends Default {
complete = true;
status = toImg("tick") + _("Complete");
} else {
status = toImg("clock") +
status =
(snark.storage.getPriority(f.getCanonicalPath()) < 0 ? toImg("cancel") : toImg("clock")) +
(100 * (length - remaining) / length) + "% " + _("complete") +
" (" + DataHelper.formatSize2(remaining) + _("bytes remaining") + ")";
}
@ -1435,22 +1463,41 @@ public class I2PSnarkServlet extends Default {
buf.append("</TD><TD class=\"").append(rowClass).append(" snarkFileStatus\">");
//buf.append(dfmt.format(new Date(item.lastModified())));
buf.append(status);
buf.append("</TD></TR>\n");
buf.append("</TD>");
if (showPriority) {
buf.append("<td>");
File f = item.getFile();
if ((!complete) && (!item.isDirectory()) && f != null) {
int pri = snark.storage.getPriority(f.getCanonicalPath());
buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
if (pri > 0)
buf.append("checked=\"true\"");
buf.append('>').append(_("High"));
buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
if (pri == 0)
buf.append("checked=\"true\"");
buf.append('>').append(_("Normal"));
buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
if (pri < 0)
buf.append("checked=\"true\"");
buf.append('>').append(_("Do not download"));
showSaveButton = true;
}
if (parent)
{
buf.append("<tfoot align=\"left\"><tr><td colspan=\"3\"><A HREF=\"");
// corrupts utf-8
//buf.append(URI.encodePath(URI.addPaths(base,"../")));
buf.append(URI.addPaths(base,"../"));
buf.append("\"><img border=\"0\" src=\"/themes/snark/ubergine/images/up.png\"> ")
.append(_("Up to higher level directory")).append("</A></td></tr></thead>\n");
buf.append("</td>");
}
buf.append("</TR>\n");
}
if (showSaveButton) {
buf.append("<thead><tr><th colspan=\"3\">&nbsp;</th><th align=\"center\"><input type=\"submit\" value=\"");
buf.append(_("Save priorities"));
buf.append("\" name=\"foo\" ></th></tr></thead>\n");
}
buf.append("</TABLE>\n");
buf.append("</div></div></center></BODY></HTML>\n");
if (showPriority)
buf.append("</form>");
buf.append("</div></div></BODY></HTML>\n");
return buf.toString();
}
@ -1515,6 +1562,26 @@ buf.append("</div></div></center></BODY></HTML>\n");
return "<img alt=\"\" height=\"16\" width=\"16\" src=\"/i2psnark/_icons/" + icon + ".png\"> ";
}
/** @since 0.8.1 */
private void savePriorities(Snark snark, Map postParams) {
Set<Map.Entry> entries = postParams.entrySet();
for (Map.Entry entry : entries) {
String key = (String)entry.getKey();
if (key.startsWith("pri.")) {
try {
String file = key.substring(4);
String val = ((String[])entry.getValue())[0]; // jetty arrays
int pri = Integer.parseInt(val);
snark.storage.setPriority(file, pri);
//System.err.println("Priority now " + pri + " for " + file);
} catch (Throwable t) { t.printStackTrace(); }
}
}
if (snark.coordinator != null)
snark.coordinator.updatePiecePriorities();
_manager.saveTorrentStatus(snark.storage.getMetaInfo(), snark.storage.getBitField(), snark.storage.getFilePriorities());
}
/** inner class, don't bother reindenting */
private static class FetchAndAdd implements Runnable {

View File

@ -175,7 +175,7 @@ public class EditBean extends IndexBean {
}
public String getAccessList(int tunnel) {
return getProperty(tunnel, "i2cp.accessList", "").replaceAll(",", "\n");
return getProperty(tunnel, "i2cp.accessList", "").replace(",", "\n");
}
public boolean getClose(int tunnel) {

View File

@ -666,7 +666,7 @@ public class IndexBean {
}
public void setAccessList(String val) {
if (val != null)
_otherOptions.put("i2cp.accessList", val.trim().replaceAll("\r\n", ",").replaceAll("\n", ",").replaceAll(" ", ","));
_otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
}
public void setCloseTime(String val) {
if (val != null) {

View File

@ -159,7 +159,7 @@
%>
</label>
<input type="text" size="30" id="targetDestination" name="targetDestination" title="Destination of the Tunnel" value="<%=editBean.getClientDestination(curTunnel)%>" class="freetext" />
<span class="comment">(<%=intl._("name or destination")%>)</span>
<span class="comment">(<%=intl._("name or destination")%>; <%=intl._("b32 not recommended")%>)</span>
</div>
<% } %>
<% if (!"streamrclient".equals(tunnelType)) { %>

View File

@ -45,6 +45,10 @@ public class I2PSocketEepGet extends EepGet {
/** this replaces _proxy in the superclass. Sadly, I2PSocket does not extend Socket. */
private I2PSocket _socket;
/** from ConnectionOptions */
private static final String PROP_CONNECT_DELAY = "i2p.streaming.connectDelay";
private static final String CONNECT_DELAY = "500";
public I2PSocketEepGet(I2PAppContext ctx, I2PSocketManager mgr, int numRetries, String outputFile, String url) {
this(ctx, mgr, numRetries, -1, -1, outputFile, null, url);
}
@ -123,6 +127,10 @@ public class I2PSocketEepGet extends EepGet {
Properties props = new Properties();
props.setProperty(I2PSocketOptions.PROP_CONNECT_TIMEOUT, "" + CONNECT_TIMEOUT);
props.setProperty(I2PSocketOptions.PROP_READ_TIMEOUT, "" + INACTIVITY_TIMEOUT);
// This is important - even if the underlying socket doesn't have a connect delay,
// we want to set it for this connection, so the request headers will go out
// in the SYN packet, saving one RTT.
props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY);
I2PSocketOptions opts = _socketManager.buildOptions(props);
_socket = _socketManager.connect(dest, opts);
} else {

View File

@ -24,7 +24,10 @@ public class ConfigLoggingHelper extends HelperBase {
public String getMaxFileSize() {
int bytes = _context.logManager().getFileSize();
if (bytes <= 0) return "1.00 MB";
return DataHelper.formatSize2(bytes) + 'B';
// "&nbsp;" comes back in the POST as 0xc2 0xa0
// non-breaking space is U+00A0 which is 0xc2 0xa0 in UTF-8.
// we could figure out where the UTF-8 problem is but why bother.
return DataHelper.formatSize2(bytes).replace("&nbsp;", " ") + 'B';
}
public String getLogLevelTable() {
StringBuilder buf = new StringBuilder(32*1024);

View File

@ -142,7 +142,7 @@ public class ConfigUpdateHandler extends FormHandler {
}
if ( (_updateURL != null) && (_updateURL.length() > 0) ) {
_updateURL = _updateURL.replaceAll("\r\n", ",").replaceAll("\n", ",");
_updateURL = _updateURL.replace("\r\n", ",").replace("\n", ",");
String oldURL = _context.router().getConfigSetting(PROP_UPDATE_URL);
if ( (oldURL == null) || (!_updateURL.equals(oldURL)) ) {
_context.router().setConfigSetting(PROP_UPDATE_URL, _updateURL);
@ -151,7 +151,7 @@ public class ConfigUpdateHandler extends FormHandler {
}
if ( (_trustedKeys != null) && (_trustedKeys.length() > 0) ) {
_trustedKeys = _trustedKeys.replaceAll("\r\n", ",").replaceAll("\n", ",");
_trustedKeys = _trustedKeys.replace("\r\n", ",").replace("\n", ",");
String oldKeys = new TrustedUpdate(_context).getTrustedKeysString();
if ( (oldKeys == null) || (!_trustedKeys.equals(oldKeys)) ) {
_context.router().setConfigSetting(PROP_TRUSTED_KEYS, _trustedKeys);

View File

@ -40,7 +40,7 @@ public class ConfigUpdateHelper extends HelperBase {
public String getUpdateURL() {
String url = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL);
if (url != null)
return url.replaceAll(",", "\n");
return url.replace(",", "\n");
else
return ConfigUpdateHandler.DEFAULT_UPDATE_URL;
}

View File

@ -31,9 +31,9 @@ public class LogsHelper extends HelperBase {
}
String str = FileUtil.readTextFile(f.getAbsolutePath(), 250, false);
if (str == null)
return "";
return _("File not found") + ": <b><code>" + f.getAbsolutePath() + "</code></b>";
else {
str = str.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
str = str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
return _("File location") + ": <b><code>" + f.getAbsolutePath() + "</code></b> <pre>" + str + "</pre>";
}
}
@ -54,12 +54,14 @@ public class LogsHelper extends HelperBase {
buf.append("<code>\n");
for (int i = msgs.size(); i > 0; i--) {
String msg = msgs.get(i - 1);
msg = msg.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
buf.append("<li>");
if (colorize) {
String color;
// Homeland Security Advisory System
// http://www.dhs.gov/xinfoshare/programs/Copy_of_press_release_0046.shtm
// but pink instead of yellow for WARN
// FIXME doesnt work for translated levels
if (msg.contains("CRIT"))
color = "#cc0000";
else if (msg.contains("ERROR"))
@ -71,7 +73,7 @@ public class LogsHelper extends HelperBase {
else
color = "#006600";
buf.append("<font color=\"").append(color).append("\">");
buf.append(msg.replaceAll("<", "&lt;").replaceAll(">", "&gt;"));
buf.append(msg);
buf.append("</font>");
} else {
buf.append(msg);

View File

@ -15,6 +15,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Poedit-Language: German\n"
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126

View File

@ -15,8 +15,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: German\n"
"X-Poedit-Basepath: /home/lee/work/i2p/monotone/i2p.i2p/apps/routerconsole/java\n"
"X-Poedit-Language: French\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/config_jsp.java:106
msgid "config networking"

View File

@ -15,6 +15,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Poedit-Language: Dutch\n"
#: ../../../router/java/src/net/i2p/router/Blocklist.java:126

View File

@ -47,6 +47,9 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
private int _maxTotalConnsPerHour;
private int _maxTotalConnsPerDay;
// NOTE - almost all the options are below, but see
// I2PSocketOptions in ministreaming for a few more
public static final int PROFILE_BULK = 1;
public static final int PROFILE_INTERACTIVE = 2;

View File

@ -0,0 +1,21 @@
Bundled in ../java/lib/ are the binaries for systray4j version 2.4.1 2004-03-28,
which is still the latest.
Files are from systray4j-2.4.1-win32.zip.
SHA1Sums:
28acaea97816f53d188d01fd88b72e670e67286b systray4j-2.4.1-win32.zip
a7f5e02c3652f3f1a72559e54ee69226b8b97859 systray4j.dll
947bd91c483494256cf48ad87c211e8701b4f85b systray4j.jar
systray4j is GPLv2, see LICENSE.systray4j.txt.
I2P systray code in ../java/src is public domain.
SysTray is really obsolete. It supports Windows and kde3 only.
We only instantiate it on Windows.
The java.awt.SystemTray classes added in Java 6
(and used by apps/desktopgui) are the way to go now.
We could either rewrite this to use SystemTray, or switch to desktopgui.

View File

@ -184,4 +184,20 @@ public class SysTray implements SysTrayMenuListener {
_sysTrayMenu.addItem(_itemOpenConsole);
refreshDisplay();
}
/**
* Starts SysTray, even on linux (but requires kde3 libsystray4j.so to do anything)
* @since 0.8.1
*/
public static void main(String args[]) {
System.err.println("SysTray4j version " + SysTrayMenu.VERSION);
System.err.println("Hit ^C to exit");
new SysTray();
Thread t = Thread.currentThread();
synchronized(t) {
try {
t.wait();
} catch (InterruptedException ie) {}
}
}
}

View File

@ -6,6 +6,14 @@
<!--
<property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
-->
<!-- Add Apache Harmony's Pack200 library if you don't have java.util.jar.Pack200
See core/java/src/net/i2p/util/FileUtil.java for code changes required
to use this library instead of Sun's version.
Or to comment it all out if you don't have either.
-->
<!--
<property name="javac.classpath" value="/PATH/TO/pack200.jar" />
-->
<!-- You probably don't want to change anything from here down -->
<target name="help" depends="all" />

View File

@ -17,10 +17,11 @@
</target>
<!-- only used if not set by a higher build.xml -->
<property name="javac.compilerargs" value="" />
<property name="javac.classpath" value="" />
<target name="compile" depends="depend">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" >
<javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="${javac.classpath}" >
<compilerarg line="${javac.compilerargs}" />
</javac>
</target>

View File

@ -1076,7 +1076,8 @@ public class DataHelper {
}
/**
* Like formatSize but with a space after the number
* Like formatSize but with a non-breaking space after the number
* Use only in HTML
* @since 0.7.14
*/
public static String formatSize2(long bytes) {
@ -1091,11 +1092,11 @@ public class DataHelper {
String str = fmt.format(val);
switch (scale) {
case 1: return str + " K";
case 2: return str + " M";
case 3: return str + " G";
case 4: return str + " T";
default: return bytes + " ";
case 1: return str + "&nbsp;K";
case 2: return str + "&nbsp;M";
case 3: return str + "&nbsp;G";
case 4: return str + "&nbsp;T";
default: return bytes + "&nbsp;";
}
}

View File

@ -13,10 +13,18 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
// Pack200 import
// you must also uncomment the correct line in unpack() below
// For gcj, gij, etc., comment both out
//
// For Sun, OpenJDK, IcedTea, etc, use this
import java.util.jar.Pack200;
// For Apache Harmony or if you put its pack200.jar in your library directory use this
//import org.apache.harmony.unpack200.Archive;
/**
* General helper methods for messing with files
@ -119,7 +127,7 @@ public class FileUtil {
if (entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack")) {
target = new File(targetDir, entry.getName().substring(0, entry.getName().length() - ".pack".length()));
JarOutputStream fos = new JarOutputStream(new FileOutputStream(target));
Pack200.newUnpacker().unpack(in, fos);
unpack(in, fos);
fos.close();
System.err.println("INFO: File [" + entry.getName() + "] extracted and unpacked");
} else {
@ -189,9 +197,7 @@ public class FileUtil {
} else {
if (p200TestRequired &&
(entry.getName().endsWith(".jar.pack") || entry.getName().endsWith(".war.pack"))) {
try {
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
} catch (Exception e) { // ClassNotFoundException but compiler not happy with that
if (!isPack200Supported()) {
System.err.println("ERROR: Zip verify failed, your JVM does not support unpack200");
return false;
}
@ -224,6 +230,40 @@ public class FileUtil {
}
}
/**
* This won't work right if one of the two options in unpack() is commented out.
* @since 0.8.1
*/
private static boolean isPack200Supported() {
try {
Class.forName("java.util.jar.Pack200", false, ClassLoader.getSystemClassLoader());
return true;
} catch (Exception e) {}
try {
Class.forName("org.apache.harmony.pack200.Archive", false, ClassLoader.getSystemClassLoader());
return true;
} catch (Exception e) {}
return false;
}
/**
* Caller must close streams
* @since 0.8.1
*/
private static void unpack(InputStream in, JarOutputStream out) throws Exception {
// For Sun, OpenJDK, IcedTea, etc, use this
Pack200.newUnpacker().unpack(in, out);
// ------------------
// For Apache Harmony or if you put its pack200.jar in your library directory use this
//(new Archive(in, out)).unpack();
// ------------------
// For gcj, gij, etc., use this
//throw new IOException("Pack200 not supported");
}
/**
* Read in the last few lines of a (newline delimited) textfile, or null if
* the file doesn't exist.
@ -352,6 +392,18 @@ public class FileUtil {
boolean copied = FileUtil.copy(args[1], args[2], false);
if (!copied)
System.err.println("Error copying [" + args[1] + "] to [" + args[2] + "]");
} else if ("unzip".equals(args[0])) {
File f = new File(args[1]);
File to = new File("tmp");
to.mkdir();
boolean copied = verifyZip(f);
if (!copied)
System.err.println("Error verifying " + args[1]);
copied = extractZip(f, to);
if (copied)
System.err.println("Unzipped [" + args[1] + "] to [" + to + "]");
else
System.err.println("Error unzipping [" + args[1] + "] to [" + to + "]");
}
}

View File

@ -13,6 +13,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
@ -429,7 +430,8 @@ public class LogManager {
v = v.substring(0, v.length() - 1);
char mod = v.charAt(v.length() - 1);
if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1);
double val = Double.parseDouble(v);
// output to form was in current locale, so have to parse it back that way
double val = (new DecimalFormat()).parse(v.trim()).doubleValue();
switch (mod) {
case 'K':
val *= 1024;

View File

@ -56,7 +56,7 @@ public class SecureFileOutputStream extends FileOutputStream {
* Tries to set the permissions to 600,
* ignores errors
*/
private static void setPerms(File f) {
public static void setPerms(File f) {
if (!canSetPerms)
return;
try {

View File

@ -1,3 +1,157 @@
2010-11-03 zzz
* Merge and snark fixups
2010-11-01 zzz
* ClientConnectionRunner: Add synch to fix race causing AIOOBE
(http://forum.i2p/viewtopic.php?t=5061)
* configlogging.jsp: Parse log limit with current locale
(ticket #118)
* i2psnark:
- Limit number of parallel requests of a single piece when in the end game
- Shorten and weight the speed tracker so the display is more
reflective of current speed
* logs.jsp: Add message if wrapper log not found
(ticket #103)
2010-10-30 zzz
* i2psnark:
- Priority mapping bugfix
- Close files as we go when creating/checking
so we don't run out of file descriptors
- Update request queues after priority change
- Only add wanted pieces to wanted list at startup
- Make sure lastRequest is null when it should be
- Delay during StopAll so we don't close the tunnel before the
stopped announces go out and reopen it
- Logging tweaks
2010-10-27 zzz
* i2psnark:
- Don't stay interested if we run out of pieces
to request (thanks sponge)
- Enhance debug mode to show requests
- Priority mapping bugfix
* Transport: Avoid rare NPE at startup
2010-10-24 zzz
* FileUtil: Make it easier to compile without Pack200, or with
Apache Harmony's Pack200, add unzip to main()
* i2psnark: Catch a race after disconnect()
* NTCP: Catch a race after stop()
* Router: Set permissions on wrapper.log when not called by RouterLaunch
* Systray: New doc and main()
2010-10-19 zzz
* Escape & in logs and i2psnark (much more to do)
* JobImpl: Deprecate two debugging methods
* replaceAll() -> replace() when we don't need regex
2010-10-15 zzz
* i2psnark: Add file priority feature
* I2PSocketEepGet: Set connect delay to save a RTT, will
speed announces in i2psnark
2010-10-12 zzz
*** 1.6 or higher JDK now required to build
* configlogging.jsp:
- Add easy way to add an override
- Make file size specifier more flexible
* Console:
- Sort RouterAddress options on netdb.jsp and peers.jsp
- Remove unused web-*.xml file from war
* Crypto:
- Convert all ArrayList caching to LBQs in YKGenerator,
HMACGenerator, and AESKeyCache.
- Change DSAEngine params from Hash to new SHA1Hash, since
these were really 20 byte hashes, not 32 byte Hashes.
- Add stats to track YKGenerator caching success
- Fix YKGenerator precalculation to be much more useful by
increasing the cache size and dramatically shortening the delay
- Option cleanups
- YKGenerator cleanups
- Mark HMAC256Generator unused
* EepGet: Reset length variable on redirect
* Files: Change permissions to 600/700 for all written files/directories.
Now requires Java 1.6 to build, but only 1.5+ to run.
(requires 1.6 to set permissiomns)
* GeoIP: Fix locking bug causing lookups to stop
* Hash: Throw IAE if data length is not 32 bytes,
now that DSAEngine abuse is gone
* HTTPResponseOutputStream:
- More caching
- Stats cleanup
- Max header length check
- Catch OOM
- Initializer cleanup
- Javadoc
* I2CP:
- Add new option i2cp.messageReliability=none, which prevents the
router from sending MessageStatusMessages back in reply to an
outbound SendMessageMessage. Since the streaming lib always ignored
the MSMs anyway, make it the default for streaming.
This will reduce the I2CP traffic significantly.
MSM handling now avoided, but it is still fairly broken, see
comments in I2PSessionImpl2.
- Cleanups to replace method calls with fields
- More cleanups, javadoc, rate reduction
* i2psnark:
- Compact response format
- Add link to finished torrent in message box
- Don't let one bad torrent prevent others from
starting or stopping
- Sort peers by completion %
- Add some missing mime types to web.xml
- shouldLog() cleanup
* i2ptunnel:
- Now that streaming flush() is fixed, use it in IRCClient, and
for initial data in I2PTunnel runner, to avoid the 250 ms
passive flush delay
- Add hostname DSA signature field, to be used for addkey forms.
Experimental, may be commented out later.
- More header blocking (thanks telecomix!)
- Remove unused web-*.xml file from war
* Installer: Add startup hint for non-x86
* Javadoc updates all over the place
* LogConsoleBuffer: Java 5
* Naming:
- Increase cache size and expiration time
- Add clearCache() method
- Don't use EepGet or Exec for b32
- Javadoc updates
* NetDB:
- Expire unreachable routers quickly, even if they don't have introducers,
so we don't have old data on routers that ran out of introducers.
- Fix rare NPEs at shutdown
* NTCP:
- Cleanups
* Streaming:
- Make flush() block less, by waiting only for "accept" into the
streaming queue rather than "completion" (i.e. ACK from the far end).
This prevents complete stalls when flushing, and should help performance
of apps that use flush(), like i2psnark (and SAM?).
close() still does a flush that waits for completion, as i2ptunnel
doesn't like a fast return from close().
- cleanups
* SusiDNS:
- Remove unused web-*.xml file from war
* TransportManager: Convert _transports from a List to a CHM
to prevent a rare concurrent exception
* Tunnels:
- Don't use peers < 0.7.9 for tunnels due to the old
message corruption bugs
- Javadoc
- Cleanups
* UDP:
- Beginnings of destroy message support
- Try to avoid running out of introducers by relaxing selection criteria
and increasing minimum number of potential introducers
- Avoid rare AIOOBE
- PacketBuilder refactor
- Make most classes package private
- Comments
- Logging cleanup
- Comment out a main()
2010-10-22 sponge
* Sanity and some fixs for slackware package

View File

@ -39,11 +39,21 @@ public abstract class JobImpl implements Job {
return buf.toString();
}
/**
* @deprecated
* As of 0.8.1, this is a noop, as it just adds classes to the log manager
* class list for no good reason. Logging in jobs is almost always
* set explicitly rather than by class name.
*/
void addedToQueue() {
if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
_addedBy = new Exception();
//if (_context.logManager().getLog(getClass()).shouldLog(Log.DEBUG))
// _addedBy = new Exception();
}
/**
* @deprecated
* @return null always
*/
public Exception getAddedBy() { return _addedBy; }
public long getMadeReadyOn() { return _madeReadyOn; }
public void madeReady() { _madeReadyOn = _context.clock().now(); }

View File

@ -142,8 +142,9 @@ public class JobQueue {
public void addJob(Job job) {
if (job == null || !_alive) return;
if (job instanceof JobImpl)
((JobImpl)job).addedToQueue();
// This does nothing
//if (job instanceof JobImpl)
// ((JobImpl)job).addedToQueue();
long numReady = 0;
boolean alreadyExists = false;

View File

@ -218,6 +218,19 @@ public class Router {
// NOW we start all the activity
_context.initAll();
// Set wrapper.log permissions.
// Just hope this is the right location, we don't know for sure,
// but this is the same method used in LogsHelper and we have no complaints.
// (we could look for the wrapper.config file and parse it I guess...)
// If we don't have a wrapper, RouterLaunch does this for us.
if (System.getProperty("wrapper.version") != null) {
File f = new File(System.getProperty("java.io.tmpdir"), "wrapper.log");
if (!f.exists())
f = new File(_context.getBaseDir(), "wrapper.log");
if (f.exists())
SecureFileOutputStream.setPerms(f);
}
_routerInfo = null;
_higherVersionSeen = false;
_log = _context.logManager().getLog(Router.class);

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 05;
public final static long BUILD = 9;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -361,6 +361,8 @@ public class ClientConnectionRunner {
// TunnelPool.locked_buildNewLeaseSet() ensures that leases are sorted,
// so the comparison will always work.
int leases = set.getLeaseCount();
// synch so _currentLeaseSet isn't changed out from under us
synchronized (this) {
if (_currentLeaseSet != null && _currentLeaseSet.getLeaseCount() == leases) {
for (int i = 0; i < leases; i++) {
if (! _currentLeaseSet.getLease(i).getTunnelId().equals(set.getLease(i).getTunnelId()))
@ -376,6 +378,7 @@ public class ClientConnectionRunner {
}
}
}
}
if (_log.shouldLog(Log.INFO))
_log.info("Current leaseSet " + _currentLeaseSet + "\nNew leaseSet " + set);
LeaseRequestState state = null;
@ -590,7 +593,7 @@ public class ClientConnectionRunner {
+ " for session [" + _sessionId.getSessionId()
+ "] (with nonce=2), retrying after ["
+ (_context.clock().now() - _lastTried)
+ "]", getAddedBy());
+ "]");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Updating message status for message " + _messageId + " to "

View File

@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl {
if (_expiration < now) {
if (_log.shouldLog(Log.WARN))
_log.warn("Timed out sending message " + _message + " directly (expiration = "
+ new Date(_expiration) + ") to " + _targetHash.toBase64(), getAddedBy());
+ new Date(_expiration) + ") to " + _targetHash.toBase64());
if (_onFail != null)
getContext().jobQueue().addJob(_onFail);
return;
@ -104,7 +104,7 @@ public class SendMessageDirectJob extends JobImpl {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to find the router to send to: " + _targetHash
+ " after searching for " + (getContext().clock().now()-_searchOn)
+ "ms, message: " + _message, getAddedBy());
+ "ms, message: " + _message);
if (_onFail != null)
getContext().jobQueue().addJob(_onFail);
}

View File

@ -136,17 +136,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
@Override
public boolean isBacklogged(Hash dest) {
return _manager.isBacklogged(dest);
return _manager != null && _manager.isBacklogged(dest);
}
@Override
public boolean isEstablished(Hash dest) {
return _manager.isEstablished(dest);
return _manager != null && _manager.isEstablished(dest);
}
@Override
public boolean wasUnreachable(Hash dest) {
return _manager.wasUnreachable(dest);
return _manager != null && _manager.wasUnreachable(dest);
}
@Override

View File

@ -615,8 +615,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
if (_log.shouldLog(Log.INFO))
_log.info("Type " + msg.getMessage().getType() + " pri " + msg.getPriority() + " slot " + slot);
boolean removed = _outbound.remove(msg);
if ((!removed) && _log.shouldLog(Log.ERROR))
_log.info("Already removed??? " + msg.getMessage().getType());
if ((!removed) && _log.shouldLog(Log.WARN))
_log.warn("Already removed??? " + msg.getMessage().getType());
}
_currentOutbound = msg;
}

View File

@ -2,6 +2,7 @@ package net.i2p.router.transport.ntcp;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
@ -24,9 +25,9 @@ import net.i2p.util.Log;
*/
public class NTCPSendFinisher {
private static final int THREADS = 4;
private I2PAppContext _context;
private NTCPTransport _transport;
private Log _log;
private final I2PAppContext _context;
private final NTCPTransport _transport;
private final Log _log;
private int _count;
private ThreadPoolExecutor _executor;
@ -47,7 +48,12 @@ public class NTCPSendFinisher {
}
public void add(OutNetMessage msg) {
try {
_executor.execute(new RunnableEvent(msg));
} catch (RejectedExecutionException ree) {
// race with stop()
_log.warn("NTCP send finisher stopped, discarding msg.afterSend()");
}
}
// not really needed for now but in case we want to add some hooks like afterExecute()

View File

@ -317,7 +317,7 @@ class TestJob extends JobImpl {
public String getName() { return "Tunnel test timeout"; }
public void runJob() {
if (_log.shouldLog(Log.WARN))
_log.warn("Timeout: found? " + _found, getAddedBy());
_log.warn("Timeout: found? " + _found);
if (!_found) {
// don't clog up the SKM with old one-tag tagsets
if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) {

View File

@ -81,7 +81,7 @@ public class SendGarlicJob extends JobImpl {
long after = getContext().clock().now();
if ( (after - before) > 1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Building the garlic took too long [" + (after-before)+" ms]", getAddedBy());
_log.warn("Building the garlic took too long [" + (after-before)+" ms]");
} else {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Building the garlic was fast! " + (after - before) + " ms");