diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectCloseTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectCloseTest.java
index b62e610a2d97bbede2e5248508b3f3b23280455e..e3557bcb50fcb3471e3c2b05ea6a94a998fd54d4 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectCloseTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectCloseTest.java
@@ -1,19 +1,13 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.Properties;
 
 import org.junit.Test;
 
-import junit.framework.TestCase;
 
 import net.i2p.I2PAppContext;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
-import net.i2p.data.Destination;
 import net.i2p.util.Log;
 
 /**
@@ -22,7 +16,7 @@ import net.i2p.util.Log;
  * EOF.
  *
  */
-public class ConnectCloseTest extends TestCase {
+public class ConnectCloseTest extends StreamingTestBase {
     private Log _log;
     private I2PSession _server;
     
@@ -38,28 +32,28 @@ public class ConnectCloseTest extends TestCase {
         runClient(context, createSession());
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private void runServer(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ServerRunner(ctx, session));
-        t.setName("server");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private class ServerRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    @Override
+    protected Properties getProperties() {
+        return System.getProperties();
+    }
+
+    @Override
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
+    }
+
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return new ServerRunner(ctx,session);
+    }
+
+
+
+    private class ServerRunner extends RunnerBase {
         public ServerRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ServerRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -83,14 +77,9 @@ public class ConnectCloseTest extends TestCase {
         
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -117,18 +106,4 @@ public class ConnectCloseTest extends TestCase {
         }
         
     }
-    
-    private I2PSession createSession() {
-        try {
-            I2PClient client = I2PClientFactory.createClient();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-            Destination dest = client.createDestination(baos);
-            I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
-            sess.connect();
-            return sess;
-        } catch (Exception e) {
-            _log.error("error running", e);
-            throw new RuntimeException("b0rk b0rk b0rk");
-        }
-    }
 }
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectInactivityTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectInactivityTest.java
index 96937d781024d2292b4caa9c8b3df3c8c1ecf3d0..f3d0cb345b7ccc3f40ed7350beddc91e765f1459 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectInactivityTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectInactivityTest.java
@@ -1,24 +1,19 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.util.Properties;
 
 import org.junit.Test;
 
-import junit.framework.TestCase;
 
 import net.i2p.I2PAppContext;
 import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
-import net.i2p.data.Destination;
 import net.i2p.util.Log;
 
 /**
  *
  */
-public class ConnectInactivityTest extends TestCase{
+public class ConnectInactivityTest extends StreamingTestBase {
     private Log _log;
     private I2PSession _client;
     private I2PSession _server;
@@ -37,28 +32,19 @@ public class ConnectInactivityTest extends TestCase{
         runClient(context, _client);
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(false);
-        t.start();
+    @Override
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
     }
     
-    private void runServer(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ServerRunner(ctx, session));
-        t.setName("server");
-        t.setDaemon(false);
-        t.start();
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return new ServerRunner(ctx,session);
     }
     
-    private class ServerRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ServerRunner extends RunnerBase {
         public ServerRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ServerRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -81,14 +67,9 @@ public class ConnectInactivityTest extends TestCase{
         
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -98,7 +79,7 @@ public class ConnectInactivityTest extends TestCase{
                 _log.debug("manager created");
                 I2PSocket socket = mgr.connect(_server.getMyDestination());
                 _log.debug("socket created");
-                try { Thread.sleep(10*60*1000); } catch (InterruptedException ie) {}
+                Thread.sleep(10*60*1000); 
                 socket.close();
                 _log.debug("socket closed");
                 //_session.destroySession();
@@ -109,20 +90,11 @@ public class ConnectInactivityTest extends TestCase{
         
     }
     
-    private I2PSession createSession() {
-        try {
-            I2PClient client = I2PClientFactory.createClient();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-            Destination dest = client.createDestination(baos);
-            Properties p = new Properties();
-            p.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
-            p.setProperty(I2PClient.PROP_TCP_PORT, "10001");
-            I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), p);
-            sess.connect();
-            return sess;
-        } catch (Exception e) {
-            _log.error("error running", e);
-            throw new RuntimeException("b0rk b0rk b0rk");
-        }
+    @Override
+    protected Properties getProperties() {
+        Properties p = new Properties();
+        p.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
+        p.setProperty(I2PClient.PROP_TCP_PORT, "10001");
+        return p;
     }
 }
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTest.java
index 5d6237816cfedab2b6a4e9f99003c314b0c96927..c64585c3f6eacfe5fd9a160852fff512e92aa6b4 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTest.java
@@ -1,25 +1,19 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.util.Properties;
 
 import org.junit.Before;
 import org.junit.Test;
 
-import junit.framework.TestCase;
-
 import net.i2p.I2PAppContext;
 import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
-import net.i2p.data.Destination;
 import net.i2p.util.Log;
 
 /**
  *
  */
-public class ConnectTest  extends TestCase {
+public class ConnectTest extends StreamingTestBase {
     private Log _log;
     private I2PSession _server;
     
@@ -43,28 +37,23 @@ public class ConnectTest  extends TestCase {
         }
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private void runServer(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ServerRunner(ctx, session));
-        t.setName("server");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private class ServerRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    @Override
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
+    }
+
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return new ServerRunner(ctx,session);
+    }
+
+
+
+    private class ServerRunner extends RunnerBase {
         public ServerRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ServerRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -87,14 +76,9 @@ public class ConnectTest  extends TestCase {
         
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -117,17 +101,8 @@ public class ConnectTest  extends TestCase {
         
     }
     
-    private I2PSession createSession() {
-        try {
-            I2PClient client = I2PClientFactory.createClient();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-            Destination dest = client.createDestination(baos);
-            I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), System.getProperties());
-            sess.connect();
-            return sess;
-        } catch (Exception e) {
-            _log.error("error running", e);
-            throw new RuntimeException("b0rk b0rk b0rk");
-        }
+    @Override
+    protected Properties getProperties() {
+        return System.getProperties();
     }
 }
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTimeoutTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTimeoutTest.java
index 3e671a850007ced1bf99eefe4555297582212e17..732077cdfd9c67b52ef4ded3b8f48047e89e69df 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTimeoutTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/ConnectTimeoutTest.java
@@ -1,13 +1,10 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.Properties;
 
 import org.junit.Test;
 
-import junit.framework.TestCase;
-
 import net.i2p.I2PAppContext;
 import net.i2p.client.I2PClient;
 import net.i2p.client.I2PClientFactory;
@@ -19,10 +16,9 @@ import net.i2p.util.Log;
  * Try to connect to a new nonexistant peer and, of course,
  * timeout.
  */
-public class ConnectTimeoutTest  extends TestCase {
+public class ConnectTimeoutTest  extends StreamingTestBase {
     private Log _log;
     private I2PSession _client;
-    private I2PSession _server;
     private Destination _serverDest;
     
     @Test
@@ -37,26 +33,18 @@ public class ConnectTimeoutTest  extends TestCase {
         runClient(context, _client);
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(true);
-        t.start();
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
             try {
-                I2PSocketManager mgr = I2PSocketManagerFactory.createManager("localhost", 10001, getProps());
+                I2PSocketManager mgr = I2PSocketManagerFactory.createManager("localhost", 10001, getProperties());
                 _log.debug("manager created");
                 _log.debug("options: " + mgr.getDefaultOptions());
                 I2PSocket socket = mgr.connect(_serverDest);
@@ -73,18 +61,13 @@ public class ConnectTimeoutTest  extends TestCase {
         
     }
     
-    private I2PSession createSession() throws Exception {
-        I2PClient client = I2PClientFactory.createClient();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-        Destination dest = client.createDestination(baos);
-        Properties p = getProps();
-
-        I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), p);
-        sess.connect();
-        return sess;
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return null;
     }
     
-    private static Properties getProps() {
+    @Override
+    protected Properties getProperties() {
         Properties p = new Properties();
         p.setProperty(I2PSocketManagerFactory.PROP_MANAGER, I2PSocketManagerFull.class.getName());
         p.setProperty("tunnels.depthInbound", "0");
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoLargeTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoLargeTest.java
index f47112827c3a7bbaa5199b9336c3a639d56cb9a3..fde60b922409f847c59ba3f4848b759d5bd002a1 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoLargeTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoLargeTest.java
@@ -1,27 +1,21 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Properties;
 
 import org.junit.Test;
 
-import junit.framework.TestCase;
 
 import net.i2p.I2PAppContext;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
 import net.i2p.data.Base64;
-import net.i2p.data.Destination;
 import net.i2p.util.Log;
 
 /**
  *
  */
-public class EchoLargeTest extends TestCase {
+public class EchoLargeTest extends StreamingTestBase {
     private Log _log;
     private I2PSession _client;
     private I2PSession _server;
@@ -40,28 +34,28 @@ public class EchoLargeTest extends TestCase {
         runClient(context, _client);
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private void runServer(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ServerRunner(ctx, session));
-        t.setName("server");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private class ServerRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    @Override
+    protected Properties getProperties() {
+        return new Properties();
+    }
+
+    @Override
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
+    }
+
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return new ServerRunner(ctx,session);
+    }
+
+
+
+    private class ServerRunner extends RunnerBase {
         public ServerRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ServerRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -105,14 +99,9 @@ public class EchoLargeTest extends TestCase {
         
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -178,18 +167,4 @@ public class EchoLargeTest extends TestCase {
         }
         
     }
-    
-    private I2PSession createSession() {
-        try {
-            I2PClient client = I2PClientFactory.createClient();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-            Destination dest = client.createDestination(baos);
-            I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), new Properties());
-            sess.connect();
-            return sess;
-        } catch (Exception e) {
-            _log.error("error running", e);
-            throw new RuntimeException("b0rk b0rk b0rk");
-        }
-    }
 }
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoTest.java
index b110175b70fabcc8604cc73767b07010e62e3761..55ade2e2aa4606e4e4bd0a3c4daafd3ed79e2e7d 100644
--- a/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoTest.java
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/EchoTest.java
@@ -1,26 +1,20 @@
 package net.i2p.client.streaming;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Properties;
 
 import org.junit.Test;
 
-import junit.framework.TestCase;
 
 import net.i2p.I2PAppContext;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
-import net.i2p.data.Destination;
 import net.i2p.util.Log;
 
 /**
  *
  */
-public class EchoTest extends TestCase {
+public class EchoTest extends StreamingTestBase {
     private Log _log;
     private I2PSession _client;
     private I2PSession _server;
@@ -39,28 +33,28 @@ public class EchoTest extends TestCase {
         runClient(context, _client);
     }
     
-    private void runClient(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ClientRunner(ctx, session));
-        t.setName("client");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private void runServer(I2PAppContext ctx, I2PSession session) {
-        Thread t = new Thread(new ServerRunner(ctx, session));
-        t.setName("server");
-        t.setDaemon(true);
-        t.start();
-    }
     
-    private class ServerRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    @Override
+    protected Properties getProperties() {
+        return new Properties();
+    }
+
+    @Override
+    protected Runnable getClient(I2PAppContext ctx, I2PSession session) {
+        return new ClientRunner(ctx,session);
+    }
+
+    @Override
+    protected Runnable getServer(I2PAppContext ctx, I2PSession session) {
+        return new ServerRunner(ctx,session);
+    }
+
+
+
+    private class ServerRunner extends RunnerBase {
         public ServerRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ServerRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -104,14 +98,9 @@ public class EchoTest extends TestCase {
         
     }
     
-    private class ClientRunner implements Runnable {
-        private I2PAppContext _context;
-        private I2PSession _session;
-        private Log _log;
+    private class ClientRunner extends RunnerBase {
         public ClientRunner(I2PAppContext ctx, I2PSession session) {
-            _context = ctx;
-            _session = session;
-            _log = ctx.logManager().getLog(ClientRunner.class);
+            super(ctx,session);
         }
         
         public void run() {
@@ -157,18 +146,4 @@ public class EchoTest extends TestCase {
         }
         
     }
-    
-    private I2PSession createSession() {
-        try {
-            I2PClient client = I2PClientFactory.createClient();
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
-            Destination dest = client.createDestination(baos);
-            I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), new Properties());
-            sess.connect();
-            return sess;
-        } catch (Exception e) {
-            _log.error("error running", e);
-            throw new RuntimeException("b0rk b0rk b0rk");
-        }
-    }
 }
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/StreamSinkTest.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/StreamSinkTestStandalone.java
similarity index 100%
rename from apps/streaming/java/test/junit/net/i2p/client/streaming/StreamSinkTest.java
rename to apps/streaming/java/test/junit/net/i2p/client/streaming/StreamSinkTestStandalone.java
diff --git a/apps/streaming/java/test/junit/net/i2p/client/streaming/StreamingTestBase.java b/apps/streaming/java/test/junit/net/i2p/client/streaming/StreamingTestBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6069f89f4a295e7b43b3ad1484248d23d6451e0
--- /dev/null
+++ b/apps/streaming/java/test/junit/net/i2p/client/streaming/StreamingTestBase.java
@@ -0,0 +1,60 @@
+package net.i2p.client.streaming;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Properties;
+
+import net.i2p.I2PAppContext;
+import net.i2p.client.I2PClient;
+import net.i2p.client.I2PClientFactory;
+import net.i2p.client.I2PSession;
+import net.i2p.util.Log;
+
+import junit.framework.TestCase;
+
+abstract class StreamingTestBase extends TestCase {
+
+    protected abstract Properties getProperties();
+    
+    protected I2PSession createSession() throws Exception {
+        I2PClient client = I2PClientFactory.createClient();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
+        client.createDestination(baos);
+        Properties p = getProperties();
+
+        I2PSession sess = client.createSession(new ByteArrayInputStream(baos.toByteArray()), p);
+        sess.connect();
+        return sess;
+    }
+    
+    protected abstract Runnable getClient(I2PAppContext ctx, I2PSession session);
+    
+    protected final void runClient(I2PAppContext ctx, I2PSession session) {
+        Thread t = new Thread(getClient(ctx,session));
+        t.setName("client");
+        t.setDaemon(true);
+        t.start();
+    }
+    
+    protected abstract class RunnerBase implements Runnable {
+        
+        protected final I2PAppContext _context;
+        protected final I2PSession _session;
+        protected final Log _log;
+        
+        protected RunnerBase(I2PAppContext ctx, I2PSession session) {
+            _context = ctx;
+            _session = session;
+            _log = ctx.logManager().getLog(this.getClass());
+        }
+    }
+    
+    protected abstract Runnable getServer(I2PAppContext ctx, I2PSession session);
+   
+    protected final void runServer(I2PAppContext ctx, I2PSession session) {
+        Thread t = new Thread(getServer(ctx,session));
+        t.setName("servert");
+        t.setDaemon(false);
+        t.start();
+    }
+}