Builders for incoming and outgoing headers and batches. The protocol and serial components can now be used to serialise, sign, deserialise and verify real bundles (except for message parsing).

This commit is contained in:
akwizgran
2011-07-12 16:50:20 +01:00
parent e0509db45d
commit 3d549ea6ac
33 changed files with 703 additions and 294 deletions

View File

@@ -1,15 +1,15 @@
package net.sf.briar.api.db;
import java.io.IOException;
import java.security.SignatureException;
import java.security.GeneralSecurityException;
import java.util.Map;
import java.util.Set;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.BundleBuilder;
import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message;
@@ -51,7 +51,7 @@ public interface DatabaseComponent {
* Generates a bundle of acknowledgements, subscriptions, and batches of
* messages for the given contact.
*/
Bundle generateBundle(ContactId c, BundleBuilder bundleBuilder) throws DbException, IOException, SignatureException;
void generateBundle(ContactId c, BundleWriter bundleBuilder) throws DbException, IOException, GeneralSecurityException;
/** Returns the IDs of all contacts. */
Set<ContactId> getContacts() throws DbException;
@@ -73,7 +73,7 @@ public interface DatabaseComponent {
* messages received from the given contact. Some or all of the messages
* in the bundle may be stored.
*/
void receiveBundle(ContactId c, Bundle b) throws DbException, IOException, SignatureException;
void receiveBundle(ContactId c, BundleReader b) throws DbException, IOException, GeneralSecurityException;
/** Removes a contact (and all associated state) from the database. */
void removeContact(ContactId c) throws DbException;

View File

@@ -1,5 +1,7 @@
package net.sf.briar.api.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.SignatureException;
public interface BatchBuilder {
@@ -11,5 +13,5 @@ public interface BatchBuilder {
void setSignature(byte[] sig);
/** Builds and returns the batch. */
Batch build() throws SignatureException;
Batch build() throws IOException, SignatureException, InvalidKeyException;
}

View File

@@ -1,21 +0,0 @@
package net.sf.briar.api.protocol;
import java.io.IOException;
import java.security.SignatureException;
/**
* A bundle of acknowledgements, subscriptions, transport details and batches.
*/
public interface Bundle {
/** Returns the size of the serialised bundle in bytes. */
long getSize() throws IOException;
/** Returns the bundle's header. */
Header getHeader() throws IOException, SignatureException;
/**
* Returns the next batch of messages, or null if there are no more batches.
*/
Batch getNextBatch() throws IOException, SignatureException;
}

View File

@@ -0,0 +1,25 @@
package net.sf.briar.api.protocol;
import java.io.IOException;
import java.security.GeneralSecurityException;
/**
* An interface for reading a bundle of acknowledgements, subscriptions,
* transport details and batches.
*/
public interface BundleReader {
/** Returns the size of the serialised bundle in bytes. */
long getSize() throws IOException;
/** Returns the bundle's header. */
Header getHeader() throws IOException, GeneralSecurityException;
/**
* Returns the next batch of messages, or null if there are no more batches.
*/
Batch getNextBatch() throws IOException, GeneralSecurityException;
/** Finishes reading the bundle. */
void close() throws IOException;
}

View File

@@ -2,7 +2,11 @@ package net.sf.briar.api.protocol;
import java.io.IOException;
public interface BundleBuilder {
/**
* An interface for writing a bundle of acknowledgements, subscriptions,
* transport details and batches.
*/
public interface BundleWriter {
/** Returns the bundle's capacity in bytes. */
long getCapacity() throws IOException;
@@ -13,6 +17,6 @@ public interface BundleBuilder {
/** Adds a batch of messages to the bundle. */
void addBatch(Batch b) throws IOException;
/** Builds and returns the bundle. */
Bundle build() throws IOException;
/** Finishes writing the bundle. */
void close() throws IOException;
}

View File

@@ -1,24 +1,24 @@
package net.sf.briar.api.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Map;
import java.util.Set;
public interface HeaderBuilder {
/** Adds acknowledgements to the header. */
void addAcks(Set<BatchId> acks) throws IOException;
void addAcks(Iterable<BatchId> acks);
/** Adds subscriptions to the header. */
void addSubscriptions(Set<GroupId> subs) throws IOException;
void addSubscriptions(Iterable<GroupId> subs);
/** Adds transport details to the header. */
void addTransports(Map<String, String> transports) throws IOException;
void addTransports(Map<String, String> transports);
/** Sets the sender's signature over the contents of the header. */
void setSignature(byte[] sig) throws IOException;
void setSignature(byte[] sig);
/** Builds and returns the header. */
Header build() throws SignatureException;
Header build() throws IOException, SignatureException, InvalidKeyException;
}

View File

@@ -1,6 +1,8 @@
package net.sf.briar.api.protocol;
public interface Message {
import net.sf.briar.api.serial.Raw;
public interface Message extends Raw {
/** Returns the message's unique identifier. */
MessageId getId();
@@ -22,7 +24,4 @@ public interface Message {
/** Returns the size of the message in bytes. */
int getSize();
/** Returns the message in wire format. */
byte[] getBody();
}

View File

@@ -9,6 +9,7 @@ public interface Reader {
boolean eof() throws IOException;
void setReadLimit(long limit);
void resetReadLimit();
void close() throws IOException;
boolean hasBoolean() throws IOException;
boolean readBoolean() throws IOException;

View File

@@ -6,6 +6,8 @@ import java.util.Map;
public interface Writer {
void close() throws IOException;
void writeBoolean(boolean b) throws IOException;
void writeUint7(byte b) throws IOException;
@@ -31,6 +33,4 @@ public interface Writer {
void writeMapEnd() throws IOException;
void writeNull() throws IOException;
void close() throws IOException;
}

View File

@@ -467,7 +467,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(4, m.getAuthor().getBytes());
ps.setLong(5, m.getTimestamp());
ps.setInt(6, m.getSize());
ps.setBlob(7, new ByteArrayInputStream(m.getBody()));
ps.setBlob(7, new ByteArrayInputStream(m.getBytes()));
ps.setInt(8, 0);
int rowsAffected = ps.executeUpdate();
assert rowsAffected == 1;

View File

@@ -1,6 +1,7 @@
package net.sf.briar.db;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SignatureException;
import java.util.HashSet;
import java.util.Iterator;
@@ -18,8 +19,9 @@ import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.BundleBuilder;
import net.sf.briar.api.protocol.BundleId;
import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.HeaderBuilder;
@@ -190,8 +192,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
}
public Bundle generateBundle(ContactId c, BundleBuilder b)
throws DbException, IOException, SignatureException {
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
IOException, GeneralSecurityException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
HeaderBuilder h;
// Add acks
@@ -280,15 +282,13 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// more messages trickling in but we can't wait forever
if(size * 2 < Batch.MAX_SIZE) break;
}
Bundle bundle = b.build();
if(LOG.isLoggable(Level.FINE))
LOG.fine("Bundle generated, " + bundle.getSize() + " bytes");
b.close();
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
System.gc();
return bundle;
}
private Batch fillBatch(ContactId c, long capacity) throws DbException,
SignatureException {
IOException, GeneralSecurityException {
contactLock.readLock().lock();
try {
if(!containsContact(c)) throw new NoSuchContactException();
@@ -441,8 +441,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
}
public void receiveBundle(ContactId c, Bundle b) throws DbException,
IOException, SignatureException {
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
IOException, GeneralSecurityException {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received bundle from " + c + ", "
+ b.getSize() + " bytes");
@@ -529,52 +529,64 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
// Store the messages
int batches = 0;
for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) {
Batch batch = null;
while((batch = b.getNextBatch()) != null) {
storeBatch(c, batch);
batches++;
waitForPermissionToWrite();
contactLock.readLock().lock();
try {
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
subscriptionLock.readLock().lock();
try {
Txn txn = db.startTransaction();
try {
int received = 0, stored = 0;
for(Message m : batch.getMessages()) {
received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, c)) stored++;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, c, batch.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
contactLock.readLock().unlock();
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches");
b.close();
retransmitLostBatches(c, h.getId());
System.gc();
}
private void storeBatch(ContactId c, Batch b) throws DbException {
waitForPermissionToWrite();
contactLock.readLock().lock();
try {
if(!containsContact(c)) throw new NoSuchContactException();
messageLock.writeLock().lock();
try {
messageStatusLock.writeLock().lock();
try {
subscriptionLock.readLock().lock();
try {
Txn txn = db.startTransaction();
try {
int received = 0, stored = 0;
for(Message m : b.getMessages()) {
received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, c)) stored++;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, c, b.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
} finally {
messageStatusLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
contactLock.readLock().unlock();
}
}
private void retransmitLostBatches(ContactId c, BundleId b)
throws DbException {
// Find any lost batches that need to be retransmitted
Set<BatchId> lost;
contactLock.readLock().lock();
@@ -586,7 +598,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
try {
Txn txn = db.startTransaction();
try {
lost = db.addReceivedBundle(txn, c, h.getId());
lost = db.addReceivedBundle(txn, c, b);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
@@ -629,7 +641,6 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
contactLock.readLock().unlock();
}
}
System.gc();
}
public void removeContact(ContactId c) throws DbException {

View File

@@ -1,6 +1,7 @@
package net.sf.briar.db;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SignatureException;
import java.util.HashSet;
import java.util.Iterator;
@@ -17,8 +18,9 @@ import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.BundleBuilder;
import net.sf.briar.api.protocol.BundleId;
import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.HeaderBuilder;
@@ -143,8 +145,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
}
public Bundle generateBundle(ContactId c, BundleBuilder b)
throws DbException, IOException, SignatureException {
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
IOException, GeneralSecurityException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
HeaderBuilder h;
// Add acks
@@ -215,15 +217,13 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
// more messages trickling in but we can't wait forever
if(size * 2 < Batch.MAX_SIZE) break;
}
Bundle bundle = b.build();
if(LOG.isLoggable(Level.FINE))
LOG.fine("Bundle generated, " + bundle.getSize() + " bytes");
b.close();
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
System.gc();
return bundle;
}
private Batch fillBatch(ContactId c, long capacity) throws DbException,
SignatureException {
IOException, GeneralSecurityException {
synchronized(contactLock) {
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
@@ -335,8 +335,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
}
public void receiveBundle(ContactId c, Bundle b) throws DbException,
IOException, SignatureException {
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
IOException, GeneralSecurityException {
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received bundle from " + c + ", "
+ b.getSize() + " bytes");
@@ -402,40 +402,52 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
// Store the messages
int batches = 0;
for(Batch batch = b.getNextBatch(); batch != null; batch = b.getNextBatch()) {
Batch batch = null;
while((batch = b.getNextBatch()) != null) {
storeBatch(c, batch);
batches++;
waitForPermissionToWrite();
synchronized(contactLock) {
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
synchronized(subscriptionLock) {
Txn txn = db.startTransaction();
try {
int received = 0, stored = 0;
for(Message m : batch.getMessages()) {
received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, c)) stored++;
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches");
b.close();
retransmitLostBatches(c, h.getId());
System.gc();
}
private void storeBatch(ContactId c, Batch b) throws DbException {
waitForPermissionToWrite();
synchronized(contactLock) {
if(!containsContact(c)) throw new NoSuchContactException();
synchronized(messageLock) {
synchronized(messageStatusLock) {
synchronized(subscriptionLock) {
Txn txn = db.startTransaction();
try {
int received = 0, stored = 0;
for(Message m : b.getMessages()) {
received++;
GroupId g = m.getGroup();
if(db.containsSubscription(txn, g)) {
if(storeMessage(txn, m, c)) stored++;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, c, batch.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + received
+ " messages, stored " + stored);
db.addBatchToAck(txn, c, b.getId());
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
}
}
}
}
if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + batches + " batches");
}
private void retransmitLostBatches(ContactId c, BundleId b)
throws DbException {
// Find any lost batches that need to be retransmitted
Set<BatchId> lost;
synchronized(contactLock) {
@@ -444,7 +456,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
synchronized(messageStatusLock) {
Txn txn = db.startTransaction();
try {
lost = db.addReceivedBundle(txn, c, h.getId());
lost = db.addReceivedBundle(txn, c, b);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
@@ -472,7 +484,6 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
}
}
}
System.gc();
}
public void removeContact(ContactId c) throws DbException {

View File

@@ -0,0 +1,44 @@
package net.sf.briar.protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.util.ArrayList;
import java.util.List;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
abstract class BatchBuilderImpl implements BatchBuilder {
protected final List<Message> messages = new ArrayList<Message>();
protected final KeyPair keyPair;
protected final Signature signature;
protected final MessageDigest messageDigest;
private final WriterFactory writerFactory;
protected BatchBuilderImpl(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
this.keyPair = keyPair;
this.signature = signature;
this.messageDigest = messageDigest;
this.writerFactory = writerFactory;
}
public void addMessage(Message m) {
messages.add(m);
}
protected byte[] getSignableRepresentation() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeList(messages);
w.close();
return out.toByteArray();
}
}

View File

@@ -1,7 +1,7 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.SignatureException;
import java.security.GeneralSecurityException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -10,7 +10,7 @@ import java.util.Set;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Bundle;
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.HeaderBuilder;
@@ -24,26 +24,32 @@ import net.sf.briar.api.serial.Reader;
import com.google.inject.Provider;
/** A bundle that deserialises its contents on demand using a reader. */
abstract class BundleReader implements Bundle {
class BundleReaderImpl implements BundleReader {
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
private final Reader r;
private final long size;
private final MessageParser messageParser;
private final Provider<HeaderBuilder> headerBuilderProvider;
private final Provider<BatchBuilder> batchBuilderProvider;
private State state = State.START;
BundleReader(Reader r, MessageParser messageParser,
BundleReaderImpl(Reader r, long size, MessageParser messageParser,
Provider<HeaderBuilder> headerBuilderProvider,
Provider<BatchBuilder> batchBuilderProvider) {
this.r = r;
this.size = size;
this.messageParser = messageParser;
this.headerBuilderProvider = headerBuilderProvider;
this.batchBuilderProvider = batchBuilderProvider;
}
public Header getHeader() throws IOException, SignatureException {
public long getSize() {
return size;
}
public Header getHeader() throws IOException, GeneralSecurityException {
if(state != State.START) throw new IllegalStateException();
r.setReadLimit(Header.MAX_SIZE);
Set<BatchId> acks = new HashSet<BatchId>();
@@ -69,7 +75,7 @@ abstract class BundleReader implements Bundle {
return h.build();
}
public Batch getNextBatch() throws IOException, SignatureException {
public Batch getNextBatch() throws IOException, GeneralSecurityException {
if(state == State.FIRST_BATCH) {
r.readListStart();
state = State.MORE_BATCHES;
@@ -91,4 +97,8 @@ abstract class BundleReader implements Bundle {
b.setSignature(sig);
return b.build();
}
public void close() throws IOException {
r.close();
}
}

View File

@@ -4,14 +4,14 @@ import java.io.IOException;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.BundleBuilder;
import net.sf.briar.api.protocol.BundleWriter;
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.serial.Writer;
/** A bundle builder that serialises its contents using a writer. */
abstract class BundleWriter implements BundleBuilder {
class BundleWriterImpl implements BundleWriter {
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
@@ -19,7 +19,7 @@ abstract class BundleWriter implements BundleBuilder {
private final long capacity;
private State state = State.START;
BundleWriter(Writer w, long capacity) {
BundleWriterImpl(Writer w, long capacity) {
this.w = w;
this.capacity = capacity;
}
@@ -48,12 +48,12 @@ abstract class BundleWriter implements BundleBuilder {
}
if(state != State.MORE_BATCHES) throw new IllegalStateException();
w.writeListStart();
for(Message m : b.getMessages()) w.writeRaw(m.getBody());
for(Message m : b.getMessages()) w.writeRaw(m.getBytes());
w.writeListEnd();
w.writeRaw(b.getSignature());
}
void close() throws IOException {
public void close() throws IOException {
if(state == State.FIRST_BATCH) {
w.writeListStart();
state = State.MORE_BATCHES;

View File

@@ -1,30 +0,0 @@
package net.sf.briar.protocol;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.HeaderBuilder;
import net.sf.briar.api.protocol.MessageParser;
import net.sf.briar.api.serial.ReaderFactory;
import com.google.inject.Provider;
class FileBundle extends BundleReader {
private final File file;
FileBundle(File file, ReaderFactory readerFactory,
MessageParser messageParser,
Provider<HeaderBuilder> headerBuilderProvider,
Provider<BatchBuilder> batchBuilderProvider) throws IOException {
super(readerFactory.createReader(new FileInputStream(file)),
messageParser, headerBuilderProvider, batchBuilderProvider);
this.file = file;
}
public long getSize() throws IOException {
return file.length();
}
}

View File

@@ -1,41 +0,0 @@
package net.sf.briar.protocol;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.HeaderBuilder;
import net.sf.briar.api.protocol.MessageParser;
import net.sf.briar.api.serial.ReaderFactory;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.Provider;
public class FileBundleBuilder extends BundleWriter {
private final File file;
private final ReaderFactory readerFactory;
private final MessageParser messageParser;
private final Provider<HeaderBuilder> headerBuilderProvider;
private final Provider<BatchBuilder> batchBuilderProvider;
FileBundleBuilder(File file, long capacity, WriterFactory writerFactory,
ReaderFactory readerFactory, MessageParser messageParser,
Provider<HeaderBuilder> headerBuilderProvider,
Provider<BatchBuilder> batchBuilderProvider) throws IOException {
super(writerFactory.createWriter(new FileOutputStream(file)), capacity);
this.file = file;
this.readerFactory = readerFactory;
this.messageParser = messageParser;
this.headerBuilderProvider = headerBuilderProvider;
this.batchBuilderProvider = batchBuilderProvider;
}
public Bundle build() throws IOException {
super.close();
return new FileBundle(file, readerFactory, messageParser,
headerBuilderProvider, batchBuilderProvider);
}
}

View File

@@ -0,0 +1,63 @@
package net.sf.briar.protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.HeaderBuilder;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
abstract class HeaderBuilderImpl implements HeaderBuilder {
protected final List<BatchId> acks = new ArrayList<BatchId>();
protected final List<GroupId> subs = new ArrayList<GroupId>();
protected final Map<String, String> transports =
new LinkedHashMap<String, String>();
protected final KeyPair keyPair;
protected final Signature signature;
protected final MessageDigest messageDigest;
private final WriterFactory writerFactory;
protected HeaderBuilderImpl(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
this.keyPair = keyPair;
this.signature = signature;
this.messageDigest = messageDigest;
this.writerFactory = writerFactory;
}
public void addAcks(Iterable<BatchId> acks) {
for(BatchId ack : acks) this.acks.add(ack);
}
public void addSubscriptions(Iterable<GroupId> subs) {
for(GroupId sub : subs) this.subs.add(sub);
}
public void addTransports(Map<String, String> transports) {
for(String key : transports.keySet()) {
this.transports.put(key, transports.get(key));
}
}
protected byte[] getSignableRepresentation() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeList(acks);
w.writeList(subs);
w.writeMap(transports);
w.close();
return out.toByteArray();
}
}

View File

@@ -0,0 +1,40 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.serial.WriterFactory;
public class IncomingBatchBuilder extends BatchBuilderImpl {
IncomingBatchBuilder(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
super(keyPair, signature, messageDigest, writerFactory);
}
private byte[] sig = null;
public void setSignature(byte[] sig) {
this.sig = sig;
}
public Batch build() throws IOException, SignatureException,
InvalidKeyException {
if(sig == null) throw new IllegalStateException();
byte[] raw = getSignableRepresentation();
signature.initVerify(keyPair.getPublic());
signature.update(raw);
signature.verify(sig);
messageDigest.reset();
messageDigest.update(raw);
messageDigest.update(sig);
byte[] hash = messageDigest.digest();
return new BatchImpl(new BatchId(hash), raw.length, messages, sig);
}
}

View File

@@ -0,0 +1,47 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import java.util.HashSet;
import java.util.Set;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.BundleId;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.serial.WriterFactory;
class IncomingHeaderBuilder extends HeaderBuilderImpl {
private byte[] sig = null;
IncomingHeaderBuilder(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
super(keyPair, signature, messageDigest, writerFactory);
}
public void setSignature(byte[] sig) {
this.sig = sig;
}
public Header build() throws IOException, SignatureException,
InvalidKeyException {
if(sig == null) throw new IllegalStateException();
byte[] raw = getSignableRepresentation();
signature.initVerify(keyPair.getPublic());
signature.update(raw);
signature.verify(sig);
messageDigest.reset();
messageDigest.update(raw);
messageDigest.update(sig);
byte[] hash = messageDigest.digest();
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
Set<GroupId> subSet = new HashSet<GroupId>(subs);
return new HeaderImpl(new BundleId(hash), raw.length, ackSet, subSet,
transports, sig);
}
}

View File

@@ -48,7 +48,7 @@ public class MessageImpl implements Message {
return body.length;
}
public byte[] getBody() {
public byte[] getBytes() {
return body;
}

View File

@@ -0,0 +1,37 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.serial.WriterFactory;
public class OutgoingBatchBuilder extends BatchBuilderImpl {
OutgoingBatchBuilder(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
super(keyPair, signature, messageDigest, writerFactory);
}
public void setSignature(byte[] sig) {
throw new UnsupportedOperationException();
}
public Batch build() throws IOException, SignatureException,
InvalidKeyException {
byte[] raw = getSignableRepresentation();
signature.initSign(keyPair.getPrivate());
signature.update(raw);
byte[] sig = signature.sign();
messageDigest.reset();
messageDigest.update(raw);
messageDigest.update(sig);
byte[] hash = messageDigest.digest();
return new BatchImpl(new BatchId(hash), raw.length, messages, sig);
}
}

View File

@@ -0,0 +1,44 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import java.util.HashSet;
import java.util.Set;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.BundleId;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.serial.WriterFactory;
public class OutgoingHeaderBuilder extends HeaderBuilderImpl {
OutgoingHeaderBuilder(KeyPair keyPair, Signature signature,
MessageDigest messageDigest, WriterFactory writerFactory) {
super(keyPair, signature, messageDigest, writerFactory);
}
public void setSignature(byte[] sig) {
throw new UnsupportedOperationException();
}
public Header build() throws IOException, SignatureException,
InvalidKeyException {
byte[] raw = getSignableRepresentation();
signature.initSign(keyPair.getPrivate());
signature.update(raw);
byte[] sig = signature.sign();
messageDigest.reset();
messageDigest.update(raw);
messageDigest.update(sig);
byte[] hash = messageDigest.digest();
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
Set<GroupId> subSet = new HashSet<GroupId>(subs);
return new HeaderImpl(new BundleId(hash), raw.length, ackSet, subSet,
transports, sig);
}
}

View File

@@ -5,7 +5,7 @@ import java.io.InputStream;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.ReaderFactory;
class ReaderFactoryImpl implements ReaderFactory {
public class ReaderFactoryImpl implements ReaderFactory {
public Reader createReader(InputStream in) {
return new ReaderImpl(in);

View File

@@ -53,6 +53,10 @@ class ReaderImpl implements Reader {
readLimit = 0L;
}
public void close() throws IOException {
in.close();
}
public boolean hasBoolean() throws IOException {
if(!started) readNext(true);
if(eof) return false;

View File

@@ -5,7 +5,7 @@ import java.io.OutputStream;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
class WriterFactoryImpl implements WriterFactory {
public class WriterFactoryImpl implements WriterFactory {
public Writer createWriter(OutputStream out) {
return new WriterImpl(out);

View File

@@ -18,6 +18,11 @@ class WriterImpl implements Writer {
this.out = out;
}
public void close() throws IOException {
out.flush();
out.close();
}
public void writeBoolean(boolean b) throws IOException {
if(b) out.write(Tag.TRUE);
else out.write(Tag.FALSE);
@@ -156,9 +161,4 @@ class WriterImpl implements Writer {
public void writeNull() throws IOException {
out.write(Tag.NULL);
}
public void close() throws IOException {
out.flush();
out.close();
}
}

View File

@@ -21,6 +21,7 @@
<test name='net.sf.briar.i18n.I18nTest'/>
<test name='net.sf.briar.invitation.InvitationWorkerTest'/>
<test name='net.sf.briar.protocol.BundleReaderTest'/>
<test name='net.sf.briar.protocol.BundleReadWriteTest'/>
<test name='net.sf.briar.protocol.BundleWriterTest'/>
<test name='net.sf.briar.serial.ReaderImplTest'/>
<test name='net.sf.briar.serial.WriterImplTest'/>

View File

@@ -1,7 +1,5 @@
package net.sf.briar.db;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -18,9 +16,9 @@ import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.BundleBuilder;
import net.sf.briar.api.protocol.BundleId;
import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.HeaderBuilder;
@@ -507,7 +505,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@Test
public void testGenerateBundleThrowsExceptionIfContactIsMissing()
throws DbException, IOException, SignatureException {
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
@@ -518,7 +516,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class);
final BundleWriter bundleBuilder = context.mock(BundleWriter.class);
context.checking(new Expectations() {{
// Check that the contact is still in the DB
oneOf(database).startTransaction();
@@ -539,8 +537,7 @@ public abstract class DatabaseComponentTest extends TestCase {
}
@Test
public void testGenerateBundle() throws DbException, IOException,
SignatureException {
public void testGenerateBundle() throws Exception {
final long headerSize = 1234L;
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
@@ -552,12 +549,11 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class);
final BundleWriter bundleWriter = context.mock(BundleWriter.class);
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
final Header header = context.mock(Header.class);
final BatchBuilder batchBuilder = context.mock(BatchBuilder.class);
final Batch batch = context.mock(Batch.class);
final Bundle bundle = context.mock(Bundle.class);
context.checking(new Expectations() {{
allowing(database).startTransaction();
will(returnValue(txn));
@@ -582,11 +578,11 @@ public abstract class DatabaseComponentTest extends TestCase {
// Build the header
oneOf(headerBuilder).build();
will(returnValue(header));
oneOf(bundleBuilder).getCapacity();
oneOf(bundleWriter).getCapacity();
will(returnValue(1024L * 1024L));
oneOf(header).getSize();
will(returnValue(headerSize));
oneOf(bundleBuilder).addHeader(header);
oneOf(bundleWriter).addHeader(header);
// Add a batch to the bundle
oneOf(database).getSendableMessages(txn, contactId,
Batch.MAX_SIZE - headerSize);
@@ -604,25 +600,24 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).addOutstandingBatch(
txn, contactId, batchId, messages);
// Add the batch to the bundle
oneOf(bundleBuilder).addBatch(batch);
oneOf(bundleWriter).addBatch(batch);
// Check whether to add another batch
oneOf(batch).getSize();
will(returnValue((long) message.getSize()));
// No, just send the bundle
oneOf(bundleBuilder).build();
will(returnValue(bundle));
oneOf(bundleWriter).close();
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
headerBuilderProvider, batchBuilderProvider);
db.generateBundle(contactId, bundleBuilder);
db.generateBundle(contactId, bundleWriter);
context.assertIsSatisfied();
}
@Test
public void testReceiveBundleThrowsExceptionIfContactIsMissing()
throws DbException, IOException, SignatureException {
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
@@ -633,7 +628,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
final Bundle bundle = context.mock(Bundle.class);
final BundleReader bundleReader = context.mock(BundleReader.class);
context.checking(new Expectations() {{
// Check that the contact is still in the DB
oneOf(database).startTransaction();
@@ -646,7 +641,7 @@ public abstract class DatabaseComponentTest extends TestCase {
headerBuilderProvider, batchBuilderProvider);
try {
db.receiveBundle(contactId, bundle);
db.receiveBundle(contactId, bundleReader);
assertTrue(false);
} catch(NoSuchContactException expected) {}
@@ -654,8 +649,7 @@ public abstract class DatabaseComponentTest extends TestCase {
}
@Test
public void testReceivedBundle() throws DbException, IOException,
SignatureException {
public void testReceiveBundle() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
@@ -666,7 +660,7 @@ public abstract class DatabaseComponentTest extends TestCase {
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
final Bundle bundle = context.mock(Bundle.class);
final BundleReader bundleReader = context.mock(BundleReader.class);
final Header header = context.mock(Header.class);
final Batch batch = context.mock(Batch.class);
context.checking(new Expectations() {{
@@ -676,7 +670,7 @@ public abstract class DatabaseComponentTest extends TestCase {
allowing(database).containsContact(txn, contactId);
will(returnValue(true));
// Header
oneOf(bundle).getHeader();
oneOf(bundleReader).getHeader();
will(returnValue(header));
// Acks
oneOf(header).getAcks();
@@ -692,7 +686,7 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(transports));
oneOf(database).setTransports(txn, contactId, transports);
// Batches
oneOf(bundle).getNextBatch();
oneOf(bundleReader).getNextBatch();
will(returnValue(batch));
oneOf(batch).getMessages();
will(returnValue(Collections.singleton(message)));
@@ -706,8 +700,9 @@ public abstract class DatabaseComponentTest extends TestCase {
will(returnValue(batchId));
oneOf(database).addBatchToAck(txn, contactId, batchId);
// Any more batches? Nope
oneOf(bundle).getNextBatch();
oneOf(bundleReader).getNextBatch();
will(returnValue(null));
oneOf(bundleReader).close();
// Lost batches
oneOf(header).getId();
will(returnValue(bundleId));
@@ -718,7 +713,7 @@ public abstract class DatabaseComponentTest extends TestCase {
DatabaseComponent db = createDatabaseComponent(database, cleaner,
headerBuilderProvider, batchBuilderProvider);
db.receiveBundle(contactId, bundle);
db.receiveBundle(contactId, bundleReader);
context.assertIsSatisfied();
}

View File

@@ -110,7 +110,7 @@ public class H2DatabaseTest extends TestCase {
assertEquals(authorId, m1.getAuthor());
assertEquals(timestamp, m1.getTimestamp());
assertEquals(size, m1.getSize());
assertTrue(Arrays.equals(body, m1.getBody()));
assertTrue(Arrays.equals(body, m1.getBytes()));
// Delete the records
db.removeContact(txn, contactId);
db.removeMessage(txn, messageId);

View File

@@ -0,0 +1,156 @@
package net.sf.briar.protocol;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.HeaderBuilder;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.MessageParser;
import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.serial.FormatException;
import net.sf.briar.api.serial.Reader;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.serial.ReaderFactoryImpl;
import net.sf.briar.serial.WriterFactoryImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.inject.Provider;
public class BundleReadWriteTest extends TestCase {
private static final String SIGNATURE_ALGO = "SHA256withRSA";
private static final String KEY_PAIR_ALGO = "RSA";
private static final String DIGEST_ALGO = "SHA-256";
private final File testDir = TestUtils.getTestDirectory();
private final File bundle = new File(testDir, "bundle");
private final long capacity = 1024L;
private final BatchId ack = new BatchId(TestUtils.getRandomId());
private final Set<BatchId> acks = Collections.singleton(ack);
private final GroupId sub = new GroupId(TestUtils.getRandomId());
private final Set<GroupId> subs = Collections.singleton(sub);
private final Map<String, String> transports =
Collections.singletonMap("foo", "bar");
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
private final AuthorId authorId = new AuthorId(TestUtils.getRandomId());
private final long timestamp = System.currentTimeMillis();
private final byte[] messageBody = new byte[123];
private final Message message = new MessageImpl(messageId, MessageId.NONE,
sub, authorId, timestamp, messageBody);
// FIXME: This test should not depend on an impl in another component
private final WriterFactory wf = new WriterFactoryImpl();
private final KeyPair keyPair;
private final Signature sig;
private final MessageDigest digest;
public BundleReadWriteTest() throws NoSuchAlgorithmException {
super();
keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair();
sig = Signature.getInstance(SIGNATURE_ALGO);
digest = MessageDigest.getInstance(DIGEST_ALGO);
assertEquals(digest.getDigestLength(), UniqueId.LENGTH);
}
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testWriteBundle() throws Exception {
HeaderBuilder h = new OutgoingHeaderBuilder(keyPair, sig, digest, wf);
h.addAcks(acks);
h.addSubscriptions(subs);
h.addTransports(transports);
Header header = h.build();
BatchBuilder b = new OutgoingBatchBuilder(keyPair, sig, digest, wf);
b.addMessage(message);
Batch batch = b.build();
FileOutputStream out = new FileOutputStream(bundle);
Writer writer = new WriterFactoryImpl().createWriter(out);
BundleWriter w = new BundleWriterImpl(writer, capacity);
w.addHeader(header);
w.addBatch(batch);
w.close();
assertTrue(bundle.exists());
assertTrue(bundle.length() > messageBody.length);
}
@Test
public void testWriteAndReadBundle() throws Exception {
testWriteBundle();
MessageParser messageParser = new MessageParser() {
public Message parseMessage(byte[] body) throws FormatException,
SignatureException {
// FIXME: Really parse the message
return message;
}
};
Provider<HeaderBuilder> headerBuilderProvider =
new Provider<HeaderBuilder>() {
public HeaderBuilder get() {
return new IncomingHeaderBuilder(keyPair, sig, digest, wf);
}
};
Provider<BatchBuilder> batchBuilderProvider =
new Provider<BatchBuilder>() {
public BatchBuilder get() {
return new IncomingBatchBuilder(keyPair, sig, digest, wf);
}
};
FileInputStream in = new FileInputStream(bundle);
Reader reader = new ReaderFactoryImpl().createReader(in);
BundleReader r = new BundleReaderImpl(reader, bundle.length(),
messageParser, headerBuilderProvider, batchBuilderProvider);
Header h = r.getHeader();
assertEquals(acks, h.getAcks());
assertEquals(subs, h.getSubscriptions());
assertEquals(transports, h.getTransports());
Batch b = r.getNextBatch();
assertEquals(Collections.singletonList(message), b.getMessages());
assertNull(r.getNextBatch());
r.close();
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -12,6 +12,7 @@ import net.sf.briar.TestUtils;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchBuilder;
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.HeaderBuilder;
@@ -46,7 +47,7 @@ public class BundleReaderTest extends TestCase {
private final byte[] batchSig = TestUtils.getRandomId();
@Test
public void testGetHeader() throws IOException, SignatureException {
public void testGetHeader() throws Exception {
Mockery context = new Mockery();
final Reader reader = context.mock(Reader.class);
final MessageParser messageParser = context.mock(MessageParser.class);
@@ -82,7 +83,7 @@ public class BundleReaderTest extends TestCase {
oneOf(headerBuilder).build();
will(returnValue(header));
}});
BundleReader r = createBundleReader(reader, messageParser,
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
headerBuilderProvider, batchBuilderProvider);
assertEquals(header, r.getHeader());
@@ -91,8 +92,7 @@ public class BundleReaderTest extends TestCase {
}
@Test
public void testBatchBeforeHeaderThrowsException() throws IOException,
SignatureException {
public void testBatchBeforeHeaderThrowsException() throws Exception {
Mockery context = new Mockery();
final Reader reader = context.mock(Reader.class);
final MessageParser messageParser = context.mock(MessageParser.class);
@@ -102,7 +102,7 @@ public class BundleReaderTest extends TestCase {
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
BundleReader r = createBundleReader(reader, messageParser,
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
headerBuilderProvider, batchBuilderProvider);
try {
@@ -114,8 +114,30 @@ public class BundleReaderTest extends TestCase {
}
@Test
public void testGetHeaderNoBatches() throws IOException,
public void testCloseBeforeHeaderDoesNotThrowException() throws IOException,
SignatureException {
Mockery context = new Mockery();
final Reader reader = context.mock(Reader.class);
final MessageParser messageParser = context.mock(MessageParser.class);
@SuppressWarnings("unchecked")
final Provider<HeaderBuilder> headerBuilderProvider =
context.mock(Provider.class);
@SuppressWarnings("unchecked")
final Provider<BatchBuilder> batchBuilderProvider =
context.mock(Provider.class, "batchBuilderProvider");
context.checking(new Expectations() {{
oneOf(reader).close();
}});
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
headerBuilderProvider, batchBuilderProvider);
r.close();
context.assertIsSatisfied();
}
@Test
public void testGetHeaderNoBatches() throws Exception {
Mockery context = new Mockery();
final Reader reader = context.mock(Reader.class);
final MessageParser messageParser = context.mock(MessageParser.class);
@@ -155,19 +177,21 @@ public class BundleReaderTest extends TestCase {
oneOf(reader).hasListEnd();
will(returnValue(true));
oneOf(reader).readListEnd();
// Close
oneOf(reader).close();
}});
BundleReader r = createBundleReader(reader, messageParser,
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
headerBuilderProvider, batchBuilderProvider);
assertEquals(header, r.getHeader());
assertNull(r.getNextBatch());
r.close();
context.assertIsSatisfied();
}
@Test
public void testGetHeaderOneBatch() throws IOException,
SignatureException {
public void testGetHeaderOneBatch() throws Exception {
Mockery context = new Mockery();
final Reader reader = context.mock(Reader.class);
final MessageParser messageParser = context.mock(MessageParser.class);
@@ -226,26 +250,17 @@ public class BundleReaderTest extends TestCase {
oneOf(reader).hasListEnd();
will(returnValue(true));
oneOf(reader).readListEnd();
// Close
oneOf(reader).close();
}});
BundleReader r = createBundleReader(reader, messageParser,
BundleReader r = new BundleReaderImpl(reader, size, messageParser,
headerBuilderProvider, batchBuilderProvider);
assertEquals(header, r.getHeader());
assertEquals(batch, r.getNextBatch());
assertNull(r.getNextBatch());
r.close();
context.assertIsSatisfied();
}
private BundleReader createBundleReader(Reader reader,
MessageParser messageParser,
Provider<HeaderBuilder> headerBuilderProvider,
Provider<BatchBuilder> batchBuilderProvider) {
return new BundleReader(reader, messageParser, headerBuilderProvider,
batchBuilderProvider) {
public long getSize() {
return size;
}
};
}
}

View File

@@ -9,7 +9,7 @@ import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Bundle;
import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.Message;
@@ -21,6 +21,7 @@ import org.junit.Test;
public class BundleWriterTest extends TestCase {
private final long capacity = 1024L * 1024L;
private final BatchId ack = new BatchId(TestUtils.getRandomId());
private final Set<BatchId> acks = Collections.singleton(ack);
private final GroupId sub = new GroupId(TestUtils.getRandomId());
@@ -28,7 +29,6 @@ public class BundleWriterTest extends TestCase {
private final Map<String, String> transports =
Collections.singletonMap("foo", "bar");
private final byte[] headerSig = TestUtils.getRandomId();
private final long capacity = 1024L * 1024L;
private final byte[] messageBody = new byte[123];
private final byte[] batchSig = TestUtils.getRandomId();
@@ -59,7 +59,7 @@ public class BundleWriterTest extends TestCase {
will(returnValue(headerSig));
oneOf(writer).writeRaw(headerSig);
}});
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
w.addHeader(header);
@@ -91,7 +91,7 @@ public class BundleWriterTest extends TestCase {
will(returnValue(headerSig));
oneOf(writer).writeRaw(headerSig);
}});
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
w.addHeader(header);
@@ -103,7 +103,7 @@ public class BundleWriterTest extends TestCase {
Mockery context = new Mockery();
final Writer writer = context.mock(Writer.class);
final Batch batch = context.mock(Batch.class);
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
try {
w.addBatch(batch);
@@ -117,7 +117,7 @@ public class BundleWriterTest extends TestCase {
public void testCloseBeforeHeaderThrowsException() throws IOException {
Mockery context = new Mockery();
final Writer writer = context.mock(Writer.class);
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
try {
w.close();
@@ -159,7 +159,7 @@ public class BundleWriterTest extends TestCase {
oneOf(writer).writeListEnd();
oneOf(writer).close();
}});
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
w.addHeader(header);
w.close();
@@ -200,7 +200,7 @@ public class BundleWriterTest extends TestCase {
oneOf(writer).writeListStart();
oneOf(batch).getMessages();
will(returnValue(Collections.singleton(message)));
oneOf(message).getBody();
oneOf(message).getBytes();
will(returnValue(messageBody));
oneOf(writer).writeRaw(messageBody);
oneOf(writer).writeListEnd();
@@ -211,7 +211,7 @@ public class BundleWriterTest extends TestCase {
oneOf(writer).writeListStart();
oneOf(batch).getMessages();
will(returnValue(Collections.singleton(message)));
oneOf(message).getBody();
oneOf(message).getBytes();
will(returnValue(messageBody));
oneOf(writer).writeRaw(messageBody);
oneOf(writer).writeListEnd();
@@ -222,7 +222,7 @@ public class BundleWriterTest extends TestCase {
oneOf(writer).writeListEnd();
oneOf(writer).close();
}});
BundleWriter w = createBundleWriter(writer);
BundleWriter w = new BundleWriterImpl(writer, capacity);
w.addHeader(header);
w.addBatch(batch);
@@ -231,12 +231,4 @@ public class BundleWriterTest extends TestCase {
context.assertIsSatisfied();
}
private BundleWriter createBundleWriter(Writer writer) {
return new BundleWriter(writer, capacity) {
public Bundle build() throws IOException {
return null;
}
};
}
}