diff --git a/api/net/sf/briar/api/serial/ObjectReader.java b/api/net/sf/briar/api/serial/ObjectReader.java
index 012f5f800aae92025570e16520fc8b38e81d5a16..2f345e9ce26c24e8846fe36194d986e3969d43c0 100644
--- a/api/net/sf/briar/api/serial/ObjectReader.java
+++ b/api/net/sf/briar/api/serial/ObjectReader.java
@@ -1,8 +1,9 @@
 package net.sf.briar.api.serial;
 
 import java.io.IOException;
+import java.security.GeneralSecurityException;
 
 public interface ObjectReader<T> {
 
-	T readObject(Reader r) throws IOException;
+	T readObject(Reader r) throws IOException, GeneralSecurityException;
 }
diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java
index 731b860ce029136bff1fa11ecceb20e124091182..3bf5b6a278f8fad48fc45a1daee8968208371c72 100644
--- a/api/net/sf/briar/api/serial/Reader.java
+++ b/api/net/sf/briar/api/serial/Reader.java
@@ -1,6 +1,7 @@
 package net.sf.briar.api.serial;
 
 import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.util.List;
 import java.util.Map;
 
@@ -42,16 +43,18 @@ public interface Reader {
 	byte[] readRaw() throws IOException;
 
 	boolean hasList() throws IOException;
-	List<Object> readList() throws IOException;
-	<E> List<E> readList(Class<E> e) throws IOException;
+	List<Object> readList() throws IOException, GeneralSecurityException;
+	<E> List<E> readList(Class<E> e) throws IOException,
+	GeneralSecurityException;
 	boolean hasListStart() throws IOException;
 	void readListStart() throws IOException;
 	boolean hasListEnd() throws IOException;
 	void readListEnd() throws IOException;
 
 	boolean hasMap() throws IOException;
-	Map<Object, Object> readMap() throws IOException;
-	<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException;
+	Map<Object, Object> readMap() throws IOException, GeneralSecurityException;
+	<K, V> Map<K, V> readMap(Class<K> k, Class<V> v) throws IOException,
+	GeneralSecurityException;
 	boolean hasMapStart() throws IOException;
 	void readMapStart() throws IOException;
 	boolean hasMapEnd() throws IOException;
@@ -63,5 +66,6 @@ public interface Reader {
 	boolean hasUserDefinedTag() throws IOException;
 	int readUserDefinedTag() throws IOException;
 	void readUserDefinedTag(int tag) throws IOException;
-	<T> T readUserDefinedObject(int tag) throws IOException;
+	<T> T readUserDefinedObject(int tag) throws IOException,
+	GeneralSecurityException;
 }
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..336312888b16fbd527d72c71c6fa56b85e64d5f3
--- /dev/null
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -0,0 +1,62 @@
+package net.sf.briar.protocol;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.List;
+
+import net.sf.briar.api.protocol.Batch;
+import net.sf.briar.api.protocol.BatchId;
+import net.sf.briar.api.protocol.Message;
+import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.serial.ObjectReader;
+import net.sf.briar.api.serial.Reader;
+
+public class BatchReader implements ObjectReader<Batch> {
+
+	private final PublicKey publicKey;
+	private final Signature signature;
+	private final MessageDigest messageDigest;
+	private final ObjectReader<Message> messageReader;
+	private final BatchFactory batchFactory;
+
+	BatchReader(PublicKey publicKey, Signature signature,
+			MessageDigest messageDigest, ObjectReader<Message> messageReader,
+			BatchFactory batchFactory) {
+		this.publicKey = publicKey;
+		this.signature = signature;
+		this.messageDigest = messageDigest;
+		this.messageReader = messageReader;
+		this.batchFactory = batchFactory;
+	}
+
+	public Batch readObject(Reader reader) throws IOException,
+	GeneralSecurityException {
+		// Initialise the input stream
+		CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE);
+		DigestingConsumer digesting = new DigestingConsumer(messageDigest);
+		messageDigest.reset();
+		SigningConsumer signing = new SigningConsumer(signature);
+		signature.initVerify(publicKey);
+		// Read the signed data
+		reader.addConsumer(counting);
+		reader.addConsumer(digesting);
+		reader.addConsumer(signing);
+		reader.addObjectReader(Tags.MESSAGE, messageReader);
+		List<Message> messages = reader.readList(Message.class);
+		reader.removeObjectReader(Tags.MESSAGE);
+		reader.removeConsumer(signing);
+		// Read and verify the signature
+		reader.readUserDefinedTag(Tags.SIGNATURE);
+		byte[] sig = reader.readRaw();
+		reader.removeConsumer(digesting);
+		reader.removeConsumer(counting);
+		if(!signature.verify(sig)) throw new SignatureException();
+		// Build and return the batch
+		BatchId id = new BatchId(messageDigest.digest());
+		return batchFactory.createBatch(id, messages);
+	}
+}
diff --git a/components/net/sf/briar/protocol/BundleReaderImpl.java b/components/net/sf/briar/protocol/BundleReaderImpl.java
index c5ff59d09110163585b0a4c3463124ed43c3a7c3..42c47cc6015900d974d020d217dfc0f92fd30ab8 100644
--- a/components/net/sf/briar/protocol/BundleReaderImpl.java
+++ b/components/net/sf/briar/protocol/BundleReaderImpl.java
@@ -2,20 +2,10 @@ package net.sf.briar.protocol;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
 
 import net.sf.briar.api.protocol.Batch;
-import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.BundleReader;
-import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Header;
-import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.Tags;
 import net.sf.briar.api.serial.FormatException;
 import net.sf.briar.api.serial.ObjectReader;
@@ -26,102 +16,44 @@ class BundleReaderImpl implements BundleReader {
 	private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
 
 	private final Reader reader;
-	private final PublicKey publicKey;
-	private final Signature signature;
-	private final MessageDigest messageDigest;
-	private final ObjectReader<Message> messageReader;
-	private final HeaderFactory headerFactory;
-	private final BatchFactory batchFactory;
+	private final ObjectReader<Header> headerReader;
+	private final ObjectReader<Batch> batchReader;
 	private State state = State.START;
 
-	BundleReaderImpl(Reader reader, PublicKey publicKey, Signature signature,
-			MessageDigest messageDigest, ObjectReader<Message> messageReader,
-			HeaderFactory headerFactory, BatchFactory batchFactory) {
+	BundleReaderImpl(Reader reader, ObjectReader<Header> headerReader,
+			ObjectReader<Batch> batchReader) {
 		this.reader = reader;
-		this.publicKey = publicKey;
-		this.signature = signature;
-		this.messageDigest = messageDigest;
-		this.messageReader = messageReader;
-		this.headerFactory = headerFactory;
-		this.batchFactory = batchFactory;
+		this.headerReader = headerReader;
+		this.batchReader = batchReader;
 	}
 
 	public Header getHeader() throws IOException, GeneralSecurityException {
 		if(state != State.START) throw new IllegalStateException();
-		state = State.FIRST_BATCH;
-		// Initialise the input stream
-		CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE);
-		SigningConsumer signing = new SigningConsumer(signature);
-		signature.initVerify(publicKey);
-		// Read the initial tag
+		reader.addObjectReader(Tags.HEADER, headerReader);
 		reader.readUserDefinedTag(Tags.HEADER);
-		// Read the signed data
-		reader.addConsumer(counting);
-		reader.addConsumer(signing);
-		reader.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
-		reader.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
-		// Acks
-		Collection<BatchId> acks = reader.readList(BatchId.class);
-		// Subs
-		Collection<GroupId> subs = reader.readList(GroupId.class);
-		// Transports
-		reader.readUserDefinedTag(Tags.TRANSPORTS);
-		Map<String, String> transports =
-			reader.readMap(String.class, String.class);
-		// Timestamp
-		reader.readUserDefinedTag(Tags.TIMESTAMP);
-		long timestamp = reader.readInt64();
-		if(timestamp < 0L) throw new FormatException();
-		reader.removeObjectReader(Tags.GROUP_ID);
-		reader.removeObjectReader(Tags.BATCH_ID);
-		reader.removeConsumer(signing);
-		// Read and verify the signature
-		reader.readUserDefinedTag(Tags.SIGNATURE);
-		byte[] sig = reader.readRaw();
-		reader.removeConsumer(counting);
-		if(!signature.verify(sig)) throw new SignatureException();
-		// Build and return the header
-		return headerFactory.createHeader(acks, subs, transports, timestamp);
+		Header h = reader.readUserDefinedObject(Tags.HEADER);
+		reader.removeObjectReader(Tags.HEADER);
+		state = State.FIRST_BATCH;
+		return h;
 	}
 
 	public Batch getNextBatch() throws IOException, GeneralSecurityException {
 		if(state == State.FIRST_BATCH) {
 			reader.readListStart();
+			reader.addObjectReader(Tags.BATCH, batchReader);
 			state = State.MORE_BATCHES;
 		}
 		if(state != State.MORE_BATCHES) throw new IllegalStateException();
 		if(reader.hasListEnd()) {
+			reader.removeObjectReader(Tags.BATCH);
 			reader.readListEnd();
 			// That should be all
 			if(!reader.eof()) throw new FormatException();
 			state = State.END;
 			return null;
 		}
-		// Initialise the input stream
-		CountingConsumer counting = new CountingConsumer(Batch.MAX_SIZE);
-		DigestingConsumer digesting = new DigestingConsumer(messageDigest);
-		messageDigest.reset();
-		SigningConsumer signing = new SigningConsumer(signature);
-		signature.initVerify(publicKey);
-		// Read the initial tag
 		reader.readUserDefinedTag(Tags.BATCH);
-		// Read the signed data
-		reader.addConsumer(counting);
-		reader.addConsumer(digesting);
-		reader.addConsumer(signing);
-		reader.addObjectReader(Tags.MESSAGE, messageReader);
-		List<Message> messages = reader.readList(Message.class);
-		reader.removeObjectReader(Tags.MESSAGE);
-		reader.removeConsumer(signing);
-		// Read and verify the signature
-		reader.readUserDefinedTag(Tags.SIGNATURE);
-		byte[] sig = reader.readRaw();
-		reader.removeConsumer(digesting);
-		reader.removeConsumer(counting);
-		if(!signature.verify(sig)) throw new SignatureException();
-		// Build and return the batch
-		BatchId id = new BatchId(messageDigest.digest());
-		return batchFactory.createBatch(id, messages);
+		return reader.readUserDefinedObject(Tags.BATCH);
 	}
 
 	public void finish() throws IOException {
diff --git a/components/net/sf/briar/protocol/HeaderReader.java b/components/net/sf/briar/protocol/HeaderReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..24f5f02d8d0d340cc18142185c13027ffabd0004
--- /dev/null
+++ b/components/net/sf/briar/protocol/HeaderReader.java
@@ -0,0 +1,66 @@
+package net.sf.briar.protocol;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Collection;
+import java.util.Map;
+
+import net.sf.briar.api.protocol.BatchId;
+import net.sf.briar.api.protocol.GroupId;
+import net.sf.briar.api.protocol.Header;
+import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.serial.FormatException;
+import net.sf.briar.api.serial.ObjectReader;
+import net.sf.briar.api.serial.Reader;
+
+class HeaderReader implements ObjectReader<Header> {
+
+	private final PublicKey publicKey;
+	private final Signature signature;
+	private final HeaderFactory headerFactory;
+
+	HeaderReader(PublicKey publicKey, Signature signature,
+			HeaderFactory headerFactory) {
+		this.publicKey = publicKey;
+		this.signature = signature;
+		this.headerFactory = headerFactory;
+	}
+
+	public Header readObject(Reader reader) throws IOException,
+	GeneralSecurityException {
+		// Initialise the input stream
+		CountingConsumer counting = new CountingConsumer(Header.MAX_SIZE);
+		SigningConsumer signing = new SigningConsumer(signature);
+		signature.initVerify(publicKey);
+		// Read the signed data
+		reader.addConsumer(counting);
+		reader.addConsumer(signing);
+		// Acks
+		reader.addObjectReader(Tags.BATCH_ID, new BatchIdReader());
+		Collection<BatchId> acks = reader.readList(BatchId.class);
+		reader.removeObjectReader(Tags.BATCH_ID);
+		// Subs
+		reader.addObjectReader(Tags.GROUP_ID, new GroupIdReader());
+		Collection<GroupId> subs = reader.readList(GroupId.class);
+		reader.removeObjectReader(Tags.GROUP_ID);
+		// Transports
+		reader.readUserDefinedTag(Tags.TRANSPORTS);
+		Map<String, String> transports =
+			reader.readMap(String.class, String.class);
+		// Timestamp
+		reader.readUserDefinedTag(Tags.TIMESTAMP);
+		long timestamp = reader.readInt64();
+		if(timestamp < 0L) throw new FormatException();
+		reader.removeConsumer(signing);
+		// Read and verify the signature
+		reader.readUserDefinedTag(Tags.SIGNATURE);
+		byte[] sig = reader.readRaw();
+		reader.removeConsumer(counting);
+		if(!signature.verify(sig)) throw new SignatureException();
+		// Build and return the header
+		return headerFactory.createHeader(acks, subs, transports, timestamp);
+	}
+}
diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java
index 11d0c2ddafd62d4577f2074e63030c944f419bb3..dfc2b45243207c394c06b890abf287cfab0ca2d6 100644
--- a/components/net/sf/briar/serial/ReaderImpl.java
+++ b/components/net/sf/briar/serial/ReaderImpl.java
@@ -2,6 +2,7 @@ package net.sf.briar.serial;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -315,11 +316,13 @@ class ReaderImpl implements Reader {
 		|| (next & Tag.SHORT_MASK) == Tag.SHORT_LIST;
 	}
 
-	public List<Object> readList() throws IOException {
+	public List<Object> readList() throws IOException,
+	GeneralSecurityException {
 		return readList(Object.class);
 	}
 
-	public <E> List<E> readList(Class<E> e) throws IOException {
+	public <E> List<E> readList(Class<E> e) throws IOException,
+	GeneralSecurityException {
 		if(!hasList()) throw new FormatException();
 		if(next == Tag.LIST) {
 			readNext(false);
@@ -337,7 +340,8 @@ class ReaderImpl implements Reader {
 		}
 	}
 
-	private <E> List<E> readList(Class<E> e, int length) throws IOException {
+	private <E> List<E> readList(Class<E> e, int length) throws IOException,
+	GeneralSecurityException {
 		assert length >= 0;
 		List<E> list = new ArrayList<E>();
 		for(int i = 0; i < length; i++) list.add(readObject(e));
@@ -356,7 +360,7 @@ class ReaderImpl implements Reader {
 		readNext(true);
 	}
 
-	private Object readObject() throws IOException {
+	private Object readObject() throws IOException, GeneralSecurityException {
 		if(!started) throw new IllegalStateException();
 		if(hasUserDefinedTag()) {
 			ObjectReader<?> o = objectReaders.get(readUserDefinedTag());
@@ -383,7 +387,8 @@ class ReaderImpl implements Reader {
 	}
 
 	@SuppressWarnings("unchecked")
-	private <T> T readObject(Class<T> t) throws IOException {
+	private <T> T readObject(Class<T> t) throws IOException,
+	GeneralSecurityException {
 		try {
 			return (T) readObject();
 		} catch(ClassCastException e) {
@@ -417,11 +422,13 @@ class ReaderImpl implements Reader {
 		|| (next & Tag.SHORT_MASK) == Tag.SHORT_MAP;
 	}
 
-	public Map<Object, Object> readMap() throws IOException {
+	public Map<Object, Object> readMap() throws IOException,
+	GeneralSecurityException {
 		return readMap(Object.class, Object.class);
 	}
 
-	public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v)	throws IOException {
+	public <K, V> Map<K, V> readMap(Class<K> k, Class<V> v)	throws IOException,
+	GeneralSecurityException {
 		if(!hasMap()) throw new FormatException();
 		if(next == Tag.MAP) {
 			readNext(false);
@@ -440,7 +447,7 @@ class ReaderImpl implements Reader {
 	}
 
 	private <K, V> Map<K, V> readMap(Class<K> k, Class<V> v, int size)
-	throws IOException {
+	throws IOException, GeneralSecurityException {
 		assert size >= 0;
 		Map<K, V> m = new HashMap<K, V>();
 		for(int i = 0; i < size; i++) m.put(readObject(k), readObject(v));
@@ -500,7 +507,8 @@ class ReaderImpl implements Reader {
 		if(readUserDefinedTag() != tag) throw new FormatException();
 	}
 
-	public <T> T readUserDefinedObject(int tag) throws IOException {
+	public <T> T readUserDefinedObject(int tag) throws IOException,
+	GeneralSecurityException {
 		ObjectReader<?> o = objectReaders.get(tag);
 		if(o == null) throw new FormatException();
 		try {
diff --git a/test/net/sf/briar/protocol/BundleReadWriteTest.java b/test/net/sf/briar/protocol/BundleReadWriteTest.java
index b44092a8173503e5289016d80dfdd395285ffc68..c20e261abd3a5145a13487f0e47f57f9ba9e6b73 100644
--- a/test/net/sf/briar/protocol/BundleReadWriteTest.java
+++ b/test/net/sf/briar/protocol/BundleReadWriteTest.java
@@ -124,13 +124,15 @@ public class BundleReadWriteTest extends TestCase {
 
 		testWriteBundle();
 
-		MessageReader messageReader =
-			new MessageReader(keyParser, sig1, dig1);
 		FileInputStream in = new FileInputStream(bundle);
 		Reader reader = rf.createReader(in);
-		BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig,
-				dig, messageReader, new HeaderFactoryImpl(),
-				new BatchFactoryImpl());
+		MessageReader messageReader = new MessageReader(keyParser, sig1, dig1);
+		HeaderReader headerReader = new HeaderReader(keyPair.getPublic(), sig,
+				new HeaderFactoryImpl());
+		BatchReader batchReader = new BatchReader(keyPair.getPublic(), sig, dig,
+				messageReader, new BatchFactoryImpl());
+		BundleReader r = new BundleReaderImpl(reader, headerReader,
+				batchReader);
 
 		Header h = r.getHeader();
 		assertEquals(acks, h.getAcks());
@@ -164,13 +166,15 @@ public class BundleReadWriteTest extends TestCase {
 		f.writeByte(b + 1);
 		f.close();
 
-		MessageReader messageReader =
-			new MessageReader(keyParser, sig1, dig1);
 		FileInputStream in = new FileInputStream(bundle);
 		Reader reader = rf.createReader(in);
-		BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig,
-				dig, messageReader, new HeaderFactoryImpl(),
-				new BatchFactoryImpl());
+		MessageReader messageReader = new MessageReader(keyParser, sig1, dig1);
+		HeaderReader headerReader = new HeaderReader(keyPair.getPublic(), sig,
+				new HeaderFactoryImpl());
+		BatchReader batchReader = new BatchReader(keyPair.getPublic(), sig, dig,
+				messageReader, new BatchFactoryImpl());
+		BundleReader r = new BundleReaderImpl(reader, headerReader,
+				batchReader);
 
 		Header h = r.getHeader();
 		assertEquals(acks, h.getAcks());
diff --git a/test/net/sf/briar/protocol/BundleReaderImplTest.java b/test/net/sf/briar/protocol/BundleReaderImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..228a1a5ec026e094a5eb909dd6b6bd9650571555
--- /dev/null
+++ b/test/net/sf/briar/protocol/BundleReaderImplTest.java
@@ -0,0 +1,5 @@
+package net.sf.briar.protocol;
+
+public class BundleReaderImplTest {
+
+}
diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java
index f4e63071c8fee922b422144f3321e353bae277ad..83e836696323dc6221aac968f074430912a71cb9 100644
--- a/test/net/sf/briar/serial/ReaderImplTest.java
+++ b/test/net/sf/briar/serial/ReaderImplTest.java
@@ -22,7 +22,7 @@ public class ReaderImplTest extends TestCase {
 	private ReaderImpl r = null;
 
 	@Test
-	public void testReadBoolean() throws IOException {
+	public void testReadBoolean() throws Exception {
 		setContents("FFFE");
 		assertFalse(r.readBoolean());
 		assertTrue(r.readBoolean());
@@ -30,7 +30,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadInt8() throws IOException {
+	public void testReadInt8() throws Exception {
 		setContents("FD00" + "FDFF" + "FD7F" + "FD80");
 		assertEquals((byte) 0, r.readInt8());
 		assertEquals((byte) -1, r.readInt8());
@@ -40,7 +40,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadInt16() throws IOException {
+	public void testReadInt16() throws Exception {
 		setContents("FC0000" + "FCFFFF" + "FC7FFF" + "FC8000");
 		assertEquals((short) 0, r.readInt16());
 		assertEquals((short) -1, r.readInt16());
@@ -50,7 +50,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadInt32() throws IOException {
+	public void testReadInt32() throws Exception {
 		setContents("FB00000000" + "FBFFFFFFFF" + "FB7FFFFFFF" + "FB80000000");
 		assertEquals(0, r.readInt32());
 		assertEquals(-1, r.readInt32());
@@ -60,7 +60,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadInt64() throws IOException {
+	public void testReadInt64() throws Exception {
 		setContents("FA0000000000000000" + "FAFFFFFFFFFFFFFFFF" +
 				"FA7FFFFFFFFFFFFFFF" + "FA8000000000000000");
 		assertEquals(0L, r.readInt64());
@@ -71,7 +71,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadIntAny() throws IOException {
+	public void testReadIntAny() throws Exception {
 		setContents("00" + "7F" + "FD80" + "FDFF" + "FC0080" + "FC7FFF" +
 				"FB00008000" + "FB7FFFFFFF" + "FA0000000080000000");
 		assertEquals(0L, r.readIntAny());
@@ -87,7 +87,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadFloat32() throws IOException {
+	public void testReadFloat32() throws Exception {
 		// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
 		// http://steve.hollasch.net/cgindex/coding/ieeefloat.html
 		setContents("F900000000" + "F93F800000" + "F940000000" + "F9BF800000" +
@@ -104,7 +104,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadFloat64() throws IOException {
+	public void testReadFloat64() throws Exception {
 		setContents("F80000000000000000" + "F83FF0000000000000" +
 				"F84000000000000000" + "F8BFF0000000000000" +
 				"F88000000000000000" + "F8FFF0000000000000" +
@@ -121,7 +121,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadString() throws IOException {
+	public void testReadString() throws Exception {
 		setContents("F703666F6F" + "83666F6F" + "F700" + "80");
 		assertEquals("foo", r.readString());
 		assertEquals("foo", r.readString());
@@ -131,7 +131,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadRaw() throws IOException {
+	public void testReadRaw() throws Exception {
 		setContents("F603010203" + "93010203" + "F600" + "90");
 		assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
 		assertTrue(Arrays.equals(new byte[] {1, 2, 3}, r.readRaw()));
@@ -141,7 +141,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadShortList() throws IOException {
+	public void testReadShortList() throws Exception {
 		setContents("A" + "3" + "01" + "83666F6F" + "FC0080");
 		List<Object> l = r.readList(Object.class);
 		assertNotNull(l);
@@ -153,7 +153,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadList() throws IOException {
+	public void testReadList() throws Exception {
 		setContents("F5" + "03" + "01" + "83666F6F" + "FC0080");
 		List<Object> l = r.readList(Object.class);
 		assertNotNull(l);
@@ -165,7 +165,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadListTypeSafe() throws IOException {
+	public void testReadListTypeSafe() throws Exception {
 		setContents("A" + "3" + "01" + "02" + "03");
 		List<Byte> l = r.readList(Byte.class);
 		assertNotNull(l);
@@ -177,7 +177,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadShortMap() throws IOException {
+	public void testReadShortMap() throws Exception {
 		setContents("B" + "2" + "83666F6F" + "7B" + "90" + "F0");
 		Map<Object, Object> m = r.readMap(Object.class, Object.class);
 		assertNotNull(m);
@@ -190,7 +190,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadMap() throws IOException {
+	public void testReadMap() throws Exception {
 		setContents("F4" + "02" + "83666F6F" + "7B" + "90" + "F0");
 		Map<Object, Object> m = r.readMap(Object.class, Object.class);
 		assertNotNull(m);
@@ -203,7 +203,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadMapTypeSafe() throws IOException {
+	public void testReadMapTypeSafe() throws Exception {
 		setContents("B" + "2" + "83666F6F" + "7B" + "80" + "F0");
 		Map<String, Byte> m = r.readMap(String.class, Byte.class);
 		assertNotNull(m);
@@ -215,7 +215,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedList() throws IOException {
+	public void testReadDelimitedList() throws Exception {
 		setContents("F3" + "01" + "83666F6F" + "FC0080" + "F1");
 		List<Object> l = r.readList(Object.class);
 		assertNotNull(l);
@@ -227,7 +227,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedListElements() throws IOException {
+	public void testReadDelimitedListElements() throws Exception {
 		setContents("F3" + "01" + "83666F6F" + "FC0080" + "F1");
 		assertTrue(r.hasListStart());
 		r.readListStart();
@@ -243,7 +243,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedListTypeSafe() throws IOException {
+	public void testReadDelimitedListTypeSafe() throws Exception {
 		setContents("F3" + "01" + "02" + "03" + "F1");
 		List<Byte> l = r.readList(Byte.class);
 		assertNotNull(l);
@@ -255,7 +255,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedMap() throws IOException {
+	public void testReadDelimitedMap() throws Exception {
 		setContents("F2" + "83666F6F" + "7B" + "90" + "F0" + "F1");
 		Map<Object, Object> m = r.readMap(Object.class, Object.class);
 		assertNotNull(m);
@@ -268,7 +268,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedMapEntries() throws IOException {
+	public void testReadDelimitedMapEntries() throws Exception {
 		setContents("F2" + "83666F6F" + "7B" + "90" + "F0" + "F1");
 		assertTrue(r.hasMapStart());
 		r.readMapStart();
@@ -287,7 +287,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadDelimitedMapTypeSafe() throws IOException {
+	public void testReadDelimitedMapTypeSafe() throws Exception {
 		setContents("F2" + "83666F6F" + "7B" + "80" + "F0" + "F1");
 		Map<String, Byte> m = r.readMap(String.class, Byte.class);
 		assertNotNull(m);
@@ -300,7 +300,7 @@ public class ReaderImplTest extends TestCase {
 
 	@Test
 	@SuppressWarnings("unchecked")
-	public void testReadNestedMapsAndLists() throws IOException {
+	public void testReadNestedMapsAndLists() throws Exception {
 		setContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" +
 				"A" + "1" + "01");
 		Map<Object, Object> m = r.readMap(Object.class, Object.class);
@@ -319,7 +319,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadUserDefinedTag() throws IOException {
+	public void testReadUserDefinedTag() throws Exception {
 		setContents("C0" + "DF" + "EF" + "20" + "EF" + "FB7FFFFFFF");
 		assertEquals(0, r.readUserDefinedTag());
 		assertEquals(31, r.readUserDefinedTag());
@@ -329,7 +329,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadUserDefinedObject() throws IOException {
+	public void testReadUserDefinedObject() throws Exception {
 		setContents("C0" + "83666F6F");
 		// Add an object reader for a user-defined type
 		r.addObjectReader(0, new ObjectReader<Foo>() {
@@ -342,7 +342,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadListUsingObjectReader() throws IOException {
+	public void testReadListUsingObjectReader() throws Exception {
 		setContents("A" + "1" + "C0" + "83666F6F");
 		// Add an object reader for a user-defined type
 		r.addObjectReader(0, new ObjectReader<Foo>() {
@@ -357,7 +357,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadMapUsingObjectReader() throws IOException {
+	public void testReadMapUsingObjectReader() throws Exception {
 		setContents("B" + "1" + "C0" + "83666F6F" + "C1" + "83626172");
 		// Add object readers for two user-defined types
 		r.addObjectReader(0, new ObjectReader<Foo>() {
@@ -379,7 +379,7 @@ public class ReaderImplTest extends TestCase {
 	}
 
 	@Test
-	public void testReadEmptyInput() throws IOException {
+	public void testReadEmptyInput() throws Exception {
 		setContents("");
 		assertTrue(r.eof());
 	}