diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
index 2869193a0a703344af2867123b60bcb884c26932..8ffde7363b9fbcf158d3009e00fbad4be62ec1fa 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
@@ -215,7 +215,7 @@ class PeerCoordinator implements PeerListener
 
   public Storage getStorage() { return storage; }
 
-  // for web page detailed stats
+  /** for web page detailed stats */
   public List<Peer> peerList()
   {
         return new ArrayList(peers);
@@ -446,6 +446,12 @@ class PeerCoordinator implements PeerListener
     synchronized (downloaded_old) {
         Arrays.fill(downloaded_old, 0);
     }
+    // failsafe
+    synchronized(wantedPieces) {
+        for (Piece pc : wantedPieces) {
+            pc.clear();
+        }
+    }
     timer.schedule((CHECK_PERIOD / 2) + _random.nextInt((int) CHECK_PERIOD));
   }
 
@@ -750,8 +756,12 @@ class PeerCoordinator implements PeerListener
             // AND if there are almost no wanted pieces left (real end game).
             // If we do end game all the time, we generate lots of extra traffic
             // when the seeder is super-slow and all the peers are "caught up"
-            if (wantedSize > END_GAME_THRESHOLD)
+            if (wantedSize > END_GAME_THRESHOLD) {
+                if (_log.shouldLog(Log.INFO))
+                    _log.info("Nothing to request, " + requested.size() + " being requested and " +
+                              wantedSize + " still wanted");
                 return null;  // nothing to request and not in end game
+            }
             // let's not all get on the same piece
             // Even better would be to sort by number of requests
             if (record)
@@ -1078,10 +1088,11 @@ class PeerCoordinator implements PeerListener
   /** Called when a peer is removed, to prevent it from being used in 
    * rarest-first calculations.
    */
-  public void removePeerFromPieces(Peer peer) {
+  private void removePeerFromPieces(Peer peer) {
       synchronized(wantedPieces) {
           for (Piece piece : wantedPieces) {
               piece.removePeer(peer);
+              piece.setRequested(peer, false);
           }
       } 
   }
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
index 8f80d449644aefbad1990abb3bc57458e2746044..95bef3c4f981c0fbef55be361769790724dd12c4 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
@@ -682,6 +682,7 @@ class PeerState implements DataLoader
                   _log.debug(peer + " addRequest() we are choked, delaying requestNextPiece()");
               return;
           }
+          // huh? rv unused
           more_pieces = requestNextPiece();
         } else if (more_pieces) // We want something
           {
@@ -711,6 +712,8 @@ class PeerState implements DataLoader
       }
 
     // failsafe
+    // However this is bad as it thrashes the peer when we change our mind
+    // Ticket 691 cause here?
     if (interesting && lastRequest == null && outstandingRequests.isEmpty())
         setInteresting(false);
 
@@ -784,6 +787,8 @@ class PeerState implements DataLoader
     }
 
     // failsafe
+    // However this is bad as it thrashes the peer when we change our mind
+    // Ticket 691 cause here?
     if (outstandingRequests.isEmpty())
         lastRequest = null;
 
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Piece.java b/apps/i2psnark/java/src/org/klomp/snark/Piece.java
index 3ca03ad98865bbf976336ecd225ed86bc59bcc73..9a2e2e0e75344c75935ff8729b59c3d6b41a4204 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Piece.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Piece.java
@@ -12,7 +12,7 @@ class Piece implements Comparable {
     private final int id;
     private final Set<PeerID> peers;
     /** @since 0.8.3 */
-    private Set<PeerID> requests;
+    private volatile Set<PeerID> requests;
     /** @since 0.8.1 */
     private int priority;
     
@@ -54,7 +54,10 @@ class Piece implements Comparable {
     /** caller must synchronize */
     public boolean addPeer(Peer peer) { return this.peers.add(peer.getPeerID()); }
 
-    /** caller must synchronize */
+    /**
+     * Caller must synchronize.
+     * @return true if removed
+     */
     public boolean removePeer(Peer peer) { return this.peers.remove(peer.getPeerID()); }
 
     /**
@@ -104,6 +107,17 @@ class Piece implements Comparable {
     public int getRequestCount() {
         return this.requests == null ? 0 : this.requests.size();
     } 
+
+    /**
+     * Clear all knowledge of peers
+     * Caller must synchronize
+     * @since 0.9.3
+     */
+    public void clear() {
+        peers.clear();
+        if (requests != null)
+            requests.clear();
+    } 
     
     /** @return default 0 @since 0.8.1 */
     public int getPriority() { return this.priority; }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
index 34c2174a9a1292530b954ab1d8c95b26e776925a..a811e05b94de7ed1dc31d0a7c9c71614918853d2 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -38,11 +38,14 @@ import net.i2p.util.PortMapper;
 import net.i2p.util.SecureDirectory;
 import net.i2p.util.SecureFileOutputStream;
 import net.i2p.util.ShellCommand;
+import net.i2p.util.SystemVersion;
 import net.i2p.util.VersionComparator;
 
+import org.mortbay.jetty.AbstractConnector;
 import org.mortbay.jetty.Connector;
 import org.mortbay.jetty.NCSARequestLog;
 import org.mortbay.jetty.Server;
+import org.mortbay.jetty.bio.SocketConnector;
 import org.mortbay.jetty.handler.ContextHandlerCollection;
 import org.mortbay.jetty.handler.DefaultHandler;
 import org.mortbay.jetty.handler.HandlerCollection;
@@ -54,6 +57,7 @@ import org.mortbay.jetty.security.HashUserRealm;
 import org.mortbay.jetty.security.Constraint;
 import org.mortbay.jetty.security.ConstraintMapping;
 import org.mortbay.jetty.security.SecurityHandler;
+import org.mortbay.jetty.security.SslSocketConnector;
 import org.mortbay.jetty.security.SslSelectChannelConnector;
 import org.mortbay.jetty.servlet.ServletHandler;
 import org.mortbay.jetty.servlet.ServletHolder;
@@ -240,8 +244,7 @@ public class RouterConsoleRunner implements RouterApp {
         try {
             //TODO: move away from routerconsole into a separate application.
             //ApplicationManager?
-            VersionComparator v = new VersionComparator();
-            boolean recentJava = v.compare(System.getProperty("java.runtime.version"), "1.6") >= 0;
+            boolean recentJava = SystemVersion.isJava6();
             // default false for now
             boolean desktopguiEnabled = ctx.getBooleanProperty("desktopgui.enabled");
             if (recentJava && desktopguiEnabled) {
@@ -397,12 +400,22 @@ public class RouterConsoleRunner implements RouterApp {
                         //    _server.addListener('[' + host + "]:" + _listenPort);
                         //else
                         //    _server.addListener(host + ':' + _listenPort);
-                        SelectChannelConnector lsnr = new SelectChannelConnector();
+                        AbstractConnector lsnr;
+                        if (SystemVersion.isJava6() && !SystemVersion.isGNU()) {
+                            SelectChannelConnector slsnr = new SelectChannelConnector();
+                            slsnr.setUseDirectBuffers(false);  // default true seems to be leaky
+                            lsnr = slsnr;
+                        } else {
+                            // Jetty 6 and NIO on Java 5 don't get along that well
+                            // Also: http://jira.codehaus.org/browse/JETTY-1238
+                            // "Do not use GCJ with Jetty, it will not work."
+                            // Actually it does if you don't use NIO
+                            lsnr = new SocketConnector();
+                        }
                         lsnr.setHost(host);
                         lsnr.setPort(lport);
                         lsnr.setMaxIdleTime(90*1000);  // default 10 sec
                         lsnr.setName("ConsoleSocket");   // all with same name will use the same thread pool
-                        lsnr.setUseDirectBuffers(false);  // default true seems to be leaky
                         //_server.addConnector(lsnr);
                         connectors.add(lsnr);
                         boundAddresses++;
@@ -451,22 +464,37 @@ public class RouterConsoleRunner implements RouterApp {
                             }
                             // TODO if class not found use SslChannelConnector
                             // Sadly there's no common base class with the ssl methods in it
-                            SslSelectChannelConnector ssll = new SslSelectChannelConnector();
-                            ssll.setHost(host);
-                            ssll.setPort(sslPort);
+                            AbstractConnector ssll;
+                            if (SystemVersion.isJava6() && !SystemVersion.isGNU()) {
+                                SslSelectChannelConnector sssll = new SslSelectChannelConnector();
+                                // the keystore path and password
+                                sssll.setKeystore(keyStore.getAbsolutePath());
+                                sssll.setPassword(ctx.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
+                                // the X.509 cert password (if not present, verifyKeyStore() returned false)
+                                sssll.setKeyPassword(ctx.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
+                                sssll.setUseDirectBuffers(false);  // default true seems to be leaky
+                                ssll = sssll;
+                            } else {
+                                // Jetty 6 and NIO on Java 5 don't get along that well
+                                SslSocketConnector sssll = new SslSocketConnector();
                             // the keystore path and password
-                            ssll.setKeystore(keyStore.getAbsolutePath());
-                            ssll.setPassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
+                                sssll.setKeystore(keyStore.getAbsolutePath());
+                                sssll.setPassword(ctx.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD));
                             // the X.509 cert password (if not present, verifyKeyStore() returned false)
-                            ssll.setKeyPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
+                                sssll.setKeyPassword(ctx.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
+                                ssll = sssll;
+                            }
+                            ssll.setHost(host);
+                            ssll.setPort(sslPort);
                             ssll.setMaxIdleTime(90*1000);  // default 10 sec
                             ssll.setName("ConsoleSocket");   // all with same name will use the same thread pool
-                            ssll.setUseDirectBuffers(false);  // default true seems to be leaky
                             //_server.addConnector(ssll);
                             connectors.add(ssll);
                             boundAddresses++;
                         } catch (Exception e) {
                             System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
+                            if (SystemVersion.isGNU())
+                                System.err.println("Probably because GNU classpath does not support Sun keystores");
                             System.err.println("You may ignore this warning if the console is still available at https://localhost:" + sslPort);
                         }
                     }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
index 825133c0eeb4cb3bb8d89f513519d53f281fb5f0..199cf5e4e3bec88e52217a581c6641f0d7b954e6 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
@@ -67,12 +67,10 @@ public class StatSummarizer implements Runnable {
     
     public void run() {
         // JRobin 1.5.9 crashes these JVMs
-        String vendor = System.getProperty("java.vendor");
-        if (vendor.startsWith("Apache") ||                      // Harmony
-            vendor.startsWith("GNU Classpath") ||               // JamVM
-            vendor.startsWith("Free Software Foundation")) {    // gij
+        if (SystemVersion.isApache() ||            // Harmony
+            SystemVersion.isGNU()) {               // JamVM or gij
             _log.logAlways(Log.WARN, "Graphing not supported with this JVM: " +
-                                     vendor + ' ' +
+                                     System.getProperty("java.vendor") + ' ' +
                                      System.getProperty("java.version") + " (" +
                                      System.getProperty("java.runtime.name") + ' ' +
                                      System.getProperty("java.runtime.version") + ')');
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 c520a2bb5635dc943cdafe7d2f86a87f7031ff6e..99a64df949764cb84a6e2a544a1b0edaee240e05 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -456,10 +456,11 @@ public class SummaryHelper extends HelperBase {
     
     /** compare translated nicknames - put "shared clients" first in the sort */
     private class AlphaComparator implements Comparator<Destination> {
+        private final String xsc = _("shared clients");
+
         public int compare(Destination lhs, Destination rhs) {
             String lname = getName(lhs);
             String rname = getName(rhs);
-            String xsc = _("shared clients");
             if (lname.equals(xsc))
                 return -1;
             if (rname.equals(xsc))
diff --git a/core/java/src/net/i2p/crypto/SHA1.java b/core/java/src/net/i2p/crypto/SHA1.java
index 7ea68de562984c2581a946020ca0280aa0c81c11..bfe07630ca2bc97408efd9a15dc38b1dfa77f699 100644
--- a/core/java/src/net/i2p/crypto/SHA1.java
+++ b/core/java/src/net/i2p/crypto/SHA1.java
@@ -20,6 +20,8 @@ import java.security.DigestException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
+import net.i2p.util.SystemVersion;
+
 /**
  * NOTE: As of 0.8.7, use getInstance() instead of new SHA1(), which will
  * return the JVM's MessageDigest if it is faster.
@@ -94,10 +96,8 @@ public final class SHA1 extends MessageDigest implements Cloneable {
     static {
         // oddly, Bitzi is faster than Oracle - see test results below
         boolean useBitzi = true;
-        String vendor = System.getProperty("java.vendor");
-        if (vendor.startsWith("Apache") ||                      // Harmony
-            vendor.startsWith("GNU Classpath") ||               // JamVM
-            vendor.startsWith("Free Software Foundation")) {    // gij
+        if (SystemVersion.isApache() ||            // Harmony
+            SystemVersion.isGNU()) {               // JamVM or gij
             try {
                 MessageDigest.getInstance("SHA-1");
                 useBitzi = false;
diff --git a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
index f09c9711c3e4c1c84bc984d42c5ecee0bf99bcae..5ef5b7801f88002a6bf2cb1edc705139bad7a34d 100644
--- a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
+++ b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
@@ -9,7 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue;
 public class ReusableGZIPInputStream extends ResettableGZIPInputStream {
     // Apache Harmony 5.0M13 Deflater doesn't work after reset()
     // Neither does Android
-    private static final boolean ENABLE_CACHING = !(System.getProperty("java.vendor").startsWith("Apache") ||
+    private static final boolean ENABLE_CACHING = !(SystemVersion.isApache() ||
                                                     SystemVersion.isAndroid());
     private static final LinkedBlockingQueue<ReusableGZIPInputStream> _available;
     static {
diff --git a/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java b/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
index fe686095e440e54556ff9dbcd7a44e76ee363f44..3cbf1dd3356c6d4fb30b8210ad097ddc3d33e318 100644
--- a/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
+++ b/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
@@ -20,7 +20,7 @@ import net.i2p.data.DataHelper;
 public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
     // Apache Harmony 5.0M13 Deflater doesn't work after reset()
     // Neither does Android
-    private static final boolean ENABLE_CACHING = !(System.getProperty("java.vendor").startsWith("Apache") ||
+    private static final boolean ENABLE_CACHING = !(SystemVersion.isApache() ||
                                                     SystemVersion.isAndroid());
     private static final LinkedBlockingQueue<ReusableGZIPOutputStream> _available;
     static {
diff --git a/core/java/src/net/i2p/util/SystemVersion.java b/core/java/src/net/i2p/util/SystemVersion.java
index d43cddc8987442b48ce84e520cf7f10c22fd2116..484a293238cba423364b390ce15c1197b46b94fd 100644
--- a/core/java/src/net/i2p/util/SystemVersion.java
+++ b/core/java/src/net/i2p/util/SystemVersion.java
@@ -15,14 +15,23 @@ public abstract class SystemVersion {
 
     private static final boolean _isWin = System.getProperty("os.name").startsWith("Win");
     private static final boolean _isMac = System.getProperty("os.name").startsWith("Mac");
-    private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _isAndroid;
+    private static final boolean _isApache;
+    private static final boolean _isGNU;
     private static final boolean _is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
                                          System.getProperty("os.arch").contains("64");
+    private static final boolean _hasWrapper = System.getProperty("wrapper.version") != null;
 
     private static final boolean _oneDotSix;
     private static final int _androidSDK;
 
     static {
+        String vendor = System.getProperty("java.vendor");
+        _isAndroid = vendor.contains("Android");
+        _isApache = vendor.startsWith("Apache");
+        _isGNU = vendor.startsWith("GNU Classpath") ||               // JamVM
+                 vendor.startsWith("Free Software Foundation");      // gij
+
         int sdk = 0;
         if (_isAndroid) {
             try {
@@ -52,6 +61,20 @@ public abstract class SystemVersion {
         return _isAndroid;
     }
 
+    /**
+     *  Apache Harmony JVM, or Android
+     */
+    public static boolean isApache() {
+        return _isApache || _isAndroid;
+    }
+
+    /**
+     *  gij or JamVM with GNU Classpath
+     */
+    public static boolean isGNU() {
+        return _isGNU;
+    }
+
     /**
      *  Better than (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0
      *  as it handles Android also, where java.version = "0".
@@ -90,6 +113,6 @@ public abstract class SystemVersion {
      *  Same as I2PAppContext.hasWrapper()
      */
     public static boolean hasWrapper() {
-        return System.getProperty("wrapper.version") != null;
+        return _hasWrapper;
     }
 }
diff --git a/history.txt b/history.txt
index 191680e4ba596d755fd5c1620e482669fda889f1..e10156df615b40daa95c34dc1025927223ae969d 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,8 @@
+2012-10-14 zzz
+ * Console: Use non-nio connector for Java 5 and JamVM/gij
+            (tickets #715 and #743)
+ * i2psnark: Fix request tracking bug preventing piece requests
+
 2012-10-11 kytv
 * Italian translation updates from Transifex
 * i2prouter:
diff --git a/installer/resources/eepsite/jetty.xml b/installer/resources/eepsite/jetty.xml
index ba30a55ebd8264e540799807a8eeaf874ad8fb1d..0e873ca7709e5fea1d4818035b137562779d705a 100644
--- a/installer/resources/eepsite/jetty.xml
+++ b/installer/resources/eepsite/jetty.xml
@@ -90,7 +90,9 @@
 
     <!-- Use this connector for many frequently idle connections
          and for threadless continuations.
-         Not recommended on Java 5 - comment out and uncomment the
+         Not recommended on Java 5 - comment this out, and uncomment the
+         SocketConnector below.
+         Do not use for gij or JamVM - comment this out, and uncomment the
          SocketConnector below.
     -->    
     <Call name="addConnector">
diff --git a/installer/resources/i2prouter b/installer/resources/i2prouter
index d49877795540071e606a033c16e2b7ed109a1846..fde3a0d4706dea92313a43f6b8d66417ef822940 100644
--- a/installer/resources/i2prouter
+++ b/installer/resources/i2prouter
@@ -45,7 +45,7 @@ GETTEXT=$(which gettext > /dev/null 2>&1)
 
 # Where to install the systemd service
 SYSTEMD_SERVICE="/etc/systemd/system/${APP_NAME}.service"
-if grep -q systemd /proc/cmdline; then
+if grep -q systemd /proc/1/comm ; then
     USE_SYSTEMD=1
 fi
 
@@ -1321,6 +1321,8 @@ installdaemon() {
                                 echo "esac"  >> /etc/rc.d/${APP_NAME}
                                 chmod 755 /etc/rc.d/${APP_NAME}
                                 chown root:root /etc/rc.d/${APP_NAME}
+                                eval echo `gettext ' The $APP_LONG_NAME daemon has been installed.'`
+                                eval echo `gettext ' Add \"i2p\" to the DAEMONS variable in /etc/rc.conf to enable.'`
                             else
                                 # We'll end up here if systemd is enabled.
                                 # If systemd is enabled we don't need the initscript
@@ -1330,10 +1332,6 @@ installdaemon() {
                         if [ ! -f "${SYSTEMD_SERVICE}" ]; then
                             installsystemd
                         fi
-                    if ! grep -q systemd /proc/cmdline; then
-                        eval echo `gettext ' The $APP_LONG_NAME daemon has been installed.'`
-                        eval echo `gettext ' Add \"i2p\" to the DAEMONS variable in /etc/rc.conf to enable.'`
-                    fi
                 fi
             elif [ -f /etc/SuSE-release ] ; then
                 eval echo `gettext 'Detected SuSE or SLES:'`
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 31c35f8c3e7f27dae7384d36e3443978e8ab5e40..ea17ad55f3270be34b24fd56bd0b1495da601896 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 14;
+    public final static long BUILD = 15;
 
     /** for example "-test" */
     public final static String EXTRA = "";