diff --git a/apps/BOB/src/net/i2p/BOB/DoCMDS.java b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
index 16da28ce9c019e2b588cacf7d83d0983e260e6e0..1a2d2a19a6f0c7552e4798c3bbcfd701b4f657c3 100644
--- a/apps/BOB/src/net/i2p/BOB/DoCMDS.java
+++ b/apps/BOB/src/net/i2p/BOB/DoCMDS.java
@@ -1263,11 +1263,11 @@ public class DoCMDS implements Runnable {
 												tunnel = new MUXlisten(database, nickinfo, _log);
 												Thread t = new Thread(tunnel);
 												t.start();
-												try {
-													Thread.sleep(1000 * 10); // Slow down the startup.
-												} catch(InterruptedException ie) {
-													// ignore it
-												}
+												// try {
+												//	Thread.sleep(1000 * 10); // Slow down the startup.
+												// } catch(InterruptedException ie) {
+												//	// ignore it
+												// }
 												out.println("OK tunnel starting");
 											} catch (I2PException e) {
 												out.println("ERROR starting tunnel: " + e);
diff --git a/apps/BOB/src/net/i2p/BOB/I2Plistener.java b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
index a8115893db8ca2f650bf976407356e647ecb0354..3bb94e0b37582344111d946e4b9ceaf38b4d0281 100644
--- a/apps/BOB/src/net/i2p/BOB/I2Plistener.java
+++ b/apps/BOB/src/net/i2p/BOB/I2Plistener.java
@@ -82,18 +82,6 @@ public class I2Plistener implements Runnable {
 die:		{
 
 			serverSocket.setSoTimeout(50);
-//			try {
-//				if (info.exists("INPORT")) {
-//					tgwatch = 2;
-//				}
-//			} catch (Exception e) {
-//				try {
-//					runlock();
-//				} catch (Exception e2) {
-//					break die;
-//				}
-//				break die;
-//			}
 			boolean spin = true;
 			while (spin) {
 
@@ -129,40 +117,12 @@ die:		{
 						t.start();
 					}
 
-				} catch (I2PException e) {
+				} catch (Exception e) {
 					//	System.out.println("Exception " + e);
 				}
 			}
 		}
 		// System.out.println("I2Plistener: Close");
-
-
-		// Previous level does this cleanup now.
-		//
-		// try {
-		//	serverSocket.close();
-		// } catch (I2PException e) {
-			// nop
-		//}
-		// need to kill off the socket manager too.
-		// I2PSession session = socketManager.getSession();
-		// if (session != null) {
-			// System.out.println("I2Plistener: destroySession");
-		//	try {
-		//		session.destroySession();
-		//	} catch (I2PSessionException ex) {
-				// nop
-		//	}
-		//}
-		// System.out.println("I2Plistener: Waiting for children");
-		// while (Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
-		//	try {
-		//		Thread.sleep(100); //sleep for 100 ms (One tenth second)
-		//	} catch (Exception e) {
-				// nop
-		//	}
-		//}
-
 	// System.out.println("I2Plistener: Done.");
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
index 0984823b6e631ee19f8ff5a73e185fa3cdae808d..ad5e2701b494bbe9486bbc6b1b6731903a1110a7 100644
--- a/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
+++ b/apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
@@ -23,7 +23,6 @@
  */
 package net.i2p.BOB;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
diff --git a/apps/BOB/src/net/i2p/BOB/MUXlisten.java b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
index dc30c5445d0d31ca3e668e5b1cf14a491f21b112..2c7a64924725f590defeb6768ac0a77db6902458 100644
--- a/apps/BOB/src/net/i2p/BOB/MUXlisten.java
+++ b/apps/BOB/src/net/i2p/BOB/MUXlisten.java
@@ -133,7 +133,7 @@ public class MUXlisten implements Runnable {
 	 */
 	public void run() {
 		I2PServerSocket SS = null;
-		int ticks = 1200; // Allow 120 seconds, no more.
+		int ticks = 100; // Allow 10 seconds, no more.
 		try {
 			wlock();
 			try {
@@ -152,11 +152,11 @@ public class MUXlisten implements Runnable {
 		}
 //		socketManager.addDisconnectListener(new DisconnectListener());
 
-quit:
+		quit:
 		{
 			try {
 				tg = new ThreadGroup(N);
-die:
+				die:
 				{
 					// toss the connections to a new threads.
 					// will wrap with TCP and UDP when UDP works
@@ -255,7 +255,7 @@ die:
 					try {
 						session.destroySession();
 					} catch (I2PSessionException ex) {
-					// nop
+						// nop
 					}
 				}
 				try {
@@ -266,8 +266,8 @@ die:
 				// Wait for child threads and thread groups to die
 				// System.out.println("MUXlisten: waiting for children");
 				if (tg.activeCount() + tg.activeGroupCount() != 0) {
+					tg.interrupt(); // give my stuff a small smack.
 					while ((tg.activeCount() + tg.activeGroupCount() != 0) && ticks != 0) {
-						tg.interrupt(); // unwedge any blocking threads.
 						ticks--;
 						try {
 							Thread.sleep(100); //sleep for 100 ms (One tenth second)
@@ -288,8 +288,24 @@ die:
 			}
 		} // quit
 
-		// This is here to catch when something fucks up REALLY bad.
+		// zero out everything.
+		try {
+			wlock();
+			try {
+				info.add("STARTING", new Boolean(false));
+				info.add("STOPPING", new Boolean(false));
+				info.add("RUNNING", new Boolean(false));
+			} catch (Exception e) {
+				wunlock();
+				return;
+			}
+			wunlock();
+		} catch (Exception e) {
+		}
+
+		// This is here to catch when something fucks up REALLY bad, like those annoying stuck threads!
 		if (tg != null) {
+			tg.interrupt(); // give my stuff a small smack again.
 			if (SS != null) {
 				try {
 					SS.close();
@@ -303,63 +319,85 @@ die:
 				} catch (IOException e) {
 				}
 			}
+			I2PSession session = socketManager.getSession();
+			if (session != null) {
+				// System.out.println("I2Plistener: destroySession");
+				try {
+					session.destroySession();
+				} catch (I2PSessionException ex) {
+					// nop
+					}
+			}
 			try {
 				socketManager.destroySocketManager();
 			} catch (Exception e) {
 				// nop
 			}
-			ticks = 600; // 60 seconds
+			// ticks = 100; // 10 seconds
 			if (tg.activeCount() + tg.activeGroupCount() != 0) {
-				while ((tg.activeCount() + tg.activeGroupCount() != 0) && ticks != 0) {
-					tg.interrupt(); // unwedge any blocking threads.
-					ticks--;
+				int foo = tg.activeCount() + tg.activeGroupCount();
+				int bar = foo;
+				String boner = tg.getName();
+				System.out.println("BOB: MUXlisten: Waiting on threads for " + boner);
+				System.out.println("\n\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner);
+				visit(tg, 0, boner);
+				System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n\n");
+				// Happily spin forever :-(
+				while ((tg.activeCount() + tg.activeGroupCount() != 0)) {
+					foo = tg.activeCount() + tg.activeGroupCount();
+					if (foo != bar) {
+						System.out.println("\n\nBOB: MUXlisten: ThreadGroup dump BEGIN " + boner);
+						visit(tg, 0, boner);
+						System.out.println("BOB: MUXlisten: ThreadGroup dump END " + boner + "\n\n");
+					}
+					bar = foo;
+					try {
+						session = socketManager.getSession();
+						if (session != null) {
+							// System.out.println("I2Plistener: destroySession");
+							try {
+								session.destroySession();
+							} catch (I2PSessionException ex) {
+								// nop
+							}
+						}
+						try {
+							socketManager.destroySocketManager();
+						} catch (Exception e) {
+							// nop
+						}
+					} catch (Exception e) {
+						// nop
+					}
+					// tg.interrupt(); // unwedge any blocking threads.
+					// ticks--;
 					try {
 						Thread.sleep(100); //sleep for 100 ms (One tenth second)
 					} catch (InterruptedException ex) {
 						// nop
 					}
 				}
+				System.out.println("BOB: MUXlisten: Threads went away. Success: " + boner);
 			}
-			if (tg.activeCount() + tg.activeGroupCount() == 0) {
-				tg.destroy();
-				// Zap reference to the ThreadGroup so the JVM can GC it.
-				tg = null;
-			} else {
-				System.out.println("BOB: MUXlisten: Can't kill threads. Please send the following dump to sponge@mail.i2p");
-				System.out.println("\n\nBOB: MUXlisten: ThreadGroup dump BEGIN");
-				visit(tg, 0);
-				System.out.println("BOB: MUXlisten: ThreadGroup dump END\n\n");
-			}
+//			if (tg.activeCount() + tg.activeGroupCount() == 0) {
+			tg.destroy();
+			// Zap reference to the ThreadGroup so the JVM can GC it.
+			tg = null;
+//			} else {
+//				System.out.println("BOB: MUXlisten: Forcibly killing threads.");
+//				System.out.println("\n\nBOB: MUXlisten: ThreadGroup dump BEGIN");
+//				visit(tg, 0);
+//				System.out.println("BOB: MUXlisten: ThreadGroup dump END\n\n");
+//				nuke(tg,0);
+//				tg.destroy();
+//				// Zap reference to the ThreadGroup so the JVM can GC it.
+//				tg = null;
+//			}
 		}
-
-		// This is here to catch when something fucks up REALLY bad.
-//		if (tg != null) {
-//			System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
-//			System.out.println("BOB: MUXlisten: Please email the following dump to sponge@mail.i2p");
-//			WrapperManager.requestThreadDump();
-//			System.out.println("BOB: MUXlisten: Something fucked up REALLY bad!");
-//			System.out.println("BOB: MUXlisten: Please email the above dump to sponge@mail.i2p");
-//		}
-		// zero out everything.
-		try {
-			wlock();
-			try {
-				info.add("STARTING", new Boolean(false));
-				info.add("STOPPING", new Boolean(false));
-				info.add("RUNNING", new Boolean(false));
-			} catch (Exception e) {
-				wunlock();
-				return;
-			}
-			wunlock();
-		} catch (Exception e) {
-		}
-
 	}
 
 
 	// Debugging...
-
 	/** 
 	 *	Find the root thread group and print them all.
 	 *
@@ -371,7 +409,7 @@ die:
 		}
 
 		// Visit each thread group
-		visit(root, 0);
+		visit(root, 0, root.getName());
 	}
 
 	/**
@@ -379,7 +417,7 @@ die:
 	 * @param group ThreadGroup to visit
 	 * @param level Current level
 	 */
-	private static void visit(ThreadGroup group, int level) {
+	private static void visit(ThreadGroup group, int level, String tn) {
 		// Get threads in `group'
 		int numThreads = group.activeCount();
 		Thread[] threads = new Thread[numThreads * 2];
@@ -389,7 +427,36 @@ die:
 		for (int i = 0; i < numThreads; i++) {
 			// Get thread
 			Thread thread = threads[i];
-			System.out.println("BOB: MUXlisten: " + indent + thread.toString());
+			System.out.println("BOB: MUXlisten: " + tn + ": " + indent + thread.toString());
+		}
+
+		// Get thread subgroups of `group'
+		int numGroups = group.activeGroupCount();
+		ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
+		numGroups = group.enumerate(groups, false);
+
+		// Recursively visit each subgroup
+		for (int i = 0; i < numGroups; i++) {
+			visit(groups[i], level + 1, groups[i].getName());
+		}
+	}
+
+	private static void nuke(ThreadGroup group, int level) {
+		// Get threads in `group'
+		int numThreads = group.activeCount();
+		Thread[] threads = new Thread[numThreads * 2];
+		numThreads = group.enumerate(threads, false);
+		// Enumerate each thread in `group' and stop it.
+		for (int i = 0; i < numThreads; i++) {
+			// Get thread
+			Thread thread = threads[i];
+			try {
+				if (thread.isAlive()) {
+					thread.stop();
+				}
+			} catch (SecurityException se) {
+				//nop
+			}
 		}
 
 		// Get thread subgroups of `group'
@@ -399,7 +466,14 @@ die:
 
 		// Recursively visit each subgroup
 		for (int i = 0; i < numGroups; i++) {
-			visit(groups[i], level + 1);
+			nuke(groups[i], level + 1);
+		}
+		try {
+			group.destroy();
+		} catch (IllegalThreadStateException IE) {
+			//nop
+		} catch (SecurityException se) {
+			//nop
 		}
 	}
 }
diff --git a/apps/BOB/src/net/i2p/BOB/TCPio.java b/apps/BOB/src/net/i2p/BOB/TCPio.java
index d4b353c5498053592e88e8531c919ae2123d24bd..d92a5cef07f8d0025f184a7f0c9d125e8e56fe80 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPio.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPio.java
@@ -87,23 +87,13 @@ public class TCPio implements Runnable {
 		boolean spin = true;
 		try {
 			while(spin) {
-				// database.getReadLock();
-				// info.getReadLock();
-				// spin = info.get("RUNNING").equals(Boolean.TRUE);
-				// info.releaseReadLock();
-				// database.releaseReadLock();
 				b = Ain.read(a, 0, 1);
-				// System.out.println(info.get("NICKNAME").toString() + " " + b);
 				if(b > 0) {
 					Aout.write(a, 0, b);
 				} else if(b == 0) {
 					Thread.yield(); // this should act like a mini sleep.
 					if(Ain.available() == 0) {
-//						try {
-							// Thread.yield();
 							Thread.sleep(10);
-//						} catch(InterruptedException ex) {
-//						}
 					}
 				} else {
 					/* according to the specs:
@@ -119,19 +109,16 @@ public class TCPio implements Runnable {
 					return;
 				}
 			}
-			// System.out.println("TCPio: RUNNING = false");
 		} catch(Exception e) {
 			// Eject!!! Eject!!!
 			//System.out.println("TCPio: Caught an exception " + e);
 			try {
 				Ain.close();
 			} catch (IOException ex) {
-//				Logger.getLogger(TCPio.class.getName()).log(Level.SEVERE, null, ex);
 			}
 			try {
 				Aout.close();
 			} catch (IOException ex) {
-//				Logger.getLogger(TCPio.class.getName()).log(Level.SEVERE, null, ex);
 			}
 			return;
 		}
diff --git a/apps/BOB/src/net/i2p/BOB/TCPlistener.java b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
index 78155eb787057f4e00e4e1beeed61fc57b9218e7..4ce888090bc6d0dc84a1815f417a9d060945c323 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPlistener.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPlistener.java
@@ -73,16 +73,6 @@ public class TCPlistener implements Runnable {
 		info.releaseReadLock();
 	}
 
-	private void wlock() throws Exception {
-		database.getWriteLock();
-		info.getWriteLock();
-	}
-
-	private void wunlock() throws Exception {
-		info.releaseWriteLock();
-		database.releaseWriteLock();
-	}
-
 	/**
 	 * Simply listen on TCP port, and thread connections
 	 *
@@ -116,7 +106,7 @@ die:		{
 			}
 			try {
 				Socket server = new Socket();
-				listener.setSoTimeout(50); // Half of the expected time from MUXlisten
+				listener.setSoTimeout(50); // We don't block, we cycle and check.
 				while (spin) {
 					try {
 						rlock();
@@ -141,73 +131,20 @@ die:		{
 					}
 					if (g) {
 						// toss the connection to a new thread.
-						TCPtoI2P conn_c = new TCPtoI2P(socketManager, server /* , info, database */);
+						TCPtoI2P conn_c = new TCPtoI2P(socketManager, server);
 						Thread t = new Thread(conn_c, "BOBTCPtoI2P");
 						t.start();
 						g = false;
 					}
 				}
-				//System.out.println("TCPlistener: destroySession");
 				listener.close();
 			} catch (IOException ioe) {
 				try {
 					listener.close();
 				} catch (IOException e) {
 				}
-				// Fatal failure, cause a stop event
-				try {
-					rlock();
-					try {
-						spin = info.get("RUNNING").equals(Boolean.TRUE);
-					} catch (Exception e) {
-						runlock();
-						break die;
-					}
-				} catch (Exception e) {
-					break die;
-				}
-				if (spin) {
-					try {
-						wlock();
-						try {
-							info.add("STOPPING", new Boolean(true));
-							info.add("RUNNING", new Boolean(false));
-						} catch (Exception e) {
-							wunlock();
-							break die;
-						}
-					} catch (Exception e) {
-						break die;
-					}
-					try {
-						wunlock();
-					} catch (Exception e) {
-						break die;
-					}
-				}
 			}
 		}
-		// Previous level does this cleanup now.
-		//
-		// need to kill off the socket manager too.
-		// I2PSession session = socketManager.getSession();
-		// if (session != null) {
-		//	try {
-		//		session.destroySession();
-		//	} catch (I2PSessionException ex) {
-				// nop
-		//	}
-		//}
-		//System.out.println("TCPlistener: Waiting for children");
-		//while (Thread.activeCount() > tgwatch) { // wait for all threads in our threadgroup to finish
-		//	try {
-		//		Thread.sleep(100); //sleep for 100 ms (One tenth second)
-		//	} catch (Exception e) {
-		//		// nop
-		//	}
-		//}
 	//System.out.println("TCPlistener: Done.");
 	}
 }
-
-
diff --git a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
index c376e16fe1bf8af9dd1e8f5ac25cd5d3a745936c..5fefae017852e2e58e1b98934b48a4845ca316f9 100644
--- a/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
+++ b/apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
@@ -151,13 +151,8 @@ public class TCPtoI2P implements Runnable {
 					t.start();
 					q.start();
 					while(t.isAlive() && q.isAlive()) { // AND is used here to kill off the other thread
-//						try {
 							Thread.sleep(10); //sleep for 10 ms
-//						} catch(InterruptedException e) {
-							// nop
-//						}
 					}
-					// System.out.println("TCPtoI2P: Going away...");
 
 				} catch(I2PException e) {
 					Emsg("ERROR " + e.toString(), out);
@@ -166,7 +161,7 @@ public class TCPtoI2P implements Runnable {
 				} catch(NoRouteToHostException e) {
 					Emsg("ERROR " + e.toString(), out);
 				} catch(InterruptedIOException e) {
-					Emsg("ERROR " + e.toString(), out);
+					// We're breaking away.
 				}
 
 			} catch(Exception e) {
diff --git a/apps/addressbook/build.xml b/apps/addressbook/build.xml
index b28cec2dcc4299e5749d0419cba018cf89494fd5..6332c5bcc6d6345d0c42103bf94faf3a949725ee 100644
--- a/apps/addressbook/build.xml
+++ b/apps/addressbook/build.xml
@@ -6,6 +6,7 @@
 	<property name="dist"  location="dist"/>
 	<property name="jar" value="addressbook.jar"/>
 	<property name="war" value="addressbook.war"/>
+	<property name="javac.compilerargs" value="" />
 	
 	<target name="init">
 		<mkdir dir="${build}"/>
@@ -38,6 +39,7 @@
 	<target name="compile" depends="init, depend">
 		<javac debug="true" deprecation="on" source="1.5" target="1.5" 
                        srcdir="${src}" destdir="${build}">
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../../core/java/build/i2p.jar" />
                 <pathelement location="../jetty/jettylib/javax.servlet.jar" />
diff --git a/apps/desktopgui/src/router/configuration/PeerHelper.java b/apps/desktopgui/src/router/configuration/PeerHelper.java
index f5930970559cbbe4c3c63613678f7f85c2749205..54d71cbc2f621f7662bf943a691a19ed6285c9b9 100644
--- a/apps/desktopgui/src/router/configuration/PeerHelper.java
+++ b/apps/desktopgui/src/router/configuration/PeerHelper.java
@@ -160,6 +160,6 @@ public class PeerHelper {
     public final static String PROP_I2NP_INTERNAL_UDP_PORT = "i2np.udp.internalPort";
     public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
     public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
-    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoip";
-    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
 }
diff --git a/apps/i2psnark/java/build.xml b/apps/i2psnark/java/build.xml
index 9689fda06823a17019d39cd05c3488b19a433d70..d70233ac6e9404d541addc9993c7a86d6fca7491 100644
--- a/apps/i2psnark/java/build.xml
+++ b/apps/i2psnark/java/build.xml
@@ -24,6 +24,7 @@
             </classpath>
         </depend>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="depend">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -31,7 +32,9 @@
             srcdir="./src" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../jetty/jettylib/org.mortbay.jetty.jar:../../jetty/jettylib/javax.servlet.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="builddep, compile">
         <jar destfile="./build/i2psnark.jar" basedir="./build/obj" includes="**/*.class" excludes="**/*Servlet.class">
@@ -55,6 +58,7 @@
     <target name="standalone_prep" depends="war">
         <javac debug="true" deprecation="on" source="1.5" target="1.5" 
                destdir="./build" srcdir="src/" includes="org/klomp/snark/web/RunStandalone.java" >
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../../jetty/jettylib/commons-logging.jar" />
                 <pathelement location="../../jetty/jettylib/commons-el.jar" />
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerMonitorTask.java b/apps/i2psnark/java/src/org/klomp/snark/PeerMonitorTask.java
index 229391cd71f804c394ac3047515f9428c72c2db6..3ee8932f61f1653d5ba9dc3143fdd5c833001ee7 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerMonitorTask.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerMonitorTask.java
@@ -23,6 +23,8 @@ package org.klomp.snark;
 import java.util.Iterator;
 import java.util.TimerTask;
 
+import net.i2p.data.DataHelper;
+
 /**
  * TimerTask that monitors the peers and total up/download speeds.
  * Works together with the main Snark class to report periodical statistics.
@@ -83,21 +85,12 @@ class PeerMonitorTask extends TimerTask
 
     // Print some statistics
     long downloaded = coordinator.getDownloaded();
-    String totalDown;
-    if (downloaded >= 10 * 1024 * 1024)
-      totalDown = (downloaded / (1024 * 1024)) + "MB";
-    else
-      totalDown = (downloaded / 1024 )+ "KB";
+    String totalDown = DataHelper.formatSize(downloaded) + "B";
     long uploaded = coordinator.getUploaded();
-    String totalUp;
-    if (uploaded >= 10 * 1024 * 1024)
-      totalUp = (uploaded / (1024 * 1024)) + "MB";
-    else
-      totalUp = (uploaded / 1024) + "KB";
+    String totalUp = DataHelper.formatSize(uploaded) + "B";
     
     int needP = coordinator.storage.needed();
-    long needMB
-      = needP * coordinator.metainfo.getPieceLength(0) / (1024 * 1024);
+    long needMB = needP * coordinator.metainfo.getPieceLength(0) / (1024 * 1024);
     int totalP = coordinator.metainfo.getPieces();
     long totalMB = coordinator.metainfo.getTotalLength() / (1024 * 1024);
     
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
index 054b58262b3ed013db52214738a6f76766cad0e5..378cd8758c94dbed20080b2fc6e2fd40c1ffe4b0 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
@@ -60,9 +60,9 @@ class PeerState
   // If we have te resend outstanding requests (true after we got choked).
   private boolean resend = false;
 
-  private final static int MAX_PIPELINE = 3;               // this is for outbound requests
+  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 = 32*1024; // Snark was 16K, i2p-bt uses 64KB
+  public final static int PARTSIZE = 16*1024; // outbound request
   private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this
 
   PeerState(Peer peer, PeerListener listener, MetaInfo metainfo,
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index 60c44f3d5976fa2a9fa9d805757d8e2e42261be6..a45bf5161a76958a28a9834a456e1a068de35dc1 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -362,7 +362,7 @@ public class SnarkManager implements Snark.CompleteListener {
     public Properties getConfig() { return _config; }
     
     /** hardcoded for sanity.  perhaps this should be customizable, for people who increase their ulimit, etc. */
-    private static final int MAX_FILES_PER_TORRENT = 256;
+    private static final int MAX_FILES_PER_TORRENT = 512;
     
     /** set of filenames that we are dealing with */
     public Set listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } }
@@ -543,16 +543,18 @@ public class SnarkManager implements Snark.CompleteListener {
             return "Too many files in " + info.getName() + " (" + files.size() + "), deleting it";
         } else if (info.getPieces() <= 0) {
             return "No pieces in " + info.getName() + "?  deleting it";
-        } else if (info.getPieceLength(0) > 1*1024*1024) {
-            return "Pieces are too large in " + info.getName() + " (" + info.getPieceLength(0)/1024 + "KB), deleting it";
-        } else if (info.getTotalLength() > 10*1024*1024*1024l) {
+        } else if (info.getPieceLength(0) > Storage.MAX_PIECE_SIZE) {
+            return "Pieces are too large in " + info.getName() + " (" + DataHelper.formatSize(info.getPieceLength(0)) +
+                   "B), deleting it";
+        } else if (info.getTotalLength() > Storage.MAX_TOTAL_SIZE) {
             System.out.println("torrent info: " + info.toString());
             List lengths = info.getLengths();
             if (lengths != null)
                 for (int i = 0; i < lengths.size(); i++)
                     System.out.println("File " + i + " is " + lengths.get(i) + " long");
             
-            return "Torrents larger than 10GB are not supported yet (because we're paranoid): " + info.getName() + ", deleting it";
+            return "Torrents larger than " + DataHelper.formatSize(Storage.MAX_TOTAL_SIZE) +
+                   "B are not supported yet (because we're paranoid): " + info.getName() + ", deleting it";
         } else {
             // ok
             return null;
@@ -637,8 +639,7 @@ public class SnarkManager implements Snark.CompleteListener {
     public void torrentComplete(Snark snark) {
         File f = new File(snark.torrent);
         long len = snark.meta.getTotalLength();
-        addMessage("Download complete of " + f.getName() 
-                   + (len < 5*1024*1024 ? " (size: " + (len/1024) + "KB)" : " (size: " + (len/(1024*1024l)) + "MB)"));
+        addMessage("Download complete of " + f.getName() + " (size: " + DataHelper.formatSize(len) + "B)");
         updateStatus(snark);
     }
     
@@ -694,6 +695,7 @@ public class SnarkManager implements Snark.CompleteListener {
 //       , "mastertracker", "http://VzXD~stRKbL3MOmeTn1iaCQ0CFyTmuFHiKYyo0Rd~dFPZFCYH-22rT8JD7i-C2xzYFa4jT5U2aqHzHI-Jre4HL3Ri5hFtZrLk2ax3ji7Qfb6qPnuYkuiF2E2UDmKUOppI8d9Ye7tjdhQVCy0izn55tBaB-U7UWdcvSK2i85sauyw3G0Gfads1Rvy5-CAe2paqyYATcDmGjpUNLoxbfv9KH1KmwRTNH6k1v4PyWYYnhbT39WfKMbBjSxVQRdi19cyJrULSWhjxaQfJHeWx5Z8Ev4bSPByBeQBFl2~4vqy0S5RypINsRSa3MZdbiAAyn5tr5slWR6QdoqY3qBQgBJFZppy-3iWkFqqKgSxCPundF8gdDLC5ddizl~KYcYKl42y9SGFHIukH-TZs8~em0~iahzsqWVRks3zRG~tlBcX2U3M2~OJs~C33-NKhyfZT7-XFBREvb8Szmd~p66jDxrwOnKaku-G6DyoQipJqIz4VHmY9-y5T8RrUcJcM-5lVoMpAAAA.i2p/announce.php=http://tracker.mastertracker.i2p/"
 //       , "Galen", "http://5jpwQMI5FT303YwKa5Rd38PYSX04pbIKgTaKQsWbqoWjIfoancFdWCShXHLI5G5ofOb0Xu11vl2VEMyPsg1jUFYSVnu4-VfMe3y4TKTR6DTpetWrnmEK6m2UXh91J5DZJAKlgmO7UdsFlBkQfR2rY853-DfbJtQIFl91tbsmjcA5CGQi4VxMFyIkBzv-pCsuLQiZqOwWasTlnzey8GcDAPG1LDcvfflGV~6F5no9mnuisZPteZKlrv~~TDoXTj74QjByWc4EOYlwqK8sbU9aOvz~s31XzErbPTfwiawiaZ0RUI-IDrKgyvmj0neuFTWgjRGVTH8bz7cBZIc3viy6ioD-eMQOrXaQL0TCWZUelRwHRvgdPiQrxdYQs7ixkajeHzxi-Pq0EMm5Vbh3j3Q9kfUFW3JjFDA-MLB4g6XnjCbM5J1rC0oOBDCIEfhQkszru5cyLjHiZ5yeA0VThgu~c7xKHybv~OMXION7V8pBKOgET7ZgAkw1xgYe3Kkyq5syAAAA.i2p/tr/announce.php=http://galen.i2p/tr/"
        , "crstrack", "http://b4G9sCdtfvccMAXh~SaZrPqVQNyGQbhbYMbw6supq2XGzbjU4NcOmjFI0vxQ8w1L05twmkOvg5QERcX6Mi8NQrWnR0stLExu2LucUXg1aYjnggxIR8TIOGygZVIMV3STKH4UQXD--wz0BUrqaLxPhrm2Eh9Hwc8TdB6Na4ShQUq5Xm8D4elzNUVdpM~RtChEyJWuQvoGAHY3ppX-EJJLkiSr1t77neS4Lc-KofMVmgI9a2tSSpNAagBiNI6Ak9L1T0F9uxeDfEG9bBSQPNMOSUbAoEcNxtt7xOW~cNOAyMyGydwPMnrQ5kIYPY8Pd3XudEko970vE0D6gO19yoBMJpKx6Dh50DGgybLQ9CpRaynh2zPULTHxm8rneOGRcQo8D3mE7FQ92m54~SvfjXjD2TwAVGI~ae~n9HDxt8uxOecAAvjjJ3TD4XM63Q9TmB38RmGNzNLDBQMEmJFpqQU8YeuhnS54IVdUoVQFqui5SfDeLXlSkh4vYoMU66pvBfWbAAAA.i2p/tracker/announce.php=http://crstrack.i2p/tracker/"
+       , "Postman2", "http://lnQ6yoBTxQuQU8EQ1FlF395ITIQF-HGJxUeFvzETLFnoczNjQvKDbtSB7aHhn853zjVXrJBgwlB9sO57KakBDaJ50lUZgVPhjlI19TgJ-CxyHhHSCeKx5JzURdEW-ucdONMynr-b2zwhsx8VQCJwCEkARvt21YkOyQDaB9IdV8aTAmP~PUJQxRwceaTMn96FcVenwdXqleE16fI8CVFOV18jbJKrhTOYpTtcZKV4l1wNYBDwKgwPx5c0kcrRzFyw5~bjuAKO~GJ5dR7BQsL7AwBoQUS4k1lwoYrG1kOIBeDD3XF8BWb6K3GOOoyjc1umYKpur3G~FxBuqtHAsDRICkEbKUqJ9mPYQlTSujhNxiRIW-oLwMtvayCFci99oX8MvazPS7~97x0Gsm-onEK1Td9nBdmq30OqDxpRtXBimbzkLbR1IKObbg9HvrKs3L-kSyGwTUmHG9rSQSoZEvFMA-S0EXO~o4g21q1oikmxPMhkeVwQ22VHB0-LZJfmLr4SAAAA.i2p/announce.php=http://tracker2.postman.i2p/"
     };
     
     /** comma delimited list of name=announceURL=baseURL for the trackers to be displayed */
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
index 69e5a198f56a5117b800c44200554b9937f60811..51b4f97ebb67776c353070279af35ab057418368 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
@@ -56,10 +56,11 @@ public class Storage
   boolean changed;
 
   /** The default piece size. */
-  private static int MIN_PIECE_SIZE = 256*1024;
-  private static int MAX_PIECE_SIZE = 1024*1024;
+  private static final int MIN_PIECE_SIZE = 256*1024;
+  public static final int MAX_PIECE_SIZE = 1024*1024;
   /** The maximum number of pieces in a torrent. */
-  private static long MAX_PIECES = 100*1024/20;
+  public static final int MAX_PIECES = 10*1024;
+  public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
 
   /**
    * Creates a new storage based on the supplied MetaInfo.  This will
diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
index 52e10957396f18211db30057d87dd886b2d3361d..515594219e378dcd246b52d4415c76b254d5e5ba 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java
@@ -502,9 +502,10 @@ public class I2PSnarkServlet extends HttpServlet {
         out.write(filename);
         if (remaining == 0)
             out.write("</a>");
-        // temporarily hardcoded for postman and anonymity, requires bytemonsoon patch for lookup by info_hash
+        // temporarily hardcoded for postman* and anonymity, requires bytemonsoon patch for lookup by info_hash
         String announce = snark.meta.getAnnounce();
-        if (announce.startsWith("http://YRgrgTLG") || announce.startsWith("http://8EoJZIKr")) {
+        if (announce.startsWith("http://YRgrgTLG") || announce.startsWith("http://8EoJZIKr") ||
+            announce.startsWith("http://lnQ6yoBT")) {
             Map trackers = _manager.getTrackers();
             for (Iterator iter = trackers.entrySet().iterator(); iter.hasNext(); ) {
                 Map.Entry entry = (Map.Entry)iter.next();
diff --git a/apps/i2ptunnel/java/build.xml b/apps/i2ptunnel/java/build.xml
index 564f6fc4b85b3e75aad91dcf58ec90e882e88291..5ab73d5e2f5bca78f7aca2b519f71e3b3a88e06a 100644
--- a/apps/i2ptunnel/java/build.xml
+++ b/apps/i2ptunnel/java/build.xml
@@ -22,6 +22,7 @@
             </classpath>
         </depend>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="depend">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -29,7 +30,9 @@
             srcdir="./src" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="builddep, compile">
         <jar destfile="./build/i2ptunnel.jar" basedir="./build/obj" includes="**/*.class">
@@ -73,6 +76,7 @@
         </java>
         <javac debug="true" deprecation="on" source="1.5" target="1.5" 
                destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
                 <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
index d781866d95f4edf8d94138d3990006f470c40d82..9e102aa9858b5ac265cdd3d4aba8a7ffe5aca8e1 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java
@@ -122,7 +122,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna
         }
 
         if (wwwProxy != null) {
-            StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
+            StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
             while (tok.hasMoreTokens())
                 _proxyList.add(tok.nextToken().trim());
         }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
index 277328363ee5d4d6bffccf0750a3e6b4a0fbdd10..53c3136b20f86a6d5c5a6053ffe2350859ff7f6b 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
@@ -152,7 +152,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
         }
 
         if (wwwProxy != null) {
-            StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
+            StringTokenizer tok = new StringTokenizer(wwwProxy, ", ");
             while (tok.hasMoreTokens())
                 proxyList.add(tok.nextToken().trim());
         }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
index 8b62702198213b321a3d73bcdc86a321f84dc223..3b5bff785d08377ee6a326e96a4f5d3a0f4c2049 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java
@@ -46,7 +46,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
               notifyThis, 
               "IRCHandler " + (++__clientId), tunnel, pkf);
         
-        StringTokenizer tok = new StringTokenizer(destinations, ",");
+        StringTokenizer tok = new StringTokenizer(destinations, ", ");
         dests = new ArrayList(1);
         while (tok.hasMoreTokens()) {
             String destination = tok.nextToken();
diff --git a/apps/jetty/build.xml b/apps/jetty/build.xml
index 643dd79fdcb77b1c908ef84be030b3b654224e60..dab064c5ec24b3f403bd747d0a1824f4945e7f0d 100644
--- a/apps/jetty/build.xml
+++ b/apps/jetty/build.xml
@@ -6,6 +6,7 @@
     <property name="jetty.url" value="http://dist.codehaus.org/jetty/jetty-5.1.x/jetty-5.1.12.zip" />
     <property name="jetty.filename" value="jetty-5.1.12.zip" />
     <property name="verified.filename" value="verified.txt" />
+    <property name="javac.compilerargs" value="" />
 
     <target name="all" depends="build" />
 
@@ -90,9 +91,11 @@
         <mkdir dir="./build/obj" />
         <javac 
             srcdir="./java/src" 
-            debug="true" deprecation="on" source="1.5" target="1.5" 
+            debug="true" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="./jettylib/commons-logging.jar:./jettylib/javax.servlet.jar:./jettylib/org.mortbay.jetty.jar" />
+            classpath="./jettylib/commons-logging.jar:./jettylib/javax.servlet.jar:./jettylib/org.mortbay.jetty.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile">
         <jar destfile="./jettylib/org.mortbay.jetty.jar" basedir="./build/obj" includes="**/*.class" update="true" >
diff --git a/apps/jetty/java/src/org/mortbay/http/HttpContext.java b/apps/jetty/java/src/org/mortbay/http/HttpContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..061d3ad17f971f853ebc3216bef59952bbcbc329
--- /dev/null
+++ b/apps/jetty/java/src/org/mortbay/http/HttpContext.java
@@ -0,0 +1,2198 @@
+// ========================================================================
+// $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $
+// Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at 
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+package org.mortbay.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.UnknownHostException;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.commons.logging.Log;
+import org.mortbay.log.LogFactory;
+import org.mortbay.http.ResourceCache.ResourceMetaData;
+import org.mortbay.http.handler.ErrorPageHandler;
+import org.mortbay.util.Container;
+import org.mortbay.util.EventProvider;
+import org.mortbay.util.IO;
+import org.mortbay.util.LazyList;
+import org.mortbay.util.LifeCycle;
+import org.mortbay.util.LogSupport;
+import org.mortbay.util.MultiException;
+import org.mortbay.util.Resource;
+import org.mortbay.util.URI;
+
+
+/* ------------------------------------------------------------ */
+/** Context for a collection of HttpHandlers.
+ * HTTP Context provides an ordered container for HttpHandlers
+ * that share the same path prefix, filebase, resourcebase and/or
+ * classpath.
+ * <p>
+ * A HttpContext is analagous to a ServletContext in the
+ * Servlet API, except that it may contain other types of handler
+ * other than servlets.
+ * <p>
+ * A ClassLoader is created for the context and it uses
+ * Thread.currentThread().getContextClassLoader(); as it's parent loader.
+ * The class loader is initialized during start(), when a derived
+ * context calls initClassLoader() or on the first call to loadClass()
+ * <p>
+ *
+ * <B>Note. that order is important when configuring a HttpContext.
+ * For example, if resource serving is enabled before servlets, then resources
+ * take priority.</B>
+ *
+ * @see HttpServer
+ * @see HttpHandler
+ * @see org.mortbay.jetty.servlet.ServletHttpContext
+ * @version $Id: HttpContext.java,v 1.136 2006/02/21 09:47:43 gregwilkins Exp $
+ * @author Greg Wilkins (gregw)
+ */
+public class HttpContext extends Container
+                         implements LifeCycle,
+                                    HttpHandler,
+                                    EventProvider,
+                                    Serializable
+{
+    private static Log log = LogFactory.getLog(HttpContext.class);
+
+    /* ------------------------------------------------------------ */
+    /** File class path attribute.
+     * If this name is set as a context init parameter, then the attribute
+     * name given will be used to set the file classpath for the context as a
+     * context attribute.
+     */
+    public final static String __fileClassPathAttr=
+        "org.mortbay.http.HttpContext.FileClassPathAttribute";
+
+    public final static String __ErrorHandler=
+        "org.mortbay.http.ErrorHandler";
+
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    // These attributes are serialized by WebApplicationContext, which needs
+    // to be updated if you add to these
+    private String _contextPath;
+    private List _vhosts=new ArrayList(2);
+    private List _hosts=new ArrayList(2);
+    private List _handlers=new ArrayList(3);
+    private Map _attributes = new HashMap(3);
+    private boolean _redirectNullPath=true;
+    private boolean _statsOn=false;
+    private PermissionCollection _permissions;
+    private boolean _classLoaderJava2Compliant=true;
+    private ResourceCache _resources;
+    private String[] _systemClasses=new String [] {"java.","javax.servlet.","javax.xml.","org.mortbay.","org.xml.","org.w3c.","org.apache.commons.logging."};
+    private String[] _serverClasses = new String[] {"-org.mortbay.http.PathMap","-org.mortbay.jetty.servlet.Invoker","-org.mortbay.jetty.servlet.JSR154Filter","-org.mortbay.jetty.servlet.Default","org.mortbay.jetty.Server","org.mortbay.http.","org.mortbay.start.","org.mortbay.stop."};
+  
+    /* ------------------------------------------------------------ */
+    private String _contextName;
+    private String _classPath;
+    private Map _initParams = new HashMap(11);
+    private UserRealm _userRealm;
+    private String _realmName;
+    private PathMap _constraintMap=new PathMap();
+    private Authenticator _authenticator;
+    private RequestLog _requestLog;
+
+
+    private String[] _welcomes=
+    {
+        "welcome.html",
+        "index.html",
+        "index.htm",
+        "index.jsp"
+    };
+
+
+    /* ------------------------------------------------------------ */
+    private transient boolean _gracefulStop;
+    private transient ClassLoader _parent;
+    private transient ClassLoader _loader;
+    private transient HttpServer _httpServer;
+    private transient File _tmpDir;
+    private transient HttpHandler[] _handlersArray;
+    private transient String[] _vhostsArray;
+
+
+    /* ------------------------------------------------------------ */
+    transient Object _statsLock=new Object[0];
+    transient long _statsStartedAt;
+    transient int _requests;
+    transient int _requestsActive;
+    transient int _requestsActiveMax;
+    transient int _responses1xx; // Informal
+    transient int _responses2xx; // Success
+    transient int _responses3xx; // Redirection
+    transient int _responses4xx; // Client Error
+    transient int _responses5xx; // Server Error
+
+
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     */
+    public HttpContext()
+    {
+        setAttribute(__ErrorHandler,  new ErrorPageHandler()); 
+        _resources=new ResourceCache();
+        addComponent(_resources);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     * @param httpServer
+     * @param contextPathSpec
+     */
+    public HttpContext(HttpServer httpServer,String contextPathSpec)
+    {
+        this();
+        setHttpServer(httpServer);
+        setContextPath(contextPathSpec);
+    }
+
+    /* ------------------------------------------------------------ */
+    private void readObject(java.io.ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+        _statsLock=new Object[0];
+        getHandlers();
+        for (int i=0;i<_handlersArray.length;i++)
+            _handlersArray[i].initialize(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the ThreadLocal HttpConnection.
+     * Get the HttpConnection for current thread, if any.  This method is
+     * not static in order to control access.
+     * @return HttpConnection for this thread.
+     */
+    public HttpConnection getHttpConnection()
+    {
+        return HttpConnection.getHttpConnection();
+    }
+
+    /* ------------------------------------------------------------ */
+    void setHttpServer(HttpServer httpServer)
+    {
+        _httpServer=httpServer;
+        _contextName=null;
+      
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpServer getHttpServer()
+    {
+        return _httpServer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setStopGracefully(boolean graceful)
+    {
+	_gracefulStop=graceful;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getStopGracefully()
+    {
+	return _gracefulStop;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static String canonicalContextPathSpec(String contextPathSpec)
+    {
+        // check context path
+        if (contextPathSpec==null ||
+            contextPathSpec.indexOf(',')>=0 ||
+            contextPathSpec.startsWith("*"))
+            throw new IllegalArgumentException ("Illegal context spec:"+contextPathSpec);
+
+        if(!contextPathSpec.startsWith("/"))
+	    contextPathSpec='/'+contextPathSpec;
+
+        if (contextPathSpec.length()>1)
+        {
+            if (contextPathSpec.endsWith("/"))
+                contextPathSpec+="*";
+            else if (!contextPathSpec.endsWith("/*"))
+                contextPathSpec+="/*";
+        }
+
+        return contextPathSpec;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setContextPath(String contextPathSpec)
+    {
+        if (_httpServer!=null)
+            _httpServer.removeMappings(this);
+
+        contextPathSpec=canonicalContextPathSpec(contextPathSpec);
+
+        if (contextPathSpec.length()>1)
+            _contextPath=contextPathSpec.substring(0,contextPathSpec.length()-2);
+        else
+            _contextPath="/";
+
+        _contextName=null;
+
+        if (_httpServer!=null)
+            _httpServer.addMappings(this);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The context prefix
+     */
+    public String getContextPath()
+    {
+        return _contextPath;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Add a virtual host alias to this context.
+     * @see #setVirtualHosts
+     * @param hostname A hostname. A null host name means any hostname is
+     * acceptable. Host names may String representation of IP addresses.
+     */
+    public void addVirtualHost(String hostname)
+    {
+        // Note that null hosts are also added.
+        if (!_vhosts.contains(hostname))
+        {
+            _vhosts.add(hostname);
+            _contextName=null;
+
+            if (_httpServer!=null)
+            {
+                if (_vhosts.size()==1)
+                    _httpServer.removeMapping(null,this);
+                _httpServer.addMapping(hostname,this);
+            }
+            _vhostsArray=null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** remove a virtual host alias to this context.
+     * @see #setVirtualHosts
+     * @param hostname A hostname. A null host name means any hostname is
+     * acceptable. Host names may String representation of IP addresses.
+     */
+    public void removeVirtualHost(String hostname)
+    {
+        // Note that null hosts are also added.
+        if (_vhosts.remove(hostname))
+        {
+            _contextName=null;
+            if (_httpServer!=null)
+            {
+                _httpServer.removeMapping(hostname,this);
+                if (_vhosts.size()==0)
+                    _httpServer.addMapping(null,this);
+            }
+            _vhostsArray=null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the virtual hosts for the context.
+     * Only requests that have a matching host header or fully qualified
+     * URL will be passed to that context with a virtual host name.
+     * A context with no virtual host names or a null virtual host name is
+     * available to all requests that are not served by a context with a
+     * matching virtual host name.
+     * @param hosts Array of virtual hosts that this context responds to. A
+     * null host name or null/empty array means any hostname is acceptable.
+     * Host names may String representation of IP addresses.
+     */
+    public void setVirtualHosts(String[] hosts)
+    {
+        List old = new ArrayList(_vhosts);
+
+        if (hosts!=null)
+	{
+	    for (int i=0;i<hosts.length;i++)
+	    {
+		boolean existing=old.remove(hosts[i]);
+		if (!existing)
+		    addVirtualHost(hosts[i]);
+	    }
+	}
+
+        for (int i=0;i<old.size();i++)
+            removeVirtualHost((String)old.get(i));
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the virtual hosts for the context.
+     * Only requests that have a matching host header or fully qualified
+     * URL will be passed to that context with a virtual host name.
+     * A context with no virtual host names or a null virtual host name is
+     * available to all requests that are not served by a context with a
+     * matching virtual host name.
+     * @return Array of virtual hosts that this context responds to. A
+     * null host name or empty array means any hostname is acceptable.
+     * Host names may be String representation of IP addresses.
+     */
+    public String[] getVirtualHosts()
+    {
+        if (_vhostsArray!=null)
+            return _vhostsArray;
+        if (_vhosts==null)
+            _vhostsArray=new String[0];
+        else
+        {
+            _vhostsArray=new String[_vhosts.size()];
+            _vhostsArray=(String[])_vhosts.toArray(_vhostsArray);
+        }
+        return _vhostsArray;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Set the hosts for the context.
+     * Set the real hosts that this context will accept requests for.
+     * If not null or empty, then only requests from HttpListeners for hosts
+     * in this array are accepted by this context. 
+     * Unlike virutal hosts, this value is not used by HttpServer for
+     * matching a request to a context.
+     */
+    public void setHosts(String[] hosts)
+        throws UnknownHostException
+    {
+        if (hosts==null || hosts.length==0)
+            _hosts=null;
+        else
+        {
+            _hosts=new ArrayList();
+            for (int i=0;i<hosts.length;i++)
+                if (hosts[i]!=null)
+                    _hosts.add(InetAddress.getByName(hosts[i]));
+        }
+        
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the hosts for the context.
+     */
+    public String[] getHosts()
+    {
+        if (_hosts==null || _hosts.size()==0)
+            return null;
+        String[] hosts=new String[_hosts.size()];
+        for (int i=0;i<hosts.length;i++)
+        {
+            InetAddress a = (InetAddress)_hosts.get(i);
+            if (a!=null)
+                hosts[i]=a.getHostName();
+        }
+        return hosts;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Set system classes.
+     * System classes cannot be overriden by context classloaders.
+     * @param classes array of classname Strings.  Names ending with '.' are treated as package names. Names starting with '-' are treated as
+     * negative matches and must be listed before any enclosing packages.
+     */
+    public void setSystemClasses(String[] classes)
+    {
+        _systemClasses=classes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get system classes.
+     * System classes cannot be overriden by context classloaders.
+     * @return array of classname Strings.  Names ending with '.' are treated as package names. Names starting with '-' are treated as
+     * negative matches and must be listed before any enclosing packages. Null if not set.
+     */
+    public String[] getSystemClasses()
+    {
+        return _systemClasses;
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /** Set system classes.
+     * Servers classes cannot be seen by context classloaders.
+     * @param classes array of classname Strings.  Names ending with '.' are treated as package names. Names starting with '-' are treated as
+     * negative matches and must be listed before any enclosing packages.
+     */
+    public void setServerClasses(String[] classes)
+    {
+        _serverClasses=classes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get system classes.
+     * System classes cannot be seen by context classloaders.
+     * @return array of classname Strings.  Names ending with '.' are treated as package names. Names starting with '-' are treated as
+     * negative matches and must be listed before any enclosing packages. Null if not set.
+     */
+    public String[] getServerClasses()
+    {
+        return _serverClasses;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void setHandlers(HttpHandler[] handlers)
+    {
+        List old = new ArrayList(_handlers);
+
+	if (handlers!=null)
+	{
+	    for (int i=0;i<handlers.length;i++)
+	    {
+		boolean existing=old.remove(handlers[i]);
+		if (!existing)
+		    addHandler(handlers[i]);
+	    }
+	}
+
+        for (int i=0;i<old.size();i++)
+            removeHandler((HttpHandler)old.get(i));
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get all handlers.
+     * @return List of all HttpHandlers
+     */
+    public HttpHandler[] getHandlers()
+    {
+        if (_handlersArray!=null)
+            return _handlersArray;
+        if (_handlers==null)
+            _handlersArray=new HttpHandler[0];
+        else
+        {
+            _handlersArray=new HttpHandler[_handlers.size()];
+            _handlersArray=(HttpHandler[])_handlers.toArray(_handlersArray);
+        }
+        return _handlersArray;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Add a handler.
+     * @param i The position in the handler list
+     * @param handler The handler.
+     */
+    public synchronized void addHandler(int i,HttpHandler handler)
+    {
+        _handlers.add(i,handler);
+        _handlersArray=null;
+
+        HttpContext context = handler.getHttpContext();
+        if (context==null)
+            handler.initialize(this);
+        else if (context!=this)
+            throw new IllegalArgumentException("Handler in another HttpContext");
+        addComponent(handler);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add a HttpHandler to the context.
+     * @param handler
+     */
+    public synchronized void addHandler(HttpHandler handler)
+    {
+        addHandler(_handlers.size(),handler);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get handler index.
+     * @param handler instance
+     * @return Index of handler in context or -1 if not found.
+     */
+    public int getHandlerIndex(HttpHandler handler)
+    {
+        for (int h=0;h<_handlers.size();h++)
+        {
+            if ( handler == _handlers.get(h))
+                return h;
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get a handler by class.
+     * @param handlerClass
+     * @return The first handler that is an instance of the handlerClass
+     */
+    public synchronized HttpHandler getHandler(Class handlerClass)
+    {
+        for (int h=0;h<_handlers.size();h++)
+        {
+            HttpHandler handler = (HttpHandler)_handlers.get(h);
+            if (handlerClass.isInstance(handler))
+                return handler;
+        }
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Remove a handler.
+     * The handler must be stopped before being removed.
+     * @param i index of handler
+     */
+    public synchronized HttpHandler removeHandler(int i)
+    {
+        HttpHandler handler = _handlersArray[i];
+        if (handler.isStarted())
+            try{handler.stop();} catch (InterruptedException e){log.warn(LogSupport.EXCEPTION,e);}
+        _handlers.remove(i);
+        _handlersArray=null;
+        removeComponent(handler);
+        return handler;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Remove a handler.
+     * The handler must be stopped before being removed.
+     */
+    public synchronized void removeHandler(HttpHandler handler)
+    {
+        if (handler.isStarted())
+            try{handler.stop();} catch (InterruptedException e){log.warn(LogSupport.EXCEPTION,e);}
+        _handlers.remove(handler);
+        removeComponent(handler);
+        _handlersArray=null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Set context init parameter.
+     * Init Parameters differ from attributes as they can only
+     * have string values, servlets cannot set them and they do
+     * not have a package scoped name space.
+     * @param param param name
+     * @param value param value or null
+     */
+    public void setInitParameter(String param, String value)
+    {
+        _initParams.put(param,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get context init parameter.
+     * @param param param name
+     * @return param value or null
+     */
+    public String getInitParameter(String param)
+    {
+        return (String)_initParams.get(param);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get context init parameter.
+     * @return Enumeration of names
+     */
+    public Enumeration getInitParameterNames()
+    {
+        return Collections.enumeration(_initParams.keySet());
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set a context attribute.
+     * @param name attribute name
+     * @param value attribute value
+     */
+    public synchronized void setAttribute(String name, Object value)
+    {
+        _attributes.put(name,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param name attribute name
+     * @return attribute value or null
+     */
+    public Object getAttribute(String name)
+    {
+        return _attributes.get(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public Map getAttributes()
+    {
+        return _attributes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void setAttributes(Map attributes)
+    {
+        _attributes=attributes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return enumaration of names.
+     */
+    public Enumeration getAttributeNames()
+    {
+        return Collections.enumeration(_attributes.keySet());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param name attribute name
+     */
+    public synchronized void removeAttribute(String name)
+    {
+        _attributes.remove(name);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void flushCache()
+    {
+        _resources.flushCache();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String[] getWelcomeFiles()
+    {
+        return _welcomes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setWelcomeFiles(String[] welcomes)
+    {
+        if (welcomes==null)
+            _welcomes=new String[0];
+        else
+            _welcomes=welcomes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addWelcomeFile(String welcomeFile)
+    {
+        if (welcomeFile.startsWith("/") ||
+            welcomeFile.startsWith(java.io.File.separator) ||
+            welcomeFile.endsWith("/") ||
+            welcomeFile.endsWith(java.io.File.separator))
+            log.warn("Invalid welcome file: "+welcomeFile);
+        List list = new ArrayList(Arrays.asList(_welcomes));
+        list.add(welcomeFile);
+        _welcomes=(String[])list.toArray(_welcomes);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void removeWelcomeFile(String welcomeFile)
+    {
+        List list = new ArrayList(Arrays.asList(_welcomes));
+        list.remove(welcomeFile);
+        _welcomes=(String[])list.toArray(_welcomes);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getWelcomeFile(Resource resource)
+        throws IOException
+    {
+        if (!resource.isDirectory())
+            return null;
+
+        for (int i=0;i<_welcomes.length;i++)
+        {
+            Resource welcome=resource.addPath(_welcomes[i]);
+            if (welcome.exists())
+                return _welcomes[i];
+        }
+
+        return null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Get the context classpath.
+     * This method only returns the paths that have been set for this
+     * context and does not include any paths from a parent or the
+     * system classloader.
+     * Note that this may not be a legal javac classpath.
+     * @return a comma or ';' separated list of class
+     * resources. These may be jar files, directories or URLs to jars
+     * or directories.
+     * @see #getFileClassPath()
+     */
+    public String getClassPath()
+    {
+        return _classPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the file classpath of the context.
+     * This method makes a best effort to return a complete file
+     * classpath for the context.
+     * It is obtained by walking the classloader hierarchy and looking for
+     * URLClassLoaders.  The system property java.class.path is also checked for
+     * file elements not already found in the loader hierarchy.
+     * @return Path of files and directories for loading classes.
+     * @exception IllegalStateException HttpContext.initClassLoader
+     * has not been called.
+     */
+    public String getFileClassPath()
+        throws IllegalStateException
+    {
+	
+        ClassLoader loader = getClassLoader();
+        if (loader==null)
+            throw new IllegalStateException("Context classloader not initialized");
+            
+        LinkedList paths =new LinkedList();
+        LinkedList loaders=new LinkedList();
+        
+        // Walk the loader hierarchy
+       	while (loader !=null)
+       	{
+            loaders.add(0,loader);
+            loader = loader.getParent();
+       	}
+       	
+       	// Try to handle java2compliant modes
+       	loader=getClassLoader();
+       	if (loader instanceof ContextLoader && !((ContextLoader)loader).isJava2Compliant())
+       	{
+            loaders.remove(loader);
+            loaders.add(0,loader);
+       	}
+        
+        for (int i=0;i<loaders.size();i++)
+        {
+            loader=(ClassLoader)loaders.get(i);
+            
+            if (log.isDebugEnabled()) log.debug("extract paths from "+loader);
+            if (loader instanceof URLClassLoader)
+            {
+                URL[] urls = ((URLClassLoader)loader).getURLs();
+                for (int j=0;urls!=null && j<urls.length;j++)
+                {
+                    try
+                    {
+                        Resource path = Resource.newResource(urls[j]);
+                        if (log.isTraceEnabled()) log.trace("path "+path);
+                        File file = path.getFile();
+                        if (file!=null)
+                            paths.add(file.getAbsolutePath());
+                    }
+                    catch(Exception e)
+                    {
+                        LogSupport.ignore(log,e);
+                    }
+                }	
+            }	
+       	}
+       	
+        // Add the system classpath elements from property.
+        String jcp=System.getProperty("java.class.path");
+        if (jcp!=null)
+        {
+            StringTokenizer tok=new StringTokenizer(jcp,File.pathSeparator);
+            while (tok.hasMoreTokens())
+            {
+                String path=tok.nextToken();
+                if (!paths.contains(path))
+                {
+                    if(log.isTraceEnabled())log.trace("PATH="+path);
+                    paths.add(path);
+                }
+                else
+                    if(log.isTraceEnabled())log.trace("done="+path);			
+            }
+        }
+        
+        StringBuffer buf = new StringBuffer();
+        Iterator iter = paths.iterator();
+        while(iter.hasNext())
+        {
+            if (buf.length()>0)
+                buf.append(File.pathSeparator);
+            buf.append(iter.next().toString());
+        }
+        
+        if (log.isDebugEnabled()) log.debug("fileClassPath="+buf);
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Sets the class path for the context.
+     * A class path is only required for a context if it uses classes
+     * that are not in the system class path.
+     * @param classPath a comma or ';' separated list of class
+     * resources. These may be jar files, directories or URLs to jars
+     * or directories.
+     */
+    public void setClassPath(String classPath)
+    {
+        _classPath=classPath;
+        if (isStarted())
+            log.warn("classpath set while started");
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add the class path element  to the context.
+     * A class path is only required for a context if it uses classes
+     * that are not in the system class path.
+     * @param classPath a comma or ';' separated list of class
+     * resources. These may be jar files, directories or URLs to jars
+     * or directories.
+     */
+    public void addClassPath(String classPath)
+    {
+        if (_classPath==null || _classPath.length()==0)
+            _classPath=classPath;
+        else
+            _classPath+=","+classPath;
+
+        if (isStarted())
+            log.warn("classpath set while started");
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add elements to the class path for the context from the jar and zip files found
+     *  in the specified resource.
+     * @param lib the resource that contains the jar and/or zip files.
+     * @param append true if the classpath entries are to be appended to any
+     * existing classpath, or false if they replace the existing classpath.
+     * @see #setClassPath(String)
+     */
+    public void addClassPaths(Resource lib)
+    {
+        if (isStarted())
+            log.warn("classpaths set while started");
+
+        if (lib.exists() && lib.isDirectory())
+        {
+            String[] files=lib.list();
+            for (int f=0;files!=null && f<files.length;f++)
+            {
+                try {
+                    Resource fn=lib.addPath(files[f]);
+                    String fnlc=fn.getName().toLowerCase();
+                    if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))
+                    {
+                        addClassPath(fn.toString());
+                    }
+                }
+                catch (Exception ex)
+                {
+                    log.warn(LogSupport.EXCEPTION,ex);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get Java2 compliant classloading.
+     * @return If true, the class loader will conform to the java 2
+     * specification and delegate all loads to the parent classloader. If
+     * false, the context classloader only delegate loads for system classes
+     * or classes that it can't find itself.
+     */
+    public boolean isClassLoaderJava2Compliant()
+    {
+        return _classLoaderJava2Compliant;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set Java2 compliant classloading.
+     * @param compliant If true, the class loader will conform to the java 2
+     * specification and delegate all loads to the parent classloader. If
+     * false, the context classloader only delegate loads for system classes
+     * or classes that it can't find itself.
+     */
+    public void setClassLoaderJava2Compliant(boolean compliant)
+    {
+        _classLoaderJava2Compliant = compliant;
+        if (_loader!=null && (_loader instanceof ContextLoader))
+            ((ContextLoader)_loader).setJava2Compliant(compliant);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set temporary directory for context.
+     * The javax.servlet.context.tempdir attribute is also set.
+     * @param dir Writable temporary directory.
+     */
+    public void setTempDirectory(File dir)
+    {
+        if (isStarted())
+            throw new IllegalStateException("Started");
+
+        if (dir!=null)
+        {
+            try{dir=new File(dir.getCanonicalPath());}
+            catch (IOException e){log.warn(LogSupport.EXCEPTION,e);}
+        }
+
+        if (dir!=null && !dir.exists())
+        {
+            dir.mkdir();
+            dir.deleteOnExit();
+        }
+
+        if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
+            throw new IllegalArgumentException("Bad temp directory: "+dir);
+
+        _tmpDir=dir;
+        setAttribute("javax.servlet.context.tempdir",_tmpDir);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get Context temporary directory.
+     * A tempory directory is generated if it has not been set.  The
+     * "javax.servlet.context.tempdir" attribute is consulted and if
+     * not set, the host, port and context are used to generate a
+     * directory within the JVMs temporary directory.
+     *
+     * Patched for IPV6 support in I2P to replace ':' in the dir name which
+     * Windows can't handle.
+     *
+     * @return Temporary directory as a File.
+     */
+    public File getTempDirectory()
+    {
+        if (_tmpDir!=null)
+            return _tmpDir;
+
+        // Initialize temporary directory
+        //
+        // I'm afraid that this is very much black magic.
+        // but if you can think of better....
+        Object t = getAttribute("javax.servlet.context.tempdir");
+
+        if (t!=null && (t instanceof File))
+        {
+            _tmpDir=(File)t;
+            if (_tmpDir.isDirectory() && _tmpDir.canWrite())
+                return _tmpDir;
+        }
+
+        if (t!=null && (t instanceof String))
+        {
+            try
+            {
+                _tmpDir=new File((String)t);
+
+                if (_tmpDir.isDirectory() && _tmpDir.canWrite())
+                {
+                    if(log.isDebugEnabled())log.debug("Converted to File "+_tmpDir+" for "+this);
+                    setAttribute("javax.servlet.context.tempdir",_tmpDir);
+                    return _tmpDir;
+                }
+            }
+            catch(Exception e)
+            {
+                log.warn(LogSupport.EXCEPTION,e);
+            }
+        }
+
+        // No tempdir so look for a WEB-INF/work directory to use as tempDir base
+        File work=null;
+        try
+        {
+            work=new File(System.getProperty("jetty.home"),"work");
+            if (!work.exists() || !work.canWrite() || !work.isDirectory())
+                work=null;
+        }
+        catch(Exception e)
+        {
+            LogSupport.ignore(log,e);
+        }
+
+        // No tempdir set so make one!
+        try
+        {
+            HttpListener httpListener=_httpServer.getListeners()[0];
+
+            String vhost = null;
+            for (int h=0;vhost==null && _vhosts!=null && h<_vhosts.size();h++)
+                vhost=(String)_vhosts.get(h);
+            String host=httpListener.getHost();
+            String temp="Jetty_"+
+                (host==null?"":host)+
+                "_"+
+                httpListener.getPort()+
+                "_"+
+                (vhost==null?"":vhost)+
+                getContextPath();
+
+            temp=temp.replace('/','_');
+            temp=temp.replace('.','_');
+            temp=temp.replace('\\','_');
+            temp=temp.replace(':','_');
+
+            
+            if (work!=null)
+                _tmpDir=new File(work,temp);
+            else
+            {
+                _tmpDir=new File(System.getProperty("java.io.tmpdir"),temp);
+                
+                if (_tmpDir.exists())
+                {
+                    if(log.isDebugEnabled())log.debug("Delete existing temp dir "+_tmpDir+" for "+this);
+                    if (!IO.delete(_tmpDir))
+                    {
+                        if(log.isDebugEnabled())log.debug("Failed to delete temp dir "+_tmpDir);
+                    }
+                
+                    if (_tmpDir.exists())
+                    {
+                        String old=_tmpDir.toString();
+                        _tmpDir=File.createTempFile(temp+"_","");
+                        if (_tmpDir.exists())
+                            _tmpDir.delete();
+                        log.warn("Can't reuse "+old+", using "+_tmpDir);
+                    }
+                }
+            }
+
+            if (!_tmpDir.exists())
+                _tmpDir.mkdir();
+            if (work==null)
+                _tmpDir.deleteOnExit();
+            if(log.isDebugEnabled())log.debug("Created temp dir "+_tmpDir+" for "+this);
+        }
+        catch(Exception e)
+        {
+            _tmpDir=null;
+            LogSupport.ignore(log,e);
+        }
+
+        if (_tmpDir==null)
+        {
+            try{
+                // that didn't work, so try something simpler (ish)
+                _tmpDir=File.createTempFile("JettyContext","");
+                if (_tmpDir.exists())
+                    _tmpDir.delete();
+                _tmpDir.mkdir();
+                _tmpDir.deleteOnExit();
+                if(log.isDebugEnabled())log.debug("Created temp dir "+_tmpDir+" for "+this);
+            }
+            catch(IOException e)
+            {
+                log.fatal(e); System.exit(1);
+            }
+        }
+
+        setAttribute("javax.servlet.context.tempdir",_tmpDir);
+        return _tmpDir;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    /** Set ClassLoader.
+     * @param loader The loader to be used by this context.
+     */
+    public synchronized void setClassLoader(ClassLoader loader)
+    {
+        if (isStarted())
+            throw new IllegalStateException("Started");
+        _loader=loader;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Get the classloader.
+     * If no classloader has been set and the context has been loaded
+     * normally, then null is returned.
+     * If no classloader has been set and the context was loaded from
+     * a classloader, that loader is returned.
+     * If a classloader has been set and no classpath has been set then
+     * the set classloader is returned.
+     * If a classloader and a classpath has been set, then a new
+     * URLClassloader initialized on the classpath with the set loader as a
+     * partent is return.
+     * @return Classloader or null.
+     */
+    public synchronized ClassLoader getClassLoader()
+    {
+        return _loader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set Parent ClassLoader.
+     * By default the parent loader is the thread context classloader
+     * of the thread that calls initClassLoader.  If setClassLoader is
+     * called, then the parent is ignored.
+     * @param loader The class loader to use for the parent loader of
+     * the context classloader.
+     */
+    public synchronized void setParentClassLoader(ClassLoader loader)
+    {
+        if (isStarted())
+            throw new IllegalStateException("Started");
+        _parent=loader;
+    }
+
+    /* ------------------------------------------------------------ */
+    public ClassLoader getParentClassLoader()
+    {
+        return _parent;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Initialize the context classloader.
+     * Initialize the context classloader with the current parameters.
+     * Any attempts to change the classpath after this call will
+     * result in a IllegalStateException
+     * @param forceContextLoader If true, a ContextLoader is always if
+     * no loader has been set.
+     */
+    protected void initClassLoader(boolean forceContextLoader)
+        throws MalformedURLException, IOException
+    {
+        ClassLoader parent=_parent;
+        if (_loader==null)
+        {
+            // If no parent, then try this threads classes loader as parent
+            if (parent==null)
+                parent=Thread.currentThread().getContextClassLoader();
+
+            // If no parent, then try this classes loader as parent
+            if (parent==null)
+                parent=this.getClass().getClassLoader();
+
+            if(log.isDebugEnabled())log.debug("Init classloader from "+_classPath+
+                       ", "+parent+" for "+this);
+
+            if (forceContextLoader || _classPath!=null || _permissions!=null)
+            {
+                ContextLoader loader=new ContextLoader(this,_classPath,parent,_permissions);
+                loader.setJava2Compliant(_classLoaderJava2Compliant);
+                _loader=loader;
+            }
+            else
+                _loader=parent;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized Class loadClass(String className)
+        throws ClassNotFoundException
+    {
+        if (_loader==null)
+        {
+            try{initClassLoader(false);}
+            catch(Exception e)
+            {
+                log.warn(LogSupport.EXCEPTION,e);
+                return null;
+            }
+        }
+
+        if (className==null)
+            return null;
+
+        if (_loader == null) 
+            return Class.forName(className); 
+        return _loader.loadClass(className);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the realm name.
+     * @param realmName The name to use to retrieve the actual realm
+     * from the HttpServer
+     */
+    public void setRealmName(String realmName)
+    {
+        _realmName=realmName;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getRealmName()
+    {
+        return _realmName;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the  realm.
+     */
+    public void setRealm(UserRealm realm)
+    {
+        _userRealm=realm;
+    }
+
+    /* ------------------------------------------------------------ */
+    public UserRealm getRealm()
+    {
+        return _userRealm;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Authenticator getAuthenticator()
+    {
+        return _authenticator;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setAuthenticator(Authenticator authenticator)
+    {
+        _authenticator=authenticator;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addSecurityConstraint(String pathSpec, SecurityConstraint sc)
+    {
+        Object scs = _constraintMap.get(pathSpec);
+        scs = LazyList.add(scs,sc);
+        _constraintMap.put(pathSpec,scs);
+        
+        if(log.isDebugEnabled())log.debug("added "+sc+" at "+pathSpec);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void clearSecurityConstraints()
+    {
+        _constraintMap.clear();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean checkSecurityConstraints(
+        String pathInContext,
+        HttpRequest request,
+        HttpResponse response)
+        throws HttpException, IOException
+    {
+        UserRealm realm= getRealm();
+
+        List scss= _constraintMap.getMatches(pathInContext);
+        String pattern=null;
+        if (scss != null && scss.size() > 0)
+        {
+            Object constraints= null;
+
+            // for each path match
+            // Add only constraints that have the correct method
+            // break if the matching pattern changes.  This allows only
+            // constraints with matching pattern and method to be combined.
+            loop:
+            for (int m= 0; m < scss.size(); m++)
+            {
+                Map.Entry entry= (Map.Entry)scss.get(m);
+                Object scs= entry.getValue();
+                String p=(String)entry.getKey();
+                for (int c=0;c<LazyList.size(scs);c++)
+                {
+                	SecurityConstraint sc=(SecurityConstraint)LazyList.get(scs,c);
+					if (!sc.forMethod(request.getMethod()))
+						continue;
+						
+					if (pattern!=null && !pattern.equals(p))
+						break loop;
+					pattern=p;	
+	                constraints= LazyList.add(constraints, sc);
+                }
+            }
+            
+            return SecurityConstraint.check(
+                LazyList.getList(constraints),
+                _authenticator,
+                realm,
+                pathInContext,
+                request,
+                response);
+        }
+        request.setUserPrincipal(HttpRequest.__NOT_CHECKED);
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set null path redirection.
+     * @param b if true a /context request will be redirected to
+     * /context/ if there is not path in the context.
+     */
+    public void setRedirectNullPath(boolean b)
+    {
+        _redirectNullPath=b;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if a /context request is redirected to /context/ if
+     * there is not path in the context.
+     */
+    public boolean isRedirectNullPath()
+    {
+        return _redirectNullPath;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    /** Set the permissions to be used for this context.
+     * The collection of permissions set here are used for all classes
+     * loaded by this context.  This is simpler that creating a
+     * security policy file, as not all code sources may be statically
+     * known.
+     * @param permissions
+     */
+    public void setPermissions(PermissionCollection permissions)
+    {
+        _permissions=permissions;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the permissions to be used for this context.
+     */
+    public PermissionCollection getPermissions()
+    {
+        return _permissions;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add a permission to this context.
+     * The collection of permissions set here are used for all classes
+     * loaded by this context.  This is simpler that creating a
+     * security policy file, as not all code sources may be statically
+     * known.
+     * @param permission
+     */
+    public void addPermission(Permission permission)
+    {
+        if (_permissions==null)
+            _permissions=new Permissions();
+        _permissions.add(permission);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Handler request.
+     * Determine the path within the context and then call
+     * handle(pathInContext,request,response).
+     * @param request
+     * @param response
+     * @return True if the request has been handled.
+     * @exception HttpException
+     * @exception IOException
+     */
+    public void handle(HttpRequest request,
+                       HttpResponse response)
+        throws HttpException, IOException
+    {
+        if (!isStarted() || _gracefulStop)
+            return;
+
+        // reject requests by real host
+        if (_hosts!=null && _hosts.size()>0)
+        {
+            Object o = request.getHttpConnection().getConnection();
+            if (o instanceof Socket)
+            {
+                Socket s=(Socket)o;
+                if (!_hosts.contains(s.getLocalAddress()))
+                {
+                    if(log.isDebugEnabled())log.debug(s.getLocalAddress()+" not in "+_hosts);
+                    return;
+                }
+            }
+        }
+        
+        // handle stats
+        if (_statsOn)
+        {
+            synchronized(_statsLock)
+            {
+                _requests++;
+                _requestsActive++;
+                if (_requestsActive>_requestsActiveMax)
+                    _requestsActiveMax=_requestsActive;
+            }
+        }
+
+        String pathInContext = URI.canonicalPath(request.getPath());
+        if (pathInContext==null)
+        {
+            // Must be a bad request.
+            throw new HttpException(HttpResponse.__400_Bad_Request);
+        }
+
+        if (_contextPath.length()>1)
+            pathInContext=pathInContext.substring(_contextPath.length());
+
+        if (_redirectNullPath && (pathInContext==null ||
+                                  pathInContext.length()==0))
+        {
+            StringBuffer buf=request.getRequestURL();
+            buf.append("/");
+            String q=request.getQuery();
+            if (q!=null&&q.length()!=0)
+                buf.append("?"+q);
+                
+            response.sendRedirect(buf.toString());
+            if (log.isDebugEnabled())
+                log.debug(this+" consumed all of path "+
+                             request.getPath()+
+                             ", redirect to "+buf.toString());
+            return;
+        }
+
+        String pathParams=null;
+        int semi = pathInContext.lastIndexOf(';');
+        if (semi>=0)
+        {
+            int pl = pathInContext.length()-semi;
+            String ep=request.getEncodedPath();
+            if(';'==ep.charAt(ep.length()-pl))
+            {
+                pathParams=pathInContext.substring(semi+1);
+                pathInContext=pathInContext.substring(0,semi);
+            }
+        }
+
+        try
+        {
+            handle(pathInContext,pathParams,request,response);
+        }
+        finally
+        {
+            if (_userRealm!=null && request.hasUserPrincipal())
+                _userRealm.disassociate(request.getUserPrincipal());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Handler request.
+     * Call each HttpHandler until request is handled.
+     * @param pathInContext Path in context
+     * @param pathParams Path parameters such as encoded Session ID
+     * @param request
+     * @param response
+     * @return True if the request has been handled.
+     * @exception HttpException
+     * @exception IOException
+     */
+    public void handle(String pathInContext,
+                       String pathParams,
+                       HttpRequest request,
+                       HttpResponse response)
+        throws HttpException, IOException
+    {
+        Object old_scope= null;
+        try
+        {
+            old_scope= enterContextScope(request,response);
+            HttpHandler[] handlers= getHandlers();
+            for (int k= 0; k < handlers.length; k++)
+            {
+                HttpHandler handler= handlers[k];
+                if (handler == null)
+                {
+                    handlers= getHandlers();
+                    k= -1;
+                    continue;
+                }
+                if (!handler.isStarted())
+                {
+                    if (log.isDebugEnabled())
+                        log.debug(handler + " not started in " + this);
+                    continue;
+                }
+                if (log.isDebugEnabled())
+                    log.debug("Handler " + handler);
+                handler.handle(pathInContext, pathParams, request, response);
+                if (request.isHandled())
+                {
+                    if (log.isDebugEnabled())
+                        log.debug("Handled by " + handler);
+                    return;
+                }
+            }
+            return;
+        }
+        finally
+        {
+            leaveContextScope(request, response, old_scope);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Enter the context scope.
+     * This method is called (by handle or servlet dispatchers) to indicate that
+     * request handling is entering the scope of this context.  The opaque scope object
+     * returned, should be passed to the leaveContextScope method.
+     */
+    public Object enterContextScope(HttpRequest request, HttpResponse response)
+    {
+        // Save the thread context loader
+        Thread thread = Thread.currentThread();
+        ClassLoader cl=thread.getContextClassLoader();
+        HttpContext c=response.getHttpContext();
+
+        Scope scope=null;
+        if (cl!=HttpContext.class.getClassLoader() || c!=null)
+        {
+            scope=new Scope();
+            scope._classLoader=cl;
+            scope._httpContext=c;
+        }
+        
+        if (_loader!=null)
+            thread.setContextClassLoader(_loader);
+        response.setHttpContext(this);
+            
+        return scope;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Leave the context scope.
+     * This method is called (by handle or servlet dispatchers) to indicate that
+     * request handling is leaveing the scope of this context.  The opaque scope object
+     * returned by enterContextScope should be passed in.
+     */
+    public void leaveContextScope(HttpRequest request, HttpResponse response,Object oldScope)
+    {
+        if (oldScope==null)
+        {
+            Thread.currentThread()
+                .setContextClassLoader(HttpContext.class.getClassLoader());
+            response.setHttpContext(null);
+        }
+        else
+        {
+            Scope old = (Scope)oldScope;
+            Thread.currentThread().setContextClassLoader(old._classLoader);
+            response.setHttpContext(old._httpContext);
+        }
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    public String getHttpContextName()
+    {
+        if (_contextName==null)
+            _contextName = (_vhosts.size()>1?(_vhosts.toString()+":"):"")+_contextPath;
+        return _contextName;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setHttpContextName(String s)
+    {
+        _contextName=s;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return "HttpContext["+getContextPath()+","+getHttpContextName()+"]";
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString(boolean detail)
+    {
+        return "HttpContext["+getContextPath()+","+getHttpContextName()+"]" +
+            (detail?("="+_handlers):"");
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    protected synchronized void doStart()
+        throws Exception
+    {
+        if (isStarted())
+            return;
+
+        if (_httpServer.getServerClasses()!=null)
+            _serverClasses=_httpServer.getServerClasses();
+        if (_httpServer.getSystemClasses()!=null)
+            _systemClasses=_httpServer.getSystemClasses();
+        
+        _resources.start();
+        
+        statsReset();
+
+        if (_httpServer==null)
+            throw new IllegalStateException("No server for "+this);
+
+        // start the context itself
+        _resources.getMimeMap();
+        _resources.getEncodingMap();
+
+        // Setup realm
+        if (_userRealm==null && _authenticator!=null)
+        {
+            _userRealm=_httpServer.getRealm(_realmName);
+            if (_userRealm==null)
+                log.warn("No Realm: "+_realmName);
+        }
+
+        // setup the context loader
+        initClassLoader(false);
+
+        // Set attribute if needed
+        String attr = getInitParameter(__fileClassPathAttr);
+        if (attr!=null && attr.length()>0)
+            setAttribute(attr,getFileClassPath());
+
+        // Start the handlers
+        Thread thread = Thread.currentThread();
+        ClassLoader lastContextLoader=thread.getContextClassLoader();
+        try
+        {
+            if (_loader!=null)
+                thread.setContextClassLoader(_loader);
+
+            if (_requestLog!=null)
+                _requestLog.start();
+            
+            startHandlers();
+        }
+        finally
+        {
+            thread.setContextClassLoader(lastContextLoader);
+            getHandlers();
+        }
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Start the handlers.
+     * This is called by start after the classloader has been
+     * initialized and set as the thread context loader.
+     * It may be specialized to provide custom handling
+     * before any handlers are started.
+     * @exception Exception
+     */
+    protected void startHandlers()
+        throws Exception
+    {
+        // Prepare a multi exception
+        MultiException mx = new MultiException();
+
+        Iterator handlers = _handlers.iterator();
+        while(handlers.hasNext())
+        {
+            HttpHandler handler=(HttpHandler)handlers.next();
+            if (!handler.isStarted())
+                try{handler.start();}catch(Exception e){mx.add(e);}
+        }
+        mx.ifExceptionThrow();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Stop the context.
+     * @param graceful If true and statistics are on, then this method will wait
+     * for requestsActive to go to zero before calling stop()
+     */
+    public void stop(boolean graceful)
+        throws InterruptedException
+    {
+        boolean gs=_gracefulStop;
+        try
+        {
+            _gracefulStop=true;
+            
+            // wait for all requests to complete.
+            while (graceful && _statsOn && _requestsActive>0 && _httpServer!=null)
+                try {Thread.sleep(100);}
+            catch (InterruptedException e){throw e;}
+            catch (Exception e){LogSupport.ignore(log,e);}
+            
+            stop();
+        }
+        finally
+        {
+            _gracefulStop=gs;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Stop the context.
+     */
+    protected void doStop()
+        throws Exception
+    {
+        if (_httpServer==null)
+            throw new InterruptedException("Destroy called");
+
+        synchronized(this)
+        {
+            // Notify the container for the stop
+            Thread thread = Thread.currentThread();
+            ClassLoader lastContextLoader=thread.getContextClassLoader();
+            try
+            {
+                if (_loader!=null)
+                    thread.setContextClassLoader(_loader);
+                Iterator handlers = _handlers.iterator();
+                while(handlers.hasNext())
+                {
+                    HttpHandler handler=(HttpHandler)handlers.next();
+                    if (handler.isStarted())
+                    {
+                        try{handler.stop();}
+                        catch(Exception e){log.warn(LogSupport.EXCEPTION,e);}
+                    }
+                }
+                
+                if (_requestLog!=null)
+                    _requestLog.stop();
+            }
+            finally
+            {
+                thread.setContextClassLoader(lastContextLoader);
+            }
+            
+            // TODO this is a poor test
+            if (_loader instanceof ContextLoader)
+            {
+                ((ContextLoader)_loader).destroy();
+                LogFactory.release(_loader);
+            }
+            
+            _loader=null;
+        }
+        _resources.flushCache();
+        _resources.stop();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Destroy a context.
+     * Destroy a context and remove it from the HttpServer. The
+     * HttpContext must be stopped before it can be destroyed.
+     */
+    public void destroy()
+    {
+        if (isStarted())
+            throw new IllegalStateException("Started");
+
+        if (_httpServer!=null)
+            _httpServer.removeContext(this);
+
+        _httpServer=null;
+        
+        if (_handlers!=null)
+            _handlers.clear();
+        
+        _handlers=null;
+        _parent=null;
+        _loader=null;
+        if (_attributes!=null)
+            _attributes.clear();
+        _attributes=null;
+        if (_initParams!=null)
+            _initParams.clear();
+        _initParams=null;
+        if (_vhosts!=null)
+            _vhosts.clear();
+        _vhosts=null;
+        _hosts=null;
+        _tmpDir=null;
+
+        _permissions=null;
+        
+        removeComponent(_resources);
+        if (_resources!=null)
+        {
+            _resources.flushCache();
+            if (_resources.isStarted())
+                try{_resources.stop();}catch(Exception e){LogSupport.ignore(log,e);}
+                _resources.destroy();
+        }
+        _resources=null;
+        
+        super.destroy();
+        
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Set the request log.
+     * @param log RequestLog to use.
+     */
+    public void setRequestLog(RequestLog log)
+    {
+        _requestLog=log;
+    }
+
+    /* ------------------------------------------------------------ */
+    public RequestLog getRequestLog()
+    {
+        return _requestLog;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Send an error response.
+     * This method may be specialized to provide alternative error handling for
+     * errors generated by the container.  The default implemenation calls HttpResponse.sendError
+     * @param response the response to send
+     * @param code The error code
+     * @param msg The message for the error or null for the default
+     * @throws IOException Problem sending response.
+     */
+    public void sendError(HttpResponse response,int code,String msg)
+    	throws IOException
+    {
+        response.sendError(code,msg);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Send an error response.
+     * This method obtains the responses context and call sendError for context specific
+     * error handling.
+     * @param response the response to send
+     * @param code The error code
+     * @param msg The message for the error or null for the default
+     * @throws IOException Problem sending response.
+     */
+    public static void sendContextError(HttpResponse response,int code,String msg)
+    	throws IOException
+    {
+        HttpContext context = response.getHttpContext();
+        if (context!=null)
+            context.sendError(response,code,msg);
+        else
+            response.sendError(code,msg);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** True set statistics recording on for this context.
+     * @param on If true, statistics will be recorded for this context.
+     */
+    public void setStatsOn(boolean on)
+    {
+        log.info("setStatsOn "+on+" for "+this);
+        _statsOn=on;
+        statsReset();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getStatsOn() {return _statsOn;}
+
+    /* ------------------------------------------------------------ */
+    public long getStatsOnMs()
+    {return _statsOn?(System.currentTimeMillis()-_statsStartedAt):0;}
+
+    /* ------------------------------------------------------------ */
+    public void statsReset()
+    {
+        synchronized(_statsLock)
+        {
+            if (_statsOn)
+                _statsStartedAt=System.currentTimeMillis();
+            _requests=0;
+            _requestsActiveMax=_requestsActive;
+            _responses1xx=0;
+            _responses2xx=0;
+            _responses3xx=0;
+            _responses4xx=0;
+            _responses5xx=0;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of requests handled by this context
+     * since last call of statsReset(). If setStatsOn(false) then this
+     * is undefined.
+     */
+    public int getRequests() {return _requests;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Number of requests currently active.
+     * Undefined if setStatsOn(false).
+     */
+    public int getRequestsActive() {return _requestsActive;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Maximum number of active requests
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getRequestsActiveMax() {return _requestsActiveMax;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of responses with a 2xx status returned
+     * by this context since last call of statsReset(). Undefined if
+     * if setStatsOn(false).
+     */
+    public int getResponses1xx() {return _responses1xx;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of responses with a 100 status returned
+     * by this context since last call of statsReset(). Undefined if
+     * if setStatsOn(false).
+     */
+    public int getResponses2xx() {return _responses2xx;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of responses with a 3xx status returned
+     * by this context since last call of statsReset(). Undefined if
+     * if setStatsOn(false).
+     */
+    public int getResponses3xx() {return _responses3xx;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of responses with a 4xx status returned
+     * by this context since last call of statsReset(). Undefined if
+     * if setStatsOn(false).
+     */
+    public int getResponses4xx() {return _responses4xx;}
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of responses with a 5xx status returned
+     * by this context since last call of statsReset(). Undefined if
+     * if setStatsOn(false).
+     */
+    public int getResponses5xx() {return _responses5xx;}
+
+
+    /* ------------------------------------------------------------ */
+    /** Log a request and response.
+     * Statistics are also collected by this method.
+     * @param request
+     * @param response
+     */
+    public void log(HttpRequest request,
+                    HttpResponse response,
+                    int length)
+    {
+        if (_statsOn)
+        {
+            synchronized(_statsLock)
+            {
+                if (--_requestsActive<0)
+                    _requestsActive=0;
+
+                if (response!=null)
+                {
+                    switch(response.getStatus()/100)
+                    {
+                      case 1: _responses1xx++;break;
+                      case 2: _responses2xx++;break;
+                      case 3: _responses3xx++;break;
+                      case 4: _responses4xx++;break;
+                      case 5: _responses5xx++;break;
+                    }
+                }
+            }
+        }
+
+        if (_requestLog!=null &&
+            request!=null &&
+            response!=null)
+            _requestLog.log(request,response,length);
+        else if (_httpServer!=null)
+            _httpServer.log(request,response,length);
+    }
+
+    
+
+    /* ------------------------------------------------------------ */
+    /* Class to save scope of nested context calls
+     */
+    private static class Scope 
+    {
+        ClassLoader _classLoader;
+        HttpContext _httpContext;
+    }
+
+    /* 
+     * @see org.mortbay.http.HttpHandler#getName()
+     */
+    public String getName()
+    {
+        return this.getContextPath();
+    }
+
+    /* 
+     * @see org.mortbay.http.HttpHandler#getHttpContext()
+     */
+    public HttpContext getHttpContext()
+    {
+        return this;
+    }
+
+    /* 
+     * @see org.mortbay.http.HttpHandler#initialize(org.mortbay.http.HttpContext)
+     */
+    public void initialize(HttpContext context)
+    {
+        throw new UnsupportedOperationException();
+    }   
+    
+    
+    /**
+     * @return
+     */
+    public Resource getBaseResource()
+    {
+        return _resources.getBaseResource();
+    }
+    /**
+     * @param type
+     * @return
+     */
+    public String getEncodingByMimeType(String type)
+    {
+        return _resources.getEncodingByMimeType(type);
+    }
+    /**
+     * @return
+     */
+    public Map getEncodingMap()
+    {
+        return _resources.getEncodingMap();
+    }
+    /**
+     * @return
+     */
+    public int getMaxCachedFileSize()
+    {
+        return _resources.getMaxCachedFileSize();
+    }
+    /**
+     * @return
+     */
+    public int getMaxCacheSize()
+    {
+        return _resources.getMaxCacheSize();
+    }
+    /**
+     * @param filename
+     * @return
+     */
+    public String getMimeByExtension(String filename)
+    {
+        return _resources.getMimeByExtension(filename);
+    }
+    /**
+     * @return
+     */
+    public Map getMimeMap()
+    {
+        return _resources.getMimeMap();
+    }
+    /**
+     * @param pathInContext
+     * @return
+     * @throws IOException
+     */
+    public Resource getResource(String pathInContext) throws IOException
+    {
+        return _resources.getResource(pathInContext);
+    }
+    /**
+     * @return
+     */
+    public String getResourceBase()
+    {
+        return _resources.getResourceBase();
+    }
+    /**
+     * @param resource
+     * @return
+     */
+    public ResourceMetaData getResourceMetaData(Resource resource)
+    {
+        return _resources.getResourceMetaData(resource);
+    }
+    /**
+     * @param base
+     */
+    public void setBaseResource(Resource base)
+    {
+        _resources.setBaseResource(base);
+    }
+    /**
+     * @param encodingMap
+     */
+    public void setEncodingMap(Map encodingMap)
+    {
+        _resources.setEncodingMap(encodingMap);
+    }
+    /**
+     * @param maxCachedFileSize
+     */
+    public void setMaxCachedFileSize(int maxCachedFileSize)
+    {
+        _resources.setMaxCachedFileSize(maxCachedFileSize);
+    }
+    /**
+     * @param maxCacheSize
+     */
+    public void setMaxCacheSize(int maxCacheSize)
+    {
+        _resources.setMaxCacheSize(maxCacheSize);
+    }
+    /**
+     * @param mimeMap
+     */
+    public void setMimeMap(Map mimeMap)
+    {
+        _resources.setMimeMap(mimeMap);
+    }
+    /**
+     * @param extension
+     * @param type
+     */
+    public void setMimeMapping(String extension, String type)
+    {
+        _resources.setMimeMapping(extension, type);
+    }
+    /**
+     * @param resourceBase
+     */
+    public void setResourceBase(String resourceBase)
+    {
+        _resources.setResourceBase(resourceBase);
+    }
+    /**
+     * @param mimeType
+     * @param encoding
+     */
+    public void setTypeEncoding(String mimeType, String encoding)
+    {
+        _resources.setTypeEncoding(mimeType, encoding);
+    }
+}
diff --git a/apps/ministreaming/java/build.xml b/apps/ministreaming/java/build.xml
index 48920deefef64ddbe54539c94f827c8587bc7743..1f83b640ddbe78e3ff8c8395f0372a56bdf31714 100644
--- a/apps/ministreaming/java/build.xml
+++ b/apps/ministreaming/java/build.xml
@@ -19,10 +19,13 @@
             </classpath>
         </depend>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="depend">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
-        <javac srcdir="./src" debug="true" deprecation="on" source="1.5" target="1.5" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
+        <javac srcdir="./src" debug="true" deprecation="on" source="1.5" target="1.5" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile">
         <jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" />
diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml
index 00168963bf50b1415bba70ef900cc176fee8889c..23e1ac05bdb955ea41cf575c8e66037877b1685b 100644
--- a/apps/routerconsole/java/build.xml
+++ b/apps/routerconsole/java/build.xml
@@ -40,6 +40,7 @@
              <targetfilelist dir="." files="build/obj/net/i2p/router/web/UpdateHandler.class" />
         </dependset>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="prepare, depend, dependVersion">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -47,6 +48,7 @@
             srcdir="./src" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj">
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../../../core/java/build/i2p.jar" />
                 <pathelement location="../../../router/java/build/router.jar" />
@@ -114,6 +116,7 @@
         <javac debug="true" deprecation="on" source="1.5" target="1.5" 
                destdir="../jsp/WEB-INF/classes/" 
                srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
                 <pathelement location="../../jetty/jettylib/javax.servlet.jar" />
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
index 9beeb33cf500b7183f0a9fd8237e74b3e0c5b5e8..5d7e412bf3dc5be8d61cfd1dd4f00848194c87d7 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
@@ -18,8 +18,8 @@ public class ConfigNetHelper extends HelperBase {
     public final static String PROP_I2NP_INTERNAL_UDP_PORT = "i2np.udp.internalPort";
     public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
     public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
-    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoip";
-    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
     public String getNtcphostname() {
         if (!TransportManager.enableNTCP(_context))
             return "\" disabled=\"true";
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
index a3280ac446c4af4ad3f29c1633103762e1f73a6e..886ba6abb6dda80e96c60a736ed444debf83a61e 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
@@ -10,17 +10,21 @@ import net.i2p.router.RouterContext;
 public class NetDbHelper extends HelperBase {
     private String _routerPrefix;
     private boolean _full = false;
+    private boolean _lease = false;
     
     public NetDbHelper() {}
     
     public void setRouter(String r) { _routerPrefix = r; }
-    public void setFull(String f) { _full = "1".equals(f); };
+    public void setFull(String f) { _full = "1".equals(f); }
+    public void setLease(String l) { _lease = "1".equals(l); }
     
     public String getNetDbSummary() {
         try {
             if (_out != null) {
                 if (_routerPrefix != null)
                     _context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
+                else if (_lease)
+                    _context.netDb().renderLeaseSetHTML(_out);
                 else
                     _context.netDb().renderStatusHTML(_out, _full);
                 return "";
@@ -28,6 +32,8 @@ public class NetDbHelper extends HelperBase {
                 ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
                 if (_routerPrefix != null)
                     _context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
+                else if (_lease)
+                    _context.netDb().renderLeaseSetHTML(new OutputStreamWriter(baos));
                 else
                     _context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
                 return new String(baos.toByteArray());
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index 1b302ff0b92393ef4ca5866f06ca8c9f43354548..a275e2a612b790313cdde81fde1eb67d15dbb55c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -364,7 +364,10 @@ public class SummaryHelper extends HelperBase {
             Destination client = (Destination)iter.next();
             String name = getName(client);
             
-            buf.append("<b>*</b> ").append(name).append("<br />\n");
+            buf.append("<b>*</b> ");
+            buf.append("<a href=\"tunnels.jsp#").append(client.calculateHash().toBase64().substring(0,4));
+            buf.append("\" target=\"_top\" title=\"Show tunnels\">");
+            buf.append(name).append("</a><br />\n");
             LeaseSet ls = _context.netDb().lookupLeaseSetLocally(client.calculateHash());
             if (ls != null) {
                 long timeToExpire = ls.getEarliestLeaseDate() - _context.clock().now();
@@ -375,10 +378,6 @@ public class SummaryHelper extends HelperBase {
             } else {
                 buf.append("<i>No leases</i><br />\n");
             }
-            buf.append("<a href=\"tunnels.jsp#").append(client.calculateHash().toBase64().substring(0,4));
-            buf.append("\" target=\"_top\">Details</a> ");
-            buf.append("<a href=\"configtunnels.jsp#").append(client.calculateHash().toBase64().substring(0,4));
-            buf.append("\" target=\"_top\">Config</a><br />\n");
         }
         buf.append("<hr />\n");
         return buf.toString();
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
index 81f8d6e8d970c2836b24a5d2fb2fd87b1041d901..84d54263ea1bd503486dccca1e39b88051534ac3 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
@@ -92,14 +92,25 @@ public class UpdateHandler {
         return _updateRunner.getStatus();
     }
     
+    public boolean isDone() {
+        if(this._updateRunner == null)
+            return true;
+        return this._updateRunner.isDone();
+    }
+    
     public class UpdateRunner implements Runnable, EepGet.StatusListener {
         protected boolean _isRunning;
+        protected boolean done;
         protected String _status;
         public UpdateRunner() { 
-            _isRunning = false; 
+            _isRunning = false;
+            this.done = false;
             _status = "<b>Updating</b>";
         }
         public boolean isRunning() { return _isRunning; }
+        public boolean isDone() {
+            return this.done;
+        }
         public String getStatus() { return _status; }
         public void run() {
             _isRunning = true;
@@ -159,6 +170,7 @@ public class UpdateHandler {
             f.delete();
             if (err == null) {
                 String policy = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_POLICY);
+                this.done = true;
                 if ("install".equals(policy)) {
                     _log.log(Log.CRIT, "Update was VERIFIED, restarting to install it");
                     _status = "<b>Update verified</b><br />Restarting";
diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp
index ae357d69f1e7fd1965daa2b02d5925500df3d3e9..53c9192638b650d3f901ab50f52e0b754035e875 100644
--- a/apps/routerconsole/jsp/config.jsp
+++ b/apps/routerconsole/jsp/config.jsp
@@ -78,7 +78,7 @@
     the <i>Status: Firewalled</i> line), or you can manually require them here.  
     Users behind symmetric NATs, such as OpenBSD's pf, are not currently supported.</p>
 <input type="submit" name="recheckReachability" value="Check network reachability..." />
- <hr />
+ <p>
  <b>Inbound TCP connection configuration:</b><br />
  Externally reachable hostname or IP address:
     <input name ="ntcphost" type="text" size="16" value="<jsp:getProperty name="nethelper" property="ntcphostname" />" />
@@ -118,7 +118,13 @@
  <li><b>Firewalled</b> - Your UDP port appears to be firewalled.
      As the firewall detection methods are not 100% reliable, this may occasionally be displayed in error.
      However, if it appears consistently, you should check whether both your external and internal
-     firewalls are open on port 8887.
+     firewalls are open on port 8887. I2P will work fine when firewalled, there is no reason for concern.
+     When firewalled, the router uses "introducers" to relay inbound connections.
+     However, you will get more participating traffic and help the network more if you can open your
+     firewall(s). If you think you have already done so, remember that you may have both a hardware
+     and a software firewall, or be behind an additional, institutional firewall you cannot control.
+     Also, some routers cannot correctly forward both TCP and UDP on a single port, or may have other
+     limitations or bugs that prevent them from passing traffic through to I2P.
  <li><b>Testing</b> - The router is currently testing whether your UDP port is firewalled.
  <li><b>Hidden</b> - The router is not configured to publish its address,
      therefore it does not expect incoming connections.
@@ -142,7 +148,8 @@
  <li><b>ERR - Private TCP Address</b> - You must never advertise an unroutable IP address such as
      127.0.0.1 or 192.168.1.1 as your external address. Correct the address or disable inbound TCP above.
  <li><b>ERR - SymmetricNAT</b> - I2P detected that you are firewalled by a Symmetric NAT.
-     I2P does not work behind this type of firewall.
+     I2P does not work well behind this type of firewall. You will probably not be able to
+     accept inbound connections, which will limit your participation in the network.
  <li><b>ERR - UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart</b> -
      I2P was unable to bind to port 8887 or other configured port.
      Check to see if another program is using port 8887. If so, stop that program or configure
diff --git a/apps/routerconsole/jsp/configlogging.jsp b/apps/routerconsole/jsp/configlogging.jsp
index b660a15198d52f64a6a3e82c691bfb092b454d46..8f4c069e07ab43c9e2979ec8074db6583018dfc4 100644
--- a/apps/routerconsole/jsp/configlogging.jsp
+++ b/apps/routerconsole/jsp/configlogging.jsp
@@ -41,7 +41,9 @@
   <hr />
   <b>Log levels:</b> <br />
   <b>Default log level:</b>
-   <jsp:getProperty name="logginghelper" property="defaultLogLevelBox" /><br />
+   <jsp:getProperty name="logginghelper" property="defaultLogLevelBox" />
+   <i>(DEBUG and INFO are not recommended defaults, as they will drastically slow down your router)</i>
+   <br />
    <jsp:getProperty name="logginghelper" property="logLevelTable" />
  <hr />
  <input type="submit" name="shouldsave" value="Save changes" /> 
diff --git a/apps/routerconsole/jsp/default.css b/apps/routerconsole/jsp/default.css
index 527a7c9bea4a947a9655e516fd0d696381c7e47d..d5d2fd48bb90d7e6d4dd18755a0fdf13857dbb4d 100644
--- a/apps/routerconsole/jsp/default.css
+++ b/apps/routerconsole/jsp/default.css
@@ -5,6 +5,7 @@ body {
 	text-align: center;
 	background-color: white;
 	color: black;
+	font-size: 100%;
 }
 
 .hide {
@@ -15,6 +16,11 @@ img {
 	border: none;
 }
 
+pre {
+	width: 100%;
+	overflow-x: scroll;
+}
+
 div.logo {
 	float: left;
 	width: 200px;
@@ -30,23 +36,25 @@ div.toolbar {
 	font-weight: bold;
 }
 
-div.routersummary {
-	/* width: 8em; */
-	/* height: 5em; */
-	/* position: fixed; */
+div.routersummaryouter {
 	float: left;
-	width: 200px;
-	/* left: 1em; */
-	/* top: 1em; */
+	width: 215px;
+	margin: 0;
+	padding: 0;
+	border: 0;
+	clear: left; /* fixes a bug in Opera */
+	overflow: auto;
+}
+
+div.routersummary {
+	background-color: #fafaff;
+	width: 195px;
+	color: inherit;
 	margin: 0em;
-	padding: .5em;
+	padding: 5px;
 	text-align: left;
 	border: medium solid #efefff;
-	background-color: #fafaff;
-	color: inherit;
-	font-size: small;
-	clear: left; /* fixes a bug in Opera */
-	overflow: auto;
+	font-size: 0.82em;
 }
 
 div.warning {
@@ -67,6 +75,10 @@ div.main {
 	color: inherit;
 }
 
+div.main textarea {
+	width: 100% !important;
+}
+
 div.news {
 	margin: 0em 1em 1em 224px;
 	padding: .5em 1em;
diff --git a/apps/routerconsole/jsp/logs.jsp b/apps/routerconsole/jsp/logs.jsp
index 36d3562fe025c909cb07c483f48fb920660c2f60..a102912488fe59f56ab86d674260eecc053d662d 100644
--- a/apps/routerconsole/jsp/logs.jsp
+++ b/apps/routerconsole/jsp/logs.jsp
@@ -11,6 +11,14 @@
 <%@include file="summary.jsp" %>
 
 <div class="main" id="main">
+ <h4>Version:</h4><a name="version"> </a>
+ Please include this information in bug reports.
+ <p>
+I2P <jsp:getProperty name="helper" property="version" /><br />
+<%=System.getProperty("java.vendor")%> <%=System.getProperty("java.version")%><br />
+<%=System.getProperty("os.name")%> <%=System.getProperty("os.arch")%> <%=System.getProperty("os.version")%><br />
+ </p>
+ <hr />
  <jsp:useBean class="net.i2p.router.web.LogsHelper" id="logsHelper" scope="request" />
  <jsp:setProperty name="logsHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
  <h4>Router logs:</h4>
@@ -21,12 +29,6 @@
  <hr />
  <h4>Service (Wrapper) logs:</h4><a name="servicelogs"> </a>
  <jsp:getProperty name="logsHelper" property="serviceLogs" />
- <hr />
- <h4>Java Version:</h4><a name="version"> </a>
- <pre>
-<%=System.getProperty("java.vendor")%> <%=System.getProperty("java.version")%>
-<%=System.getProperty("os.name")%> <%=System.getProperty("os.arch")%> <%=System.getProperty("os.version")%>
- </pre>
 </div>
 
 </body>
diff --git a/apps/routerconsole/jsp/netdb.jsp b/apps/routerconsole/jsp/netdb.jsp
index 08a1377d37ff35a188da0076466dc0cc95e9b519..392888bc6728b136135565e180695a15a32882d3 100644
--- a/apps/routerconsole/jsp/netdb.jsp
+++ b/apps/routerconsole/jsp/netdb.jsp
@@ -16,6 +16,7 @@
  <jsp:setProperty name="netdbHelper" property="writer" value="<%=out%>" />
  <jsp:setProperty name="netdbHelper" property="full" value="<%=request.getParameter("f")%>" />
  <jsp:setProperty name="netdbHelper" property="router" value="<%=request.getParameter("r")%>" />
+ <jsp:setProperty name="netdbHelper" property="lease" value="<%=request.getParameter("l")%>" />
  <jsp:getProperty name="netdbHelper" property="netDbSummary" />
 </div>
 
diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp
index 48f3b4fefd9e2c9dcd153cdb2d7f555105da2b46..4b7aaa908446e928636c28f4be9d37fdc902ee55 100644
--- a/apps/routerconsole/jsp/summary.jsp
+++ b/apps/routerconsole/jsp/summary.jsp
@@ -1,102 +1,30 @@
-<%@page import="net.i2p.router.web.SummaryHelper" %>
-<jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
-<jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
-
-<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
-<jsp:setProperty name="reseed" property="*" />
-<jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
-<jsp:setProperty name="update" property="*" />
-<jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
-<jsp:useBean class="net.i2p.router.web.ConfigUpdateHelper" id="uhelper" scope="request" />
-<jsp:setProperty name="uhelper" property="*" />
-<jsp:setProperty name="uhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
-
+<% // put width here too to prevent bad layout at startup %>
+<div class="routersummaryouter" style="width: 215px;">
+<%
+    // skip the iframe if refresh disabled
+    String d = request.getParameter("refresh");
+    String newDelay = "";
+    if (d == null || "".equals(d))
+        d = System.getProperty("routerconsole.summaryRefresh");
+    else
+        // pass the new delay parameter to the iframe
+        newDelay = "?refresh=" + d;
+    if (!"0".equals(d))
+        out.print("<iframe src=\"summaryframe.jsp" + newDelay + "\" height=\"1500\" width=\"100%\" scrolling=\"auto\" frameborder=\"0\" allowtransparency=\"true\">\n");
+%>
 <div class="routersummary">
- <center><b><a href="config.jsp">Configuration</a>&nbsp;&nbsp;<a href="help.jsp">Help</a></b></center>
- <hr />
-
- <u><b>General</b></u><br />
- <b>Ident:</b> (<a title="Your router identity is <jsp:getProperty name="helper" property="ident" />, never reveal it to anyone" href="netdb.jsp?r=.">view</a>)<br />
- <b>Version:</b> <jsp:getProperty name="helper" property="version" /><br />
- <b>Uptime:</b> <jsp:getProperty name="helper" property="uptime" /><br />
- <b>Now:</b> <jsp:getProperty name="helper" property="time" /><br />
- <b>Reachability:</b> <a href="config.jsp#help"><jsp:getProperty name="helper" property="reachability" /></a><%
-    if (helper.updateAvailable()) {
-        // display all the time so we display the final failure message
-        out.print("<br />" + update.getStatus());
-        if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) {
-        } else {
-            long nonce = new java.util.Random().nextLong();
-            String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
-            if (prev != null) System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
-            System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
-            String uri = request.getRequestURI();
-            out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
-            out.print("<input type=\"hidden\" name=\"updateNonce\" value=\"" + nonce + "\" >\n");
-            out.print("<input type=\"submit\" value=\"Download " + uhelper.getUpdateVersion() + " Update\" ></form></p>\n");
-        }
+<%@include file="summarynoframe.jsp" %>
+<%
+    // d defined above
+    if (!"0".equals(d)) {
+        out.print("</div></iframe>\n");
+    } else {
+        // since we don't have an iframe this will reload the base page, and
+        // the new delay will be passed to the iframe above
+        out.print("<hr /><p><form action=\"" + request.getRequestURI() + "\" method=\"GET\">\n");
+        out.print("<b>Refresh (s):</b> <input size=\"3\" type=\"text\" name=\"refresh\" value=\"60\" />\n");
+        out.print("<button type=\"submit\">Enable</button>\n");
+        out.print("</form></p></div>\n");
     }
- %>
- <p>
- <%=net.i2p.router.web.ConfigRestartBean.renderStatus(request.getRequestURI(), request.getParameter("action"), request.getParameter("consoleNonce"))%>
- </p>
- <hr />
- 
- <u><b><a href="peers.jsp">Peers</a></b></u><br />
- <b>Active:</b> <jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /><br />
- <b>Fast:</b> <jsp:getProperty name="helper" property="fastPeers" /><br />
- <b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
- <b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
- <b>Known:</b> <jsp:getProperty name="helper" property="allPeers" /><br /><%
-    if (helper.getActivePeers() <= 0) {
-        %><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
-    }
-    // If showing the reseed link is allowed
-    if (helper.allowReseed()) {
-        if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
-            // While reseed occurring, show status message instead
-            out.print("<i>" + System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","") + "</i><br />");
-        } else {
-            // While no reseed occurring, show reseed link
-            long nonce = new java.util.Random().nextLong();
-            String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
-            if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
-            System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
-            String uri = request.getRequestURI();
-            out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
-            out.print("<input type=\"hidden\" name=\"reseedNonce\" value=\"" + nonce + "\" >\n");
-            out.print("<button type=\"submit\" >Reseed</button></form></p>\n");
-        }
-    }
-    // If a new reseed ain't running, and the last reseed had errors, show error message
-    if ("false".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
-        String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
-        if (reseedErrorMessage.length() > 0) {
-            out.print("<i>" + reseedErrorMessage + "</i><br />");
-        }
-    }
- %><hr />
- 
- <u><b><a href="config.jsp" title="Configure the bandwidth limits">Bandwidth in/out</a></b></u><br />
- <b>1s:</b> <jsp:getProperty name="helper" property="inboundSecondKBps" />/<jsp:getProperty name="helper" property="outboundSecondKBps" />KBps<br />
- <b>5m:</b> <jsp:getProperty name="helper" property="inboundFiveMinuteKBps" />/<jsp:getProperty name="helper" property="outboundFiveMinuteKBps" />KBps<br />
- <b>Total:</b> <jsp:getProperty name="helper" property="inboundLifetimeKBps" />/<jsp:getProperty name="helper" property="outboundLifetimeKBps" />KBps<br />
- <b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br />
- <hr />
- 
- <jsp:getProperty name="helper" property="destinations" />
- 
- <u><b>Tunnels in/out</b></u><br />
- <b>Exploratory:</b> <jsp:getProperty name="helper" property="inboundTunnels" />/<jsp:getProperty name="helper" property="outboundTunnels" /><br />
- <b>Client:</b> <jsp:getProperty name="helper" property="inboundClientTunnels" />/<jsp:getProperty name="helper" property="outboundClientTunnels" /><br />
- <b>Participating:</b> <jsp:getProperty name="helper" property="participatingTunnels" /><br />
- <hr />
- 
- <u><b>Congestion</b></u><br />
- <b>Job lag:</b> <jsp:getProperty name="helper" property="jobLag" /><br />
- <b>Message delay:</b> <jsp:getProperty name="helper" property="messageDelay" /><br />
- <b>Tunnel lag:</b> <jsp:getProperty name="helper" property="tunnelLag" /><br />
- <b>Handle backlog:</b> <jsp:getProperty name="helper" property="inboundBacklog" /><br />
- <b><jsp:getProperty name="helper" property="tunnelStatus" /></b><br />
- 
+%>
 </div>
diff --git a/apps/routerconsole/jsp/summaryframe.jsp b/apps/routerconsole/jsp/summaryframe.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..627fe328c8ad5888d96c3bf464b029f1ca443680
--- /dev/null
+++ b/apps/routerconsole/jsp/summaryframe.jsp
@@ -0,0 +1,68 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+
+<%
+/*
+ * All links in the summary bar must have target="_top"
+ * so they don't load in the iframe
+ */
+%>
+
+<html><head>
+<title>Summary Bar</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<%
+    // try hard to avoid an error page in the iframe after shutdown
+    String action = request.getParameter("action");
+    String d = request.getParameter("refresh");
+    boolean shutdownSoon = "shutdownImmediate".equals(action) || "restartImmediate".equals(action);
+    if (!shutdownSoon) {
+        if (d == null || "".equals(d)) {
+            d = System.getProperty("routerconsole.summaryRefresh");
+            if (d == null || "".equals(d))
+                d = "60";
+        } else {
+            System.setProperty("routerconsole.summaryRefresh", d);
+        }
+        // we probably don't get here if d == "0" since caught in summary.jsp, but just
+        // to be sure...
+        if (!"0".equals(d)) {
+            // doesn't work for restart or shutdown with no expl. tunnels,
+            // since the call to ConfigRestartBean.renderStatus() hasn't happened yet...
+            long timeleft = net.i2p.router.web.ConfigRestartBean.getRestartTimeRemaining();
+            long delay = 60;
+            try { delay = Long.parseLong(d); } catch (NumberFormatException nfe) {}
+            if (delay*1000 < timeleft + 5000)
+                out.print("<meta http-equiv=\"refresh\" content=\"" + d + "\" />\n");
+            else
+                shutdownSoon = true;
+        }
+    }
+%>
+<link rel="stylesheet" href="default.css" type="text/css" />
+</head>
+
+<body style="margin: 0;">
+
+<div class="routersummary">
+<%@include file="summarynoframe.jsp" %>
+<%
+    // d and shutdownSoon defined above
+    if (!shutdownSoon) {
+        out.print("<hr /><p><form action=\"summaryframe.jsp\" method=\"GET\">\n");
+        if ("0".equals(d)) {
+            out.print("<b>Refresh (s):<b> <input size=\"3\" type=\"text\" name=\"refresh\" value=\"60\" />\n");
+            out.print("<button type=\"submit\">Enable</button>\n");
+        } else {
+            // this will load in the iframe but subsequent pages will not have the iframe
+            out.print("<input type=\"hidden\" name=\"refresh\" value=\"0\" />\n");
+            out.print("<button type=\"submit\">Disable " + d + "s Refresh</button>\n");
+        }
+        out.print("</form></p>\n");
+    }
+%>
+</div>
+
+</body>
+</html>
diff --git a/apps/routerconsole/jsp/summarynoframe.jsp b/apps/routerconsole/jsp/summarynoframe.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..f3f8b800246fa7f02d8dede10c92cba8dbd92708
--- /dev/null
+++ b/apps/routerconsole/jsp/summarynoframe.jsp
@@ -0,0 +1,100 @@
+<%@page import="net.i2p.router.web.SummaryHelper" %>
+<jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
+<jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
+
+<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
+<jsp:setProperty name="reseed" property="*" />
+<jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
+<jsp:setProperty name="update" property="*" />
+<jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
+<jsp:useBean class="net.i2p.router.web.ConfigUpdateHelper" id="uhelper" scope="request" />
+<jsp:setProperty name="uhelper" property="*" />
+<jsp:setProperty name="uhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
+
+ <center><b><a href="config.jsp" target="_top">Configuration</a>&nbsp;&nbsp;<a href="help.jsp" target="_top">Help</a></b></center>
+ <hr />
+
+ <u><b>General</b></u><br />
+ <b>Ident:</b> (<a title="Your router identity is <jsp:getProperty name="helper" property="ident" />, never reveal it to anyone" href="netdb.jsp?r=." target="_top">view</a>)<br />
+ <b>Version:</b> <jsp:getProperty name="helper" property="version" /><br />
+ <b>Uptime:</b> <jsp:getProperty name="helper" property="uptime" /><br />
+ <b>Now:</b> <jsp:getProperty name="helper" property="time" /><br />
+ <b>Reachability:</b> <a href="config.jsp#help" target="_top"><jsp:getProperty name="helper" property="reachability" /></a><%
+    if (helper.updateAvailable()) {
+        // display all the time so we display the final failure message
+        out.print("<br />" + update.getStatus());
+        if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"))) {
+        } else if(!update.isDone()) {
+            long nonce = new java.util.Random().nextLong();
+            String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
+            if (prev != null) System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
+            System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
+            String uri = request.getRequestURI();
+            out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
+            out.print("<input type=\"hidden\" name=\"updateNonce\" value=\"" + nonce + "\" />\n");
+            out.print("<input type=\"submit\" value=\"Download " + uhelper.getUpdateVersion() + " Update\" /></form></p>\n");
+        }
+    }
+ %>
+ <p>
+ <%=net.i2p.router.web.ConfigRestartBean.renderStatus(request.getRequestURI(), request.getParameter("action"), request.getParameter("consoleNonce"))%>
+ </p>
+ <hr />
+ 
+ <u><b><a href="peers.jsp" target="_top">Peers</a></b></u><br />
+ <b>Active:</b> <jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /><br />
+ <b>Fast:</b> <jsp:getProperty name="helper" property="fastPeers" /><br />
+ <b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
+ <b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
+ <b>Known:</b> <jsp:getProperty name="helper" property="allPeers" /><br /><%
+    if (helper.getActivePeers() <= 0) {
+        %><b><a href="config.jsp" target="_top">check your NAT/firewall</a></b><br /><%
+    }
+    // If showing the reseed link is allowed
+    if (helper.allowReseed()) {
+        if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
+            // While reseed occurring, show status message instead
+            out.print("<i>" + System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","") + "</i><br />");
+        } else {
+            // While no reseed occurring, show reseed link
+            long nonce = new java.util.Random().nextLong();
+            String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
+            if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
+            System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
+            String uri = request.getRequestURI();
+            out.print("<p><form action=\"" + uri + "\" method=\"GET\">\n");
+            out.print("<input type=\"hidden\" name=\"reseedNonce\" value=\"" + nonce + "\" />\n");
+            out.print("<button type=\"submit\" >Reseed</button></form></p>\n");
+        }
+    }
+    // If a new reseed ain't running, and the last reseed had errors, show error message
+    if ("false".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
+        String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
+        if (reseedErrorMessage.length() > 0) {
+            out.print("<i>" + reseedErrorMessage + "</i><br />");
+        }
+    }
+ %><hr />
+ 
+ <u><b><a href="config.jsp" title="Configure the bandwidth limits" target="_top">Bandwidth in/out</a></b></u><br />
+ <b>1s:</b> <jsp:getProperty name="helper" property="inboundSecondKBps" />/<jsp:getProperty name="helper" property="outboundSecondKBps" />KBps<br />
+ <b>5m:</b> <jsp:getProperty name="helper" property="inboundFiveMinuteKBps" />/<jsp:getProperty name="helper" property="outboundFiveMinuteKBps" />KBps<br />
+ <b>Total:</b> <jsp:getProperty name="helper" property="inboundLifetimeKBps" />/<jsp:getProperty name="helper" property="outboundLifetimeKBps" />KBps<br />
+ <b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br />
+ <hr />
+ 
+ <jsp:getProperty name="helper" property="destinations" />
+ 
+ <u><b>Tunnels in/out</b></u><br />
+ <b>Exploratory:</b> <jsp:getProperty name="helper" property="inboundTunnels" />/<jsp:getProperty name="helper" property="outboundTunnels" /><br />
+ <b>Client:</b> <jsp:getProperty name="helper" property="inboundClientTunnels" />/<jsp:getProperty name="helper" property="outboundClientTunnels" /><br />
+ <b>Participating:</b> <jsp:getProperty name="helper" property="participatingTunnels" /><br />
+ <hr />
+ 
+ <u><b>Congestion</b></u><br />
+ <b>Job lag:</b> <jsp:getProperty name="helper" property="jobLag" /><br />
+ <b>Message delay:</b> <jsp:getProperty name="helper" property="messageDelay" /><br />
+ <b>Tunnel lag:</b> <jsp:getProperty name="helper" property="tunnelLag" /><br />
+ <b>Handle backlog:</b> <jsp:getProperty name="helper" property="inboundBacklog" /><br />
+ <b><jsp:getProperty name="helper" property="tunnelStatus" /></b><br />
+ 
diff --git a/apps/sam/java/build.xml b/apps/sam/java/build.xml
index bb692f2db3c3d0c8ef0b5cd8d7bad0fe95ac8b89..78f9cdfb0a26af4c91cb8a504b8f1b50d5b0e1f8 100644
--- a/apps/sam/java/build.xml
+++ b/apps/sam/java/build.xml
@@ -21,6 +21,7 @@
             </classpath>
         </depend>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="depend">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -28,14 +29,18 @@
             srcdir="./src" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="compileTest" depends="compile">
         <javac 
             srcdir="./test" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile">
         <jar destfile="./build/sam.jar" basedir="./build/obj" includes="**/*.class">
diff --git a/apps/streaming/java/build.xml b/apps/streaming/java/build.xml
index a32ca077c336db1dc1175ef5901b8adebc7fad9b..7740a60226169b9facff377f6d00c040373004c2 100644
--- a/apps/streaming/java/build.xml
+++ b/apps/streaming/java/build.xml
@@ -21,6 +21,7 @@
             </classpath>
         </depend>
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile" depends="depend">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -28,14 +29,18 @@
             srcdir="./src" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="compileTest" depends="compile">
         <javac 
             srcdir="./test" 
             debug="true" deprecation="on" source="1.5" target="1.5" 
             destdir="./build/obj" 
-            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
+            classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="builddep, compile">
         <jar destfile="./build/streaming.jar" basedir="./build/obj" includes="**/*.class" />
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
index 2d198ad66e75bc4af62fa1aa24fe7b2ab293dd60..f4b00c1b567592d816a449eb1dc0c58dd5771fcd 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionHandler.java
@@ -2,8 +2,6 @@ package net.i2p.client.streaming;
 
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
-import java.util.ArrayList;
-import java.util.List;
 
 import net.i2p.I2PAppContext;
 import net.i2p.util.Log;
@@ -126,7 +124,7 @@ class ConnectionHandler {
                 if (timeoutMs <= 0) {
                     try {
                        syn = _synQueue.take(); // waits forever
-                    } catch (InterruptedException ie) { break;}
+                    } catch (InterruptedException ie) { } // { break;}
                 } else {
                     long remaining = expiration - _context.clock().now();
                     // (dont think this applies anymore for LinkedBlockingQueue)
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
index 197b9275412b1f8fa538c78e1a518b3b421d0de5..827be5b04fb6e035629c582a533716eae2eea625 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
@@ -213,7 +213,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
                         timeRemaining = 10*1000;
                     wait(timeRemaining);
                 }
-            } catch (InterruptedException ie) { break; }
+            } catch (InterruptedException ie) { }//{ break; }
         }
         if (!writeSuccessful())
             releasePayload();
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
index 3ed8327ddfb534bfd722abd325a6a4919602ab10..a2aacf82e2c0ea56daee7d2e3a9a0124e5406ccc 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java
@@ -55,7 +55,7 @@ class SchedulerChooser {
         }
         
         public void eventOccurred(Connection con) {
-            _log.log(Log.CRIT, "Yell at jrandom: Event occurred on " + con, new Exception("source"));
+            _log.log(Log.ERROR, "Yell at jrandom: Event occurred on " + con, new Exception("source"));
         }
         public boolean accept(Connection con) { return true; }
     };
diff --git a/apps/susidns/src/build.xml b/apps/susidns/src/build.xml
index d3f5f1662c72284c6677379b447a60970f436753..be34b4879e3fa706f1325a0e2d4d670b89b109bf 100644
--- a/apps/susidns/src/build.xml
+++ b/apps/susidns/src/build.xml
@@ -22,10 +22,13 @@
         <pathelement location="${lib}/ant.jar" />
         <pathelement location="../../../core/java/build/i2p.jar" />
  	</path>
+	<property name="javac.compilerargs" value="" />
  	<target name="compile">
 		<mkdir dir="${bin}" />
 		<javac debug="true" deprecation="on" source="1.5" target="1.5"
- 			classpathref="cp" destdir="${bin}" srcdir="${src}" includes="**/*.java" />
+ 			classpathref="cp" destdir="${bin}" srcdir="${src}" includes="**/*.java" >
+			<compilerarg line="${javac.compilerargs}" />
+		</javac>
 	</target>
     <target name="precompilejsp" unless="precompilejsp.uptodate">
         <delete file="WEB-INF/web-fragment.xml" />
@@ -45,6 +48,7 @@
         </java>
         <javac debug="true" deprecation="on" source="1.5" target="1.5" 
                destdir="${bin}" srcdir="./WEB-INF/classes" includes="**/*.java" classpathref="cp">
+		<compilerarg line="${javac.compilerargs}" />
          </javac>
         <copy file="WEB-INF/web-template.xml" tofile="WEB-INF/web-out.xml" />
         <loadfile property="jspc.web.fragment" srcfile="WEB-INF/web-fragment.xml" />
diff --git a/apps/susimail/build.xml b/apps/susimail/build.xml
index abf2a88cf71c2829a9ca9beeada5ad658f02e7f7..886f9471f6425982bb3f71a2747118fa02c0ac31 100644
--- a/apps/susimail/build.xml
+++ b/apps/susimail/build.xml
@@ -5,11 +5,13 @@
     <target name="builddep">
         <ant dir="../jetty/" target="build" />
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile">
         <javac 
             srcdir="./src/src"
             debug="true" deprecation="off" source="1.5" target="1.5" 
             destdir="./src/WEB-INF/classes">
+            <compilerarg line="${javac.compilerargs}" />
             <classpath>
                 <pathelement location="../jetty/jettylib/javax.servlet.jar" />
                 <pathelement location="../jetty/jettylib/org.mortbay.jetty.jar" />
diff --git a/apps/systray/java/build.xml b/apps/systray/java/build.xml
index 19db56d5ca76c2b7424ec3ef21a232dae0545005..1597f20b359a24ec9d940c847cd2057b1fef1df3 100644
--- a/apps/systray/java/build.xml
+++ b/apps/systray/java/build.xml
@@ -5,6 +5,7 @@
     <target name="builddep">
         <ant dir="../../../core/java/" target="build" />
     </target>
+    <property name="javac.compilerargs" value="" />
     <target name="compile">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
@@ -16,7 +17,9 @@
             target="1.5"
             destdir="./build/obj"
             includes="**/*.java"
-            classpath="./lib/systray4j.jar:../../../core/java/build/i2p.jar" />
+            classpath="./lib/systray4j.jar:../../../core/java/build/i2p.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile" unless="jar.uptodate">
         <mkdir dir="./build/jar_temp" />
diff --git a/build.xml b/build.xml
index 3a30ff8e7f8d5684907200a377179c624d863152..453b0d44c3c415927179845757d4a3269f2e131d 100644
--- a/build.xml
+++ b/build.xml
@@ -1,6 +1,13 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
 <project basedir="." default="all" name="i2p">
    
+    <!-- Things you might want to change -->
+    <!-- This keeps gcj a lot quieter -->
+    <!--
+        <property name="javac.compilerargs" value="-warn:-unchecked,raw,unused,serial" />
+    -->
+
+    <!-- You probably don't want to change anything from here down -->
     <target name="all" >
         <echo message="Useful targets: " />
         <echo message="  pkg:       distclean then package everything up (installer, clean tarball, update tarball)" />
@@ -9,6 +16,7 @@
         <echo message="  tarball:   tar the full install into i2p.tar.bz2 (extracts to build a new clean install)" />
         <echo message="  updater:   tar the built i2p specific files into an i2pupdate.zip (extracts safely over existing installs)" />
         <echo message="  updaterWithJetty:   tar the built i2p specific files and jetty into an i2pupdate.zip (extracts safely over existing installs)" />
+        <echo message="  updaterWithJettyFixes: updater including local jetty patches" />
         <echo message="  updaterSmall:   updater with the essentials only - no SAM, i2psnark, SusiMail, SusiDNS, news.xml, or history.txt" />
         <echo message="  updaterRouter:  updater with the i2p.jar and router.jar only" />
         <echo message="  distclean: clean up all derived files" />
@@ -189,7 +197,7 @@
             <fileset dir="." includes="**/*.java~" />
         </delete>
     </target>
-    <target name="pkg" depends="distclean, updater, tarball, installer" />
+    <target name="pkg" depends="distclean, updaterWithJettyFixes, tarball, installer" />
     <target name="pkgclean">
         <delete dir="pkg-temp" />
         <delete>
@@ -321,6 +329,9 @@
     <target name="updaterWithJetty" depends="prepjupdate">
         <zip destfile="i2pupdate.zip" basedir="pkg-temp" />
     </target>
+    <target name="updaterWithJettyFixes" depends="prepjupdatefixes">
+        <zip destfile="i2pupdate.zip" basedir="pkg-temp" />
+    </target>
     <target name="updaterSmall" depends="prepupdateSmall">
         <zip destfile="i2pupdate.zip" basedir="pkg-temp" />
     </target>
@@ -379,6 +390,9 @@
         <copy file="build/javax.servlet.jar" todir="pkg-temp/lib/" />
         <copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
     </target>
+    <target name="prepjupdatefixes" depends="prepupdate, buildWEB">
+        <copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
+    </target>
     <target name="installer" depends="preppkg">
         <taskdef name="izpack" classpath="${basedir}/installer/lib/izpack/standalone-compiler.jar" classname="com.izforge.izpack.ant.IzPackTask" />
         <jar destfile="./pkg-temp/lib/copy.jar" basedir="./core/java/build/obj" includes="net/i2p/util/Copy.class net/i2p/util/FileUtil.class">
diff --git a/core/java/build.xml b/core/java/build.xml
index 28adbf9df288733c0aabd7aae49c4a432cea664d..a52dcb63542d68e54a4159a24022a53761bc9854 100644
--- a/core/java/build.xml
+++ b/core/java/build.xml
@@ -15,15 +15,21 @@
             destdir="./build/obj" >
         </depend>
     </target>
+    <!-- only used if not set by a higher build.xml -->
+    <property name="javac.compilerargs" 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" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="compileTest">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
-        <javac srcdir="./src:./test" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" />
+        <javac srcdir="./src:./test" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile">
         <jar destfile="./build/i2p.jar" basedir="./build/obj" includes="**/*.class" />
diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java
index 6c924fe1092d4c0e2e03679c59b8ebd7fad3a00e..685abc2f059542efc30e4b29739c6709410d32a2 100644
--- a/core/java/src/net/i2p/CoreVersion.java
+++ b/core/java/src/net/i2p/CoreVersion.java
@@ -15,7 +15,7 @@ package net.i2p;
  */
 public class CoreVersion {
     public final static String ID = "$Revision: 1.72 $ $Date: 2008-08-24 12:00:00 $";
-    public final static String VERSION = "0.7.1";
+    public final static String VERSION = "0.7.2";
 
     public static void main(String args[]) {
         System.out.println("I2P Core version: " + VERSION);
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 9e42eef5fa3e983f083c76c0b850c9da444e9b2f..a5d8ed94de3003a0e21717c41eeb0aaac2a5160b 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -16,7 +16,6 @@ import java.net.Socket;
 import java.net.UnknownHostException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -90,7 +89,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
     protected I2PAppContext _context;
 
     /** monitor for waiting until a lease set has been granted */
-    private Object _leaseSetWait = new Object();
+    private final Object _leaseSetWait = new Object();
 
     /** whether the session connection has already been closed (or not yet opened) */
     protected boolean _closed;
@@ -101,7 +100,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
     /** have we received the current date from the router yet? */
     private boolean _dateReceived;
     /** lock that we wait upon, that the SetDateMessageHandler notifies */
-    private Object _dateReceivedLock = new Object();
+    private final Object _dateReceivedLock = new Object();
     
     /** 
      * thread that we tell when new messages are available who then tells us 
@@ -253,6 +252,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
         try {
             if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "connect begin to " + _hostname + ":" + _portNum);
             _socket = new Socket(_hostname, _portNum);
+            // _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
             _out = _socket.getOutputStream();
             synchronized (_out) {
                 _out.write(I2PClient.PROTOCOL_BYTE);
diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
index a3e5e57a7f870087cff8648ca15e39bce4d7d3d9..eda48e75444cb6b1c190825a877642d6f0f58cf0 100644
--- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
@@ -4,18 +4,16 @@ package net.i2p.client;
  * public domain
  */
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.HashSet;
 import java.util.Properties;
 import java.util.Set;
 
+import java.util.concurrent.atomic.AtomicBoolean;
 import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
 import net.i2p.data.SessionKey;
-import net.i2p.data.SessionTag;
 import net.i2p.data.i2cp.MessagePayloadMessage;
 import net.i2p.util.Log;
 import net.i2p.util.SimpleScheduler;
@@ -97,6 +95,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
      *         255 disallowed
      *  @param port 1-65535 or PORT_ANY for all
      */
+    @Override
     public void addSessionListener(I2PSessionListener lsnr, int proto, int port) {
         _demultiplexer.addListener(lsnr, proto, port);
     }
@@ -107,11 +106,13 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
      *  @param proto 1-254 or 0 for all; 255 disallowed
      *  @param port 1-65535 or 0 for all
      */
+    @Override
     public void addMuxedSessionListener(I2PSessionMuxedListener l, int proto, int port) {
         _demultiplexer.addMuxedListener(l, proto, port);
     }
 
     /** removes the specified listener (only) */
+    @Override
     public void removeListener(int proto, int port) {
         _demultiplexer.removeListener(proto, port);
     }
@@ -149,6 +150,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
      *  @param fromPort 1-65535 or 0 for unset
      *  @param toPort 1-65535 or 0 for unset
      */
+    @Override
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
                                SessionKey keyUsed, Set tagsSent, long expires,
                                int proto, int fromPort, int toPort)
@@ -198,24 +200,36 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
 
     protected class MuxedAvailabilityNotifier extends AvailabilityNotifier {
         private LinkedBlockingQueue<MsgData> _msgs;
-        private boolean _alive;
+        private AtomicBoolean _alive = new AtomicBoolean(false);
         private static final int POISON_SIZE = -99999;
  
         public MuxedAvailabilityNotifier() {
             _msgs = new LinkedBlockingQueue();
         }
-        
-        public void stopNotifying() { 
-            _msgs.clear(); 
-            if (_alive) {
-                _alive = false; 
-                try {
-                    _msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0));
-                } catch (InterruptedException ie) {}
+
+        @Override
+        public void stopNotifying() {
+            boolean again = true;
+            _msgs.clear();
+            // Thread.yield();
+            if (_alive.get()) {
+                // System.out.println("I2PSessionMuxedImpl.stopNotifying()");
+                while(again) {
+                    _msgs.clear();
+                    try {
+                        _msgs.put(new MsgData(0, POISON_SIZE, 0, 0, 0));
+                        again = false;
+                        // System.out.println("I2PSessionMuxedImpl.stopNotifying() success.");
+                    } catch (InterruptedException ie) {
+                        continue;
+                    }
+                }
             }
+            _alive.set(false);
         }
         
         /** unused */
+        @Override
         public void available(long msgId, int size) { throw new IllegalArgumentException("no"); }
 
         public void available(long msgId, int size, int proto, int fromPort, int toPort) {
@@ -224,20 +238,24 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
             } catch (InterruptedException ie) {}
         }
 
+        @Override
         public void run() {
-            _alive = true;
-            while (true) {
-                MsgData msg;
+            MsgData msg;
+            _alive.set(true);
+            while (_alive.get()) {
                 try {
                     msg = _msgs.take();
                 } catch (InterruptedException ie) {
+                    _log.debug("I2PSessionMuxedImpl.run() InterruptedException " + String.valueOf(_msgs.size()) + " Messages, Alive " + _alive.toString());
                     continue;
                 }
-                if (msg.size == POISON_SIZE)
+                if (msg.size == POISON_SIZE) {
+                    // System.out.println("I2PSessionMuxedImpl.run() POISONED");
                     break;
+                }
                 try {
-                    _demultiplexer.messageAvailable(I2PSessionMuxedImpl.this, msg.id,
-                                                    msg.size, msg.proto, msg.fromPort, msg.toPort);
+                    _demultiplexer.messageAvailable(I2PSessionMuxedImpl.this,
+                        msg.id, msg.size, msg.proto, msg.fromPort, msg.toPort);
                 } catch (Exception e) {
                     _log.error("Error notifying app of message availability");
                 }
diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java
index 8a05dd9569bb744599f7d0e8dc1e51d8e6a786f4..66d10da94471cf3bbcea595eb8e6246fc23be417 100644
--- a/core/java/src/net/i2p/data/LeaseSet.java
+++ b/core/java/src/net/i2p/data/LeaseSet.java
@@ -67,7 +67,7 @@ public class LeaseSet extends DataStructureImpl {
     private boolean _checked;
 
     /** This seems like plenty  */
-    private final static int MAX_LEASES = 6;
+    public final static int MAX_LEASES = 6;
 
     public LeaseSet() {
         setDestination(null);
diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java
index 5f0e8d5e9fcdfcf9ed1470c3d477324ef6cc1b8b..c3c18866a7d251cde86d187735d02fa4cb80c668 100644
--- a/core/java/src/net/i2p/util/EepGet.java
+++ b/core/java/src/net/i2p/util/EepGet.java
@@ -159,6 +159,9 @@ public class EepGet {
                     markSize = Integer.parseInt(args[i+1]);
                     lineLen = Integer.parseInt(args[i+2]);
                     i += 2;
+                } else if (args[i].startsWith("-")) {
+                    usage();
+                    return;
                 } else {
                     url = args[i];
                 }
diff --git a/history.txt b/history.txt
index ff522acbd5807edfb0cd6a8153a61ac4a86c9f6f..cfc2af8535db50d12921eb81358c93d7736c5c8c 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,68 @@
+2009-04-27 sponge
+    * more BOB fixes, complete with warnings when things go wrong, and
+      success messages when things turn around and go right. Terminates
+      early so that applications wait no more than 10 seconds or so.
+    * Reversed a few earlier patches that caused some odd behavior.
+    * Changed some core println()'s to debugging messages.
+
+2009-04-27 zzz
+    * Build files:
+      - New updaterWithJettyFixes target, build it for pkg
+      - Pass compiler args down from top build.xml
+    * GarlicMessageBuilder: Reduce bundled tags to 40 (was 100)
+    * i2psnark: Add Postman2 tracker
+    * I2PTunnel: Allow spaces in dest and proxy lists
+    * NetDb:
+      - Adjust RouterInfo expiration down to control memory usage
+      - Display LeaseSets and RouterInfos on separate console pages
+    * NTCP:
+      - Correct the meanings of the i2np.ntcp.autoip and i2np.ntcp.autoport
+        advanced config. If you have one of these set but not the other, you
+        will have to adjust your configuration on config.jsp.
+    * RouterConsole: iframe tweaks
+    * StatisticsManager: Cleanup
+    * Streaming: Don't let jrandom yell so loud
+
+2009-04-25 sponge
+    * I2PSessionMuxedImpl atomic fixes
+    * BOB fixes. This should be the final bug wack. Good Luck to everybody!
+
+2009-04-23 zzz
+    * Blocklist: cleanup
+    * eepget: handle -h, --help, bad options, etc.
+      (http://forum.i2p/viewtopic.php?p=16261#16261)
+    * Fragmenter: don't re-throw the corrupt fragment IllegalStateException,
+      to limit the damage - root cause still not found
+    * i2psnark: (http://forum.i2p/viewtopic.php?t=3317)
+      - Change file limit to 512 (was 256)
+      - Change size limit to 10GB (was 5GB)
+      - Change request size to 16KB (was 32KB)
+      - Change pipeline to 5 (was 3)
+    * logs.jsp: Move version info to the top
+    * Jetty: Fix temp dir name handling on windows, which was
+      causing susidns not to start
+      (http://forum.i2p/viewtopic.php?t=3364)
+    * NTCP: Prevent IllegalStateException
+    * PeerProfile:
+      - Replace a hot lock with concurrent RW lock
+      - Rewrite ugly IP Restriction code
+      - Also use transport IP in restriction code
+    * RouterConsole: Make summary bar a refreshing iframe
+    * Transport: Start the previously unused CleanupUnreachable
+
+2009-04-21 sponge
+    * Code janitor work, basic corrections involving @Override, and
+      appling final where it is important. Also fixed some equals methods
+      and commented places that need fixing.
+
+2009-04-18 Complication
+    * Fix typo in "news.xml", no build number increase.
+
+* 2009-04-18  0.7.2 released
+
+2009-04-18 Complication
+    * Update versions, package release
+
 2009-04-17 sponge
     * fixed setIP, just be sure to distclean before building :-)
     * more lint taken care of as well.
diff --git a/initialNews.xml b/initialNews.xml
index 0306dcd7b25a9b1c8a0e45c4f90ebb43c28d46ab..ed08caba2dec5edbebfea6d4e5b8c269b4a8d78e 100644
--- a/initialNews.xml
+++ b/initialNews.xml
@@ -1,5 +1,5 @@
-<i2p.news date="$Date: 2009-03-29 00:00:00 $">
- <i2p.release version="0.7.1" date="2009/03/29" minVersion="0.6"
+<i2p.news date="$Date: 2009-04-18 00:00:00 $">
+ <i2p.release version="0.7.2" date="2009/04/18" minVersion="0.6"
             />
 <center><h2>Congratulations on getting I2P installed!</h2></center>
 <ul>
diff --git a/installer/install.xml b/installer/install.xml
index 125e92231c62cf72db19b979c50c5abc34d30fbf..74cd95cb4dc8a6dcd7998cd7bf18f8060de99e4a 100644
--- a/installer/install.xml
+++ b/installer/install.xml
@@ -4,7 +4,7 @@
 
     <info>
         <appname>i2p</appname>
-        <appversion>0.7.1</appversion>
+        <appversion>0.7.2</appversion>
         <authors>
             <author name="I2P" email="http://forum.i2p2.de/"/>
         </authors>
diff --git a/installer/lib/launch4j/build.xml b/installer/lib/launch4j/build.xml
index 42c77a225f04a9365f2eaba3d5e71851083f5bc0..767e7c59271a9dfac33f24e3aef5fe6620ea52b2 100644
--- a/installer/lib/launch4j/build.xml
+++ b/installer/lib/launch4j/build.xml
@@ -7,6 +7,7 @@
 	<property name="dist" location="${user.home}/dist/${ant.project.name}" />
 	<property name="jar" location="./${ant.project.name}.jar" />
 	<property name="launch4j.dir" location="." />
+	<property name="javac.compilerargs" value="" />
 
 	<path id="dist.classpath">
 		<pathelement path="${build}" />
@@ -23,6 +24,8 @@
 
 	<target name="compile" depends="init" description="compile the source">
 		<javac srcdir="${src}" destdir="${build}" classpathref="dist.classpath" source="1.4" debug="on" />
+			<compilerarg line="${javac.compilerargs}" />
+		</javac>
 		<copy todir="${build}/images">
 			<fileset dir="${src}/images">
 				<include name="**/*" />
diff --git a/installer/resources/jetty.xml b/installer/resources/jetty.xml
index b55831e7440595b1e8c2479fc844232e0af33f96..a9c04d9644858d7c28618488851a8d479583034d 100644
--- a/installer/resources/jetty.xml
+++ b/installer/resources/jetty.xml
@@ -1,6 +1,32 @@
 <?xml version="1.0" encoding="ISO-8859-1" ?> 
 <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd">
 
+<!-- ========================================================================= -->
+<!-- This file configures the Jetty server.                                    -->
+<!-- All changes require a restart of I2P.                                     -->
+<!--                                                                           -->
+<!-- Commonly changed settings:                                                -->
+<!--   * host: Change 127.0.0.1 to 0.0.0.0 in the addListener section          -->
+<!--           to access the server directly (bypassing i2p)                   -->
+<!--           from other computers. The included version of Jetty has         -->
+<!--           been patched to allow IPv6 addresses as well,                   -->
+<!--           enclosed in brackets e.g. [::1]                                 -->
+<!--   * port: Default 7658 in the addListener section                         -->
+<!--   * threads: Raise MaxThreads in the addListener section                  -->
+<!--           if you have a high-traffic site and get a lot of warnings.      -->
+<!--   * Uncomment the addWebApplications section to use to enable             -->
+<!--           war files placed in the webapps/ dir.                           -->
+<!--                                                                           -->
+<!-- I2P uses Jetty 5.1.12. We have no plans to upgrade to Jetty 6, due to     -->
+<!-- the significant changes in the API. If you need web server features not   -->
+<!-- found in Jetty 5, you may install and run Jetty 6 in a different JVM,     -->
+<!-- or run any other web server such as Apache. If you do run another         -->
+<!-- web server instead, be sure and disable the Jetty 5 server for your       -->
+<!-- eepsite on http://127.0.0.1/configclients.jsp .                           -->
+<!--                                                                           -->
+<!-- Jetty errors and warnings will appear in wrapper.log, check there         -->
+<!-- to diagnose problems.                                                     -->
+<!-- ========================================================================= -->
 
 <!-- =============================================================== -->
 <!-- Configure the Jetty Server                                      -->
diff --git a/news.xml b/news.xml
index b85e0f130898fcd25ef3c14e8febbf410687db6b..e5bb34a21a40a775cdb0e8fbdab146c5927a6037 100644
--- a/news.xml
+++ b/news.xml
@@ -1,5 +1,5 @@
-<i2p.news date="$Date: 2009-03-29 00:00:00 $">
- <i2p.release version="0.7.1" date="2009/03/29" minVersion="0.6"
+<i2p.news date="$Date: 2009-04-18 00:00:00 $">
+ <i2p.release version="0.7.2" date="2009/04/18" minVersion="0.6"
               anonurl="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/i2pupdate.sud"
               publicurl="http://dev.i2p.net/i2p/i2pupdate.sud"
               anonannouncement="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/pipermail/i2p/2005-September/000878.html" 
@@ -11,25 +11,25 @@
             publiclogs="http://www.i2p.net/meeting141" />
 <p>
 &#149;
-2009-03-29: <b>0.7.1 <a href="http://www.i2p2.i2p/release-0.7.1.html">Released</a></b>
+2009-04-18: <b>0.7.2 <a href="http://www.i2p2.i2p/release-0.7.2.html">Released</a></b>
 </p><p>
-The 0.7.1 release optimizes I2P towards better performance
-and introduces new features.
+The 0.7.2 release fixes multiple bugs and potential problems
+in I2P, while preparing ground for new functionality.
 </p><p>
-Multiple bugs are fixed, replacements to the SimpleTimer class
-should waste less time on object locking. Some old components
-are dropped and several classes refactored to avoid repeating code.
+Threading issues with the SimpleTimer class should no longer occur,
+the NTCP transport should no longer encounter null pointer exceptions,
+and "abandoned" tunnels should finish operation correctly.
 </p><p>
-Support for encrypted LeaseSets (for creation of links over I2P
-which an adversary cannot obstruct by attacking its gateways)
-becomes more complete. New tunnel types like IRC server tunnels
-and new options like delayed start and idling of tunnels
-also gain support, along with improved usability of the I2P
-Socks proxy mechanism.
+Support is added for accessing the Router Console over IPv6,
+a new message type enables I2CP applications to query
+the router's bandwidth limits, an experimental desktop interface
+for managing the router is included for the first time
+(but not enabled automatically yet) and participation of a single peer
+in too many tunnels is prevented to improve reliability and safety.
 </p><p>
-Work continues on streamlining and expanding the Router Console,
-on the BOB protocol, on I2P ports for Debian and Slackware Linux,
-on the I2PSnark client, on TCP connection properties
-and multiple other fronts. Updating is highly recommended.
+Besides other maintenance work, several old statistics calculators
+are dropped to make router profiles smaller and quicker, while
+new build scripts are supplied for I2P's big integer math library.
+Updating is highly recommended.
 </p>
 </i2p.news>
diff --git a/router/java/build.xml b/router/java/build.xml
index 95878e468fa88434d47c13d64903a8042ede1020..70f83fbd7027df90779b65bacb65554d1467cc9f 100644
--- a/router/java/build.xml
+++ b/router/java/build.xml
@@ -22,15 +22,21 @@
             </classpath>
         </depend>
     </target>
+    <!-- only used if not set by a higher build.xml -->
+    <property name="javac.compilerargs" 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" classpath="../../core/java/build/i2p.jar" />
+        <javac srcdir="./src" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="../../core/java/build/i2p.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="compileTest" depends="builddeptest">
         <mkdir dir="./build" />
         <mkdir dir="./build/obj" />
-        <javac srcdir="./src:./test" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="../../core/java/build/i2ptest.jar" />
+        <javac srcdir="./src:./test" debug="true" source="1.5" target="1.5" deprecation="on" destdir="./build/obj" classpath="../../core/java/build/i2ptest.jar" >
+            <compilerarg line="${javac.compilerargs}" />
+        </javac>
     </target>
     <target name="jar" depends="compile">
         <jar destfile="./build/router.jar" basedir="./build/obj" includes="**/*.class" />
diff --git a/router/java/src/net/i2p/data/i2np/DataMessage.java b/router/java/src/net/i2p/data/i2np/DataMessage.java
index 7e839117fa040ddadb4aa0bb85d1f789f3134b20..8ba6b13460e579a88e5898e44859a49242574595 100644
--- a/router/java/src/net/i2p/data/i2np/DataMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DataMessage.java
@@ -12,7 +12,7 @@ import java.io.IOException;
 
 import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
-import net.i2p.util.Log;
+// import net.i2p.util.Log;
 
 /**
  * Defines a message containing arbitrary bytes of data
@@ -20,11 +20,11 @@ import net.i2p.util.Log;
  * @author jrandom
  */
 public class DataMessage extends I2NPMessageImpl {
-    private final static Log _log = new Log(DataMessage.class);
+    // private final static Log _log = new Log(DataMessage.class);
     public final static int MESSAGE_TYPE = 20;
     private byte _data[];
     
-    private static final int MAX_SIZE = 64*1024;
+    // private static final int MAX_SIZE = 64*1024;  // LINT -- field hides another field, and not used
     
     public DataMessage(I2PAppContext context) {
         super(context);
@@ -81,6 +81,7 @@ public class DataMessage extends I2NPMessageImpl {
         return curIndex;
     }
     
+    @Override
     protected void written() {
         super.written();
         _data = null;
@@ -88,10 +89,12 @@ public class DataMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getData());
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DataMessage) ) {
             DataMessage msg = (DataMessage)object;
@@ -101,6 +104,7 @@ public class DataMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DataMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java
index 4a6da92e3f148ad1342cca30131e6fed4a8e6b28..79dec2ee9b41d0ba257e3d2fbf0b9d2e934fdfee 100644
--- a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java
@@ -215,6 +215,7 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getSearchKey()) +
                DataHelper.hashCode(getFrom()) +
@@ -222,6 +223,7 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
                DataHelper.hashCode(_dontIncludePeers);
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DatabaseLookupMessage) ) {
             DatabaseLookupMessage msg = (DatabaseLookupMessage)object;
@@ -234,6 +236,7 @@ public class DatabaseLookupMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DatabaseLookupMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/DatabaseSearchReplyMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseSearchReplyMessage.java
index 2f1bc87e708f9253a97b0926bbcedbaeae490652..c8dc9aa901bc651814ce1124e2318a136e8cb5cf 100644
--- a/router/java/src/net/i2p/data/i2np/DatabaseSearchReplyMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DatabaseSearchReplyMessage.java
@@ -110,6 +110,7 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DatabaseSearchReplyMessage) ) {
             DatabaseSearchReplyMessage msg = (DatabaseSearchReplyMessage)object;
@@ -121,12 +122,14 @@ public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getSearchKey()) +
         DataHelper.hashCode(getFromHash()) +
         DataHelper.hashCode(_peerHashes);
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DatabaseSearchReplyMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java
index cb838daca3d6898ecdaa34fd6ea6e56c37059442..04ef417d3adfc776afcd465daa0f10452b83d378 100644
--- a/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DatabaseStoreMessage.java
@@ -231,6 +231,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getKey()) +
                DataHelper.hashCode(getLeaseSet()) +
@@ -241,6 +242,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
                DataHelper.hashCode(getReplyGateway());
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DatabaseStoreMessage) ) {
             DatabaseStoreMessage msg = (DatabaseStoreMessage)object;
@@ -256,6 +258,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DatabaseStoreMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/DateMessage.java b/router/java/src/net/i2p/data/i2np/DateMessage.java
index 554c31ef29c040cf604b2ebf02de3bb639a45ce3..b5388eda5ae8373d0e6c8607125511d4fd99b8f0 100644
--- a/router/java/src/net/i2p/data/i2np/DateMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DateMessage.java
@@ -53,10 +53,12 @@ public class DateMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return (int)getNow();
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DateMessage) ) {
             DateMessage msg = (DateMessage)object;
@@ -66,6 +68,7 @@ public class DateMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DateMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/DeliveryInstructions.java b/router/java/src/net/i2p/data/i2np/DeliveryInstructions.java
index 3e46fc8fe5b3fa95f6397fe019b416463d3b0c77..8ad3ddff28c269a48c07b605acf182d2c84d926f 100644
--- a/router/java/src/net/i2p/data/i2np/DeliveryInstructions.java
+++ b/router/java/src/net/i2p/data/i2np/DeliveryInstructions.java
@@ -350,6 +350,7 @@ public class DeliveryInstructions extends DataStructureImpl {
                + getAdditionalInfoSize();
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof DeliveryInstructions))
             return false;
@@ -364,6 +365,7 @@ public class DeliveryInstructions extends DataStructureImpl {
                DataHelper.eq(getTunnelId(), instr.getTunnelId());
     }
     
+    @Override
     public int hashCode() {
         return (int)getDelaySeconds() +
                     getDeliveryMode() +
@@ -373,6 +375,7 @@ public class DeliveryInstructions extends DataStructureImpl {
                     DataHelper.hashCode(getTunnelId());
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(128);
         buf.append("[DeliveryInstructions: ");
diff --git a/router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java b/router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java
index 9f1d61b45acef64443bb8a4695be1deb14470582..ff8ad12cbdc4da0f3997379a6b8e552d481d116e 100644
--- a/router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java
+++ b/router/java/src/net/i2p/data/i2np/DeliveryStatusMessage.java
@@ -64,10 +64,12 @@ public class DeliveryStatusMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return (int)getMessageId() + (int)getArrival();
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof DeliveryStatusMessage) ) {
             DeliveryStatusMessage msg = (DeliveryStatusMessage)object;
@@ -78,6 +80,7 @@ public class DeliveryStatusMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[DeliveryStatusMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java b/router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java
index 8f5763ae4819af4c7e30715ee8d001abd4d6cf67..4b3f919c84d9a8ed1839f9240ee315558f1162c7 100644
--- a/router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java
+++ b/router/java/src/net/i2p/data/i2np/EndPointPrivateKey.java
@@ -44,17 +44,20 @@ public class EndPointPrivateKey extends DataStructureImpl {
 	_key.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof EndPointPublicKey))
             return false;
 	return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
     }
     
+    @Override
     public int hashCode() {
 	if (_key == null) return 0;
         return getKey().hashCode(); 
     }
     
+    @Override
     public String toString() {
         return "[EndPointPrivateKey: " + getKey() + "]";
     }
diff --git a/router/java/src/net/i2p/data/i2np/EndPointPublicKey.java b/router/java/src/net/i2p/data/i2np/EndPointPublicKey.java
index 3920d0bda2ae6a3dedb5eb21efd2853dab6085a4..a366bf73bf83943741ae31dfd9c3b682f4c4df68 100644
--- a/router/java/src/net/i2p/data/i2np/EndPointPublicKey.java
+++ b/router/java/src/net/i2p/data/i2np/EndPointPublicKey.java
@@ -44,17 +44,20 @@ public class EndPointPublicKey extends DataStructureImpl {
 	_key.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof EndPointPublicKey))
             return false;
 	return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
     }
     
+    @Override
     public int hashCode() {
 	if (_key == null) return 0;
         return getKey().hashCode(); 
     }
     
+    @Override
     public String toString() {
         return "[EndPointPublicKey: " + getKey() + "]";
     }
diff --git a/router/java/src/net/i2p/data/i2np/GarlicClove.java b/router/java/src/net/i2p/data/i2np/GarlicClove.java
index cb6808756fc3a5b4129ddd5aa7930d121cebf393..aa37994a07fe0539bbc26a6ad12b3beff4048a54 100644
--- a/router/java/src/net/i2p/data/i2np/GarlicClove.java
+++ b/router/java/src/net/i2p/data/i2np/GarlicClove.java
@@ -156,6 +156,7 @@ public class GarlicClove extends DataStructureImpl {
             _log.debug("Written cert: " + _certificate);
     }
 
+    @Override
     public byte[] toByteArray() {
         byte rv[] = new byte[estimateSize()];
         int offset = 0;
@@ -186,6 +187,7 @@ public class GarlicClove extends DataStructureImpl {
                + _certificate.size(); // certificate
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof GarlicClove))
             return false;
@@ -197,6 +199,7 @@ public class GarlicClove extends DataStructureImpl {
                DataHelper.eq(getInstructions(),  clove.getInstructions());
     }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getCertificate()) +
                (int)getCloveId() +
@@ -205,6 +208,7 @@ public class GarlicClove extends DataStructureImpl {
                DataHelper.hashCode(getInstructions());
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(128);
         buf.append("[GarlicClove: ");
diff --git a/router/java/src/net/i2p/data/i2np/GarlicMessage.java b/router/java/src/net/i2p/data/i2np/GarlicMessage.java
index 5d76a2b224b3dbecdf5d885f0c263a6616ac9d04..357e7d2f5ee0603db1853e22155063ddf5b2c03c 100644
--- a/router/java/src/net/i2p/data/i2np/GarlicMessage.java
+++ b/router/java/src/net/i2p/data/i2np/GarlicMessage.java
@@ -67,15 +67,18 @@ public class GarlicMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getData());
     }
     
+    @Override
     protected void written() {
         super.written();
         _data = null;
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof GarlicMessage) ) {
             GarlicMessage msg = (GarlicMessage)object;
@@ -85,6 +88,7 @@ public class GarlicMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[GarlicMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
index b745db9c0dd423f243c62ed63e98a93678e06739..e241ff1f4a349b294d8cdbad5ad94939faef186f 100644
--- a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
+++ b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
@@ -187,6 +187,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
             return calculateWrittenLength()+5;
     }
     
+    @Override
     public byte[] toByteArray() {
         byte data[] = new byte[getMessageSize()];
         int written = toByteArray(data);
diff --git a/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java b/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
index f89e4b8989796b5443e48ee8a09f67197faf694d..b611f46728173e34c2b49f1b05972859b83ad2e1 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
@@ -232,6 +232,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
     }
     
     
+    @Override
     public byte[] toByteArray() {
         byte rv[] = super.toByteArray();
         if (rv == null)
@@ -239,6 +240,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
         return rv;
     }
 
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getNextRouter()) +
                DataHelper.hashCode(getNextTunnelId()) +
@@ -246,6 +248,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
                DataHelper.hashCode(getReplyTunnel());
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof TunnelCreateMessage) ) {
             TunnelCreateMessage msg = (TunnelCreateMessage)object;
@@ -258,6 +261,7 @@ public class TunnelCreateMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[TunnelCreateMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java b/router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
index a5994f26de70308922e2faac825331862b479006..515436f5dd019e13fd606465ba9281812ef94983 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
@@ -87,12 +87,14 @@ public class TunnelCreateStatusMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getReceiveTunnelId()) +
                getStatus() +
                (int)getNonce();
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof TunnelCreateStatusMessage) ) {
             TunnelCreateStatusMessage msg = (TunnelCreateStatusMessage)object;
@@ -104,6 +106,7 @@ public class TunnelCreateStatusMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[TunnelCreateStatusMessage: ");
diff --git a/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java b/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java
index a07b99069a0cce3a70aa40ac37149f3ec1e3918d..77c967f5871a3d19d772c479b9dab842033ab4b3 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelDataMessage.java
@@ -112,11 +112,13 @@ public class TunnelDataMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return (int)_tunnelId +
                DataHelper.hashCode(_data);
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof TunnelDataMessage) ) {
             TunnelDataMessage msg = (TunnelDataMessage)object;
@@ -127,6 +129,7 @@ public class TunnelDataMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public byte[] toByteArray() {
         byte rv[] = super.toByteArray();
         if (rv == null)
@@ -134,6 +137,7 @@ public class TunnelDataMessage extends I2NPMessageImpl {
         return rv;
     }
 
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[TunnelDataMessage:");
diff --git a/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java b/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java
index 8fc7c9fd9e947c196ae771181396ad4682e76259..a436472e4319d679d1ff9d5ba3021a95cbb10d77 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelGatewayMessage.java
@@ -90,6 +90,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
         I2NPMessageHandler h = new I2NPMessageHandler(_context);
         readMessage(data, offset, dataSize, type, h);
     }
+    @Override
     public void readMessage(byte data[], int offset, int dataSize, int type, I2NPMessageHandler handler) throws I2NPMessageException, IOException {
         if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
         int curIndex = offset;
@@ -110,11 +111,13 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
     
     public int getType() { return MESSAGE_TYPE; }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(getTunnelId()) +
                DataHelper.hashCode(_msg);
     }
     
+    @Override
     public boolean equals(Object object) {
         if ( (object != null) && (object instanceof TunnelGatewayMessage) ) {
             TunnelGatewayMessage msg = (TunnelGatewayMessage)object;
@@ -126,6 +129,7 @@ public class TunnelGatewayMessage extends I2NPMessageImpl {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("[TunnelGatewayMessage:");
diff --git a/router/java/src/net/i2p/data/i2np/TunnelSessionKey.java b/router/java/src/net/i2p/data/i2np/TunnelSessionKey.java
index b73cbfa792037ad9d7154ed8b78522fcd6f21f22..c4effc3034ced8c3ea10419b6a9c1ccf1c058330 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelSessionKey.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelSessionKey.java
@@ -44,17 +44,20 @@ public class TunnelSessionKey extends DataStructureImpl {
 	_key.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof TunnelSessionKey))
             return false;
 	return DataHelper.eq(getKey(), ((TunnelSessionKey)obj).getKey());
     }
     
+    @Override
     public int hashCode() {
 	if (_key == null) return 0;
         return getKey().hashCode(); 
     }
     
+    @Override
     public String toString() {
         return "[TunnelSessionKey: " + getKey() + "]";
     }
diff --git a/router/java/src/net/i2p/data/i2np/TunnelSigningPrivateKey.java b/router/java/src/net/i2p/data/i2np/TunnelSigningPrivateKey.java
index ec1f887465b7dccac4a65a842e8e699ed2f769d0..03d017915f8b242ddafd659ce412e0ed781a472b 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelSigningPrivateKey.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelSigningPrivateKey.java
@@ -45,17 +45,20 @@ public class TunnelSigningPrivateKey extends DataStructureImpl {
 	_key.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof TunnelSigningPrivateKey))
             return false;
 	return DataHelper.eq(getKey(), ((TunnelSigningPrivateKey)obj).getKey());
     }
     
+    @Override
     public int hashCode() {
 	if (_key == null) return 0;
         return getKey().hashCode(); 
     }
     
+    @Override
     public String toString() {
         return "[EndPointPrivateKey: " + getKey() + "]";
     }
diff --git a/router/java/src/net/i2p/data/i2np/TunnelSigningPublicKey.java b/router/java/src/net/i2p/data/i2np/TunnelSigningPublicKey.java
index 9ce7e79adecc30fc5805b6f1cb121e66237e95e3..1f3530949226645b2d532685b39b92103ce7998d 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelSigningPublicKey.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelSigningPublicKey.java
@@ -44,17 +44,20 @@ public class TunnelSigningPublicKey extends DataStructureImpl {
 	_key.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof TunnelSigningPublicKey))
             return false;
 	return DataHelper.eq(getKey(), ((TunnelSigningPublicKey)obj).getKey());
     }
     
+    @Override
     public int hashCode() {
 	if (_key == null) return 0;
         return getKey().hashCode(); 
     }
     
+    @Override
     public String toString() {
         return "[TunnelSigningPublicKey: " + getKey() + "]";
     }
diff --git a/router/java/src/net/i2p/data/i2np/TunnelVerificationStructure.java b/router/java/src/net/i2p/data/i2np/TunnelVerificationStructure.java
index aec62c93eb03b4970bb66353c112c35b211dce9f..7c2e4f00168a394b4cd295f8e350eb0ef22fadc1 100644
--- a/router/java/src/net/i2p/data/i2np/TunnelVerificationStructure.java
+++ b/router/java/src/net/i2p/data/i2np/TunnelVerificationStructure.java
@@ -69,6 +69,7 @@ public class TunnelVerificationStructure extends DataStructureImpl {
         _authSignature.writeBytes(out);
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || !(obj instanceof TunnelVerificationStructure))
             return false;
@@ -77,11 +78,13 @@ public class TunnelVerificationStructure extends DataStructureImpl {
         DataHelper.eq(getAuthorizationSignature(), str.getAuthorizationSignature());
     }
     
+    @Override
     public int hashCode() {
         if ( (_msgHash == null) || (_authSignature == null) ) return 0;
         return getMessageHash().hashCode() + getAuthorizationSignature().hashCode();
     }
     
+    @Override
     public String toString() {
         return "[TunnelVerificationStructure: " + getMessageHash() + " " + getAuthorizationSignature() + "]";
     }
diff --git a/router/java/src/net/i2p/router/Blocklist.java b/router/java/src/net/i2p/router/Blocklist.java
index 4e94d77095aeaebaf4b02947503e1f6cea5c7ad4..7546ac4a713494b9b61de6da9efeda60996bb70a 100644
--- a/router/java/src/net/i2p/router/Blocklist.java
+++ b/router/java/src/net/i2p/router/Blocklist.java
@@ -13,14 +13,13 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.*;
 
-import net.i2p.I2PAppContext;
 import net.i2p.data.Base64;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Hash;
 import net.i2p.data.RouterAddress;
 import net.i2p.data.RouterInfo;
 import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
-import net.i2p.util.HexDump;
+import net.i2p.util.ConcurrentHashSet;
 import net.i2p.util.Log;
 
 /**
@@ -55,22 +54,18 @@ public class Blocklist {
     private RouterContext _context;
     private long _blocklist[];
     private int _blocklistSize;
-    private Object _lock;
+    private final Object _lock = new Object();
     private Entry _wrapSave;
-    private Set _inProcess;
-    private Map _peerBlocklist;
-    private Set _singleIPBlocklist;
+    private final Set<Hash> _inProcess = new HashSet(0);
+    private Map<Hash, String> _peerBlocklist = new HashMap(0);
+    private final Set<Integer> _singleIPBlocklist = new ConcurrentHashSet(0);
     
     public Blocklist(RouterContext context) {
         _context = context;
         _log = context.logManager().getLog(Blocklist.class);
         _blocklist = null;
         _blocklistSize = 0;
-        _lock = new Object();
         _wrapSave = null;
-        _inProcess = new HashSet(0);
-        _peerBlocklist = new HashMap(0);
-        _singleIPBlocklist = new HashSet(0);
     }
     
     public Blocklist() {
@@ -448,15 +443,11 @@ public class Blocklist {
     }
 
     private boolean add(int ip) {
-        synchronized(_singleIPBlocklist) {
-            return _singleIPBlocklist.add(new Integer(ip));
-        }
+        return _singleIPBlocklist.add(Integer.valueOf(ip));
     }
 
     private boolean isOnSingleList(int ip) {
-        synchronized(_singleIPBlocklist) {
-            return _singleIPBlocklist.contains(new Integer(ip));
-        }
+        return _singleIPBlocklist.contains(Integer.valueOf(ip));
     }
 
     /**
@@ -588,11 +579,11 @@ public class Blocklist {
 
     // methods to get and store the from/to values in the array
 
-    private int getFrom(long entry) {
+    private static int getFrom(long entry) {
         return (int) ((entry >> 32) & 0xffffffff);
     }
 
-    private int getTo(long entry) {
+    private static int getTo(long entry) {
         return (int) (entry & 0xffffffff);
     }
 
@@ -604,7 +595,7 @@ public class Blocklist {
      * So the size is (cough) almost 2MB for the 240,000 line splist.txt.
      *
      */
-    private long toEntry(byte ip1[], byte ip2[]) {
+    private static long toEntry(byte ip1[], byte ip2[]) {
         long entry = 0;
         for (int i = 0; i < 4; i++)
             entry |= ((long) (ip2[i] & 0xff)) << ((3-i)*8);
@@ -623,14 +614,18 @@ public class Blocklist {
         _blocklist[idx] = entry;
     }
 
-    private int toInt(byte ip[]) {
+    private static int toInt(byte ip[]) {
         int rv = 0;
         for (int i = 0; i < 4; i++)
             rv |= (ip[i] & 0xff) << ((3-i)*8);
         return rv;
     }
 
-    private String toStr(long entry) {
+    public static String toStr(byte[] ip) {
+        return toStr(toInt(ip));
+    }
+
+    private static String toStr(long entry) {
         StringBuffer buf = new StringBuffer(32);
         for (int i = 7; i >= 0; i--) {
             buf.append((entry >> (8*i)) & 0xff);
@@ -642,7 +637,7 @@ public class Blocklist {
         return buf.toString();
     }
 
-    private String toStr(int ip) {
+    private static String toStr(int ip) {
         StringBuffer buf = new StringBuffer(16);
         for (int i = 3; i >= 0; i--) {
             buf.append((ip >> (8*i)) & 0xff);
@@ -758,9 +753,7 @@ public class Blocklist {
     public void renderStatusHTML(Writer out) throws IOException {
         out.write("<h2>IP Blocklist</h2>");
         Set singles = new TreeSet();
-        synchronized(_singleIPBlocklist) {
-            singles.addAll(_singleIPBlocklist);
-        }
+        singles.addAll(_singleIPBlocklist);
         if (singles.size() > 0) {
             out.write("<table><tr><td><b>Transient IPs</b></td></tr>");
             for (Iterator iter = singles.iterator(); iter.hasNext(); ) {
diff --git a/router/java/src/net/i2p/router/ClientTunnelSettings.java b/router/java/src/net/i2p/router/ClientTunnelSettings.java
index e0f95e611bc3e19ec2b22ebb6ec5069816c69bce..6cd3981954c642a23d53395f4b649dd98fe8f15b 100644
--- a/router/java/src/net/i2p/router/ClientTunnelSettings.java
+++ b/router/java/src/net/i2p/router/ClientTunnelSettings.java
@@ -44,6 +44,7 @@ public class ClientTunnelSettings {
         _outboundSettings.writeToProperties("outbound.", props);
     }
 
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         Properties p = new Properties();
diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java
index 6d0927c63ec51cb039a6120ffecf3cf0a92211f9..f3535cf4c89b108292fdd7cbcd1202a7e8510982 100644
--- a/router/java/src/net/i2p/router/CommSystemFacade.java
+++ b/router/java/src/net/i2p/router/CommSystemFacade.java
@@ -58,6 +58,7 @@ public abstract class CommSystemFacade implements Service {
     public boolean isBacklogged(Hash dest) { return false; }
     public boolean wasUnreachable(Hash dest) { return false; }
     public boolean isEstablished(Hash dest) { return false; }
+    public byte[] getIP(Hash dest) { return null; }
     
     /** 
      * Tell other transports our address changed
diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java
index 4ab5c77b41d1d190e99e04bf2bcce80e8d15f1f7..802aedec0a6903aa2b15dc2fbd837073a00cdb62 100644
--- a/router/java/src/net/i2p/router/InNetMessagePool.java
+++ b/router/java/src/net/i2p/router/InNetMessagePool.java
@@ -35,9 +35,9 @@ public class InNetMessagePool implements Service {
     private Log _log;
     private RouterContext _context;
     private HandlerJobBuilder _handlerJobBuilders[];
-    private List _pendingDataMessages;
-    private List _pendingDataMessagesFrom;
-    private List _pendingGatewayMessages;
+    private final List _pendingDataMessages;
+    private final List _pendingDataMessagesFrom;
+    private final List _pendingGatewayMessages;
     private SharedShortCircuitDataJob _shortCircuitDataJob;
     private SharedShortCircuitGatewayJob _shortCircuitGatewayJob;
     private boolean _alive;
diff --git a/router/java/src/net/i2p/router/JobImpl.java b/router/java/src/net/i2p/router/JobImpl.java
index 84190940bbbea99ae9273ea36535452ba433bcdd..a72f852967f3053345d2ba8b811a36787a1baae9 100644
--- a/router/java/src/net/i2p/router/JobImpl.java
+++ b/router/java/src/net/i2p/router/JobImpl.java
@@ -33,6 +33,7 @@ public abstract class JobImpl implements Job {
     
     public final RouterContext getContext() { return _context; }
     
+    @Override
     public String toString() { 
         StringBuffer buf = new StringBuffer(128);
         buf.append(super.toString());
diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java
index 915288cc667899c9afcf415302b96da26833beab..8ec40f56e51d7c3243274fc145cf627879d88869 100644
--- a/router/java/src/net/i2p/router/JobQueue.java
+++ b/router/java/src/net/i2p/router/JobQueue.java
@@ -33,7 +33,7 @@ public class JobQueue {
     private RouterContext _context;
     
     /** Integer (runnerId) to JobQueueRunner for created runners */
-    private HashMap _queueRunners;
+    private final HashMap _queueRunners;
     /** a counter to identify a job runner */
     private volatile static int _runnerId = 0;
     /** list of jobs that are ready to run ASAP */
@@ -41,7 +41,7 @@ public class JobQueue {
     /** list of jobs that are scheduled for running in the future */
     private ArrayList _timedJobs;
     /** job name to JobStat for that job */
-    private SortedMap _jobStats;
+    private final SortedMap _jobStats;
     /** how many job queue runners can go concurrently */
     private int _maxRunners = 1; 
     private QueuePumper _pumper;
@@ -50,7 +50,7 @@ public class JobQueue {
     /** have we been killed or are we alive? */
     private boolean _alive;
     
-    private Object _jobLock;
+    private final Object _jobLock;
     
     /** default max # job queue runners operating */
     private final static int DEFAULT_MAX_RUNNERS = 1;
@@ -94,7 +94,7 @@ public class JobQueue {
      * queue runners wait on this whenever they're not doing anything, and 
      * this gets notified *once* whenever there are ready jobs
      */
-    private Object _runnerLock = new Object();
+    private final Object _runnerLock = new Object();
     
     public JobQueue(RouterContext context) {
         _context = context;
diff --git a/router/java/src/net/i2p/router/JobStats.java b/router/java/src/net/i2p/router/JobStats.java
index d3cf345d0253eeb08a5600df8375958099d9dbdd..ec571299311603df7e0e13c74999b8e1d6019931 100644
--- a/router/java/src/net/i2p/router/JobStats.java
+++ b/router/java/src/net/i2p/router/JobStats.java
@@ -59,7 +59,9 @@ class JobStats {
             return 0; 
     }
     
+    @Override
     public int hashCode() { return _job.hashCode(); }
+    @Override
     public boolean equals(Object obj) {
         if ( (obj != null) && (obj instanceof JobStats) ) {
             JobStats stats = (JobStats)obj;
@@ -73,6 +75,7 @@ class JobStats {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("Over ").append(getRuns()).append(" runs, job <b>").append(getName()).append("</b> took ");
diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java
index 4e2ed2c51891800665b5dbda10a52113538bcb37..9fc62a70f29a0213f450a8109faa26d13cd80e3d 100644
--- a/router/java/src/net/i2p/router/KeyManager.java
+++ b/router/java/src/net/i2p/router/KeyManager.java
@@ -41,7 +41,7 @@ public class KeyManager {
     private PublicKey _publicKey;
     private SigningPrivateKey _signingPrivateKey;
     private SigningPublicKey _signingPublicKey;
-    private Map _leaseSetKeys; // Destination --> LeaseSetKeys
+    private final Map _leaseSetKeys; // Destination --> LeaseSetKeys
     private SynchronizeKeysJob _synchronizeJob;
     
     public final static String PROP_KEYDIR = "router.keyBackupDir";
diff --git a/router/java/src/net/i2p/router/LeaseSetKeys.java b/router/java/src/net/i2p/router/LeaseSetKeys.java
index c88b0808afed3650f338c4b556d4b86fbc095af3..1017ab43db5c322d7b6e47488a40bad0013ae6fd 100644
--- a/router/java/src/net/i2p/router/LeaseSetKeys.java
+++ b/router/java/src/net/i2p/router/LeaseSetKeys.java
@@ -73,6 +73,7 @@ public class LeaseSetKeys extends DataStructureImpl {
 	_revocationKey.writeBytes(out);
     }
     
+    @Override
     public int hashCode() {
 	int rv = 0;
 	rv += DataHelper.hashCode(_dest);
@@ -81,6 +82,7 @@ public class LeaseSetKeys extends DataStructureImpl {
 	return rv;
     }
     
+    @Override
     public boolean equals(Object obj) {
 	if ( (obj != null) && (obj instanceof LeaseSetKeys) ) {
 	    LeaseSetKeys keys = (LeaseSetKeys)obj;
diff --git a/router/java/src/net/i2p/router/MessageHistory.java b/router/java/src/net/i2p/router/MessageHistory.java
index d88d653de03ff70c24e358f5931d82c5edac73d2..cafbadd1cd5bb0cb23ce3725d9536c29c47e522c 100644
--- a/router/java/src/net/i2p/router/MessageHistory.java
+++ b/router/java/src/net/i2p/router/MessageHistory.java
@@ -26,7 +26,7 @@ import net.i2p.util.Log;
 public class MessageHistory {
     private Log _log;
     private RouterContext _context;
-    private List _unwrittenEntries; // list of raw entries (strings) yet to be written
+    private final List _unwrittenEntries = new ArrayList(64); // list of raw entries (strings) yet to be written
     private String _historyFile; // where to write 
     private String _localIdent; // placed in each entry to uniquely identify the local router
     private boolean _doLog; // true == we want to log
@@ -104,7 +104,7 @@ public class MessageHistory {
             _doLog = DEFAULT_KEEP_MESSAGE_HISTORY;
             _historyFile = filename;
             _localIdent = getName(_context.routerHash());
-            _unwrittenEntries = new ArrayList(64);
+            // _unwrittenEntries = new ArrayList(64);
             updateSettings();
             // clear the history file on startup
             if (_firstPass) {
diff --git a/router/java/src/net/i2p/router/MultiRouter.java b/router/java/src/net/i2p/router/MultiRouter.java
index 144c4e1db8a9059f9d92d66ded03f5c306e2d1ea..d8d316ae8aa0291d51e3ce370f0fb7aea4d553b6 100644
--- a/router/java/src/net/i2p/router/MultiRouter.java
+++ b/router/java/src/net/i2p/router/MultiRouter.java
@@ -58,6 +58,7 @@ public class MultiRouter {
         _defaultContext.clock().setOffset(0);
         
         Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
             public void run() {
                 Thread.currentThread().setName("Router* Shutdown");
                 try { Thread.sleep(120*1000); } catch (InterruptedException ie) {}
diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
index e4a5ce08b663a7da1c9fc9752104dc33875bae0a..ded9bee26a7a9f1445c440465d9f7a819af3b9b4 100644
--- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
@@ -10,10 +10,6 @@ package net.i2p.router;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 
 import net.i2p.data.Hash;
@@ -62,5 +58,6 @@ public abstract class NetworkDatabaseFacade implements Service {
     public int getKnownRouters() { return 0; }
     public int getKnownLeaseSets() { return 0; }
     public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
+    public void renderLeaseSetHTML(Writer out) throws IOException {}
     public void renderStatusHTML(Writer out, boolean b) throws IOException {}
 }
diff --git a/router/java/src/net/i2p/router/OutNetMessage.java b/router/java/src/net/i2p/router/OutNetMessage.java
index 2240af423c2465094b244823441be641b505b4ff..e1cd2a896622d6aa3489375315f843f36ec725b5 100644
--- a/router/java/src/net/i2p/router/OutNetMessage.java
+++ b/router/java/src/net/i2p/router/OutNetMessage.java
@@ -302,6 +302,7 @@ public class OutNetMessage {
         super.finalize();
     }
     */
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(128);
         buf.append("[OutNetMessage contains ");
@@ -365,6 +366,7 @@ public class OutNetMessage {
         }
     }
     
+    @Override
     public int hashCode() {
         int rv = 0;
         rv += DataHelper.hashCode(_message);
@@ -373,7 +375,10 @@ public class OutNetMessage {
         return rv;
     }
     
+    @Override
     public boolean equals(Object obj) {
+        if(obj == null) return false;
+        if(obj.getClass() != OutNetMessage.class) return false;
         return obj == this; // two OutNetMessages are different even if they contain the same message
     }
 }
diff --git a/router/java/src/net/i2p/router/PeerManagerFacade.java b/router/java/src/net/i2p/router/PeerManagerFacade.java
index 791b77616a5600e669af4a103bd37bf92e2a4efc..61b1d3799e5d56a9c2d5d10cb09c609bb85e9605 100644
--- a/router/java/src/net/i2p/router/PeerManagerFacade.java
+++ b/router/java/src/net/i2p/router/PeerManagerFacade.java
@@ -8,7 +8,6 @@ package net.i2p.router;
  *
  */
 
-import java.io.Writer;
 import java.util.List;
 
 import net.i2p.data.Hash;
diff --git a/router/java/src/net/i2p/router/PersistentKeyRing.java b/router/java/src/net/i2p/router/PersistentKeyRing.java
index d02275ea20c72bbb045269d74e52ac1d494ff1df..f1f4fe55a117bfb0fb09714b14f41c8aad871b68 100644
--- a/router/java/src/net/i2p/router/PersistentKeyRing.java
+++ b/router/java/src/net/i2p/router/PersistentKeyRing.java
@@ -4,16 +4,12 @@ import java.io.IOException;
 import java.io.Writer;
 
 import java.util.Iterator;
-import java.util.Map;
-import java.util.TreeMap;
 
-import net.i2p.data.Base64;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.data.LeaseSet;
 import net.i2p.data.SessionKey;
-import net.i2p.router.TunnelPoolSettings;
 import net.i2p.util.KeyRing;
 
 /**
@@ -31,6 +27,7 @@ public class PersistentKeyRing extends KeyRing {
         addFromProperties();
     }
 
+    @Override
     public SessionKey put(Hash h, SessionKey sk) {
         SessionKey old = super.put(h, sk);
         if (!sk.equals(old)) {
@@ -67,6 +64,7 @@ public class PersistentKeyRing extends KeyRing {
         }
     }
 
+    @Override
     public void renderStatusHTML(Writer out) throws IOException {
         StringBuffer buf = new StringBuffer(1024);
         buf.append("\n<table border=\"1\"><tr><th align=\"left\">Destination Hash<th align=\"left\">Name or Dest.<th align=\"left\">Session Key</tr>");
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 37215f6c4b579129da3ee6ea784ebd72820a74e9..c9a95dd552208c43145fa8b634cb375b7656635f 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -9,7 +9,6 @@ package net.i2p.router;
  */
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.Writer;
@@ -53,7 +52,7 @@ import net.i2p.util.SimpleTimer;
 public class Router {
     private Log _log;
     private RouterContext _context;
-    private Properties _config;
+    private final Properties _config;
     private String _configFilename;
     private RouterInfo _routerInfo;
     private long _started;
@@ -64,7 +63,7 @@ public class Router {
     private int _gracefulExitCode;
     private I2PThread.OOMEventListener _oomListener;
     private ShutdownHook _shutdownHook;
-    private I2PThread _gracefulShutdownDetector;
+    private final I2PThread _gracefulShutdownDetector;
     
     public final static String PROP_CONFIG_FILE = "router.configLocation";
     
@@ -686,6 +685,7 @@ public class Router {
         
         out.write("\n<hr /><a name=\"netdb\"> </a>\n");
         
+        _context.netDb().renderLeaseSetHTML(out);
         _context.netDb().renderStatusHTML(out);
         
         buf.setLength(0);
@@ -1350,6 +1350,7 @@ private static class ShutdownHook extends Thread {
         _context = ctx;
         _id = ++__id;
     }
+        @Override
     public void run() {
         setName("Router " + _id + " shutdown");
         Log l = _context.logManager().getLog(Router.class);
diff --git a/router/java/src/net/i2p/router/RouterClock.java b/router/java/src/net/i2p/router/RouterClock.java
index 3174e60276db72a52c40fedd1bf9d140d306dd99..d071013b2d3b13d0553f30560bd97c67b5a4bfbe 100644
--- a/router/java/src/net/i2p/router/RouterClock.java
+++ b/router/java/src/net/i2p/router/RouterClock.java
@@ -15,11 +15,11 @@ import net.i2p.util.Log;
  */
 public class RouterClock extends Clock {
 
-    RouterContext _context;
+    RouterContext _contextRC; // LINT field hides another field
 
     public RouterClock(RouterContext context) {
         super(context);
-        _context = context;
+        _contextRC = context;
     }
 
     /**
@@ -27,6 +27,7 @@ public class RouterClock extends Clock {
      * value means that we are slow, while a negative value means we are fast.
      *
      */
+    @Override
     public void setOffset(long offsetMs, boolean force) {
 
         if (false) return;
@@ -53,10 +54,10 @@ public class RouterClock extends Clock {
             }
 
             // If so configured, check sanity of proposed clock offset
-            if (Boolean.valueOf(_context.getProperty("router.clockOffsetSanityCheck","true")).booleanValue() == true) {
+            if (Boolean.valueOf(_contextRC.getProperty("router.clockOffsetSanityCheck","true")).booleanValue() == true) {
 
                 // Try calculating peer clock skew
-                Long peerClockSkew = _context.commSystem().getFramedAveragePeerClockSkew(50);
+                Long peerClockSkew = _contextRC.commSystem().getFramedAveragePeerClockSkew(50);
 
                 if (peerClockSkew != null) {
 
@@ -88,9 +89,9 @@ public class RouterClock extends Clock {
                 getLog().info("Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
             
             if (!_statCreated)
-                _context.statManager().createRateStat("clock.skew", "How far is the already adjusted clock being skewed?", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*60 });
+                _contextRC.statManager().createRateStat("clock.skew", "How far is the already adjusted clock being skewed?", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*60 });
                 _statCreated = true;
-            _context.statManager().addRateData("clock.skew", delta, 0);
+            _contextRC.statManager().addRateData("clock.skew", delta, 0);
         } else {
             getLog().log(Log.INFO, "Initializing clock offset to " + offsetMs + "ms from " + _offset + "ms");
         }
diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java
index ba82fb839365677049205a9b15c7231037903dcc..782bc8a87550fffd01b3620a64e587a4aae36f12 100644
--- a/router/java/src/net/i2p/router/RouterContext.java
+++ b/router/java/src/net/i2p/router/RouterContext.java
@@ -58,7 +58,7 @@ public class RouterContext extends I2PAppContext {
     private MessageValidator _messageValidator;
     private MessageStateMonitor _messageStateMonitor;
     private RouterThrottle _throttle;
-    private RouterClock _clock;
+    private RouterClock _clockX;  // LINT field hides another field, hope rename won't break anything.
     private Calculator _integrationCalc;
     private Calculator _speedCalc;
     private Calculator _capacityCalc;
@@ -262,6 +262,7 @@ public class RouterContext extends I2PAppContext {
     /** how do we rank the capacity of profiles? */
     public Calculator capacityCalculator() { return _capacityCalc; }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(512);
         buf.append("RouterContext: ").append(super.toString()).append('\n');
@@ -294,6 +295,7 @@ public class RouterContext extends I2PAppContext {
      * I2PAppContext says.
      *
      */
+    @Override
     public String getProperty(String propName) {
         if (_router != null) {
             String val = _router.getConfigSetting(propName);
@@ -306,6 +308,7 @@ public class RouterContext extends I2PAppContext {
      * I2PAppContext says.
      *
      */
+    @Override
     public String getProperty(String propName, String defaultVal) {
         if (_router != null) {
             String val = _router.getConfigSetting(propName);
@@ -317,6 +320,7 @@ public class RouterContext extends I2PAppContext {
     /**
      * Return an int with an int default
      */
+    @Override
     public int getProperty(String propName, int defaultVal) {
         if (_router != null) {
             String val = _router.getConfigSetting(propName);
@@ -339,14 +343,16 @@ public class RouterContext extends I2PAppContext {
      * that it triggers initializeClock() of which we definitely
      * need the local version to run.
      */
+    @Override
     public Clock clock() {
         if (!_clockInitialized) initializeClock();
-        return _clock;
+        return _clockX;
     }
+    @Override
     protected void initializeClock() {
         synchronized (this) {
-            if (_clock == null)
-                _clock = new RouterClock(this);
+            if (_clockX == null)
+                _clockX = new RouterClock(this);
             _clockInitialized = true;
         }
     }
diff --git a/router/java/src/net/i2p/router/RouterDoSThrottle.java b/router/java/src/net/i2p/router/RouterDoSThrottle.java
index c161ddda584547957b42f5e30b0440afbde5ff7e..79471627a24996782fc018d6d0a5616812c7a887 100644
--- a/router/java/src/net/i2p/router/RouterDoSThrottle.java
+++ b/router/java/src/net/i2p/router/RouterDoSThrottle.java
@@ -19,6 +19,7 @@ class RouterDoSThrottle extends RouterThrottleImpl {
     private static final long LOOKUP_THROTTLE_PERIOD = 10*1000;
     private static final long LOOKUP_THROTTLE_MAX = 20;
     
+    @Override
     public boolean acceptNetDbLookupRequest(Hash key) { 
         // if we were going to refuse it anyway, drop it
         boolean shouldAccept = super.acceptNetDbLookupRequest(key);
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index eb1429483320cb8aa87d2acdcfebd2fb90998a18..5203f2360b8c639e5b7467177c7101dc9a20b67e 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
 public class RouterVersion {
     public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 21;
+    public final static long BUILD = 5;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/Shitlist.java b/router/java/src/net/i2p/router/Shitlist.java
index 29d384de90acc327720d158a3e145bfe443f7a05..1416cc9fe4dc5dda858680187d4a3e16c951341b 100644
--- a/router/java/src/net/i2p/router/Shitlist.java
+++ b/router/java/src/net/i2p/router/Shitlist.java
@@ -13,8 +13,6 @@ import java.io.Writer;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java
index 4a286fe3d07747d014e124d7b9e43a076beb8f3a..d037ee300eabf11255b5e2eba528be61abdd1110 100644
--- a/router/java/src/net/i2p/router/StatisticsManager.java
+++ b/router/java/src/net/i2p/router/StatisticsManager.java
@@ -53,8 +53,6 @@ public class StatisticsManager implements Service {
         _includePeerRankings = Boolean.valueOf(val);
     }
     
-    static final boolean CommentOutIn072 = RouterVersion.VERSION.equals("0.7.1");
-
     /** Retrieve a snapshot of the statistics that should be published */
     public Properties publishStatistics() { 
         Properties stats = new Properties();
@@ -123,7 +121,7 @@ public class StatisticsManager implements Service {
             //includeRate("transport.sendProcessingTime", stats, new long[] { 60*60*1000 });
             //includeRate("jobQueue.jobRunSlow", stats, new long[] { 10*60*1000l, 60*60*1000l });
             //includeRate("crypto.elGamal.encrypt", stats, new long[] { 60*60*1000 });
-            includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 });
+            includeRate("tunnel.participatingTunnels", stats, new long[] { 60*60*1000 });
             //includeRate("tunnel.testSuccessTime", stats, new long[] { 10*60*1000l });
             //includeRate("client.sendAckTime", stats, new long[] { 60*60*1000 }, true);
             //includeRate("udp.sendConfirmTime", stats, new long[] { 10*60*1000 });
@@ -133,16 +131,6 @@ public class StatisticsManager implements Service {
             //includeRate("stream.con.sendDuplicateSize", stats, new long[] { 60*60*1000 });
             //includeRate("stream.con.receiveDuplicateSize", stats, new long[] { 60*60*1000 });
 
-            if (CommentOutIn072) {
-                // Round smaller uptimes to 1 hour, to frustrate uptime tracking
-                // Round 2nd hour to 90m since peers use 2h minimum to route
-                if (publishedUptime < 60*60*1000) publishedUptime = 60*60*1000;
-                else if (publishedUptime < 2*60*60*1000) publishedUptime = 90*60*1000;
-                stats.setProperty("stat_uptime", DataHelper.formatDuration(publishedUptime));
-            } else {
-                // So that we will still get build requests
-                stats.setProperty("stat_uptime", "90m");
-            }
             //stats.setProperty("stat__rateKey", "avg;maxAvg;pctLifetime;[sat;satLim;maxSat;maxSatLim;][num;lifetimeFreq;maxFreq]");
             
             //includeRate("tunnel.decryptRequestTime", stats, new long[] { 60*1000, 10*60*1000 });
@@ -150,7 +138,7 @@ public class StatisticsManager implements Service {
             //includeRate("udp.packetVerifyTime", stats, new long[] { 60*1000 });
             
             //includeRate("tunnel.buildRequestTime", stats, new long[] { 10*60*1000 });
-            long rate = CommentOutIn072 ? 10*60*1000 : 60*60*1000;
+            long rate = 60*60*1000;
             includeRate("tunnel.buildClientExpire", stats, new long[] { rate });
             includeRate("tunnel.buildClientReject", stats, new long[] { rate });
             includeRate("tunnel.buildClientSuccess", stats, new long[] { rate });
@@ -160,20 +148,15 @@ public class StatisticsManager implements Service {
             //includeRate("tunnel.rejectTimeout", stats, new long[] { 10*60*1000 });
             //includeRate("tunnel.rejectOverloaded", stats, new long[] { 10*60*1000 });
             //includeRate("tunnel.acceptLoad", stats, new long[] { 10*60*1000 });
-            
-            _log.debug("Publishing peer rankings");
-        } else {
-            // So that we will still get build requests
-            stats.setProperty("stat_uptime", "90m");
-            _log.debug("Not publishing peer rankings");
         }
+
+        // So that we will still get build requests
+        stats.setProperty("stat_uptime", "90m");
         if (FloodfillNetworkDatabaseFacade.isFloodfill(_context.router().getRouterInfo())) {
             stats.setProperty("netdb.knownRouters", ""+_context.netDb().getKnownRouters());
             stats.setProperty("netdb.knownLeaseSets", ""+_context.netDb().getKnownLeaseSets());
         }
 
-    if (_log.shouldLog(Log.DEBUG))
-        _log.debug("Building status: " + stats);
         return stats;
     }
     
diff --git a/router/java/src/net/i2p/router/TunnelManagerFacade.java b/router/java/src/net/i2p/router/TunnelManagerFacade.java
index da0482e6ffd4072eac1416e95e3397cf0222fd20..8ddc1e060f59ea9fdcafb1488024b1374a749ce1 100644
--- a/router/java/src/net/i2p/router/TunnelManagerFacade.java
+++ b/router/java/src/net/i2p/router/TunnelManagerFacade.java
@@ -8,8 +8,6 @@ package net.i2p.router;
  *
  */
 
-import java.io.IOException;
-import java.io.Writer;
 import java.util.Set;
 
 import net.i2p.data.Destination;
diff --git a/router/java/src/net/i2p/router/TunnelPoolSettings.java b/router/java/src/net/i2p/router/TunnelPoolSettings.java
index a92470d2ee14114fd8095016a7fece2558bc2ee1..fab33d919a077b1d5af69126aca2e734f66fcc2b 100644
--- a/router/java/src/net/i2p/router/TunnelPoolSettings.java
+++ b/router/java/src/net/i2p/router/TunnelPoolSettings.java
@@ -185,6 +185,7 @@ public class TunnelPoolSettings {
         }
     }
 
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         Properties p = new Properties();
diff --git a/router/java/src/net/i2p/router/TunnelSettings.java b/router/java/src/net/i2p/router/TunnelSettings.java
index 3ea8baa5671fe87e4846a47f69919bcad2a22c7c..99249f7c75f3c7042349711874b7e54b81e26ba3 100644
--- a/router/java/src/net/i2p/router/TunnelSettings.java
+++ b/router/java/src/net/i2p/router/TunnelSettings.java
@@ -105,6 +105,7 @@ public class TunnelSettings extends DataStructureImpl {
     }
     
     
+    @Override
     public int hashCode() {
         int rv = 0;
         rv += _includeDummy ? 100 : 0;
@@ -118,6 +119,7 @@ public class TunnelSettings extends DataStructureImpl {
         return rv;
     }
     
+    @Override
     public boolean equals(Object obj) {
         if ( (obj != null) && (obj instanceof TunnelSettings) ) {
             TunnelSettings settings = (TunnelSettings)obj;
diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
index 4f979c5c937a28a5638ebbf46a3398e1a33d3e80..5611f13774680f50cbf22d1743f6e6e3b6d5d78a 100644
--- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
+++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
@@ -13,7 +13,6 @@ import java.io.OutputStream;
 import java.net.Socket;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -73,7 +72,7 @@ public class ClientConnectionRunner {
      * This contains the last 10 MessageIds that have had their (non-ack) status 
      * delivered to the client (so that we can be sure only to update when necessary)
      */
-    private List _alreadyProcessed;
+    private final List _alreadyProcessed;
     private ClientWriterRunner _writer;
     private Hash _destHashCache;
     /** are we, uh, dead */
@@ -111,7 +110,7 @@ public class ClientConnectionRunner {
             t.setDaemon(true);
             t.setPriority(I2PThread.MAX_PRIORITY);
             t.start();
-            _out = _socket.getOutputStream();
+            _out = _socket.getOutputStream(); // LINT -- OWCH! needs a better way so it can be final.
             _reader.startReading();
         } catch (IOException ioe) {
             _log.error("Error starting up the runner", ioe);
@@ -412,7 +411,7 @@ public class ClientConnectionRunner {
             }
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("after writeMessage("+ msg.getClass().getName() + "): " 
-                           + (_context.clock().now()-before) + "ms");;
+                           + (_context.clock().now()-before) + "ms");
         } catch (I2CPMessageException ime) {
             _log.error("Message exception sending I2CP message: " + ime);
             stopRunning();
@@ -464,7 +463,7 @@ public class ClientConnectionRunner {
     // this *should* be mod 65536, but UnsignedInteger is still b0rked.  FIXME
     private final static int MAX_MESSAGE_ID = 32767;
     private static volatile int _messageId = RandomSource.getInstance().nextInt(MAX_MESSAGE_ID); // messageId counter
-    private static Object _messageIdLock = new Object();
+    private final static Object _messageIdLock = new Object();
     
     static int getNextMessageId() { 
         synchronized (_messageIdLock) {
diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java
index 9b5eb7c4ccbe24886646d0c011f1aeceba41ee6d..85e7d9ec76c0bd7cf8dea1608af4b35d89e72a31 100644
--- a/router/java/src/net/i2p/router/client/ClientManager.java
+++ b/router/java/src/net/i2p/router/client/ClientManager.java
@@ -41,8 +41,8 @@ import net.i2p.util.Log;
 public class ClientManager {
     private Log _log;
     private ClientListenerRunner _listener;
-    private HashMap _runners;        // Destination --> ClientConnectionRunner
-    private Set _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
+    private final HashMap _runners;        // Destination --> ClientConnectionRunner
+    private final Set _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
     private RouterContext _ctx;
 
     /** ms to wait before rechecking for inbound messages to deliver to clients */
diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
index 51b8b4cb2513a67ba24cd2b44fb41f42af5102c8..eddf13dd8e8d672e31cc5679811f390e3d2e8fef 100644
--- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
+++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
@@ -74,9 +74,11 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
             startup();
     }
     
+    @Override
     public boolean isAlive() { return _manager != null && _manager.isAlive(); }
 
     private static final long MAX_TIME_TO_REBUILD = 10*60*1000;
+    @Override
     public boolean verifyClientLiveliness() {
         if (_manager == null) return true;
         boolean lively = true;
@@ -167,6 +169,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
         }
     }
 
+    @Override
     public boolean shouldPublishLeaseSet(Hash destinationHash) { return _manager.shouldPublishLeaseSet(destinationHash); }
     
     public void messageDeliveryStatusUpdate(Destination fromDest, MessageId id, boolean delivered) {
@@ -196,6 +199,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
         }
     }
     
+    @Override
     public void renderStatusHTML(Writer out) throws IOException { 
         if (_manager != null)
             _manager.renderStatusHTML(out); 
@@ -206,6 +210,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
      *
      * @return set of Destination objects
      */
+    @Override
     public Set listClients() {
         if (_manager != null)
             return _manager.listClients();
diff --git a/router/java/src/net/i2p/router/message/CloveSet.java b/router/java/src/net/i2p/router/message/CloveSet.java
index 1774c9be02a6640cc8da59615e5f644ed524caed..d18aa55b44baed89829747cf5e0b0b4475ec8784 100644
--- a/router/java/src/net/i2p/router/message/CloveSet.java
+++ b/router/java/src/net/i2p/router/message/CloveSet.java
@@ -42,6 +42,7 @@ public class CloveSet {
     public long getExpiration() { return _expiration; }
     public void setExpiration(long expiration) { _expiration = expiration; }
     
+    @Override
     public String toString() { 
 	StringBuffer buf = new StringBuffer(128);
 	buf.append("{");
diff --git a/router/java/src/net/i2p/router/message/GarlicConfig.java b/router/java/src/net/i2p/router/message/GarlicConfig.java
index 3b6359a89d4c2b72955ebf0c7a086da536902c04..ead346e0067f239af6f7caca0acdab0f66f549e9 100644
--- a/router/java/src/net/i2p/router/message/GarlicConfig.java
+++ b/router/java/src/net/i2p/router/message/GarlicConfig.java
@@ -156,6 +156,7 @@ public class GarlicConfig {
     
     protected String getSubData() { return ""; }
     private final static String NL = System.getProperty("line.separator");
+    @Override
     public String toString() {
 	StringBuffer buf = new StringBuffer();
 	buf.append("<garlicConfig>").append(NL);
diff --git a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
index 5d6b37a96c140a9bb26e072268077f4afed409bf..2a04de8e4edbc3526d921953af741063d9df086b 100644
--- a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
+++ b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
@@ -30,6 +30,37 @@ import net.i2p.util.Log;
  *
  */
 public class GarlicMessageBuilder {
+
+    /**
+     *  This was 100 since 0.6.1.10 (50 before that). It's important because:
+     *  - Tags are 32 bytes. So it previously added 3200 bytes to an initial message.
+     *  - Too many tags adds a huge overhead to short-duration connections
+     *    (like http, datagrams, etc.)
+     *  - Large messages have a much higher chance of being dropped due to
+     *    one of their 1KB fragments being discarded by a tunnel participant.
+     *  - This reduces the effective maximum datagram size because the client
+     *    doesn't know when tags will be bundled, so the tag size must be
+     *    subtracted from the maximum I2NP size or transport limit.
+     *
+     *  Issues with too small a value:
+     *  - When tags are sent, a reply leaseset (~1KB) is always bundled.
+     *    Maybe don't need to bundle more than every minute or so
+     *    rather than every time?
+     *  - Does the number of tags (and the threshold of 20) limit the effective
+     *    streaming lib window size? Should the threshold and the number of
+     *    sent tags be variable based on the message rate?
+     *
+     *  We have to be very careful if we implement an adaptive scheme,
+     *  since the key manager is per-router, not per-local-dest.
+     *  Or maybe that's a bad idea, and we need to move to a per-dest manager.
+     *  This needs further investigation.
+     *
+     *  So a value somewhat higher than the low threshold
+     *  seems appropriate.
+     */
+    private static final int DEFAULT_TAGS = 40;
+    private static final int LOW_THRESHOLD = 20;
+
     public static int estimateAvailableTags(RouterContext ctx, PublicKey key) {
         SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
         if (curKey == null)
@@ -41,7 +72,7 @@ public class GarlicMessageBuilder {
         return buildMessage(ctx, config, new SessionKey(), new HashSet());
     }
     public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
-        return buildMessage(ctx, config, wrappedKey, wrappedTags, 100);
+        return buildMessage(ctx, config, wrappedKey, wrappedTags, DEFAULT_TAGS);
     }
     public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, int numTagsToDeliver) {
         return buildMessage(ctx, config, wrappedKey, wrappedTags, numTagsToDeliver, false);
@@ -74,13 +105,13 @@ public class GarlicMessageBuilder {
             if (log.shouldLog(Log.DEBUG))
                 log.debug("Available tags for encryption to " + key + ": " + availTags);
 
-            if (availTags < 20) { // arbitrary threshold
+            if (availTags < LOW_THRESHOLD) { // arbitrary threshold
                 for (int i = 0; i < numTagsToDeliver; i++)
                     wrappedTags.add(new SessionTag(true));
                 if (log.shouldLog(Log.INFO))
-                    log.info("Less than 20 tags are available (" + availTags + "), so we're including more");
+                    log.info("Too few are available (" + availTags + "), so we're including more");
             } else if (ctx.sessionKeyManager().getAvailableTimeLeft(key, curKey) < 60*1000) {
-                // if we have > 20 tags, but they expire in under 30 seconds, we want more
+                // if we have enough tags, but they expire in under 30 seconds, we want more
                 for (int i = 0; i < numTagsToDeliver; i++)
                     wrappedTags.add(new SessionTag(true));
                 if (log.shouldLog(Log.INFO))
diff --git a/router/java/src/net/i2p/router/message/GarlicMessageParser.java b/router/java/src/net/i2p/router/message/GarlicMessageParser.java
index 3e99e0731688dba7b279126ac3825f600ae9b5e6..84ed50b2c56baf9b41cd6642dc759d2df31bf1f9 100644
--- a/router/java/src/net/i2p/router/message/GarlicMessageParser.java
+++ b/router/java/src/net/i2p/router/message/GarlicMessageParser.java
@@ -9,8 +9,6 @@ package net.i2p.router.message;
  */
 
 import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
 
 import net.i2p.data.Certificate;
 import net.i2p.data.DataFormatException;
diff --git a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java
index a114764f6a0286e0d30aff5e5bf95c3319c03303..05aecfb89b4ab0980a81831d0023b6f6bdd56949 100644
--- a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java
+++ b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java
@@ -106,6 +106,7 @@ public class HandleGarlicMessageJob extends JobImpl implements GarlicMessageRece
         }
     }
     
+    @Override
     public void dropped() {
         getContext().messageHistory().messageProcessingError(_message.getUniqueId(), 
                                                          _message.getClass().getName(), 
diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageStatus.java b/router/java/src/net/i2p/router/message/OutboundClientMessageStatus.java
index b24154d6f63ec475fdc6e60b9a045adde3608c50..3d15ce07cd6179475a405bb9990287d0430c4109 100644
--- a/router/java/src/net/i2p/router/message/OutboundClientMessageStatus.java
+++ b/router/java/src/net/i2p/router/message/OutboundClientMessageStatus.java
@@ -20,7 +20,7 @@ class OutboundClientMessageStatus {
     private ClientMessage _msg;
     private PayloadGarlicConfig _clove;
     private LeaseSet _leaseSet;
-    private Set _sent;
+    private final Set _sent;
     private int _numLookups;
     private boolean _success;
     private boolean _failure;
@@ -114,6 +114,7 @@ class OutboundClientMessageStatus {
         public Hash getGateway() { return _gateway; }
         public TunnelId getTunnel() { return _tunnel; }
 
+        @Override
         public int hashCode() {
             int rv = 0;
             if (_gateway != null)
@@ -123,6 +124,7 @@ class OutboundClientMessageStatus {
             return rv;
         }
 
+        @Override
         public boolean equals(Object o) {
             if (o == null) return false;
             if (o.getClass() != Tunnel.class) return false;
diff --git a/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java b/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java
index eb2c0488634cd8c3a88672a1bf3d12037689dafa..2b2a8f9c88741c865d0a6640bf2e0e7d6d9d45a5 100644
--- a/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java
+++ b/router/java/src/net/i2p/router/message/PayloadGarlicConfig.java
@@ -33,6 +33,7 @@ public class PayloadGarlicConfig extends GarlicConfig {
     }
     public I2NPMessage getPayload() { return _payload; }
  
+    @Override
     protected String getSubData() { 
 	StringBuffer buf = new StringBuffer();
 	buf.append("<payloadMessage>").append(_payload).append("</payloadMessage>");
diff --git a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java
index db696461a8f7669e0b5b3329b915592c4887a6e4..84dd56890b3c082ac25a84062eb96cbc2bfbb859 100644
--- a/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java
+++ b/router/java/src/net/i2p/router/networkdb/DatabaseLookupMessageHandler.java
@@ -28,19 +28,12 @@ public class DatabaseLookupMessageHandler implements HandlerJobBuilder {
         _context = context;
         _log = context.logManager().getLog(DatabaseLookupMessageHandler.class);
         _context.statManager().createRateStat("netDb.lookupsReceived", "How many netDb lookups have we received?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
+        // FIXME: is netDb.lookupsDropped actually used elsewhere?
         _context.statManager().createRateStat("netDb.lookupsDropped", "How many netDb lookups did we drop due to throttling?", "NetworkDatabase", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
     }
 
     public Job createJob(I2NPMessage receivedMessage, RouterIdentity from, Hash fromHash) {
         _context.statManager().addRateData("netDb.lookupsReceived", 1, 0);
-
-        if (true || _context.throttle().acceptNetDbLookupRequest(((DatabaseLookupMessage)receivedMessage).getSearchKey())) {
-            return new HandleDatabaseLookupMessageJob(_context, (DatabaseLookupMessage)receivedMessage, from, fromHash);
-        } else {
-            if (_log.shouldLog(Log.INFO)) 
-                _log.info("Dropping lookup request as throttled");
-            _context.statManager().addRateData("netDb.lookupsDropped", 1, 1);
-            return null;
-        }
+        return new HandleDatabaseLookupMessageJob(_context, (DatabaseLookupMessage)receivedMessage, from, fromHash);
     }
 }
diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java
index a953bd59595d65fb7887ac690334a187c0cc2dbb..76bc897ed149d6acb7329284fad17b52417e0565 100644
--- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseLookupMessageJob.java
@@ -262,6 +262,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
     
     public String getName() { return "Handle Database Lookup Message"; }
 
+    @Override
     public void dropped() {
         getContext().messageHistory().messageProcessingError(_message.getUniqueId(), 
                                                          _message.getClass().getName(), 
diff --git a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java
index 3a395db0ace649c6a49d1c0041f0bbba6eaab899..c4a0b9f451af4ecc04515fae490be2ab43d266c4 100644
--- a/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/HandleDatabaseStoreMessageJob.java
@@ -132,6 +132,7 @@ public class HandleDatabaseStoreMessageJob extends JobImpl {
  
     public String getName() { return "Handle Database Store Message"; }
     
+    @Override
     public void dropped() {
         getContext().messageHistory().messageProcessingError(_message.getUniqueId(), _message.getClass().getName(), "Dropped due to overload");
     }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java
index 388afa8869627306fbbc7c0f47b6d1cfb720e175..e8e0a01f102212832cd5ba92f5fe17dc8cbf6476 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/ExploreJob.java
@@ -11,7 +11,6 @@ package net.i2p.router.networkdb.kademlia;
 import java.util.List;
 
 import net.i2p.data.Hash;
-import net.i2p.data.RouterInfo;
 import net.i2p.data.TunnelId;
 import net.i2p.data.i2np.DatabaseLookupMessage;
 import net.i2p.router.RouterContext;
@@ -41,11 +40,11 @@ class ExploreJob extends SearchJob {
     /** only send the closest "dont tell me about" refs...
      *  Override to make this bigger because we want to include both the
      *  floodfills and the previously-queried peers */
-    static final int MAX_CLOSEST = 20;
+    static final int MAX_CLOSEST = 20; // LINT -- field hides another field, this isn't an override.
     
     /** Override to make this shorter, since we don't sort out the
      *  unresponsive ff peers like we do in FloodOnlySearchJob */
-    static final int PER_FLOODFILL_PEER_TIMEOUT = 5*1000;
+    static final int PER_FLOODFILL_PEER_TIMEOUT = 5*1000; // LINT -- field hides another field, this isn't an override.
 
     /**
      * Create a new search for the routingKey specified
@@ -78,6 +77,7 @@ class ExploreJob extends SearchJob {
      * @param replyGateway gateway for the reply tunnel
      * @param expiration when the search should stop
      */
+    @Override
     protected DatabaseLookupMessage buildMessage(TunnelId replyTunnelId, Hash replyGateway, long expiration) {
         DatabaseLookupMessage msg = new DatabaseLookupMessage(getContext(), true);
         msg.setSearchKey(getState().getTarget());
@@ -112,11 +112,13 @@ class ExploreJob extends SearchJob {
      * replies sent back to us directly).  This uses the similar overrides as the other buildMessage above.
      *
      */
+    @Override
     protected DatabaseLookupMessage buildMessage(long expiration) {
         return buildMessage(null, getContext().router().getRouterInfo().getIdentity().getHash(), expiration);
     }
     
     /** max # of concurrent searches */
+    @Override
     protected int getBredth() { return EXPLORE_BREDTH; }
     
     
@@ -125,6 +127,7 @@ class ExploreJob extends SearchJob {
      * number of peers that we didn't know about before.
      *
      */
+    @Override
     protected void newPeersFound(int numNewPeers) {
         // who cares about how many new peers.  well, maybe we do.  but for now,
         // we'll do the simplest thing that could possibly work.
@@ -139,5 +142,6 @@ class ExploreJob extends SearchJob {
      *
      */
     
+    @Override
     public String getName() { return "Kademlia NetDb Explore"; }
 }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupSelector.java
index b8c46fc33eecf366c8b523a438e1c697c6bdc5be..ff98d1429b5233a2f29870bc9d4782d5de706b43 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlyLookupSelector.java
@@ -3,7 +3,6 @@ package net.i2p.router.networkdb.kademlia;
 import net.i2p.data.i2np.DatabaseSearchReplyMessage;
 import net.i2p.data.i2np.DatabaseStoreMessage;
 import net.i2p.data.i2np.I2NPMessage;
-import net.i2p.router.JobImpl;
 import net.i2p.router.MessageSelector;
 import net.i2p.router.RouterContext;
 import net.i2p.util.Log;
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlySearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlySearchJob.java
index 5e68863026152c5ba58561df3759bfb06fc6c6ee..e5b5e962d6b076a2bde7f651b3f8ca08aef6661f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlySearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodOnlySearchJob.java
@@ -8,11 +8,7 @@ import java.util.List;
 
 import net.i2p.data.Hash;
 import net.i2p.data.i2np.DatabaseLookupMessage;
-import net.i2p.data.i2np.DatabaseSearchReplyMessage;
-import net.i2p.data.i2np.DatabaseStoreMessage;
-import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.router.Job;
-import net.i2p.router.JobImpl;
 import net.i2p.router.MessageSelector;
 import net.i2p.router.OutNetMessage;
 import net.i2p.router.ReplyJob;
@@ -44,8 +40,8 @@ class FloodOnlySearchJob extends FloodSearchJob {
     protected Log _log;
     private FloodfillNetworkDatabaseFacade _facade;
     protected Hash _key;
-    private List _onFind;
-    private List _onFailed;
+    private final List _onFind;
+    private final List _onFailed;
     private long _expiration;
     protected int _timeoutMs;
     private long _origExpiration;
@@ -54,9 +50,9 @@ class FloodOnlySearchJob extends FloodSearchJob {
     private volatile boolean _dead;
     private long _created;
     private boolean _shouldProcessDSRM;
-    private HashSet _unheardFrom;
+    private final HashSet _unheardFrom;
     
-    protected List _out;
+    protected final List _out;
     protected MessageSelector _replySelector;
     protected ReplyJob _onReply;
     protected Job _onTimeout;
@@ -83,6 +79,7 @@ class FloodOnlySearchJob extends FloodSearchJob {
         _created = System.currentTimeMillis();
         _shouldProcessDSRM = false;
     }
+    @Override
     void addDeferred(Job onFind, Job onFailed, long timeoutMs, boolean isLease) {
         if (_dead) {
             getContext().jobQueue().addJob(onFailed);
@@ -91,10 +88,12 @@ class FloodOnlySearchJob extends FloodSearchJob {
             if (onFailed != null) synchronized (_onFailed) { _onFailed.add(onFailed); }
         }
     }
+    @Override
     public long getExpiration() { return _expiration; }
     public long getCreated() { return _created; }
     public boolean shouldProcessDSRM() { return _shouldProcessDSRM; }
     private static final int CONCURRENT_SEARCHES = 2;
+    @Override
     public void runJob() {
         // pick some floodfill peers and send out the searches
         List floodfillPeers = _facade.getFloodfillPeers();
@@ -184,10 +183,14 @@ class FloodOnlySearchJob extends FloodSearchJob {
             failed();
         }
     }
+    @Override
     public String getName() { return "NetDb flood search (phase 1)"; }
     
+    @Override
     Hash getKey() { return _key; }
+    @Override
     void decrementRemaining() { if (_lookupsRemaining > 0) _lookupsRemaining--; }
+    @Override
     int getLookupsRemaining() { return _lookupsRemaining; }
     /** Note that we heard from the peer */
     void decrementRemaining(Hash peer) {
@@ -197,6 +200,7 @@ class FloodOnlySearchJob extends FloodSearchJob {
         }
     }
     
+    @Override
     void failed() {
         synchronized (this) {
             if (_dead) return;
@@ -224,6 +228,7 @@ class FloodOnlySearchJob extends FloodSearchJob {
             }
         }
     }
+    @Override
     void success() {
         synchronized (this) {
             if (_dead) return;
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
index 575de5e7b356fc37e94ff5f77660576d0490900a..3772b18d1ee2694f0e542ac20f2b462d73c3cc4f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java
@@ -31,7 +31,7 @@ import net.i2p.util.Log;
  */
 public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacade {
     public static final char CAPACITY_FLOODFILL = 'f';
-    private Map _activeFloodQueries;
+    private final Map _activeFloodQueries;
     private boolean _floodfillEnabled;
     /** for testing, see isFloodfill() below */
     private static String _alwaysQuery;
@@ -55,11 +55,13 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
         _context.statManager().createRateStat("netDb.republishQuantity", "How many peers do we need to send a found leaseSet to?", "NetworkDatabase", new long[] { 10*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000l });
     }
 
+    @Override
     public void startup() {
         super.startup();
         _context.jobQueue().addJob(new FloodfillMonitorJob(_context, this));
     }
 
+    @Override
     protected void createHandlers() {
         _context.inNetMessagePool().registerHandlerJobBuilder(DatabaseLookupMessage.MESSAGE_TYPE, new FloodfillDatabaseLookupMessageHandler(_context));
         _context.inNetMessagePool().registerHandlerJobBuilder(DatabaseStoreMessage.MESSAGE_TYPE, new FloodfillDatabaseStoreMessageHandler(_context, this));
@@ -70,6 +72,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
     /**
      * @throws IllegalArgumentException if the local router info is invalid
      */
+    @Override
     public void publish(RouterInfo localRouterInfo) throws IllegalArgumentException {
         if (localRouterInfo == null) throw new IllegalArgumentException("wtf, null localRouterInfo?");
         if (_context.router().isHidden()) return; // DE-nied!
@@ -77,6 +80,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
         sendStore(localRouterInfo.getIdentity().calculateHash(), localRouterInfo, null, null, PUBLISH_TIMEOUT, null);
     }
     
+    @Override
     public void sendStore(Hash key, DataStructure ds, Job onSuccess, Job onFailure, long sendTimeout, Set toIgnore) {
         // if we are a part of the floodfill netDb, don't send out our own leaseSets as part 
         // of the flooding - instead, send them to a random floodfill peer so *they* can flood 'em out.
@@ -131,6 +135,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
     private static final int FLOOD_PRIORITY = 200;
     private static final int FLOOD_TIMEOUT = 30*1000;
     
+    @Override
     protected PeerSelector createPeerSelector() { return new FloodfillPeerSelector(_context); }
     
     public void setFloodfillEnabled(boolean yes) { _floodfillEnabled = yes; }
@@ -183,6 +188,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
      * without any match)
      *
      */
+    @Override
     SearchJob search(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs, boolean isLease) {
         //if (true) return super.search(key, onFindJob, onFailedLookupJob, timeoutMs, isLease);
         if (key == null) throw new IllegalArgumentException("searchin for nothin, eh?");
@@ -282,6 +288,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
       * Search for a newer router info, drop it from the db if the search fails,
       * unless just started up or have bigger problems.
       */
+    @Override
     protected void lookupBeforeDropping(Hash peer, RouterInfo info) {
         // following are some special situations, we don't want to
         // drop the peer in these cases
@@ -356,8 +363,8 @@ class FloodSearchJob extends JobImpl {
     private Log _log;
     private FloodfillNetworkDatabaseFacade _facade;
     private Hash _key;
-    private List _onFind;
-    private List _onFailed;
+    private final List _onFind;
+    private final List _onFailed;
     private long _expiration;
     private int _timeoutMs;
     private long _origExpiration;
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
index 7504e660a02070e18b284bc779709b9ddbcf73b9..049140397fc4127a8cbe7adc501a2475ea89f68e 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillPeerSelector.java
@@ -31,10 +31,12 @@ class FloodfillPeerSelector extends PeerSelector {
      *
      * @return List of Hash for the peers selected
      */
+    @Override
     public List selectMostReliablePeers(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { 
         return selectNearestExplicitThin(key, maxNumRouters, peersToIgnore, kbuckets, true);
     }
 
+    @Override
     public List selectNearestExplicitThin(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { 
         return selectNearestExplicitThin(key, maxNumRouters, peersToIgnore, kbuckets, false);
     }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
index f3880597d8334de33014b199d9758255dcb311f4..7c37cfc18ee1ab1d8d189a7138a12889233ef5aa 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java
@@ -34,17 +34,21 @@ class FloodfillStoreJob extends StoreJob {
         _facade = facade;
     }
 
+    @Override
     protected int getParallelization() { return 1; }
+    @Override
     protected int getRedundancy() { return 1; }
 
     /**
      * Send was totally successful
      */
+    @Override
     protected void succeed() {
         super.succeed();
         if (_state != null)
             getContext().jobQueue().addJob(new FloodfillVerifyStoreJob(getContext(), _state.getTarget(), _facade));
     }
     
+    @Override
     public String getName() { return "Floodfill netDb store"; }
 }
\ No newline at end of file
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
index e3a6b6df9d372192c2118b1c5c63440a01d446aa..f8c7adf42492e52ad45f3393bf30531003cbd8ae 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseLookupMessageJob.java
@@ -30,6 +30,7 @@ public class HandleFloodfillDatabaseLookupMessageJob extends HandleDatabaseLooku
         super(ctx, receivedMessage, from, fromHash);    
     }
     
+    @Override
     protected boolean answerAllQueries() {
         if (!FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext())) return false;
         return FloodfillNetworkDatabaseFacade.isFloodfill(getContext().router().getRouterInfo());
@@ -40,6 +41,7 @@ public class HandleFloodfillDatabaseLookupMessageJob extends HandleDatabaseLooku
      * This gets the word out to routers that we are no longer floodfill, so they
      * will stop bugging us.
      */
+    @Override
     protected void sendClosest(Hash key, Set routerInfoSet, Hash toPeer, TunnelId replyTunnel) {
         super.sendClosest(key, routerInfoSet, toPeer, replyTunnel);
 
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
index 1b00a1b251373fdc084e0357b2c3173491951327..505638ecbdb31246eec5110d01c4d80c5cc9a8a4 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java
@@ -184,6 +184,7 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl {
  
     public String getName() { return "Handle Database Store Message"; }
     
+    @Override
     public void dropped() {
         getContext().messageHistory().messageProcessingError(_message.getUniqueId(), _message.getClass().getName(), "Dropped due to overload");
     }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java
index 5d8e186f50728db534008019467733dab36e6705..c6668a41e4fcefd5527dce2c5f0682228202cb2e 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketImpl.java
@@ -26,7 +26,7 @@ import net.i2p.util.RandomSource;
 class KBucketImpl implements KBucket {
     private Log _log;
     /** set of Hash objects for the peers in the kbucket */
-    private List _entries;
+    private final List _entries;
     /** we center the kbucket set on the given hash, and derive distances from this */
     private Hash _local;
     /** include if any bits equal or higher to this bit (in big endian order) */
@@ -328,6 +328,7 @@ class KBucketImpl implements KBucket {
             return BigInteger.ZERO.setBit(_begin);
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(1024);
         buf.append("KBucketImpl: ");
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java
index 6b3ca79d6c3544a63139ce72fa673b820ea38c61..ddbfb06f7f176a35a51fb976aafa8877c32426f2 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KBucketSet.java
@@ -138,6 +138,7 @@ class KBucketSet {
         _log.info(toString());
     }
     
+    @Override
     public String toString() {
         BigInteger us = new BigInteger(1, _us.getData());
         StringBuffer buf = new StringBuffer(1024);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index 6ff82c6a415f66aeab9dabb9dfb3892f37106f61..28a35862be102f5cbe5996f570691d555379cb6a 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -53,7 +53,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
     private DataStore _ds; // hash to DataStructure mapping, persisted when necessary
     /** where the data store is pushing the data */
     private String _dbDir;
-    private Set _exploreKeys; // set of Hash objects that we should search on (to fill up a bucket, not to get data)
+    private final Set _exploreKeys = new HashSet(64); // set of Hash objects that we should search on (to fill up a bucket, not to get data)
     private boolean _initialized;
     /** Clock independent time of when we started up */
     private long _started;
@@ -69,7 +69,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
      * removed when the job decides to stop running.
      *
      */
-    private Map _publishingLeaseSets;   
+    private final Map _publishingLeaseSets;
     
     /** 
      * Hash of the key currently being searched for, pointing the SearchJob that
@@ -77,7 +77,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
      * added on to the list of jobs fired on success/failure
      *
      */
-    private Map _activeRequests;
+    private final Map _activeRequests;
     
     /**
      * The search for the given key is no longer active
@@ -115,11 +115,13 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
     protected final static int MIN_REMAINING_ROUTERS = 25;
     
     /** 
-     * dont accept any dbDtore of a router over 24 hours old (unless we dont 
-     * know anyone or just started up) 
+     * limits for accepting a dbDtore of a router (unless we dont 
+     * know anyone or just started up) -- see validate() below
      */
     private final static long ROUTER_INFO_EXPIRATION = 3*24*60*60*1000l;
+    private final static long ROUTER_INFO_EXPIRATION_MIN = 3*60*60*1000l;
     private final static long ROUTER_INFO_EXPIRATION_SHORT = 90*60*1000l;
+    private final static long ROUTER_INFO_EXPIRATION_FLOODFILL = 60*60*1000l;
     
     private final static long EXPLORE_JOB_DELAY = 10*60*1000l;
     private final static long PUBLISH_JOB_DELAY = 5*60*1000l;
@@ -176,7 +178,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         _initialized = false;
         _kb = null;
         _ds = null;
-        _exploreKeys = null;
+        _exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
+        // _exploreKeys = null;
     }
     
     public void restart() {
@@ -218,7 +221,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         _kb = new KBucketSet(_context, ri.getIdentity().getHash());
         _ds = new PersistentDataStore(_context, dbDir, this);
         //_ds = new TransientDataStore();
-        _exploreKeys = new HashSet(64);
+//        _exploreKeys = new HashSet(64);
         _dbDir = dbDir;
         
         createHandlers();
@@ -331,6 +334,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         return rv;
     }
     
+    @Override
     public int getKnownRouters() { 
         if (_kb == null) return 0;
         CountRouters count = new CountRouters();
@@ -349,11 +353,13 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         }
     }
     
+    @Override
     public int getKnownLeaseSets() {  
         if (_ds == null) return 0;
         return _ds.countLeaseSets();
     }
-    
+
+    /* aparently, not used?? should be public if used elsewhere. */
     private class CountLeaseSets implements SelectionCollector {
         private int _count;
         public int size() { return _count; }
@@ -364,7 +370,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
                 _count++;
         }
     }
-    
+
     /**
      *  This is fast and doesn't use synchronization,
      *  but it includes both routerinfos and leasesets.
@@ -566,7 +572,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
                 _log.error("LeaseSet to expire too far in the future: " 
                           + leaseSet.getDestination().calculateHash().toBase64() 
                           + " expires on " + new Date(leaseSet.getEarliestLeaseDate()), new Exception("Rejecting store"));
-            return "Expired leaseSet for " + leaseSet.getDestination().calculateHash().toBase64() 
+            return "Future expiring leaseSet for " + leaseSet.getDestination().calculateHash().toBase64() 
                    + " expiring in " + DataHelper.formatDuration(age);
         }
         return null;
@@ -620,7 +626,22 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
      */
     String validate(Hash key, RouterInfo routerInfo) throws IllegalArgumentException {
         long now = _context.clock().now();
-        
+        boolean upLongEnough = _context.router().getUptime() > 60*60*1000;
+        // Once we're over 300 routers, reduce the expiration time down from the default,
+        // as a crude way of limiting memory usage.
+        // i.e. at 600 routers the expiration time will be about half the default, etc.
+        // And if we're floodfill, we can keep the expiration really short, since
+        // we are always getting the latest published to us.
+        // As the net grows this won't be sufficient, and we'll have to implement
+        // flushing some from memory, while keeping all on disk.
+        long adjustedExpiration;
+        if (FloodfillNetworkDatabaseFacade.floodfillEnabled(_context))
+            adjustedExpiration = ROUTER_INFO_EXPIRATION_FLOODFILL;
+        else
+            adjustedExpiration = Math.min(ROUTER_INFO_EXPIRATION,
+                                          ROUTER_INFO_EXPIRATION_MIN +
+                                          ((ROUTER_INFO_EXPIRATION - ROUTER_INFO_EXPIRATION_MIN) * 300 / (_kb.size() + 1)));
+
         if (!key.equals(routerInfo.getIdentity().getHash())) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Invalid store attempt! key does not match routerInfo.identity!  key = " + key + ", router = " + routerInfo);
@@ -629,7 +650,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
             if (_log.shouldLog(Log.WARN))
                 _log.warn("Invalid routerInfo signature!  forged router structure!  router = " + routerInfo);
             return "Invalid routerInfo signature on " + key.toBase64();
-        } else if (!routerInfo.isCurrent(ROUTER_INFO_EXPIRATION) && (_context.router().getUptime() > 60*60*1000) ) {
+        } else if (upLongEnough && !routerInfo.isCurrent(adjustedExpiration)) {
             if (routerInfo.getNetworkId() != Router.NETWORK_ID) {
                 _context.shitlist().shitlistRouter(key, "Peer is not in our network");
                 return "Peer is not in our network (" + routerInfo.getNetworkId() + ", wants " 
@@ -657,10 +678,10 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
             String rv = "Peer " + key.toBase64() + " is from another network, not accepting it (id=" 
                         + routerInfo.getNetworkId() + ", want " + Router.NETWORK_ID + ")";
             return rv;
-        } else if ( (_context.router().getUptime() > 60*60*1000) && (routerInfo.getPublished() < now - 2*24*60*60*1000l) ) {
+        } else if (upLongEnough && (routerInfo.getPublished() < now - 2*24*60*60*1000l) ) {
             long age = _context.clock().now() - routerInfo.getPublished();
             return "Peer " + key.toBase64() + " published " + DataHelper.formatDuration(age) + " ago";
-        } else if (!routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT) && (_context.router().getUptime() > 60*60*1000) ) {
+        } else if (upLongEnough && !routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT)) {
             if (routerInfo.getAddresses().size() <= 0)
                 return "Peer " + key.toBase64() + " published > 90m ago with no addresses";
             RouterAddress ra = routerInfo.getTargetAddress("SSU");
@@ -868,6 +889,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         }
     }
 
+    @Override
     public void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException {
         StringBuffer buf = new StringBuffer(4*1024);
         buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
@@ -895,23 +917,14 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         renderStatusHTML(out, true);
     }
 
-    public void renderStatusHTML(Writer out, boolean full) throws IOException {
-        int size = getKnownRouters() * 512;
-        if (full)
-            size *= 4;
-        StringBuffer buf = new StringBuffer(size);
+    @Override
+    public void renderLeaseSetHTML(Writer out) throws IOException {
+        StringBuffer buf = new StringBuffer(4*1024);
         buf.append("<h2>Network Database Contents</h2>\n");
-        if (!_initialized) {
-            buf.append("<i>Not initialized</i>\n");
-            out.write(buf.toString());
-            out.flush();
-            return;
-        }
+        buf.append("<a href=\"netdb.jsp\">View RouterInfo</a>");
+        buf.append("<h3>LeaseSets</h3>\n");
         Set leases = new TreeSet(new LeaseSetComparator());
         leases.addAll(getLeases());
-        buf.append("<h3>Leases</h3>\n");
-        out.write(buf.toString());
-        buf.setLength(0);
         long now = _context.clock().now();
         for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
             LeaseSet ls = (LeaseSet)iter.next();
@@ -951,7 +964,25 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
             out.write(buf.toString());
             buf.setLength(0);
         }
+        out.write(buf.toString());
+        out.flush();
+    }
+
+    @Override
+    public void renderStatusHTML(Writer out, boolean full) throws IOException {
+        int size = getKnownRouters() * 512;
+        if (full)
+            size *= 4;
+        StringBuffer buf = new StringBuffer(size);
+        out.write("<h2>Network Database Contents</h2>\n");
+        if (!_initialized) {
+            buf.append("<i>Not initialized</i>\n");
+            out.write(buf.toString());
+            out.flush();
+            return;
+        }
         
+        out.write("<a href=\"netdb.jsp?l=1\">View LeaseSets</a>");
         Hash us = _context.routerHash();
         out.write("<a name=\"routers\" /><h3>Routers (<a href=\"netdb.jsp");
         if (full)
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java
index 26c1799a36e044655d21915d293eb694802b66fd..c88897a127031dcc818d5d5302ca5ef1e19cdaaa 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PeerSelector.java
@@ -42,7 +42,7 @@ public class PeerSelector {
      *
      * @return ordered list of Hash objects
      */
-    public List selectMostReliablePeers(Hash key, int numClosest, Set alreadyChecked, KBucketSet kbuckets) {
+    public List selectMostReliablePeers(Hash key, int numClosest, Set alreadyChecked, KBucketSet kbuckets) {// LINT -- Exporting non-public type through public API
         // get the peers closest to the key
         List nearest = selectNearestExplicit(key, numClosest, alreadyChecked, kbuckets);
         return nearest;
@@ -55,7 +55,7 @@ public class PeerSelector {
      *
      * @return List of Hash for the peers selected, ordered by bucket (but intra bucket order is not defined)
      */
-    public List selectNearestExplicit(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { 
+    public List selectNearestExplicit(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) {// LINT -- Exporting non-public type through public API
         if (true)
             return selectNearestExplicitThin(key, maxNumRouters, peersToIgnore, kbuckets);
         
@@ -91,7 +91,7 @@ public class PeerSelector {
      *
      * @return List of Hash for the peers selected, ordered by bucket (but intra bucket order is not defined)
      */
-    public List selectNearestExplicitThin(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { 
+    public List selectNearestExplicitThin(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { // LINT -- Exporting non-public type through public API
         if (peersToIgnore == null)
             peersToIgnore = new HashSet(1);
         peersToIgnore.add(_context.router().getRouterInfo().getIdentity().getHash());
@@ -195,7 +195,7 @@ public class PeerSelector {
      *
      * @return List of Hash for the peers selected, ordered by bucket (but intra bucket order is not defined)
      */
-    public List selectNearest(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { 
+    public List selectNearest(Hash key, int maxNumRouters, Set peersToIgnore, KBucketSet kbuckets) { // LINT -- Exporting non-public type through public API
         // sure, this may not be exactly correct per kademlia (peers on the border of a kbucket in strict kademlia
         // would behave differently) but I can see no reason to keep around an /additional/ more complicated algorithm.
         // later if/when selectNearestExplicit gets costly, we may revisit this (since kbuckets let us cache the distance()
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
index 5d0d219dbe6fe125bbb698d0bcb49724f783b114..0186bd922529992315586b6649f40c1be397d5f3 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
@@ -56,15 +56,18 @@ class PersistentDataStore extends TransientDataStore {
         writer.start();
     }
     
+    @Override
     public void restart() {
         _dbDir = _facade.getDbDir();
     }
     
+    @Override
     public DataStructure remove(Hash key) {
         _context.jobQueue().addJob(new RemoveJob(key));
         return super.remove(key);
     }
     
+    @Override
     public void put(Hash key, DataStructure data) {
         if ( (data == null) || (key == null) ) return;
         super.put(key, data);
@@ -105,7 +108,7 @@ class PersistentDataStore extends TransientDataStore {
      * Queue up writes, write up to 600 files every 10 minutes
      */
     private class Writer implements Runnable {
-        private Map _keys;
+        private final Map _keys;
         private List _keyOrder;
         public Writer() { 
             _keys = new HashMap(64);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
index ac191a96c745d7495c77371a543828bde144d703..95af13a4322fa493abc30a702f592a76e946bfd8 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
@@ -23,7 +23,7 @@ import net.i2p.util.Log;
 public class RepublishLeaseSetJob extends JobImpl {
     private Log _log;
     private final static long REPUBLISH_LEASESET_DELAY = 5*60*1000;
-    public final static long REPUBLISH_LEASESET_TIMEOUT = 60*1000;
+    public final /* static */ long REPUBLISH_LEASESET_TIMEOUT = 60*1000;
     private Hash _dest;
     private KademliaNetworkDatabaseFacade _facade;
     /** this is actually last attempted publish */
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
index 868c6b1084cc7c776e60e1a7d12fbb937511ea63..161f2900128e32e9c6c92a9675535b2445a7ab95 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchJob.java
@@ -47,7 +47,7 @@ class SearchJob extends JobImpl {
     private boolean _isLease;
     private Job _pendingRequeueJob;
     private PeerSelector _peerSelector;
-    private List _deferredSearches;
+    private final List _deferredSearches;
     private boolean _deferredCleared;
     private long _startedOn;
     private boolean _floodfillPeersExhausted;
@@ -780,6 +780,7 @@ class SearchJob extends JobImpl {
     
     public String getName() { return "Kademlia NetDb Search"; }
     
+    @Override
     public String toString() { 
         return super.toString() + " started " 
                + DataHelper.formatDuration((getContext().clock().now() - _startedOn)) + " ago";
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchMessageSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchMessageSelector.java
index 4f3869072fef05e5568bea99325e6f7f0c131461..73b3ddf24ece64c6c882e342a5e12abe5ea08a7b 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchMessageSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchMessageSelector.java
@@ -36,6 +36,7 @@ class SearchMessageSelector implements MessageSelector {
             _log.debug("[" + _id + "] Created: " + toString());
     }
     
+    @Override
     public String toString() { 
         return "Search selector [" + _id + "] looking for a reply from " + _peer 
                + " with regards to " + _state.getTarget(); 
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java b/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java
index da00483ab4cca89f05d8679c0ba24ca698010da4..a12f58fd74e5f377f5a620ae7b15dc72eb589ec8 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/SearchState.java
@@ -17,12 +17,12 @@ import net.i2p.router.RouterContext;
  */
 class SearchState {
     private RouterContext _context;
-    private HashSet _pendingPeers;
+    private final HashSet _pendingPeers;
     private HashMap _pendingPeerTimes;
-    private HashSet _attemptedPeers;
-    private HashSet _failedPeers;
-    private HashSet _successfulPeers;
-    private HashSet _repliedPeers;
+    private final HashSet _attemptedPeers;
+    private final HashSet _failedPeers;
+    private final HashSet _successfulPeers;
+    private final HashSet _repliedPeers;
     private Hash _searchKey;
     private volatile long _completed;
     private volatile long _started;
@@ -166,6 +166,7 @@ class SearchState {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(256);
         buf.append("Searching for ").append(_searchKey);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java
index 3ec005ec5ede2de0629d45ad2624468b83ae2435..74c75cb5a3a5fead94240739b3cfdc31d540b463 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/SingleSearchJob.java
@@ -2,7 +2,6 @@ package net.i2p.router.networkdb.kademlia;
 
 import net.i2p.data.Hash;
 import net.i2p.data.i2np.DatabaseLookupMessage;
-import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
 import net.i2p.router.OutNetMessage;
 import net.i2p.router.TunnelInfo;
@@ -23,8 +22,11 @@ class SingleSearchJob extends FloodOnlySearchJob {
         super(ctx, null, key, null, null, 5*1000, false);
         _to = to;
     }
+    @Override
     public String getName() { return "NetDb search key from DSRM"; }
+    @Override
     public boolean shouldProcessDSRM() { return false; } // don't loop
+    @Override
     public void runJob() {
         _onm = getContext().messageRegistry().registerPending(_replySelector, _onReply, _onTimeout, _timeoutMs);
         DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true);
@@ -44,8 +46,10 @@ class SingleSearchJob extends FloodOnlySearchJob {
         getContext().tunnelDispatcher().dispatchOutbound(dlm, outTunnel.getSendTunnelId(0), _to);
         _lookupsRemaining = 1;
     }
+    @Override
     void failed() {
         getContext().messageRegistry().unregisterPending(_onm);
     }
+    @Override
     void success() {}
 }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreMessageSelector.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreMessageSelector.java
index 26d4fda1317a9770f9fca79f94521c18efe91fa1..e8f0eca7ea290d66d5e21ee10a1ea1db2f9e34d2 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreMessageSelector.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreMessageSelector.java
@@ -55,6 +55,7 @@ class StoreMessageSelector implements MessageSelector {
         }
     }
 
+    @Override
     public String toString() {
         StringBuffer rv = new StringBuffer(64);
         rv.append("Waiting for netDb confirm from ").append(_peer.toBase64()).append(", found? ");
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
index e7bfe3292620ee119396511f88ca1344839ee8fe..184c337a9bc587436a37842ea35b2be1ad273514 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreState.java
@@ -15,12 +15,12 @@ class StoreState {
     private RouterContext _context;
     private Hash _key;
     private DataStructure _data;
-    private HashSet _pendingPeers;
+    private final HashSet _pendingPeers;
     private HashMap _pendingPeerTimes;
-    private HashSet _successfulPeers;
-    private HashSet _successfulExploratoryPeers;
-    private HashSet _failedPeers;
-    private HashSet _attemptedPeers;
+    private final HashSet _successfulPeers;
+    private final HashSet _successfulExploratoryPeers;
+    private final HashSet _failedPeers;
+    private final HashSet _attemptedPeers;
     private int _completeCount;
     private volatile long _completed;
     private volatile long _started;
@@ -147,6 +147,7 @@ class StoreState {
         }
     }
 
+    @Override
     public String toString() { 
         StringBuffer buf = new StringBuffer(256);
         buf.append("Storing ").append(_key);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java
index d2114146827258c172ed19cf1a8f9f838795317b..5fd28cebff279c4336ece63f39dc1c1fa49d2490 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/TransientDataStore.java
@@ -10,9 +10,7 @@ package net.i2p.router.networkdb.kademlia;
 
 import java.util.Date;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
@@ -119,14 +117,17 @@ class TransientDataStore implements DataStore {
         }
     }
     
+    @Override
     public int hashCode() {
         return DataHelper.hashCode(_data);
     }
+    @Override
     public boolean equals(Object obj) {
         if ( (obj == null) || (obj.getClass() != getClass()) ) return false;
         TransientDataStore ds = (TransientDataStore)obj;
         return DataHelper.eq(ds._data, _data);
     }
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer();
         buf.append("Transient DataStore: ").append(_data.size()).append("\nKeys: ");
diff --git a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
index 87d13c16e2f03e65aab4920d29d0cf1b80400538..a1b9d7c2b14dc98503722452b618fe94f00e9185 100644
--- a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
+++ b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
@@ -44,6 +44,7 @@ public class CapacityCalculator extends Calculator {
     /** the calculator estimates over a 1 hour period */
     private static long ESTIMATE_PERIOD = 60*60*1000;
     
+    @Override
     public double calc(PeerProfile profile) {
         RateStat acceptStat = profile.getTunnelCreateResponseTime();
         RateStat rejectStat = profile.getTunnelHistory().getRejectionRate();
diff --git a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
index 26aa2b911b1da99ca0f316bc1e7ee62408018a0f..7fdc5fbce21aed41be4a5181fb74f0221aabba66 100644
--- a/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
+++ b/router/java/src/net/i2p/router/peermanager/IntegrationCalculator.java
@@ -17,6 +17,7 @@ public class IntegrationCalculator extends Calculator {
         _log = context.logManager().getLog(IntegrationCalculator.class);
     }
     
+    @Override
     public double calc(PeerProfile profile) {
         // give more weight to recent counts
         long val = profile.getDbIntroduction().getRate(24*60*60*1000l).getCurrentEventCount();
diff --git a/router/java/src/net/i2p/router/peermanager/PeerManager.java b/router/java/src/net/i2p/router/peermanager/PeerManager.java
index 1c265ee6768e49bb6f22fffb7f54000d26f144fc..146e6a3f9b05e7622a605d1bb970f9d9225205f2 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerManager.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerManager.java
@@ -37,7 +37,7 @@ class PeerManager {
     private ProfileOrganizer _organizer;
     private ProfilePersistenceHelper _persistenceHelper;
     private List _peersByCapability[];
-    private Map _capabilitiesByPeer;
+    private final Map _capabilitiesByPeer;
     
     public PeerManager(RouterContext context) {
         _context = context;
diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
index 2f311b52f90f0cdbeb30b1c937b9b2149180b2b0..ec05f69f61e09e5f881ec8adf7b9f79f6a546179 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
@@ -445,7 +445,9 @@ public class PeerProfile {
     private boolean calculateIsFailing() { return false; }
     void setIsFailing(boolean val) { _isFailing = val; }
     
+    @Override
     public int hashCode() { return (_peer == null ? 0 : _peer.hashCode()); }
+    @Override
     public boolean equals(Object obj) {
         if (obj == null) return false;
         if (obj.getClass() != PeerProfile.class) return false;
@@ -453,6 +455,7 @@ public class PeerProfile {
         PeerProfile prof = (PeerProfile)obj;
         return _peer.equals(prof.getPeer());
     }
+    @Override
     public String toString() { return "Profile: " + getPeer().toBase64(); }
     
     /**
diff --git a/router/java/src/net/i2p/router/peermanager/PeerTestJob.java b/router/java/src/net/i2p/router/peermanager/PeerTestJob.java
index 6c7aef92721b024bdde34485753d04eabaf5ccbc..c4ba39db59447f63971753d99313809c25e5419c 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerTestJob.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerTestJob.java
@@ -50,7 +50,7 @@ public class PeerTestJob extends JobImpl {
     /** number of peers to test each round */
     private int getTestConcurrency() { return 1; }
     
-    public void startTesting(PeerManager manager) { 
+    public void startTesting(PeerManager manager) { // LINT -- Exporting non-public type through public API
         _manager = manager;
         _keepTesting = true;
         getContext().jobQueue().addJob(this); 
@@ -222,6 +222,7 @@ public class PeerTestJob extends JobImpl {
             return false;
         }
         public boolean matchFound() { return _matchFound; }
+        @Override
         public String toString() {
             StringBuffer buf = new StringBuffer(64);
             buf.append("Test peer ").append(_peer.toBase64().substring(0,4));
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index 1a42a244c6c1a459c19fbda3cdff133ec430ce01..906df20368d1e6700f196450a7fd911b0a43bf6a 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -19,6 +19,8 @@ import java.util.Properties;
 import java.util.Random;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import net.i2p.data.Hash;
 import net.i2p.data.RouterAddress;
@@ -40,17 +42,17 @@ public class ProfileOrganizer {
     private Log _log;
     private RouterContext _context;
     /** H(routerIdentity) to PeerProfile for all peers that are fast and high capacity*/
-    private Map _fastPeers;
+    private Map<Hash, PeerProfile> _fastPeers;
     /** H(routerIdentity) to PeerProfile for all peers that have high capacities */
-    private Map _highCapacityPeers;
+    private Map<Hash, PeerProfile> _highCapacityPeers;
     /** H(routerIdentity) to PeerProfile for all peers that well integrated into the network and not failing horribly */
-    private Map _wellIntegratedPeers;
+    private Map<Hash, PeerProfile> _wellIntegratedPeers;
     /** H(routerIdentity) to PeerProfile for all peers that are not failing horribly */
-    private Map _notFailingPeers;
+    private Map<Hash, PeerProfile> _notFailingPeers;
     /** H(routerIdnetity), containing elements in _notFailingPeers */
-    private List _notFailingPeersList;
+    private List<Hash> _notFailingPeersList;
     /** H(routerIdentity) to PeerProfile for all peers that ARE failing horribly (but that we haven't dropped reference to yet) */
-    private Map _failingPeers;
+    private Map<Hash, PeerProfile> _failingPeers;
     /** who are we? */
     private Hash _us;
     private ProfilePersistenceHelper _persistenceHelper;
@@ -84,7 +86,7 @@ public class ProfileOrganizer {
     public static final int DEFAULT_MINIMUM_HIGH_CAPACITY_PEERS = 10;
     
     /** synchronized against this lock when updating the tier that peers are located in (and when fetching them from a peer) */
-    private Object _reorganizeLock = new Object();
+    private final ReentrantReadWriteLock _reorganizeLock = new ReentrantReadWriteLock(true);
     
     /** incredibly weak PRNG, just used for shuffling peers.  no need to waste the real PRNG on this */
     private Random _random = new Random();
@@ -112,6 +114,29 @@ public class ProfileOrganizer {
         _context.statManager().createRateStat("peer.profileReorgTime", "How long the reorg takes overall", "Peers", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
     }
     
+    private void getReadLock() {
+        _reorganizeLock.readLock().lock();
+    }
+
+    private void releaseReadLock() {
+        _reorganizeLock.readLock().unlock();
+    }
+
+    /** @return true if the lock was acquired */
+    private boolean getWriteLock() {
+        try {
+            boolean rv = _reorganizeLock.writeLock().tryLock(5000, TimeUnit.MILLISECONDS);
+            if (!rv)
+                _log.error("no lock, size is: " + _reorganizeLock.getQueueLength(), new Exception("rats"));
+            return rv;
+        } catch (InterruptedException ie) {}
+        return false;
+    }
+
+    private void releaseWriteLock() {
+        _reorganizeLock.writeLock().unlock();
+    }
+
     public void setUs(Hash us) { _us = us; }
     Hash getUs() { return _us; }
     
@@ -124,42 +149,52 @@ public class ProfileOrganizer {
      *
      */
     public PeerProfile getProfile(Hash peer) {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             return locked_getProfile(peer);
-        }
+        } finally { releaseReadLock(); }
     }
     
     /**
      * Add the new profile, returning the old value (or null if no profile existed)
      *
      */
-    public PeerProfile addProfile(PeerProfile profile) throws IllegalStateException {
+    public PeerProfile addProfile(PeerProfile profile) {
         if ( (profile == null) || (profile.getPeer() == null) ) return null;
         
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("New profile created for " + profile.getPeer().toBase64());
         
-        synchronized (_reorganizeLock) {
-            PeerProfile old = locked_getProfile(profile.getPeer());
-            profile.coalesceStats();
+        PeerProfile old = getProfile(profile.getPeer());
+        profile.coalesceStats();
+        if (!getWriteLock())
+            return old;
+        try {
             locked_placeProfile(profile);
             _strictCapacityOrder.add(profile);
-            return old;
-        }
+        } finally { releaseWriteLock(); }
+        return old;
     }
     
-    public int countFastPeers() { synchronized (_reorganizeLock) { return _fastPeers.size(); } }
-    public int countHighCapacityPeers() { synchronized (_reorganizeLock) { return _highCapacityPeers.size(); } }
-    public int countWellIntegratedPeers() { synchronized (_reorganizeLock) { return _wellIntegratedPeers.size(); } }
-    public int countNotFailingPeers() { synchronized (_reorganizeLock) { return _notFailingPeers.size(); } }
-    public int countFailingPeers() { synchronized (_reorganizeLock) { return _failingPeers.size(); } }
+    private int count(Map m) {
+        getReadLock();
+        try {
+            return m.size();
+        } finally { releaseReadLock(); }
+    }
+
+    public int countFastPeers() { return count(_fastPeers); }
+    public int countHighCapacityPeers() { return count(_highCapacityPeers); }
+    public int countWellIntegratedPeers() { return count(_wellIntegratedPeers); }
+    public int countNotFailingPeers() { return count(_notFailingPeers); }
+    public int countFailingPeers() { return count(_failingPeers); }
     
     public int countActivePeers() {
-        synchronized (_reorganizeLock) {
-            int activePeers = 0;
-            
-            long hideBefore = _context.clock().now() - 6*60*60*1000;
-            
+        int activePeers = 0;
+        long hideBefore = _context.clock().now() - 6*60*60*1000;
+       
+        getReadLock();
+        try {
             for (Iterator iter = _failingPeers.values().iterator(); iter.hasNext(); ) {
                 PeerProfile profile = (PeerProfile)iter.next();
                 if (profile.getLastSendSuccessful() >= hideBefore)
@@ -174,15 +209,21 @@ public class ProfileOrganizer {
                 else if (profile.getLastHeardFrom() >= hideBefore)
                     activePeers++;
             }
-            
-            return activePeers;
-        }
+        } finally { releaseReadLock(); }
+        return activePeers;
     }
     
-    public boolean isFast(Hash peer) { synchronized (_reorganizeLock) { return _fastPeers.containsKey(peer); } }
-    public boolean isHighCapacity(Hash peer) { synchronized (_reorganizeLock) { return _highCapacityPeers.containsKey(peer); } }
-    public boolean isWellIntegrated(Hash peer) { synchronized (_reorganizeLock) { return _wellIntegratedPeers.containsKey(peer); } }
-    public boolean isFailing(Hash peer) { synchronized (_reorganizeLock) { return _failingPeers.containsKey(peer); } }
+    private boolean isX(Map m, Hash peer) {
+        getReadLock();
+        try {
+            return m.containsKey(peer);
+        } finally { releaseReadLock(); }
+    }
+
+    public boolean isFast(Hash peer) { return isX(_fastPeers, peer); }
+    public boolean isHighCapacity(Hash peer) { return isX(_highCapacityPeers, peer); }
+    public boolean isWellIntegrated(Hash peer) { return isX(_wellIntegratedPeers, peer); }
+    public boolean isFailing(Hash peer) { return isX(_failingPeers, peer); }
         
     /** 
      * if a peer sends us more than 5 replies in a searchReply that we cannot
@@ -236,9 +277,10 @@ public class ProfileOrganizer {
         selectFastPeers(howMany, exclude, matches, 0);
     }
     public void selectFastPeers(int howMany, Set exclude, Set matches, int mask) {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             locked_selectPeers(_fastPeers, howMany, exclude, matches, mask);
-        }
+        } finally { releaseReadLock(); }
         if (matches.size() < howMany) {
             if (_log.shouldLog(Log.INFO))
                 _log.info("selectFastPeers("+howMany+"), not enough fast (" + matches.size() + ") going on to highCap");
@@ -258,7 +300,8 @@ public class ProfileOrganizer {
         selectHighCapacityPeers(howMany, exclude, matches, 0);
     }
     public void selectHighCapacityPeers(int howMany, Set exclude, Set matches, int mask) {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             // we only use selectHighCapacityPeers when we are selecting for PURPOSE_TEST
             // or we are falling back due to _fastPeers being too small, so we can always 
             // exclude the fast peers
@@ -269,7 +312,7 @@ public class ProfileOrganizer {
                 exclude.addAll(_fastPeers.keySet());
              */
             locked_selectPeers(_highCapacityPeers, howMany, exclude, matches, mask);
-        }
+        } finally { releaseReadLock(); }
         if (matches.size() < howMany) {
             if (_log.shouldLog(Log.INFO))
                 _log.info("selectHighCap("+howMany+"), not enough fast (" + matches.size() + ") going on to notFailing");
@@ -288,9 +331,10 @@ public class ProfileOrganizer {
         selectWellIntegratedPeers(howMany, exclude, matches, 0);
     }
     public void selectWellIntegratedPeers(int howMany, Set exclude, Set matches, int mask) {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             locked_selectPeers(_wellIntegratedPeers, howMany, exclude, matches, mask);
-        }
+        } finally { releaseReadLock(); }
         if (matches.size() < howMany) {
             if (_log.shouldLog(Log.INFO))
                 _log.info("selectWellIntegrated("+howMany+"), not enough integrated (" + matches.size() + ") going on to notFailing");
@@ -375,7 +419,8 @@ public class ProfileOrganizer {
             int needed = howMany - orig;
             int start = 0;
             List selected = new ArrayList(needed);
-            synchronized (_reorganizeLock) {
+            getReadLock();
+            try {
                 // we randomize the whole list when rebuilding it, but randomizing 
                 // the entire list on each peer selection is a bit crazy
                 start = _context.random().nextInt(_notFailingPeersList.size());
@@ -397,7 +442,7 @@ public class ProfileOrganizer {
                             _log.debug("Not selectable: " + cur.toBase64());
                     }
                 }
-            }
+            } finally { releaseReadLock(); }
             if (_log.shouldLog(Log.INFO))
                 _log.info("Selecting all not failing (strict? " + onlyNotFailing + " start=" + start 
                           + ") found " + selected.size() + " new peers: " + selected + " all=" + _notFailingPeersList.size() + " strict=" + _strictCapacityOrder.size());
@@ -418,25 +463,27 @@ public class ProfileOrganizer {
      *
      */
     public void selectFailingPeers(int howMany, Set exclude, Set matches) {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             locked_selectPeers(_failingPeers, howMany, exclude, matches);
-        }
-        return;
-    }
-    
-    /**
+        } finally { releaseReadLock(); }
+        return;        
+    }                  
+
+    /**                
      * Get the peers the transport layer thinks are unreachable, and
      * add in the peers with the SSU peer testing bug,
      * and peers requiring introducers.
-     *
-     */
+     *                 
+     */                
     public List selectPeersLocallyUnreachable() { 
         List n;
         int count;
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             count = _notFailingPeers.size();
             n = new ArrayList(_notFailingPeers.keySet());
-        }
+        } finally { releaseReadLock(); }
         List l = new ArrayList(count / 4);
         for (Iterator iter = n.iterator(); iter.hasNext(); ) {
             Hash peer = (Hash)iter.next();
@@ -483,7 +530,8 @@ public class ProfileOrganizer {
      *
      */
     public List selectPeersRecentlyRejecting() { 
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             long cutoff = _context.clock().now() - (20*1000);
             int count = _notFailingPeers.size();
             List l = new ArrayList(count / 128);
@@ -493,7 +541,7 @@ public class ProfileOrganizer {
                     l.add(prof.getPeer());
             }
             return l;
-        }
+        } finally { releaseReadLock(); }
     }
 
     /**
@@ -501,14 +549,15 @@ public class ProfileOrganizer {
      *
      */
     public Set selectAllPeers() {
-        synchronized (_reorganizeLock) {
+        getReadLock();
+        try {
             Set allPeers = new HashSet(_failingPeers.size() + _notFailingPeers.size() + _highCapacityPeers.size() + _fastPeers.size());
             allPeers.addAll(_failingPeers.keySet());
             allPeers.addAll(_notFailingPeers.keySet());
             allPeers.addAll(_highCapacityPeers.keySet());
             allPeers.addAll(_fastPeers.keySet());
             return allPeers;
-        }
+        } finally { releaseReadLock(); }
     }
     
     /**
@@ -532,8 +581,10 @@ public class ProfileOrganizer {
             expireOlderThan = _context.clock().now() - 6*60*60*1000;
         }
             
+        if (!getWriteLock())
+            return;
         long start = System.currentTimeMillis();
-        synchronized (_reorganizeLock) {
+        try {
             Set allPeers = _strictCapacityOrder; //new HashSet(_failingPeers.size() + _notFailingPeers.size() + _highCapacityPeers.size() + _fastPeers.size());
             //allPeers.addAll(_failingPeers.values());
             //allPeers.addAll(_notFailingPeers.values());
@@ -557,35 +608,37 @@ public class ProfileOrganizer {
             }
             sortTime = System.currentTimeMillis() - sortStart;
             _strictCapacityOrder = reordered;
-            
+
             long thresholdStart = System.currentTimeMillis();
             locked_calculateThresholds(allPeers);
             thresholdTime = System.currentTimeMillis()-thresholdStart;
-            
+
             _failingPeers.clear();
             _fastPeers.clear();
             _highCapacityPeers.clear();
             _notFailingPeers.clear();
             _notFailingPeersList.clear();
             _wellIntegratedPeers.clear();
-            
+
             long placeStart = System.currentTimeMillis();
-            
+
             for (Iterator iter = allPeers.iterator(); iter.hasNext(); ) {
                 PeerProfile profile = (PeerProfile)iter.next();
                 locked_placeProfile(profile);
             }
-            
+
             locked_unfailAsNecessary();
             locked_promoteFastAsNecessary();
 
             Collections.shuffle(_notFailingPeersList, _context.random());
-            
+
             placeTime = System.currentTimeMillis()-placeStart;
+        } finally { releaseWriteLock(); }
 
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Profiles reorganized.  averages: [integration: " + _thresholdIntegrationValue 
-                           + ", capacity: " + _thresholdCapacityValue + ", speed: " + _thresholdSpeedValue + "]");
+
+        if (_log.shouldLog(Log.INFO))
+            _log.info("Profiles reorganized.  averages: [integration: " + _thresholdIntegrationValue 
+                       + ", capacity: " + _thresholdCapacityValue + ", speed: " + _thresholdSpeedValue + "]");
             /*****
             if (_log.shouldLog(Log.DEBUG)) {
                 StringBuffer buf = new StringBuffer(512);
@@ -597,7 +650,6 @@ public class ProfileOrganizer {
                 _log.debug("fast: " + _fastPeers.values());
             }
             *****/
-        }
         
         long total = System.currentTimeMillis()-start;
         _context.statManager().addRateData("peer.profileSortTime", sortTime, profileCount);
@@ -899,11 +951,12 @@ public class ProfileOrganizer {
         all.removeAll(matches);
         all.remove(_us);
         Collections.shuffle(all, _random);
+        Set IPSet = new HashSet(8);
         for (int i = 0; (matches.size() < howMany) && (i < all.size()); i++) {
             Hash peer = (Hash)all.get(i);
             boolean ok = isSelectable(peer);
             if (ok) {
-                ok = mask <= 0 || notRestricted(peer, matches, mask);
+                ok = mask <= 0 || notRestricted(peer, IPSet, mask);
                 if ((!ok) && _log.shouldLog(Log.WARN))
                     _log.warn("IP restriction prevents " + peer + " from joining " + matches);
             }
@@ -917,77 +970,67 @@ public class ProfileOrganizer {
     /**
      * Does the peer's IP address NOT match the IP address of any peer already in the set,
      * on any transport, within a given mask?
-     * mask is 1-4 (number of bytes to match) or 0 to disable
-     * Perhaps rewrite this to just make a set of all the IP addresses rather than loop.
+     * @param mask is 1-4 (number of bytes to match)
+     * @param IPMatches all IPs so far, modified by this routine
      */
-    private boolean notRestricted(Hash peer, Set matches, int mask) {
-        if (mask <= 0) return true;
-        if (matches.size() <= 0) return true;
-        RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
-        if (pinfo == null) return false;
-        Set paddr = pinfo.getAddresses();
-        if (paddr == null || paddr.size() == 0)
+    private boolean notRestricted(Hash peer, Set IPSet, int mask) {
+        Set peerIPs = maskedIPSet(peer, mask);
+        if (containsAny(IPSet, peerIPs))
             return false;
-        List pladdr = new ArrayList(paddr);
-        List lmatches = new ArrayList(matches);
-        // for each match
-        for (int i = 0; i < matches.size(); i++) {
-            RouterInfo minfo = _context.netDb().lookupRouterInfoLocally((Hash) lmatches.get(i));
-            if (minfo == null) continue;
-            Set maddr = minfo.getAddresses();
-            if (maddr == null || maddr.size() == 0)
+        IPSet.addAll(peerIPs);
+        return true;
+    }
+
+    /**
+      * The Set of IPs for this peer, with a given mask.
+      * Includes the comm system's record of the IP, and all netDb addresses.
+      *
+      * @return an opaque set of masked IPs for this peer
+      */
+    private Set maskedIPSet(Hash peer, int mask) {
+        Set rv = new HashSet(2);
+        byte[] commIP = _context.commSystem().getIP(peer);
+        if (commIP != null)
+            rv.add(maskedIP(commIP, mask));
+        RouterInfo pinfo = _context.netDb().lookupRouterInfoLocally(peer);
+        if (pinfo == null)
+            return rv;
+        Set<RouterAddress> paddr = pinfo.getAddresses();
+        if (paddr == null)
+            return rv;
+        for (RouterAddress pa : paddr) {
+            Properties pprops = pa.getOptions();
+            if (pprops == null) continue;
+            String phost = pprops.getProperty("host");
+            if (phost == null) continue;
+            InetAddress pi;
+            try {
+                pi = InetAddress.getByName(phost);
+            } catch (UnknownHostException uhe) {
                 continue;
-            List mladdr = new ArrayList(maddr);
-            String oldphost = null;
-            // for each peer address
-            for (int j = 0; j < paddr.size(); j++) {
-                RouterAddress pa = (RouterAddress) pladdr.get(j);
-                if (pa == null) continue;
-                Properties pprops = pa.getOptions();
-                if (pprops == null) continue;
-                String phost = pprops.getProperty("host");
-                if (phost == null) continue;
-                if (oldphost != null && oldphost.equals(phost)) continue;
-                oldphost = phost;
-                InetAddress pi;
-                try {
-                    pi = InetAddress.getByName(phost);
-                } catch (UnknownHostException uhe) {
-                    continue;
-                }
-                if (pi == null) continue;
-                byte[] pib = pi.getAddress();
-                String oldmhost = null;
-                // for each match address
-                for (int k = 0; k < maddr.size(); k++) {
-                    RouterAddress ma = (RouterAddress) mladdr.get(k);
-                    if (ma == null) continue;
-                    Properties mprops = ma.getOptions();
-                    if (mprops == null) continue;
-                    String mhost = mprops.getProperty("host");
-                    if (mhost == null) continue;
-                    if (oldmhost != null && oldmhost.equals(mhost)) continue;
-                    oldmhost = mhost;
-                    InetAddress mi;
-                    try {
-                        mi = InetAddress.getByName(mhost);
-                    } catch (UnknownHostException uhe) {
-                        continue;
-                    }
-                    if (mi == null) continue;
-                    byte[] mib = mi.getAddress();
-                    // assume ipv4, compare 1 to 4 bytes
-                    // log.info("Comparing " + pi + " with " + mi);
-                    for (int m = 0; m < mask; m++) {
-                        if (pib[m] != mib[m])
-                            break;
-                        if (m == mask-1)
-                            return false; // IP match
-                    }
-                }
             }
+            if (pi == null) continue;
+            byte[] pib = pi.getAddress();
+            rv.add(maskedIP(pib, mask));
         }
-        return true;
+        return rv;
+    }
+
+    /** generate an arbitrary unique value for this ip/mask (mask = 1-4) */
+    private Integer maskedIP(byte[] ip, int mask) {
+        int rv = 0;
+        for (int i = 0; i < mask; i++)
+             rv = (rv << 8) | (ip[i] & 0xff);
+        return Integer.valueOf(rv);
+    }
+
+    /** does a contain any of the elements in b? */
+    private boolean containsAny(Set a, Set b) {
+        for (Object o : b) {
+            if (a.contains(o))
+                return true;
+        }
+        return false;
     }
 
     public boolean isSelectable(Hash peer) {
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java
index 2671c8be05ad8e72927bc983d97d7feaba383c49..b665f495c156d4e0c18378ff515dbf0eeff0e225 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java
@@ -3,10 +3,8 @@ package net.i2p.router.peermanager;
 import java.io.IOException;
 import java.io.Writer;
 import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
 import java.util.Comparator;
 import java.util.Iterator;
-import java.util.Locale;
 import java.util.Set;
 import java.util.TreeSet;
 
diff --git a/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java b/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java
index a7fc9334778883cfadcbff336fc59137b9c970ca..c5ac1c3c56d26871dc33beec002063983fd5c70b 100644
--- a/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java
+++ b/router/java/src/net/i2p/router/peermanager/SpeedCalculator.java
@@ -18,6 +18,7 @@ public class SpeedCalculator extends Calculator {
     public SpeedCalculator(RouterContext context) {
     }
     
+    @Override
     public double calc(PeerProfile profile) {
         // measures 1 minute throughput of individual tunnels
         double d = (profile.getPeakTunnel1mThroughputKBps()*1024d) + profile.getSpeedBonus();
diff --git a/router/java/src/net/i2p/router/transport/BandwidthLimitedInputStream.java b/router/java/src/net/i2p/router/transport/BandwidthLimitedInputStream.java
index ffd041019108e6ce92b4bd7ce942e56543fdd547..f47394eff1e764932c59a137ae760ade310e72f4 100644
--- a/router/java/src/net/i2p/router/transport/BandwidthLimitedInputStream.java
+++ b/router/java/src/net/i2p/router/transport/BandwidthLimitedInputStream.java
@@ -41,6 +41,7 @@ public class BandwidthLimitedInputStream extends FilterInputStream {
         _log = context.logManager().getLog(BandwidthLimitedInputStream.class);
     }
     
+    @Override
     public int read() throws IOException {
         if (_pullFromOutbound)
             _currentRequest = _context.bandwidthLimiter().requestOutbound(1, _peerSource);
@@ -56,10 +57,12 @@ public class BandwidthLimitedInputStream extends FilterInputStream {
         return in.read();
     }
     
+    @Override
     public int read(byte dest[]) throws IOException {
         return read(dest, 0, dest.length);
     }
     
+    @Override
     public int read(byte dest[], int off, int len) throws IOException {
         int read = in.read(dest, off, len);
         if (read == -1) return -1;
@@ -84,6 +87,7 @@ public class BandwidthLimitedInputStream extends FilterInputStream {
         }
         return read;
     }
+    @Override
     public long skip(long numBytes) throws IOException {
         long skip = in.skip(numBytes);
         
@@ -105,6 +109,7 @@ public class BandwidthLimitedInputStream extends FilterInputStream {
         return skip;
     }
     
+    @Override
     public void close() throws IOException {
         synchronized (this) {
             if (_currentRequest != null)
diff --git a/router/java/src/net/i2p/router/transport/BandwidthLimitedOutputStream.java b/router/java/src/net/i2p/router/transport/BandwidthLimitedOutputStream.java
index df37d951416303c7c57d0cfc4c434d0ab19b3831..0327fd25ed3cdd9346816304c68bb5530f778da8 100644
--- a/router/java/src/net/i2p/router/transport/BandwidthLimitedOutputStream.java
+++ b/router/java/src/net/i2p/router/transport/BandwidthLimitedOutputStream.java
@@ -37,6 +37,7 @@ public class BandwidthLimitedOutputStream extends FilterOutputStream {
     
     public FIFOBandwidthLimiter.Request getCurrentRequest() { return _currentRequest; }
     
+    @Override
     public void write(int val) throws IOException {
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Writing a single byte!", new Exception("Single byte from..."));
@@ -49,9 +50,11 @@ public class BandwidthLimitedOutputStream extends FilterOutputStream {
             _log.warn("Waiting to write a byte took too long [" + waited + "ms");
         out.write(val);
     }
+    @Override
     public void write(byte src[]) throws IOException {
         write(src, 0, src.length);
     }
+    @Override
     public void write(byte src[], int off, int len) throws IOException {
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Writing " + len + " bytes");
@@ -83,6 +86,7 @@ public class BandwidthLimitedOutputStream extends FilterOutputStream {
         }
     }
     
+    @Override
     public void close() throws IOException {
         synchronized (this) {
             if (_currentRequest != null)
diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
index 0eafb8fa9755da49cfbad97329af462a56d0011b..217054d4054f7682a9be005a9e4070464588cd0a 100644
--- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
+++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
@@ -58,14 +58,18 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
             _manager.restart();
     }
     
+    @Override
     public int countActivePeers() { return (_manager == null ? 0 : _manager.countActivePeers()); }
+    @Override
     public int countActiveSendPeers() { return (_manager == null ? 0 : _manager.countActiveSendPeers()); } 
+    @Override
     public boolean haveCapacity() { return (_manager == null ? false : _manager.haveCapacity()); } 
     
     /**
      * Framed average clock skew of connected peers in seconds, or null if we cannot answer.
      * Average is calculated over the middle "percentToInclude" peers.
      */
+    @Override
     public Long getFramedAveragePeerClockSkew(int percentToInclude) {
         if (_manager == null) {
             if (_log.shouldLog(Log.INFO))
@@ -121,14 +125,17 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
         GetBidsJob.getBids(_context, this, msg);
     }
     
+    @Override
     public boolean isBacklogged(Hash dest) { 
         return _manager.isBacklogged(dest); 
     }
     
+    @Override
     public boolean isEstablished(Hash dest) { 
         return _manager.isEstablished(dest); 
     }
     
+    @Override
     public boolean wasUnreachable(Hash dest) { 
         return _manager.wasUnreachable(dest); 
     }
@@ -137,21 +144,26 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
         return _manager.getIP(dest); 
     }
     
+    @Override
     public List getMostRecentErrorMessages() { 
         return _manager.getMostRecentErrorMessages(); 
     }
 
+    @Override
     public short getReachabilityStatus() { 
         if (_manager == null) return CommSystemFacade.STATUS_UNKNOWN;
         if (_context.router().isHidden()) return CommSystemFacade.STATUS_OK;
         return _manager.getReachabilityStatus(); 
     }
+    @Override
     public void recheckReachability() { _manager.recheckReachability(); }
 
+    @Override
     public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { 
         _manager.renderStatusHTML(out, urlBase, sortFlags); 
     }
     
+    @Override
     public Set createAddresses() {
         Map addresses = null;
         boolean newCreated = false;
@@ -178,8 +190,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
     
     public final static String PROP_I2NP_NTCP_HOSTNAME = "i2np.ntcp.hostname";
     public final static String PROP_I2NP_NTCP_PORT = "i2np.ntcp.port";
-    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoip";
-    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_PORT = "i2np.ntcp.autoport";
+    public final static String PROP_I2NP_NTCP_AUTO_IP = "i2np.ntcp.autoip";
     
     public static RouterAddress createNTCPAddress(RouterContext ctx) {
         if (!TransportManager.enableNTCP(ctx)) return null;
@@ -225,6 +237,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
     /**
      * UDP changed addresses, tell NTCP and restart
      */
+    @Override
     public void notifyReplaceAddress(RouterAddress UDPAddr) {
         if (UDPAddr == null)
             return;
diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
index 63bf736536e7d8d6f54d7ff1c409ec3847d3f91a..1f643b48df7fb23ce21226f8b6dbe15514383852 100644
--- a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
+++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
@@ -12,8 +12,8 @@ import net.i2p.util.Log;
 public class FIFOBandwidthLimiter {
     private Log _log;
     private I2PAppContext _context;
-    private List _pendingInboundRequests;
-    private List _pendingOutboundRequests;
+    private final List _pendingInboundRequests;
+    private final List _pendingOutboundRequests;
     /** how many bytes we can consume for inbound transmission immediately */
     private volatile int _availableInbound;
     /** how many bytes we can consume for outbound transmission immediately */
@@ -54,7 +54,7 @@ public class FIFOBandwidthLimiter {
     
     private static int __id = 0;
     
-    static long now() { 
+    public /* static */ long now() {
         // dont use the clock().now(), since that may jump
         return System.currentTimeMillis(); 
     }
@@ -769,6 +769,7 @@ public class FIFOBandwidthLimiter {
         }
         public void attach(Object obj) { _attachment = obj; }
         public Object attachment() { return _attachment; }
+        @Override
         public String toString() { return getRequestName(); }
     }
 
diff --git a/router/java/src/net/i2p/router/transport/OutboundMessageRegistry.java b/router/java/src/net/i2p/router/transport/OutboundMessageRegistry.java
index c5ca48f57acf64b5e5f7fe462e49be2177bbb583..17108a8e87c2cf67dd28d8f6c4f10af24dafad0a 100644
--- a/router/java/src/net/i2p/router/transport/OutboundMessageRegistry.java
+++ b/router/java/src/net/i2p/router/transport/OutboundMessageRegistry.java
@@ -30,11 +30,11 @@ import net.i2p.util.SimpleTimer;
 public class OutboundMessageRegistry {
     private Log _log;
     /** list of currently active MessageSelector instances */
-    private List _selectors;
+    private final List _selectors;
     /** map of active MessageSelector to either an OutNetMessage or a List of OutNetMessages causing it (for quick removal) */
-    private Map _selectorToMessage;
+    private final Map _selectorToMessage;
     /** set of active OutNetMessage (for quick removal and selector fetching) */
-    private Set _activeMessages;
+    private final Set _activeMessages;
     private CleanupTask _cleanupTask;
     private RouterContext _context;
     
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index 930734d43ccde0facf795dda0e51d5987e58d554..cff0d9a468275676760f261793095e07803c452e 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -36,6 +36,8 @@ import net.i2p.router.RouterContext;
 import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
 import net.i2p.util.ConcurrentHashSet;
 import net.i2p.util.Log;
+import net.i2p.util.SimpleScheduler;
+import net.i2p.util.SimpleTimer;
 
 /**
  * Defines a way to send a message to another peer and start listening for messages
@@ -72,6 +74,7 @@ public abstract class TransportImpl implements Transport {
         _unreachableEntries = new HashMap(16);
         _wasUnreachableEntries = new ConcurrentHashSet(16);
         _currentAddress = null;
+        SimpleScheduler.getInstance().addPeriodicEvent(new CleanupUnreachable(), 2 * UNREACHABLE_PERIOD, UNREACHABLE_PERIOD / 2);
     }
 
     /**
@@ -464,13 +467,10 @@ public abstract class TransportImpl implements Transport {
         if (!isInbound)
             markWasUnreachable(peer, false);
     }
-    private class CleanupUnreachable extends JobImpl {
-        public CleanupUnreachable(RouterContext ctx) {
-            super(ctx);
-        }
-        public String getName() { return "Cleanup " + getStyle() + " unreachable list"; }
-        public void runJob() {
-            long now = getContext().clock().now();
+
+    private class CleanupUnreachable implements SimpleTimer.TimedEvent {
+        public void timeReached() {
+            long now = _context.clock().now();
             synchronized (_unreachableEntries) {
                 for (Iterator iter = _unreachableEntries.keySet().iterator(); iter.hasNext(); ) {
                     Hash peer = (Hash)iter.next();
@@ -479,7 +479,6 @@ public abstract class TransportImpl implements Transport {
                         iter.remove();
                 }
             }
-            requeue(60*1000);
         }
     }
 
diff --git a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
index 0c062fa6cf2330a47f11dcd091a0638f51e8b936..24ac63c6b28bfde33a0b98eb85306bbf9c9ea5a1 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/EventPumper.java
@@ -33,11 +33,11 @@ public class EventPumper implements Runnable {
     private Log _log;
     private boolean _alive;
     private Selector _selector;
-    private List _bufCache;
-    private List _wantsRead;
-    private List _wantsWrite;
-    private List _wantsRegister;
-    private List _wantsConRegister;
+    private final List _bufCache;
+    private final List _wantsRead = new ArrayList(16);
+    private final List _wantsWrite = new ArrayList(4);
+    private final List _wantsRegister = new ArrayList(1);
+    private final List _wantsConRegister = new ArrayList(4);
     private NTCPTransport _transport;
     private long _expireIdleWriteTime;
     
@@ -66,10 +66,10 @@ public class EventPumper implements Runnable {
     public void startPumping() {
         if (_log.shouldLog(Log.INFO))
             _log.info("Starting pumper");
-        _wantsRead = new ArrayList(16);
-        _wantsWrite = new ArrayList(4);
-        _wantsRegister = new ArrayList(1);
-        _wantsConRegister = new ArrayList(4);
+//        _wantsRead = new ArrayList(16);
+//        _wantsWrite = new ArrayList(4);
+//        _wantsRegister = new ArrayList(1);
+//        _wantsConRegister = new ArrayList(4);
         try {
             _selector = Selector.open();
             _alive = true;
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
index 71d395e5f0b9b0b94622c4c5ad2c91419fcae415..860634d8f5620d47b9809b27afd40ea66c36f870 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPAddress.java
@@ -124,8 +124,10 @@ public class NTCPAddress {
         }
     }
     
+    @Override
     public String toString() { return _host + ":" + _port; }
     
+    @Override
     public int hashCode() {
         int rv = 0;
         rv += _port;
@@ -136,6 +138,7 @@ public class NTCPAddress {
         return rv;
     }
     
+    @Override
     public boolean equals(Object val) {
         if ( (val != null) && (val instanceof NTCPAddress) ) {
             NTCPAddress addr = (NTCPAddress)val;
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
index 070caae84c9cda31c058b3f9c85ed53674c34c3d..3acabafdc48a809aa199d060c90230684eda25d0 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java
@@ -56,13 +56,13 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
     private SocketChannel _chan;
     private SelectionKey _conKey;
     /** list of ByteBuffer containing data we have read and are ready to process, oldest first */
-    private List _readBufs;
+    private final List _readBufs;
     /**
      * list of ByteBuffers containing fully populated and encrypted data, ready to write,
      * and already cleared through the bandwidth limiter.
      */
-    private List _writeBufs;
-    private List _bwRequests;
+    private final List _writeBufs;
+    private final List _bwRequests;
     private boolean _established;
     private long _establishedOn;
     private EstablishState _establishState;
@@ -75,7 +75,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
     /**
      * pending unprepared OutNetMessage instances
      */
-    private List _outbound;
+    private final List _outbound;
     /** current prepared OutNetMessage, or null */
     private OutNetMessage _currentOutbound;
     private SessionKey _sessionKey;
@@ -96,7 +96,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
     private long _created;
     private long _nextMetaTime;
     /** unencrypted outbound metadata buffer */
-    private byte _meta[] = new byte[16];
+    private final byte _meta[] = new byte[16];
     private boolean _sendingMeta;
     /** how many consecutive sends were failed due to (estimated) send queue time */
     private int _consecutiveBacklog;
@@ -398,6 +398,8 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
                 continue;
             
             RouterInfo info = fac.lookupRouterInfoLocally(peer);
+            if (info == null)
+                continue;
 
             OutNetMessage infoMsg = new OutNetMessage(_context);
             infoMsg.setExpiration(_context.clock().now()+10*1000);
@@ -704,7 +706,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
     private static int NUM_PREP_BUFS = 5;
     private static int __liveBufs = 0;
     private static int __consecutiveExtra;
-    private static List _bufs = new ArrayList(NUM_PREP_BUFS);
+    private final static List _bufs = new ArrayList(NUM_PREP_BUFS);
     private PrepBuffer acquireBuf() {
         synchronized (_bufs) {
             if (_bufs.size() > 0) {
@@ -1093,11 +1095,17 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
         // enqueueInfoMessage(); // this often?
     }
     
+    @Override
     public int hashCode() { return System.identityHashCode(this); }
-    public boolean equals(Object obj) { return obj == this; }
+    @Override
+    public boolean equals(Object obj) {
+        if(obj == null) return false;
+        if(obj.getClass() != NTCPConnection.class) return false;
+        return obj == this;
+    }
 
-    private static List _i2npHandlers = new ArrayList(4);
-    private static I2NPMessageHandler acquireHandler(RouterContext ctx) {
+    private final static List _i2npHandlers = new ArrayList(4);
+    private final static I2NPMessageHandler acquireHandler(RouterContext ctx) {
         I2NPMessageHandler rv = null;
         synchronized (_i2npHandlers) {
             if (_i2npHandlers.size() > 0)
@@ -1127,7 +1135,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
     }
     
     private static int MAX_DATA_READ_BUFS = 16;
-    private static List _dataReadBufs = new ArrayList(16);
+    private final static List _dataReadBufs = new ArrayList(16);
     private static DataBuf acquireReadBuf() {
         synchronized (_dataReadBufs) {
             if (_dataReadBufs.size() > 0)
@@ -1289,6 +1297,7 @@ public class NTCPConnection implements FIFOBandwidthLimiter.CompleteListener {
         }
     }
 
+    @Override
     public String toString() {
         return "NTCP Connection to " +
                (_remotePeer == null ? "unknown " : _remotePeer.calculateHash().toBase64().substring(0,6)) +
diff --git a/router/java/src/net/i2p/router/transport/ntcp/Reader.java b/router/java/src/net/i2p/router/transport/ntcp/Reader.java
index 4b4dc3db1224551d3c1e06815ae07493eec917cb..2c017b5b6df696238ed16734d08ff375bbd593d7 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/Reader.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/Reader.java
@@ -17,7 +17,7 @@ import net.i2p.util.Log;
 class Reader {
     private RouterContext _context;
     private Log _log;
-    private List _pendingConnections;
+    private final List _pendingConnections;
     private List _liveReads;
     private List _readAfterLive;
     private List _runners;
diff --git a/router/java/src/net/i2p/router/transport/ntcp/Writer.java b/router/java/src/net/i2p/router/transport/ntcp/Writer.java
index 29b7258167a046cef4baaeeedfaeb0942be23497..97823313bc281ac21da7fa74c3811c66a0358201 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/Writer.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/Writer.java
@@ -16,7 +16,7 @@ import net.i2p.util.Log;
 class Writer {
     private RouterContext _context;
     private Log _log;
-    private List _pendingConnections;
+    private final List _pendingConnections;
     private List _liveWrites;
     private List _writeAfterLive;
     private List _runners;
diff --git a/router/java/src/net/i2p/router/transport/udp/ACKSender.java b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
index 66aad6f4d092e211af2c99b627ec708057fc9ab7..e2a21ca4a981dcbde735e92806edb6fbbdaf0456 100644
--- a/router/java/src/net/i2p/router/transport/udp/ACKSender.java
+++ b/router/java/src/net/i2p/router/transport/udp/ACKSender.java
@@ -18,7 +18,7 @@ public class ACKSender implements Runnable {
     private UDPTransport _transport;
     private PacketBuilder _builder;
     /** list of peers (PeerState) who we have received data from but not yet ACKed to */
-    private List _peersToACK;
+    private final List _peersToACK;
     private boolean _alive;
     
     /** how frequently do we want to send ACKs to a peer? */
diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
index e8731da05f0dcdfd981f5a1f8403261173e49f92..d36a3053d25e879da61e64ac6e8597fd4baa4a6a 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -670,7 +670,7 @@ public class EstablishmentManager {
         }
     }
     
-    public void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) {
+    public void receiveRelayResponse(RemoteHostId bob, UDPPacketReader reader) {// LINT -- Exporting non-public type through public API
         long nonce = reader.getRelayResponseReader().readNonce();
         OutboundEstablishState state = null;
         synchronized (_liveIntroductions) {
diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
index ababfa7c4cc4e33ecd9765d84afc13283b75acb3..290a5139b060c81018be0cc21b735b26e4bae32a 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java
@@ -210,7 +210,7 @@ public class InboundEstablishState {
     public synchronized void setNextSendTime(long when) { _nextSend = when; }
 
     /** RemoteHostId, uniquely identifies an attempt */
-    public RemoteHostId getRemoteHostId() { return _remoteHostId; }
+    public RemoteHostId getRemoteHostId() { return _remoteHostId; }// LINT -- Exporting non-public type through public API
 
     public synchronized void receiveSessionConfirmed(UDPPacketReader.SessionConfirmedReader conf) {
         if (_receivedIdentity == null)
@@ -331,6 +331,7 @@ public class InboundEstablishState {
         _nextSend = _lastReceive;
     }
     
+    @Override
     public String toString() {            
         StringBuffer buf = new StringBuffer(128);
         buf.append(super.toString());
diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java
index a99929f40f6437ed201310c7e67fc79b8caf7823..6c85b9fb72960a1cb394cd1cc8c279dde43eb517 100644
--- a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java
+++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java
@@ -138,6 +138,7 @@ public class InboundMessageState {
         }
         public boolean receivedComplete() { return false; }
         
+        @Override
         public String toString() { 
             StringBuffer buf = new StringBuffer(64);
             buf.append("Partial ACK of ");
@@ -162,6 +163,7 @@ public class InboundMessageState {
     }
     public int getFragmentCount() { return _lastFragment+1; }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(32);
         buf.append("Message: ").append(_messageId);
diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index bbbc35caba65b430c75a4b5687078f3f53662f34..25ab9ab6a1a01b033cece91c3a2deca9a7021410 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -25,7 +25,7 @@ public class IntroductionManager {
     /** map of relay tag to PeerState that should receive the introduction */
     private Map _outbound;
     /** list of peers (PeerState) who have given us introduction tags */
-    private List _inbound;
+    private final List _inbound;
 
     public IntroductionManager(RouterContext ctx, UDPTransport transport) {
         _context = ctx;
@@ -157,7 +157,7 @@ public class IntroductionManager {
         return found;
     }
     
-    public void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {
+    public void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {// LINT -- Exporting non-public type through public API
         if (_context.router().isHidden())
             return;
         if (_log.shouldLog(Log.INFO))
@@ -166,7 +166,7 @@ public class IntroductionManager {
         _transport.send(_builder.buildHolePunch(reader));
     }
     
-    public void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) {
+    public void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) {// LINT -- Exporting non-public type through public API
         if (_context.router().isHidden())
             return;
         long tag = reader.getRelayRequestReader().readTag();
diff --git a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java
index a6a75459efcf9786b641a24bd5604db64f3b1579..61c75c99e83d4a46e198f98a7a0b717e9457fce0 100644
--- a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java
+++ b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java
@@ -24,7 +24,7 @@ public class MessageReceiver {
     private Log _log;
     private UDPTransport _transport;
     /** list of messages (InboundMessageState) fully received but not interpreted yet */
-    private List _completeMessages;
+    private final List _completeMessages;
     private boolean _alive;
     private ByteCache _cache;
     
diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
index fc424de5bf58d40200aa27201135dc34ed369033..47a54ce116a6d23f44fc357975a6351b8127dabb 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java
@@ -51,7 +51,7 @@ public class OutboundEstablishState {
     private RemoteHostId _remoteHostId;
     private RouterIdentity _remotePeer;
     private SessionKey _introKey;
-    private List _queuedMessages;
+    private final List _queuedMessages;
     private int _currentState;
     private long _introductionNonce;
     // intro
@@ -413,7 +413,7 @@ public class OutboundEstablishState {
     }
 
     /** uniquely identifies an attempt */
-    public RemoteHostId getRemoteHostId() { return _remoteHostId; }
+    public RemoteHostId getRemoteHostId() { return _remoteHostId; }// LINT -- Exporting non-public type through public API
 
     /** we have received a real data packet, so we're done establishing */
     public synchronized void dataReceived() {
diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java
index fc6896627ed6b3be91148c232418f794ef36bb85..c9e7db02c5bf48eb08eba8af78b11f719afd738e 100644
--- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java
+++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java
@@ -286,6 +286,7 @@ public class OutboundMessageState {
         }
     }
     
+    @Override
     public String toString() {
         short sends[] = _fragmentSends;
         ByteArray messageBuf = _messageBuf;
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
index f00587bcd1b564142e0968d9c63ef850f2ad2203..dcdf5fb6d95f95ca7ed7fd830415f85b408b8559 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java
@@ -786,6 +786,7 @@ public class PacketBuilder {
     private int getOurExplicitPort() { return 0; }
     
     /** build intro packets for each of the published introducers */
+    @SuppressWarnings("static-access")
     public UDPPacket[] buildRelayRequest(UDPTransport transport, OutboundEstablishState state, SessionKey ourIntroKey) {
         UDPAddress addr = state.getRemoteAddress();
         int count = addr.getIntroducerCount();
@@ -878,7 +879,7 @@ public class PacketBuilder {
      */
     private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4);
     
-    public UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) {
+    public UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) {// LINT -- Exporting non-public type through public API
         UDPPacket packet = UDPPacket.acquire(_context, false);
         byte data[] = packet.getPacket().getData();
         Arrays.fill(data, 0, data.length, (byte)0x0);
@@ -928,7 +929,7 @@ public class PacketBuilder {
      */
     private static final byte PEER_RELAY_RESPONSE_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE << 4);
     
-    public UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey aliceIntroKey) {
+    public UDPPacket buildRelayResponse(RemoteHostId alice, PeerState charlie, long nonce, SessionKey aliceIntroKey) {// LINT -- Exporting non-public type through public API
         InetAddress aliceAddr = null;
         try {
             aliceAddr = InetAddress.getByAddress(alice.getIP());
diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
index 9e9cde1d34ceb5880fe2bfa2eec1497b05d9d65e..d32e8526dbef6f3b18625b95dc62f8de92f2f687 100644
--- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
+++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java
@@ -37,7 +37,7 @@ public class PacketHandler {
     private static final long GRACE_PERIOD = Router.CLOCK_FUDGE_FACTOR + 30*1000;
     
     
-    public PacketHandler(RouterContext ctx, UDPTransport transport, UDPEndpoint endpoint, EstablishmentManager establisher, InboundMessageFragments inbound, PeerTestManager testManager, IntroductionManager introManager) {
+    public PacketHandler(RouterContext ctx, UDPTransport transport, UDPEndpoint endpoint, EstablishmentManager establisher, InboundMessageFragments inbound, PeerTestManager testManager, IntroductionManager introManager) {// LINT -- Exporting non-public type through public API
         _context = ctx;
         _log = ctx.logManager().getLog(PacketHandler.class);
         _transport = transport;
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java
index f3cd1e269131b6ac9c6a0f347548fd4323b2b4ce..ff1f64f11b4e31915cc683ee1820d0ad6fae39c6 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java
@@ -74,13 +74,13 @@ public class PeerState {
     /** when did we last have a failed send (beginning of period) */
     private long _lastFailedSendPeriod;
     /** list of messageIds (Long) that we have received but not yet sent */
-    private List _currentACKs;
+    private final List _currentACKs;
     /** 
      * list of the most recent messageIds (Long) that we have received and sent
      * an ACK for.  We keep a few of these around to retransmit with _currentACKs,
      * hopefully saving some spurious retransmissions
      */
-    private List _currentACKsResend;
+    private final List _currentACKsResend;
     /** when did we last send ACKs to the peer? */
     private volatile long _lastACKSend;
     /** when did we decide we need to ACK to this peer? */
@@ -169,9 +169,9 @@ public class PeerState {
     private long _packetsReceived;
     
     /** list of InboundMessageState for active message */
-    private Map _inboundMessages;
+    private final Map _inboundMessages;
     /** list of OutboundMessageState */
-    private List _outboundMessages;
+    private final List _outboundMessages;
     /** which outbound message is currently being retransmitted */
     private OutboundMessageState _retransmitter;
     
@@ -802,6 +802,7 @@ public class PeerState {
         public long getMessageId() { return _msgId; }
         public boolean received(int fragmentNum) { return true; }
         public boolean receivedComplete() { return true; }
+        @Override
         public String toString() { return "Full ACK of " + _msgId; }
     }
         
@@ -1010,7 +1011,7 @@ public class PeerState {
             return MAX_RTO;
     }
     
-    public RemoteHostId getRemoteHostId() { return _remoteHostId; }
+    public RemoteHostId getRemoteHostId() { return _remoteHostId; }// LINT -- Exporting non-public type through public API
     
     public int add(OutboundMessageState state) {
         if (_dead) { 
@@ -1560,6 +1561,7 @@ public class PeerState {
     }
     */
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(64);
         buf.append(_remoteHostId.toString());
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 35c5511be4be4bc994ced8a0f24ba4408ad3b039..797cb8d4b7b14f1baf32515fb92f16103f8025a1 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -27,7 +27,7 @@ class PeerTestManager {
     private UDPTransport _transport;
     private PacketBuilder _packetBuilder;
     /** map of Long(nonce) to PeerTestState for tests currently in progress */
-    private Map _activeTests;
+    private final Map _activeTests;
     /** current test we are running, or null */
     private PeerTestState _currentTest;
     private boolean _currentTestComplete;
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java
index 57dc648d84c9981e8f11fb0a3b016c54032ad7c1..625bf648a7c9e1079ebf12c93ff18a6bcf8b300c 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestState.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestState.java
@@ -96,6 +96,7 @@ class PeerTestState {
     public int getPacketsRelayed() { return _packetsRelayed; }
     public void incrementPacketsRelayed() { ++_packetsRelayed; }
     
+    @Override
     public synchronized String toString() {
         StringBuffer buf = new StringBuffer(512);
         buf.append("Role: ");
diff --git a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java
index 117f8c5636003e52ca21d231f526f2733ce39a62..4bb2338f1f5e7e6ce86525936ea513cb7b3032b3 100644
--- a/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java
+++ b/router/java/src/net/i2p/router/transport/udp/RemoteHostId.java
@@ -26,6 +26,7 @@ final class RemoteHostId {
     public int getPort() { return _port; }
     public byte[] getPeerHash() { return _peerHash; }
     
+    @Override
     public int hashCode() {
         int rv = 0;
         for (int i = 0; _ip != null && i < _ip.length; i++)
@@ -36,6 +37,7 @@ final class RemoteHostId {
         return rv;
     }
     
+    @Override
     public boolean equals(Object obj) {
         if (obj == null) 
             throw new NullPointerException("obj is null");
@@ -45,6 +47,7 @@ final class RemoteHostId {
         return (_port == id.getPort()) && DataHelper.eq(_ip, id.getIP()) && DataHelper.eq(_peerHash, id.getPeerHash());
     }
     
+    @Override
     public String toString() { return toString(true); }
     public String toString(boolean includePort) {
         if (_ip != null) {
diff --git a/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java b/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java
index b28fb9101e0ff40ca58a1f5eac3d3c076b3facd9..9ff5a19503a040e9d4e198cf10bc9a475276940e 100644
--- a/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java
+++ b/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java
@@ -33,7 +33,7 @@ public class TimedWeightedPriorityMessageQueue implements MessageQueue, Outbound
     /** how many bytes total have been pulled off the given queue */
     private long _bytesTransferred[];
     /** lock to notify message enqueue/removal (and block for getNext()) */
-    private Object _nextLock;
+    private final Object _nextLock;
     /** have we shut down or are we still alive? */
     private boolean _alive;
     /** which queue should we pull out of next */
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
index df8b024f587126149b3da94063c9a9d02aa8394c..723817e0104b526a9e6fbcaf898c7d2c758d515d 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
@@ -40,6 +40,7 @@ public class UDPAddress {
         parse(addr);
     }
     
+    @Override
     public String toString() {
         StringBuffer rv = new StringBuffer(64);
         if (_introHosts != null) {
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPFlooder.java b/router/java/src/net/i2p/router/transport/udp/UDPFlooder.java
index dbcd8dfda4362dc8df28190e6c9d0a56d2e72a4f..14dfced396ccbe7d0afad19161158e4e03675d63 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPFlooder.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPFlooder.java
@@ -18,7 +18,7 @@ class UDPFlooder implements Runnable {
     private RouterContext _context;
     private Log _log;
     private UDPTransport _transport;
-    private List _peers;
+    private final List _peers;
     private boolean _alive;
     private static final byte _floodData[] = new byte[4096];
     
@@ -37,9 +37,10 @@ class UDPFlooder implements Runnable {
             _peers.notifyAll();
         }
     }
+    @SuppressWarnings("empty-statement")
     public void removePeer(PeerState peer) {
         synchronized (_peers) {
-            while (_peers.remove(peer))
+            while (_peers.remove(peer)) // can this be written better?
                 ;// loops until its empty
             _peers.notifyAll();
         }
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
index 46d8ae3c1490ca878f1e678e248b67b07db7d2f9..d6387f7c4e8407d0a4dec8738270f562140450dc 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java
@@ -129,7 +129,7 @@ public class UDPPacket {
     int getFragmentCount() { return _fragmentCount; }
     void setFragmentCount(int count) { _fragmentCount = count; }
     
-    public RemoteHostId getRemoteHost() {
+    public RemoteHostId getRemoteHost() {// LINT -- Exporting non-public type through public API
         if (_remoteHost == null) {
             long before = System.currentTimeMillis();
             InetAddress addr = _packet.getAddress();
@@ -238,6 +238,7 @@ public class UDPPacket {
     /** how many times we tried to validate the packet */
     int getValidateCount() { return _validateCount; }
     
+    @Override
     public String toString() {
         verifyNotReleased(); 
         StringBuffer buf = new StringBuffer(64);
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
index 159c21ae6042c04ae3f5444d471daf205795059c..5bff1789d77d92a016fa8433792fb05add487cc7 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java
@@ -105,6 +105,7 @@ public class UDPPacketReader {
     public RelayIntroReader getRelayIntroReader() { return _relayIntroReader; }
     public RelayResponseReader getRelayResponseReader() { return _relayResponseReader; }
     
+    @Override
     public String toString() {
         switch (readPayloadType()) {
             case UDPPacket.PAYLOAD_TYPE_DATA:
@@ -398,6 +399,7 @@ public class UDPPacketReader {
             return ((_message[flagOffset] & flag) != 0);
         }
         
+        @Override
         public String toString() {
             StringBuffer buf = new StringBuffer(256);
             long msAgo = _context.clock().now() - readTimestamp()*1000;
@@ -502,6 +504,7 @@ public class UDPPacketReader {
             int flagNum = fragmentNum % 7;
             return (_message[byteNum] & (1 << flagNum)) != 0x0;
         }
+        @Override
         public String toString() { 
             StringBuffer buf = new StringBuffer(64);
             buf.append("Read partial ACK of ");
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
index 3535484c9f4d8df1f13b6e546d96b7f13e8443de..72fdbbf0e45aba4de2b2d66855e277dcda7a17a4 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
@@ -25,7 +25,7 @@ public class UDPReceiver {
     private Log _log;
     private DatagramSocket _socket;
     private String _name;
-    private List _inboundQueue;
+    private final List _inboundQueue;
     private boolean _keepRunning;
     private Runner _runner;
     private UDPTransport _transport;
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPSender.java b/router/java/src/net/i2p/router/transport/udp/UDPSender.java
index 141b8789282d32db7f44e09aa8e732a2551ead4c..dbc78e8a01c61e262568be6566d69a176108daee 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPSender.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPSender.java
@@ -20,7 +20,7 @@ public class UDPSender {
     private Log _log;
     private DatagramSocket _socket;
     private String _name;
-    private List _outboundQueue;
+    private final List _outboundQueue;
     private boolean _keepRunning;
     private Runner _runner;
     
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 871af78ee4bb634e1bc98bc4196f68b0b99c638b..45de86244368b343da3f0f6903f1a94ab6ea4ca1 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -40,13 +40,13 @@ import net.i2p.util.SimpleTimer;
  *
  */
 public class UDPTransport extends TransportImpl implements TimedWeightedPriorityMessageQueue.FailedListener {
-    private RouterContext _context;
+    private RouterContext _context; // LINT -- field hides a field
     private Log _log;
     private UDPEndpoint _endpoint;
     /** Peer (Hash) to PeerState */
-    private Map _peersByIdent;
+    private final Map _peersByIdent;
     /** RemoteHostId to PeerState */
-    private Map _peersByRemoteHost;
+    private final Map _peersByRemoteHost;
     private PacketHandler _handler;
     private EstablishmentManager _establisher;
     private MessageQueue _outboundMessages;
@@ -90,7 +90,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private TransportBid _transientFail;
     
     /** list of RemoteHostId for peers whose packets we want to drop outright */
-    private List _dropList;
+    private final List _dropList;
     
     private int _expireTimeout;
 
@@ -437,7 +437,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
      * get the state for the peer at the given remote host/port, or null 
      * if no state exists
      */
-    public PeerState getPeerState(RemoteHostId hostInfo) {
+    public PeerState getPeerState(RemoteHostId hostInfo) { // LINT -- Exporting non-public type through public API
         synchronized (_peersByRemoteHost) {
             return (PeerState)_peersByRemoteHost.get(hostInfo);
         }
@@ -597,6 +597,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     }
 ***/
     
+    @Override
     public void messageReceived(I2NPMessage inMsg, RouterIdentity remoteIdent, Hash remoteIdentHash, long msToReceive, int bytesReceived) {
         if (inMsg.getType() == DatabaseStoreMessage.MESSAGE_TYPE) {
             DatabaseStoreMessage dsm = (DatabaseStoreMessage)inMsg;
@@ -673,7 +674,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         }
     }
     
-    public boolean isInDropList(RemoteHostId peer) { synchronized (_dropList) { return _dropList.contains(peer); } }
+    public boolean isInDropList(RemoteHostId peer) { synchronized (_dropList) { return _dropList.contains(peer); } }// LINT -- Exporting non-public type through public API
     
     void dropPeer(Hash peer, boolean shouldShitlist, String why) {
         PeerState state = getPeerState(peer);
@@ -935,6 +936,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static final int MIN_EXPIRE_TIMEOUT = 10*60*1000;
     
     public String getStyle() { return STYLE; }
+    @Override
     public void send(OutNetMessage msg) { 
         if (msg == null) return;
         if (msg.getTarget() == null) return;
@@ -1242,6 +1244,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
             super.afterSend(m, true);
     }
 
+    @Override
     public int countActivePeers() {
         long now = _context.clock().now();
         int active = 0;
@@ -1258,6 +1261,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         return active;
     }
     
+    @Override
     public int countActiveSendPeers() {
         long now = _context.clock().now();
         int active = 0;
@@ -1274,6 +1278,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         return active;
     }
     
+    @Override
     public boolean isEstablished(Hash dest) {
         return getPeerState(dest) != null;
     }
@@ -1284,6 +1289,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         }
     }
 
+    @Override
     public boolean haveCapacity() {
         synchronized (_peersByIdent) {
             return _peersByIdent.size() < getMaxConnections() * 4 / 5;
@@ -1294,6 +1300,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
      * Return our peer clock skews on this transport.
      * Vector composed of Long, each element representing a peer skew in seconds.
      */
+    @Override
     public Vector getClockSkews() {
 
         Vector skews = new Vector();
@@ -1419,6 +1426,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class IdleInComparator extends PeerComparator {
         private static final IdleInComparator _instance = new IdleInComparator();
         public static final IdleInComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = r.getLastReceiveTime() - l.getLastReceiveTime();
             if (rv == 0) // fallback on alpha
@@ -1430,6 +1438,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class IdleOutComparator extends PeerComparator {
         private static final IdleOutComparator _instance = new IdleOutComparator();
         public static final IdleOutComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = r.getLastSendTime() - l.getLastSendTime();
             if (rv == 0) // fallback on alpha
@@ -1441,6 +1450,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class RateInComparator extends PeerComparator {
         private static final RateInComparator _instance = new RateInComparator();
         public static final RateInComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getReceiveBps() - r.getReceiveBps();
             if (rv == 0) // fallback on alpha
@@ -1452,6 +1462,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class RateOutComparator extends PeerComparator {
         private static final RateOutComparator _instance = new RateOutComparator();
         public static final RateOutComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getSendBps() - r.getSendBps();
             if (rv == 0) // fallback on alpha
@@ -1463,6 +1474,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class UptimeComparator extends PeerComparator {
         private static final UptimeComparator _instance = new UptimeComparator();
         public static final UptimeComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = r.getKeyEstablishedTime() - l.getKeyEstablishedTime();
             if (rv == 0) // fallback on alpha
@@ -1474,6 +1486,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class SkewComparator extends PeerComparator {
         private static final SkewComparator _instance = new SkewComparator();
         public static final SkewComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = Math.abs(l.getClockSkew()) - Math.abs(r.getClockSkew());
             if (rv == 0) // fallback on alpha
@@ -1485,6 +1498,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class CwndComparator extends PeerComparator {
         private static final CwndComparator _instance = new CwndComparator();
         public static final CwndComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getSendWindowBytes() - r.getSendWindowBytes();
             if (rv == 0) // fallback on alpha
@@ -1496,6 +1510,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class SsthreshComparator extends PeerComparator {
         private static final SsthreshComparator _instance = new SsthreshComparator();
         public static final SsthreshComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getSlowStartThreshold() - r.getSlowStartThreshold();
             if (rv == 0) // fallback on alpha
@@ -1507,6 +1522,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class RTTComparator extends PeerComparator {
         private static final RTTComparator _instance = new RTTComparator();
         public static final RTTComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getRTT() - r.getRTT();
             if (rv == 0) // fallback on alpha
@@ -1518,6 +1534,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class DevComparator extends PeerComparator {
         private static final DevComparator _instance = new DevComparator();
         public static final DevComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getRTTDeviation() - r.getRTTDeviation();
             if (rv == 0) // fallback on alpha
@@ -1529,6 +1546,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class RTOComparator extends PeerComparator {
         private static final RTOComparator _instance = new RTOComparator();
         public static final RTOComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getRTO() - r.getRTO();
             if (rv == 0) // fallback on alpha
@@ -1540,6 +1558,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class MTUComparator extends PeerComparator {
         private static final MTUComparator _instance = new MTUComparator();
         public static final MTUComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getMTU() - r.getMTU();
             if (rv == 0) // fallback on alpha
@@ -1551,6 +1570,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class SendCountComparator extends PeerComparator {
         private static final SendCountComparator _instance = new SendCountComparator();
         public static final SendCountComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getPacketsTransmitted() - r.getPacketsTransmitted();
             if (rv == 0) // fallback on alpha
@@ -1562,6 +1582,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class RecvCountComparator extends PeerComparator {
         private static final RecvCountComparator _instance = new RecvCountComparator();
         public static final RecvCountComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getPacketsReceived() - r.getPacketsReceived();
             if (rv == 0) // fallback on alpha
@@ -1573,6 +1594,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class ResendComparator extends PeerComparator {
         private static final ResendComparator _instance = new ResendComparator();
         public static final ResendComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getPacketsRetransmitted() - r.getPacketsRetransmitted();
             if (rv == 0) // fallback on alpha
@@ -1584,6 +1606,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     private static class DupComparator extends PeerComparator {
         private static final DupComparator _instance = new DupComparator();
         public static final DupComparator instance() { return _instance; }
+        @Override
         protected int compare(PeerState l, PeerState r) {
             long rv = l.getPacketsReceivedDuplicate() - r.getPacketsReceivedDuplicate();
             if (rv == 0) // fallback on alpha
@@ -1628,6 +1651,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
     
     //public void renderStatusHTML(Writer out) throws IOException { renderStatusHTML(out, 0); }
     public void renderStatusHTML(Writer out, int sortFlags) throws IOException {}
+    @Override
     public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException {
         TreeSet peers = new TreeSet(getComparator(sortFlags));
         synchronized (_peersByIdent) {
@@ -1970,12 +1994,14 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
      */
     private class SharedBid extends TransportBid {
         public SharedBid(int ms) { super(); setLatencyMs(ms); }
+        @Override
         public Transport getTransport() { return UDPTransport.this; }
+        @Override
         public String toString() { return "UDP bid @ " + getLatencyMs(); }
     }
     
     private class ExpirePeerEvent implements SimpleTimer.TimedEvent {
-        private List _expirePeers;
+        private final List _expirePeers;
         private List _expireBuffer;
         private boolean _alive;
         public ExpirePeerEvent() {
@@ -2102,11 +2128,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         
         return _reachabilityStatus; 
     }
+    @Override
     public void recheckReachability() {
         _testEvent.runTest();
     }
     
-    public PeerState pickTestPeer(RemoteHostId dontInclude) {
+    public PeerState pickTestPeer(RemoteHostId dontInclude) {// LINT -- Exporting non-public type through public API
         List peers = null;
         synchronized (_peersByIdent) {
             peers = new ArrayList(_peersByIdent.values());
diff --git a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java
index 6d2e7b7396a652cfefb8c851a1e6eca02f726b25..2de7868dbfa2b21dca5a24cda9c75fafede5f0e2 100644
--- a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java
+++ b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java
@@ -49,6 +49,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
     private static final int FORCE_BATCH_FLUSH = 50;
     
     /** how long do we want to wait before flushing */
+    @Override
     public long getDelayAmount() { return getDelayAmount(true); }
     private long getDelayAmount(boolean shouldStat) {
         long rv = -1;
@@ -62,6 +63,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
         return rv;
     }
     
+    @Override
     public boolean preprocessQueue(List pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) {
         StringBuffer timingBuf = null;
         if (_log.shouldLog(Log.DEBUG)) {
diff --git a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java
index 0be137b4512e190b0e52bcddcee95f111e7dc499..2e5e988855b5d05b072a72ae15fc28d0e0d4d356 100644
--- a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java
+++ b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java
@@ -57,6 +57,7 @@ public class BatchedRouterPreprocessor extends BatchedPreprocessor {
     }
 
     /** how long should we wait before flushing */
+    @Override
     protected long getSendDelay() { 
         String freq = null;
         if (_config != null) {
@@ -77,6 +78,7 @@ public class BatchedRouterPreprocessor extends BatchedPreprocessor {
         return DEFAULT_BATCH_FREQUENCY;
     }
     
+    @Override
     protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds, String msg) {
         if (_config != null)
             _routerContext.messageHistory().fragmentMessage(messageId, numFragments, totalLength, messageIds, _config, msg);
diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
index 99b66c0c8b805e6f1ed1e3027539c33fa97036b0..43e1d13144de93911a3c84eccdb6c1fac94a16ff 100644
--- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java
@@ -26,7 +26,7 @@ import net.i2p.util.SimpleTimer;
 public class FragmentHandler {
     private I2PAppContext _context;
     private Log _log;
-    private Map _fragmentedMessages;
+    private final Map _fragmentedMessages;
     private DefragmentedReceiver _receiver;
     private int _completed;
     private int _failed;
@@ -106,7 +106,18 @@ public class FragmentHandler {
             if (_log.shouldLog(Log.ERROR))
                 _log.error("Corrupt fragment received: offset = " + offset, e);
             _context.statManager().addRateData("tunnel.corruptMessage", 1, 1);
-            throw e;
+            // java.lang.IllegalStateException: wtf, don't get the completed size when we're not complete - null fragment i=0 of 1
+            // at net.i2p.router.tunnel.FragmentedMessage.getCompleteSize(FragmentedMessage.java:194)
+            // at net.i2p.router.tunnel.FragmentedMessage.toByteArray(FragmentedMessage.java:223)
+            // at net.i2p.router.tunnel.FragmentHandler.receiveComplete(FragmentHandler.java:380)
+            // at net.i2p.router.tunnel.FragmentHandler.receiveSubsequentFragment(FragmentHandler.java:353)
+            // at net.i2p.router.tunnel.FragmentHandler.receiveFragment(FragmentHandler.java:208)
+            // at net.i2p.router.tunnel.FragmentHandler.receiveTunnelMessage(FragmentHandler.java:92)
+            // ...
+            // still trying to find root cause
+            // let's limit the damage here and skip the:
+            // .transport.udp.MessageReceiver: b0rked receiving a message.. wazza huzza hmm?
+            //throw e;
         } finally {
             // each of the FragmentedMessages populated make a copy out of the
             // payload, which they release separately, so we can release 
diff --git a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
index b0203f540d117ee52bcc12e41d422b1bb5d2c8a8..94e2c8c2487ccaf00cbe9b79f0de66f6e5d9d9cf 100644
--- a/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
+++ b/router/java/src/net/i2p/router/tunnel/FragmentedMessage.java
@@ -273,6 +273,7 @@ public class FragmentedMessage {
         }
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(128);
         buf.append("Fragments for ").append(_messageId).append(": ");
diff --git a/router/java/src/net/i2p/router/tunnel/HashSetIVValidator.java b/router/java/src/net/i2p/router/tunnel/HashSetIVValidator.java
index aea18dc3f645ddead8f2c01d85b061cafaca9329..c9241fccef255459fb89f16c3c3be46fe3cb8fac 100644
--- a/router/java/src/net/i2p/router/tunnel/HashSetIVValidator.java
+++ b/router/java/src/net/i2p/router/tunnel/HashSetIVValidator.java
@@ -9,7 +9,7 @@ import net.i2p.data.DataHelper;
  * waste lots of RAM 
  */
 class HashSetIVValidator implements IVValidator {
-    private HashSet _received;
+    private final HashSet _received;
     
     public HashSetIVValidator() {
         _received = new HashSet();
diff --git a/router/java/src/net/i2p/router/tunnel/HopConfig.java b/router/java/src/net/i2p/router/tunnel/HopConfig.java
index 740cf9025ce502517c9156cedb8ad782150a2fd7..ab0c32a3f35a455c8650d1bb8a02d0a57497e3aa 100644
--- a/router/java/src/net/i2p/router/tunnel/HopConfig.java
+++ b/router/java/src/net/i2p/router/tunnel/HopConfig.java
@@ -135,6 +135,7 @@ public class HopConfig {
         return rv;
     }
     
+    @Override
     public String toString() {
         StringBuffer buf = new StringBuffer(64);
         if (_receiveTunnelId != null) {
diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
index c5c46c365799273b468470514009162026740fdb..b1b03fb22fb3e59bcafb2ff3784649778be7c20f 100644
--- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
+++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
@@ -10,7 +10,6 @@ import net.i2p.data.i2np.DeliveryStatusMessage;
 import net.i2p.data.i2np.GarlicMessage;
 import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.data.i2np.TunnelBuildReplyMessage;
-import net.i2p.data.i2np.TunnelGatewayMessage;
 import net.i2p.router.ClientMessage;
 import net.i2p.router.RouterContext;
 import net.i2p.router.TunnelInfo;
diff --git a/router/java/src/net/i2p/router/tunnel/PumpedTunnelGateway.java b/router/java/src/net/i2p/router/tunnel/PumpedTunnelGateway.java
index a2cac90bb9a8f9b7ae350e98bbfc5c728add4538..9364092df5db80e302427e905300879a4de2bfe3 100644
--- a/router/java/src/net/i2p/router/tunnel/PumpedTunnelGateway.java
+++ b/router/java/src/net/i2p/router/tunnel/PumpedTunnelGateway.java
@@ -32,7 +32,7 @@ import net.i2p.util.Log;
  *
  */
 public class PumpedTunnelGateway extends TunnelGateway {
-    private List _prequeue;
+    private final List _prequeue;
     private TunnelGatewayPumper _pumper;
     
     /**
@@ -58,6 +58,7 @@ public class PumpedTunnelGateway extends TunnelGateway {
      * @param toRouter router to send to after the endpoint (or null for endpoint processing)
      * @param toTunnel tunnel to send to after the endpoint (or null for endpoint or router processing)
      */
+    @Override
     public void add(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
         _messagesSent++;
         Pending cur = new PendingImpl(msg, toRouter, toTunnel);
diff --git a/router/java/src/net/i2p/router/tunnel/RouterFragmentHandler.java b/router/java/src/net/i2p/router/tunnel/RouterFragmentHandler.java
index 7caa3271e6263f3c36d5619260b00cf2b0f98008..acb4231d27cf6989435576bf33deb744af384268 100644
--- a/router/java/src/net/i2p/router/tunnel/RouterFragmentHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/RouterFragmentHandler.java
@@ -16,16 +16,19 @@ public class RouterFragmentHandler extends FragmentHandler {
         _log = context.logManager().getLog(RouterFragmentHandler.class);
     }
     
+    @Override
     protected void noteReception(long messageId, int fragmentId, Object status) {
         if (_log.shouldLog(Log.INFO))
             _log.info("Received fragment " + fragmentId + " for message " + messageId + ": " + status);
         _routerContext.messageHistory().receiveTunnelFragment(messageId, fragmentId, status);
     }
+    @Override
     protected void noteCompletion(long messageId) {
         if (_log.shouldLog(Log.INFO))
             _log.info("Received complete message " + messageId);
         _routerContext.messageHistory().receiveTunnelFragmentComplete(messageId);
     }
+    @Override
     protected void noteFailure(long messageId, String status) {
         if (_log.shouldLog(Log.INFO))
             _log.info("Dropped message " + messageId + ": " + status);
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java b/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java
index 83f5fbf94e0e6418f2556989a9079fd6b480f2b4..72dccf45c19a9775aaff444a93baeb70cea64b11 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java
@@ -163,6 +163,7 @@ public class TunnelCreatorConfig implements TunnelInfo {
             _failures = 0;
     }
     
+    @Override
     public String toString() {
         // H0:1235-->H1:2345-->H2:2345
         StringBuffer buf = new StringBuffer(128);
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java
index a9ed0cc1e76df912be1ef39f6c418cb838be4e93..077a43b7de30c3886c793ac5c0b727ded695773f 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java
@@ -36,13 +36,13 @@ import net.i2p.util.SimpleTimer;
 public class TunnelGateway {
     protected I2PAppContext _context;
     protected Log _log;
-    protected List _queue;
+    protected final List _queue;
     protected QueuePreprocessor _preprocessor;
     protected Sender _sender;
     protected Receiver _receiver;
     protected long _lastFlush;
     protected int _flushFrequency;
-    protected DelayedFlush _delayedFlush;
+    protected DelayedFlush _delayedFlush;// LINT -- Exporting non-public type through public API
     protected int _messagesSent;
     
     /**
@@ -236,6 +236,7 @@ public class TunnelGateway {
             super(message, toRouter, toTunnel, _context.clock().now());
         }        
         
+        @Override
         public String toString() {
             StringBuffer buf = new StringBuffer(64);
             buf.append("Message ").append(_messageId).append(" on ");
@@ -257,6 +258,7 @@ public class TunnelGateway {
             return buf.toString();
         }
 
+        @Override
         public long getLifetime() { return _context.clock().now()-_created; }
     }
     
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java b/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
index 7e808199dea1f2b49cee2e3aaaa9661e2ad1ad3e..b840dad287bb154f7854697ed292dc95aafd62f0 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
@@ -14,7 +14,7 @@ import net.i2p.util.Log;
 public class TunnelGatewayPumper implements Runnable {
     private RouterContext _context;
     private Log _log;
-    private List _wantsPumping;
+    private final List _wantsPumping;
     private boolean _stop;
     
     /** Creates a new instance of TunnelGatewayPumper */
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGatewayZeroHop.java b/router/java/src/net/i2p/router/tunnel/TunnelGatewayZeroHop.java
index 1abceb9f25e85afccfd3a76e0ca77b67da205c04..aefe9670decd0f42b5b2b88b14b88b6aac12948f 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelGatewayZeroHop.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelGatewayZeroHop.java
@@ -12,8 +12,8 @@ import net.i2p.util.Log;
  *
  */
 public class TunnelGatewayZeroHop extends TunnelGateway {
-    private RouterContext _context;
-    private Log _log;
+    private RouterContext _context; // LINT -- field hides a field
+    private Log _log; // LINT -- field hides a field
     private TunnelCreatorConfig _config;
     private OutboundMessageDistributor _outDistributor;
     private InboundMessageDistributor _inDistributor;
@@ -37,6 +37,7 @@ public class TunnelGatewayZeroHop extends TunnelGateway {
      *
      * @param msg message received to be sent through the tunnel
      */
+    @Override
     public void add(TunnelGatewayMessage msg) {
         add(msg.getMessage(), null, null);
     }
@@ -50,6 +51,7 @@ public class TunnelGatewayZeroHop extends TunnelGateway {
      * @param toRouter router to send to after the endpoint (or null for endpoint processing)
      * @param toTunnel tunnel to send to after the endpoint (or null for endpoint or router processing)
      */
+    @Override
     public void add(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) {
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("zero hop gateway: distribute " + (_config.isInbound() ? "inbound " : " outbound ")
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
index 5fc8708572147fdf31634d91341c94c7a29c07a6..5e4a92641bb4e5adf99cb72851be12f0790f59ed 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
@@ -218,6 +218,7 @@ public class TunnelParticipant {
         }
     }
     
+    @Override
     public String toString() { 
         if (_config != null) {
             StringBuffer buf = new StringBuffer(64);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
index 06a66635432e51e055e3765a1360bfcb1686c888..05b53fbf9b4bc9874ed5d497fd7b3c9978d2b11d 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
@@ -21,11 +21,12 @@ import net.i2p.util.Log;
  *
  */
 class BuildExecutor implements Runnable {
+    private final List _recentBuildIds = new ArrayList(100);
     private RouterContext _context;
     private Log _log;
     private TunnelPoolManager _manager;
     /** list of TunnelCreatorConfig elements of tunnels currently being built */
-    private List _currentlyBuilding;
+    private final List _currentlyBuilding;
     private boolean _isRunning;
     private BuildHandler _handler;
     private boolean _repoll;
@@ -50,7 +51,7 @@ class BuildExecutor implements Runnable {
 
         // Get stat manager, get recognized bandwidth tiers
         StatManager statMgr = _context.statManager();
-        String bwTiers = _context.router().getRouterInfo().BW_CAPABILITY_CHARS;
+        String bwTiers = _context.router().getRouterInfo().BW_CAPABILITY_CHARS; // LINT -- Accessing static field "BW_CAPABILITY_CHARS"
         // For each bandwidth tier, create tunnel build agree/reject/expire stats
         for (int i = 0; i < bwTiers.length(); i++) {
             String bwTier = String.valueOf(bwTiers.charAt(i));
@@ -423,7 +424,6 @@ class BuildExecutor implements Runnable {
         }
     }
     
-    private List _recentBuildIds = new ArrayList(100);
     public boolean wasRecentlyBuilding(long replyId) {
         synchronized (_recentBuildIds) {
             return _recentBuildIds.contains(new Long(replyId));
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
index 0699d3ff4d04b0df98485f266c3c08f738161d00..38350544e68ac803fb74bf3222c9cc25b737f78b 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
@@ -39,11 +39,11 @@ class BuildHandler {
     private Job _buildMessageHandlerJob;
     private Job _buildReplyMessageHandlerJob;
     /** list of BuildMessageState, oldest first */
-    private List _inboundBuildMessages;
+    private final List _inboundBuildMessages;
     /** list of BuildReplyMessageState, oldest first */
-    private List _inboundBuildReplyMessages;
+    private final List _inboundBuildReplyMessages;
     /** list of BuildEndMessageState, oldest first */
-    private List _inboundBuildEndMessages;
+    private final List _inboundBuildEndMessages;
     private BuildMessageProcessor _processor;
     
     public BuildHandler(RouterContext ctx, BuildExecutor exec) {
@@ -607,7 +607,7 @@ class BuildHandler {
             // send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage
             // (enough layers jrandom?)
             TunnelBuildReplyMessage replyMsg = new TunnelBuildReplyMessage(_context);
-            for (int i = 0; i < state.msg.RECORD_COUNT; i++)
+            for (int i = 0; i < state.msg.RECORD_COUNT; i++) // LINT -- Accessing Static field "RECORD_COUNT"
                 replyMsg.setRecord(i, state.msg.getRecord(i));
             replyMsg.setUniqueId(req.readReplyMessageId());
             replyMsg.setMessageExpiration(_context.clock().now() + 10*1000);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java
index 76f2c3c256abd35451ff8072a2a986fd0bd30062..3cc4906ea93a3c1ef680519f218d0eb50a9effbf 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java
@@ -31,6 +31,7 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
     }
     
     // calls TestJob
+    @Override
     public void testSuccessful(int ms) {
         if (_testJob != null)
             _testJob.testSuccessful(ms);
@@ -47,6 +48,7 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
     /**
      * The tunnel failed a test, so (maybe) stop using it
      */
+    @Override
     public boolean tunnelFailed() {
         boolean rv = super.tunnelFailed();
         if (!rv) {
@@ -60,6 +62,7 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
         return rv;
     }
     
+    @Override
     public Properties getOptions() {
         if (_pool == null) return null;
         return _pool.getSettings().getUnknownOptions();
@@ -75,7 +78,8 @@ public class PooledTunnelCreatorConfig extends TunnelCreatorConfig {
     }
     public TunnelPool getTunnelPool() { return _pool; }
     
-    public void setTestJob(TestJob job) { _testJob = job; }
+
+    public void setTestJob(TestJob job) { _testJob = job; } // LINT -- Exporting non-public type through public API
     public void setExpireJob(Job job) { /* _expireJob = job; */ }
     
     // Fix memory leaks caused by references if you need to use pairedTunnel
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java
index b81178bdf75b8baf81ff43a2c075361552bef69d..24b379543e6cc58ea5039e042f557440e72eb138 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java
@@ -250,6 +250,7 @@ class TestJob extends JobImpl {
             return false;
         }
         
+        @Override
         public String toString() {
             StringBuffer rv = new StringBuffer(64);
             rv.append("Testing tunnel ").append(_cfg.toString()).append(" waiting for ");
@@ -281,6 +282,7 @@ class TestJob extends JobImpl {
             _successTime = getContext().clock().now() - ((DeliveryStatusMessage)message).getArrival();
         }
         
+        @Override
         public String toString() {
             StringBuffer rv = new StringBuffer(64);
             rv.append("Testing tunnel ").append(_cfg.toString());
@@ -306,6 +308,7 @@ class TestJob extends JobImpl {
                 testFailed(getContext().clock().now() - _started);
         }
         
+        @Override
         public String toString() {
             StringBuffer rv = new StringBuffer(64);
             rv.append("Testing tunnel ").append(_cfg.toString());
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
index 699a8be9f1468e57b09e33c235d589ff35247333..8ce5859df0877bcfb4b5c096fb74ff9b0e9aacd4 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
@@ -25,10 +25,11 @@ import net.i2p.util.Log;
  *
  */
 public class TunnelPool {
+    private final List _inProgress = new ArrayList();
     private RouterContext _context;
     private Log _log;
     private TunnelPoolSettings _settings;
-    private ArrayList<TunnelInfo> _tunnels;
+    private final ArrayList<TunnelInfo> _tunnels;
     private TunnelPeerSelector _peerSelector;
     private TunnelPoolManager _manager;
     private boolean _alive;
@@ -64,6 +65,9 @@ public class TunnelPool {
     }
     
     public void startup() {
+        synchronized (_inProgress) {
+            _inProgress.clear();
+        }
         _alive = true;
         _started = System.currentTimeMillis();
         _lastRateUpdate = _started;
@@ -90,6 +94,9 @@ public class TunnelPool {
         _lastSelectionPeriod = 0;
         _lastSelected = null;
         _context.statManager().removeRateStat(_rateName);
+        synchronized (_inProgress) {
+            _inProgress.clear();
+        }
     }
 
     TunnelPoolManager getManager() { return _manager; }
@@ -292,7 +299,9 @@ public class TunnelPool {
         int remaining = 0;
         LeaseSet ls = null;
         synchronized (_tunnels) {
-            _tunnels.remove(info);
+            boolean removed = _tunnels.remove(info);
+            if (!removed)
+                return;
             if (_settings.isInbound() && (_settings.getDestination() != null) )
                 ls = locked_buildNewLeaseSet();
             remaining = _tunnels.size();
@@ -333,12 +342,15 @@ public class TunnelPool {
         }
     }
 
+    /** This may be called multiple times from TestJob */
     public void tunnelFailed(PooledTunnelCreatorConfig cfg) {
         if (_log.shouldLog(Log.WARN))
             _log.warn(toString() + ": Tunnel failed: " + cfg);
         LeaseSet ls = null;
         synchronized (_tunnels) {
-            _tunnels.remove(cfg);
+            boolean removed = _tunnels.remove(cfg);
+            if (!removed)
+                return;
             if (_settings.isInbound() && (_settings.getDestination() != null) )
                 ls = locked_buildNewLeaseSet();
             if (_lastSelected == cfg) {
@@ -464,7 +476,7 @@ public class TunnelPool {
         if (!_alive)
             return null;
 
-        int wanted = _settings.getQuantity();
+        int wanted = Math.min(_settings.getQuantity(), LeaseSet.MAX_LEASES);
         if (_tunnels.size() < wanted) {
             if (_log.shouldLog(Log.WARN))
                 _log.warn(toString() + ": Not enough tunnels (" + _tunnels.size() + ", wanted " + wanted + ")");
@@ -866,13 +878,13 @@ public class TunnelPool {
         return cfg;
     }
     
-    private List _inProgress = new ArrayList();
     void buildComplete(PooledTunnelCreatorConfig cfg) {
         synchronized (_inProgress) { _inProgress.remove(cfg); }
         cfg.setTunnelPool(this);
         //_manager.buildComplete(cfg);
     }
     
+    @Override
     public String toString() {
         if (_settings.isExploratory()) {
             if (_settings.isInbound())
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
index acbb9345db9795ff85d8751f30e7ef42eed18b70..852ee28ff3ea3b502211a2d95917791d25cea81d 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
@@ -38,9 +38,9 @@ public class TunnelPoolManager implements TunnelManagerFacade {
     private RouterContext _context;
     private Log _log;
     /** Hash (destination) to TunnelPool */
-    private Map _clientInboundPools;
+    private final Map _clientInboundPools;
     /** Hash (destination) to TunnelPool */
-    private Map _clientOutboundPools;
+    private final Map _clientOutboundPools;
     private TunnelPool _inboundExploratory;
     private TunnelPool _outboundExploratory;
     private LoadTestManager _loadTestManager;