diff --git a/components/net/sf/briar/plugins/PluginManagerImpl.java b/components/net/sf/briar/plugins/PluginManagerImpl.java
index 652882fd4592c81bb5acb3c0d5f7e039b3c404e2..0b1b5532955eb9c5dfc0b4baf005337aab7fbe94 100644
--- a/components/net/sf/briar/plugins/PluginManagerImpl.java
+++ b/components/net/sf/briar/plugins/PluginManagerImpl.java
@@ -48,7 +48,8 @@ class PluginManagerImpl implements PluginManager {
 
 	private static final String[] DUPLEX_PLUGIN_FACTORIES = new String[] {
 		"net.sf.briar.plugins.bluetooth.BluetoothPluginFactory",
-		"net.sf.briar.plugins.socket.SimpleSocketPluginFactory"
+		"net.sf.briar.plugins.socket.SimpleSocketPluginFactory",
+		"net.sf.briar.plugins.tor.TorPluginFactory"
 	};
 
 	private final ExecutorService pluginExecutor;
diff --git a/components/net/sf/briar/plugins/tor/TorPlugin.java b/components/net/sf/briar/plugins/tor/TorPlugin.java
index 7000fc032f81b25dcf9dea7511746f578c1a306a..fb9fbee5f55ec3cec0c597c52b0862fc6f30cc1d 100644
--- a/components/net/sf/briar/plugins/tor/TorPlugin.java
+++ b/components/net/sf/briar/plugins/tor/TorPlugin.java
@@ -45,7 +45,8 @@ class TorPlugin implements DuplexPlugin {
 	private final long pollingInterval;
 
 	private boolean running = false; // Locking: this
-	private TorNetServerSocket socket = null; // Locking: this
+	private NetServerSocket socket = null; // Locking: this
+	private NetLayer netLayer = null; // Locking: this
 
 	TorPlugin(@PluginExecutor Executor pluginExecutor,
 			DuplexPluginCallback callback, long pollingInterval) {
@@ -90,13 +91,12 @@ class TorPlugin implements DuplexPlugin {
 			new TorHiddenServicePortPrivateNetAddress(addr, 80);
 		// Connect to Tor
 		NetFactory netFactory = NetFactory.getInstance();
-		NetLayer netLayer = netFactory.getNetLayerById(NetLayerIDs.TOR);
-		netLayer.waitUntilReady();
+		NetLayer nl = netFactory.getNetLayerById(NetLayerIDs.TOR);
+		nl.waitUntilReady();
 		// Publish the hidden service
 		TorNetServerSocket ss;
 		try {
-			ss = (TorNetServerSocket) netLayer.createNetServerSocket(null,
-					addrPort);
+			ss = (TorNetServerSocket) nl.createNetServerSocket(null, addrPort);
 		} catch(IOException e) {
 			if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString());
 			return;
@@ -107,6 +107,7 @@ class TorPlugin implements DuplexPlugin {
 				return;
 			}
 			socket = ss;
+			netLayer = nl;
 		}
 		String onion = addr.getPublicOnionHostname();
 		if(LOG.isLoggable(Level.INFO)) LOG.info("Listening on " + onion);
@@ -161,6 +162,10 @@ class TorPlugin implements DuplexPlugin {
 			tryToClose(socket);
 			socket = null;
 		}
+		if(netLayer != null) {
+			netLayer.clear();
+			netLayer = null;
+		}
 	}
 
 	public boolean shouldPoll() {
@@ -203,11 +208,9 @@ class TorPlugin implements DuplexPlugin {
 		TransportProperties p = callback.getRemoteProperties().get(c);
 		if(p == null) return null;
 		String onion = p.get("onion");
-		String portString = p.get("port");
-		if(onion == null || portString == null) return null;
+		if(onion == null) return null;
 		try {
-			int port = Integer.parseInt(portString);
-			TcpipNetAddress addr = new TcpipNetAddress(onion, port);
+			TcpipNetAddress addr = new TcpipNetAddress(onion, 80);
 			NetFactory netFactory = NetFactory.getInstance();
 			NetLayer netLayer = netFactory.getNetLayerById(NetLayerIDs.TOR);
 			netLayer.waitUntilReady();
diff --git a/components/net/sf/briar/plugins/tor/TorPluginFactory.java b/components/net/sf/briar/plugins/tor/TorPluginFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7caf6aa195a70df7ec528e3da811c7d19957a2a
--- /dev/null
+++ b/components/net/sf/briar/plugins/tor/TorPluginFactory.java
@@ -0,0 +1,18 @@
+package net.sf.briar.plugins.tor;
+
+import java.util.concurrent.Executor;
+
+import net.sf.briar.api.plugins.PluginExecutor;
+import net.sf.briar.api.plugins.duplex.DuplexPlugin;
+import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
+import net.sf.briar.api.plugins.duplex.DuplexPluginFactory;
+
+public class TorPluginFactory implements DuplexPluginFactory {
+
+	private static final long POLLING_INTERVAL = 15L * 60L * 1000L; // 15 mins
+
+	public DuplexPlugin createPlugin(@PluginExecutor Executor pluginExecutor,
+			DuplexPluginCallback callback) {
+		return new TorPlugin(pluginExecutor, callback, POLLING_INTERVAL);
+	}
+}
diff --git a/test/build.xml b/test/build.xml
index c28327848121a228f899cc8ce9d502f77410e3e6..f905a6999ff96c149476eed68678fe9010b75ec3 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -22,7 +22,6 @@
 			<test name='net.sf.briar.db.BasicH2Test'/>
 			<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
 			<test name='net.sf.briar.db.DatabaseComponentImplTest'/>
-			<test name='net.sf.briar.db.H2DatabaseTest'/>
 			<test name='net.sf.briar.i18n.FontManagerTest'/>
 			<test name='net.sf.briar.i18n.I18nTest'/>
 			<test name='net.sf.briar.invitation.InvitationWorkerTest'/>
@@ -64,4 +63,22 @@
 			<test name='net.sf.briar.util.ZipUtilsTest'/>
 		</junit>
 	</target>
+	<target name='test-slow' depends='depend'>
+		<junit printsummary='on' fork='yes' forkmode='once'>
+			<assertions>
+				<enable/>
+			</assertions>
+			<classpath>
+				<fileset refid='bundled-jars'/>
+				<fileset refid='test-jars'/>
+				<path refid='api-classes'/>
+				<path refid='component-classes'/>
+				<path refid='test-classes'/>
+				<path refid='util-classes'/>
+			</classpath>
+			<jvmarg value='-Djava.library.path=../lib'/>
+			<test name='net.sf.briar.db.H2DatabaseTest'/>
+			<test name='net.sf.briar.plugins.tor.TorPluginTest'/>
+		</junit>
+	</target>
 </project>
diff --git a/test/net/sf/briar/plugins/PluginManagerImplTest.java b/test/net/sf/briar/plugins/PluginManagerImplTest.java
index 861eb6593bbffb66adad6c35ccf125ad0fc6061d..feda37c3db7f85a682ca75babf7c4adb15997902 100644
--- a/test/net/sf/briar/plugins/PluginManagerImplTest.java
+++ b/test/net/sf/briar/plugins/PluginManagerImplTest.java
@@ -6,6 +6,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.api.TransportConfig;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.protocol.TransportId;
@@ -35,6 +36,8 @@ public class PluginManagerImplTest extends BriarTestCase {
 			will(returnValue(null));
 			allowing(db).addTransport(with(any(TransportId.class)));
 			will(returnValue(new TransportIndex(index.getAndIncrement())));
+			allowing(db).getConfig(with(any(TransportId.class)));
+			will(returnValue(new TransportConfig()));
 			allowing(db).getLocalProperties(with(any(TransportId.class)));
 			will(returnValue(new TransportProperties()));
 			allowing(db).getRemoteProperties(with(any(TransportId.class)));
@@ -46,7 +49,7 @@ public class PluginManagerImplTest extends BriarTestCase {
 		ExecutorService executor = Executors.newCachedThreadPool();
 		PluginManagerImpl p = new PluginManagerImpl(executor, db, poller,
 				dispatcher, uiCallback);
-		// We expect either 2 or 3 plugins to be started, depending on whether
+		// We expect either 3 or 4 plugins to be started, depending on whether
 		// the test machine has a Bluetooth device
 		int started = p.start();
 		int stopped = p.stop();
diff --git a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
index 8b7c473e63933b85e2db3d50e345d8e86a9e8faf..cf4cb3fca9b03b8f342f09108584113096d66e17 100644
--- a/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
+++ b/test/net/sf/briar/plugins/socket/SimpleSocketPluginTest.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -104,7 +104,7 @@ public class SimpleSocketPluginTest extends BriarTestCase {
 	private static class Callback implements DuplexPluginCallback {
 
 		private final Map<ContactId, TransportProperties> remote =
-			new HashMap<ContactId, TransportProperties>();
+			new Hashtable<ContactId, TransportProperties>();
 		private final CountDownLatch latch = new CountDownLatch(1);
 
 		private TransportConfig config = new TransportConfig();
diff --git a/test/net/sf/briar/plugins/tor/TorPluginTest.java b/test/net/sf/briar/plugins/tor/TorPluginTest.java
index aef1f7f6bd8c61b6d60839b60eb792307e7b37b1..9f25e82f697ec9d4a03c4f1395367c46cf0c7926 100644
--- a/test/net/sf/briar/plugins/tor/TorPluginTest.java
+++ b/test/net/sf/briar/plugins/tor/TorPluginTest.java
@@ -1,6 +1,6 @@
 package net.sf.briar.plugins.tor;
 
-import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -18,28 +18,48 @@ import org.junit.Test;
 
 public class TorPluginTest extends BriarTestCase {
 
+	private final ContactId contactId = new ContactId(1);
+
 	@Test
-	public void testCreateHiddenService() throws Exception {
-		Callback callback = new Callback();
+	public void testHiddenService() throws Exception {
 		Executor e = Executors.newCachedThreadPool();
-		TorPlugin plugin = new TorPlugin(e, callback, 0L);
-		plugin.start();
-		// The plugin should have created a hidden service
-		callback.latch.await(5, TimeUnit.MINUTES);
-		String onion = callback.local.get("onion");
+		// Create a plugin instance for the server
+		Callback serverCallback = new Callback();
+		TorPlugin serverPlugin = new TorPlugin(e, serverCallback, 0L);
+		serverPlugin.start();
+		// The plugin should create a hidden service... eventually
+		serverCallback.latch.await(5, TimeUnit.MINUTES);
+		String onion = serverCallback.local.get("onion");
 		assertNotNull(onion);
 		assertTrue(onion.endsWith(".onion"));
+		// Create another plugin instance for the client
+		Callback clientCallback = new Callback();
+		TransportProperties p = new TransportProperties();
+		p.put("onion", onion);
+		clientCallback.remote.put(contactId, p);
+		TorPlugin clientPlugin = new TorPlugin(e, clientCallback, 0L);
+		clientPlugin.start();
+		// Connect to the server's hidden service
+		DuplexTransportConnection c = clientPlugin.createConnection(contactId);
+		assertNotNull(c);
+		c.dispose(false, false);
+		assertEquals(1, serverCallback.incomingConnections);
+		// Stop the plugins
+		serverPlugin.stop();
+		clientPlugin.stop();
 	}
 
 	private static class Callback implements DuplexPluginCallback {
 
 		private final Map<ContactId, TransportProperties> remote =
-			new HashMap<ContactId, TransportProperties>();
+			new Hashtable<ContactId, TransportProperties>();
 		private final CountDownLatch latch = new CountDownLatch(1);
 
 		private TransportConfig config = new TransportConfig();
 		private TransportProperties local = new TransportProperties();
 
+		private volatile int incomingConnections = 0;
+
 		public TransportConfig getConfig() {
 			return config;
 		}
@@ -71,7 +91,9 @@ public class TorPluginTest extends BriarTestCase {
 
 		public void showMessage(String... message) {}
 
-		public void incomingConnectionCreated(DuplexTransportConnection d) {}
+		public void incomingConnectionCreated(DuplexTransportConnection d) {
+			incomingConnections++;
+		}
 
 		public void outgoingConnectionCreated(ContactId c,
 				DuplexTransportConnection d) {}