From 64bf1fbbb1cff2871a4e9203c6e9bcc3e009ab20 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 25 Jan 2013 15:38:37 +0000 Subject: [PATCH] Part 1 of a major BMP and database refactoring. Tests are broken! The old logic for selecting when to send subscription and transport updates has been removed and not yet replaced. Subscription times have been removed from subscription updates. The database expiry time has been remove from subscription updates and will later get its own update packet. Transport updates have been broken up into one update per transport. Acks for subscription and transport updates have been added. --- .../sf/briar/api/db/DatabaseComponent.java | 40 +- .../event/RemoteTransportsUpdatedEvent.java | 27 - ...Event.java => TransportsUpdatedEvent.java} | 2 +- .../src/net/sf/briar/api/protocol/Ack.java | 16 +- .../src/net/sf/briar/api/protocol/Author.java | 36 +- .../sf/briar/api/protocol/AuthorFactory.java | 2 - .../net/sf/briar/api/protocol/AuthorId.java | 5 +- .../net/sf/briar/api/protocol/GroupId.java | 3 +- .../net/sf/briar/api/protocol/Message.java | 16 +- .../net/sf/briar/api/protocol/MessageId.java | 5 +- .../briar/api/protocol/MessageVerifier.java | 1 + .../src/net/sf/briar/api/protocol/Offer.java | 16 +- .../sf/briar/api/protocol/PacketFactory.java | 20 - .../briar/api/protocol/ProtocolConstants.java | 3 - .../sf/briar/api/protocol/ProtocolReader.java | 6 + .../sf/briar/api/protocol/ProtocolWriter.java | 4 + .../net/sf/briar/api/protocol/Request.java | 23 +- .../briar/api/protocol/SubscriptionAck.java | 16 + .../api/protocol/SubscriptionUpdate.java | 31 +- .../net/sf/briar/api/protocol/Transport.java | 42 - .../sf/briar/api/protocol/TransportAck.java | 23 + .../briar/api/protocol/TransportUpdate.java | 35 +- .../src/net/sf/briar/api/protocol/Types.java | 7 +- .../briar/api/protocol/UnverifiedMessage.java | 33 + .../src/net/sf/briar/api/serial/Reader.java | 2 +- ...vertunnel.org-netlib-0.14-briar-source.jar | Bin 497573 -> 497577 bytes briar-core/src/net/sf/briar/db/Database.java | 171 +-- .../sf/briar/db/DatabaseComponentImpl.java | 270 ++-- .../src/net/sf/briar/db/DatabaseModule.java | 10 +- .../src/net/sf/briar/db/H2Database.java | 4 +- .../src/net/sf/briar/db/JdbcDatabase.java | 1202 +++++++++-------- .../net/sf/briar/invitation/Connector.java | 2 +- .../src/net/sf/briar/protocol/AckImpl.java | 19 - .../src/net/sf/briar/protocol/AckReader.java | 12 +- .../sf/briar/protocol/AuthorFactoryImpl.java | 8 +- .../src/net/sf/briar/protocol/AuthorImpl.java | 29 - .../net/sf/briar/protocol/AuthorReader.java | 8 +- .../net/sf/briar/protocol/GroupReader.java | 1 - .../net/sf/briar/protocol/MessageReader.java | 4 +- .../src/net/sf/briar/protocol/OfferImpl.java | 19 - .../net/sf/briar/protocol/OfferReader.java | 13 +- .../sf/briar/protocol/PacketFactoryImpl.java | 42 - .../net/sf/briar/protocol/ProtocolModule.java | 42 +- .../protocol/ProtocolReaderFactoryImpl.java | 26 +- .../sf/briar/protocol/ProtocolReaderImpl.java | 32 +- .../sf/briar/protocol/ProtocolWriterImpl.java | 70 +- .../net/sf/briar/protocol/RequestImpl.java | 24 - .../net/sf/briar/protocol/RequestReader.java | 13 +- .../briar/protocol/SubscriptionAckReader.java | 20 + .../protocol/SubscriptionUpdateImpl.java | 38 - .../protocol/SubscriptionUpdateReader.java | 44 +- .../sf/briar/protocol/TransportAckReader.java | 24 + .../briar/protocol/TransportUpdateImpl.java | 26 - .../briar/protocol/TransportUpdateReader.java | 61 +- .../protocol/duplex/DuplexConnection.java | 20 +- .../simplex/IncomingSimplexConnection.java | 1 + .../simplex/OutgoingSimplexConnection.java | 17 +- .../src/net/sf/briar/serial/ReaderImpl.java | 4 +- .../net/sf/briar/ProtocolIntegrationTest.java | 41 +- .../briar/db/DatabaseComponentImplTest.java | 30 +- .../sf/briar/db/DatabaseComponentTest.java | 239 +--- .../net/sf/briar/protocol/AckReaderTest.java | 35 +- .../net/sf/briar/protocol/ConstantsTest.java | 47 +- .../sf/briar/protocol/OfferReaderTest.java | 35 +- .../protocol/ProtocolWriterImplTest.java | 9 +- .../sf/briar/protocol/RequestReaderTest.java | 31 +- .../SimplexProtocolIntegrationTest.java | 16 +- 67 files changed, 1411 insertions(+), 1762 deletions(-) delete mode 100644 briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java rename briar-api/src/net/sf/briar/api/db/event/{LocalTransportsUpdatedEvent.java => TransportsUpdatedEvent.java} (66%) delete mode 100644 briar-api/src/net/sf/briar/api/protocol/PacketFactory.java create mode 100644 briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java delete mode 100644 briar-api/src/net/sf/briar/api/protocol/Transport.java create mode 100644 briar-api/src/net/sf/briar/api/protocol/TransportAck.java delete mode 100644 briar-core/src/net/sf/briar/protocol/AckImpl.java delete mode 100644 briar-core/src/net/sf/briar/protocol/AuthorImpl.java delete mode 100644 briar-core/src/net/sf/briar/protocol/OfferImpl.java delete mode 100644 briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java delete mode 100644 briar-core/src/net/sf/briar/protocol/RequestImpl.java create mode 100644 briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java delete mode 100644 briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java create mode 100644 briar-core/src/net/sf/briar/protocol/TransportAckReader.java delete mode 100644 briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java index 37675ac39..c908d3e47 100644 --- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java +++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java @@ -17,8 +17,9 @@ import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; @@ -66,6 +67,9 @@ public interface DatabaseComponent { */ void addSecrets(Collection secrets) throws DbException; + /** Adds a transport to the database. */ + void addTransport(TransportId t) throws DbException; + /** * Generates an acknowledgement for the given contact. Returns null if * there are no messages to acknowledge. @@ -98,18 +102,32 @@ public interface DatabaseComponent { */ Offer generateOffer(ContactId c, int maxMessages) throws DbException; + /** + * Generates a subscription ack for the given contact. Returns null if no + * ack is due. + */ + SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException; + /** * Generates a subscription update for the given contact. Returns null if - * an update is not due. + * no update is due. */ SubscriptionUpdate generateSubscriptionUpdate(ContactId c) throws DbException; /** - * Generates a transport update for the given contact. Returns null if an - * update is not due. + * Generates a batch of transport acks for the given contact. Returns null + * if no acks are due. */ - TransportUpdate generateTransportUpdate(ContactId c) throws DbException; + Collection generateTransportAcks(ContactId c) + throws DbException; + + /** + * Generates a batch of transport updates for the given contact. Returns + * null if no updates are due. + */ + Collection generateTransportUpdates(ContactId c) + throws DbException; /** Returns the configuration for the given transport. */ TransportConfig getConfig(TransportId t) throws DbException; @@ -120,9 +138,6 @@ public interface DatabaseComponent { /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; - /** Returns all local transports. */ - Collection getLocalTransports() throws DbException; - /** Returns the headers of all messages in the given group. */ Collection getMessageHeaders(GroupId g) throws DbException; @@ -168,7 +183,7 @@ public interface DatabaseComponent { void mergeLocalProperties(TransportId t, TransportProperties p) throws DbException; - /** Processes an acknowledgement from the given contact. */ + /** Processes an ack from the given contact. */ void receiveAck(ContactId c, Ack a) throws DbException; /** Processes a message from the given contact. */ @@ -184,10 +199,17 @@ public interface DatabaseComponent { */ Request receiveOffer(ContactId c, Offer o) throws DbException; + /** Processes a subscription ack from the given contact. */ + void receiveSubscriptionAck(ContactId c, SubscriptionAck a) + throws DbException; + /** Processes a subscription update from the given contact. */ void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) throws DbException; + /** Processes a transport ack from the given contact. */ + void receiveTransportAck(ContactId c, TransportAck a) throws DbException; + /** Processes a transport update from the given contact. */ void receiveTransportUpdate(ContactId c, TransportUpdate t) throws DbException; diff --git a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java deleted file mode 100644 index 501eb886d..000000000 --- a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.sf.briar.api.db.event; - -import java.util.Collection; - -import net.sf.briar.api.ContactId; -import net.sf.briar.api.protocol.Transport; - -/** An event that is broadcast when a contact's transports are updated. */ -public class RemoteTransportsUpdatedEvent extends DatabaseEvent { - - private final ContactId contactId; - private final Collection transports; - - public RemoteTransportsUpdatedEvent(ContactId contactId, - Collection transports) { - this.contactId = contactId; - this.transports = transports; - } - - public ContactId getContactId() { - return contactId; - } - - public Collection getTransports() { - return transports; - } -} diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java similarity index 66% rename from briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java rename to briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java index c60c23b83..782b11cfe 100644 --- a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java +++ b/briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java @@ -4,6 +4,6 @@ package net.sf.briar.api.db.event; * An event that is broadcast when the local transport properties are * updated. */ -public class LocalTransportsUpdatedEvent extends DatabaseEvent { +public class TransportsUpdatedEvent extends DatabaseEvent { } diff --git a/briar-api/src/net/sf/briar/api/protocol/Ack.java b/briar-api/src/net/sf/briar/api/protocol/Ack.java index 4608fee53..8a39f38a9 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Ack.java +++ b/briar-api/src/net/sf/briar/api/protocol/Ack.java @@ -2,9 +2,17 @@ package net.sf.briar.api.protocol; import java.util.Collection; -/** A packet acknowledging receipt of one or more messages. */ -public interface Ack { +/** A packet acknowledging receipt of one or more {@link Message}s. */ +public class Ack { - /** Returns the IDs of the acknowledged messages. */ - Collection getMessageIds(); + private final Collection acked; + + public Ack(Collection acked) { + this.acked = acked; + } + + /** Returns the identifiers of the acknowledged messages. */ + public Collection getMessageIds() { + return acked; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Author.java b/briar-api/src/net/sf/briar/api/protocol/Author.java index 52266b962..1532155c3 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Author.java +++ b/briar-api/src/net/sf/briar/api/protocol/Author.java @@ -1,17 +1,43 @@ package net.sf.briar.api.protocol; -/** A pseudonymous author of messages. */ -public interface Author { +/** A pseudonymous author of {@link Message}s. */ +public class Author { + + private final AuthorId id; + private final String name; + private final byte[] publicKey; + + public Author(AuthorId id, String name, byte[] publicKey) { + this.id = id; + this.name = name; + this.publicKey = publicKey; + } /** Returns the author's unique identifier. */ - AuthorId getId(); + public AuthorId getId() { + return id; + } /** Returns the author's name. */ - String getName(); + public String getName() { + return name; + } /** * Returns the public key that is used to verify messages signed by the * author. */ - byte[] getPublicKey(); + public byte[] getPublicKey() { + return publicKey; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof Author && id.equals(((Author) o).id); + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java b/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java index 24b211197..59c21df22 100644 --- a/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java +++ b/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java @@ -5,6 +5,4 @@ import java.io.IOException; public interface AuthorFactory { Author createAuthor(String name, byte[] publicKey) throws IOException; - - Author createAuthor(AuthorId id, String name, byte[] publicKey); } diff --git a/briar-api/src/net/sf/briar/api/protocol/AuthorId.java b/briar-api/src/net/sf/briar/api/protocol/AuthorId.java index 578c89c2a..3d572d5ad 100644 --- a/briar-api/src/net/sf/briar/api/protocol/AuthorId.java +++ b/briar-api/src/net/sf/briar/api/protocol/AuthorId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Type-safe wrapper for a byte array that uniquely identifies an author. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies an + * {@link Author}. + */ public class AuthorId extends UniqueId { public AuthorId(byte[] id) { diff --git a/briar-api/src/net/sf/briar/api/protocol/GroupId.java b/briar-api/src/net/sf/briar/api/protocol/GroupId.java index 532941867..b530f8c2f 100644 --- a/briar-api/src/net/sf/briar/api/protocol/GroupId.java +++ b/briar-api/src/net/sf/briar/api/protocol/GroupId.java @@ -3,8 +3,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; /** - * Type-safe wrapper for a byte array that uniquely identifies a group to which - * users may subscribe. + * Type-safe wrapper for a byte array that uniquely identifies a {@link Group}. */ public class GroupId extends UniqueId { diff --git a/briar-api/src/net/sf/briar/api/protocol/Message.java b/briar-api/src/net/sf/briar/api/protocol/Message.java index 23b0e745a..c5dca006e 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Message.java +++ b/briar-api/src/net/sf/briar/api/protocol/Message.java @@ -6,21 +6,27 @@ public interface Message { MessageId getId(); /** - * Returns the message's parent, or null if this is the first message in a - * thread. + * Returns the identifier of the message's parent, or null if this is the + * first message in a thread. */ MessageId getParent(); - /** Returns the group to which the message belongs. */ + /** + * Returns the identifier of the {@link Group} to which the message + * belongs, or null if this is a private message. + */ GroupId getGroup(); - /** Returns the message's author. */ + /** + * Returns the identifier of the message's {@link Author}, or null if this + * is an anonymous message. + */ AuthorId getAuthor(); /** Returns the message's subject line. */ String getSubject(); - /** Returns the timestamp created by the message's author. */ + /** Returns the timestamp created by the message's {@link Author}. */ long getTimestamp(); /** Returns the serialised message. */ diff --git a/briar-api/src/net/sf/briar/api/protocol/MessageId.java b/briar-api/src/net/sf/briar/api/protocol/MessageId.java index e540f769e..88cdb1c31 100644 --- a/briar-api/src/net/sf/briar/api/protocol/MessageId.java +++ b/briar-api/src/net/sf/briar/api/protocol/MessageId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Type-safe wrapper for a byte array that uniquely identifies a message. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies a + * {@link Message}. + */ public class MessageId extends UniqueId { public MessageId(byte[] id) { diff --git a/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java b/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java index 59313414f..f99b34875 100644 --- a/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java +++ b/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java @@ -2,6 +2,7 @@ package net.sf.briar.api.protocol; import java.security.GeneralSecurityException; +/** Verifies the signatures on an {@link UnverifiedMessage}. */ public interface MessageVerifier { Message verifyMessage(UnverifiedMessage m) throws GeneralSecurityException; diff --git a/briar-api/src/net/sf/briar/api/protocol/Offer.java b/briar-api/src/net/sf/briar/api/protocol/Offer.java index 0d12875ca..d3be3b55e 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Offer.java +++ b/briar-api/src/net/sf/briar/api/protocol/Offer.java @@ -2,9 +2,17 @@ package net.sf.briar.api.protocol; import java.util.Collection; -/** A packet offering the recipient some messages. */ -public interface Offer { +/** A packet offering the recipient one or more {@link Messages}. */ +public class Offer { - /** Returns the message IDs contained in the offer. */ - Collection getMessageIds(); + private final Collection offered; + + public Offer(Collection offered) { + this.offered = offered; + } + + /** Returns the identifiers of the offered messages. */ + public Collection getMessageIds() { + return offered; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java b/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java deleted file mode 100644 index 9c3c51116..000000000 --- a/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.util.BitSet; -import java.util.Collection; -import java.util.Map; - -public interface PacketFactory { - - Ack createAck(Collection acked); - - Offer createOffer(Collection offered); - - Request createRequest(BitSet requested, int length); - - SubscriptionUpdate createSubscriptionUpdate(Map holes, - Map subs, long expiry, long timestamp); - - TransportUpdate createTransportUpdate(Collection transports, - long timestamp); -} diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java index a2aa0d79a..5dffe995c 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java @@ -11,9 +11,6 @@ public interface ProtocolConstants { */ int MAX_PACKET_LENGTH = MIN_CONNECTION_LENGTH / 2; - /** The maximum number of transports a node may support. */ - int MAX_TRANSPORTS = 25; - /** The maximum number of properties per transport. */ int MAX_PROPERTIES_PER_TRANSPORT = 100; diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java index 24f4c8aac..4712c8345 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java @@ -18,9 +18,15 @@ public interface ProtocolReader { boolean hasRequest() throws IOException; Request readRequest() throws IOException; + boolean hasSubscriptionAck() throws IOException; + SubscriptionAck readSubscriptionAck() throws IOException; + boolean hasSubscriptionUpdate() throws IOException; SubscriptionUpdate readSubscriptionUpdate() throws IOException; + boolean hasTransportAck() throws IOException; + TransportAck readTransportAck() throws IOException; + boolean hasTransportUpdate() throws IOException; TransportUpdate readTransportUpdate() throws IOException; } diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java index 4ddd4c4fb..4637fdffc 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java @@ -16,8 +16,12 @@ public interface ProtocolWriter { void writeRequest(Request r) throws IOException; + void writeSubscriptionAck(SubscriptionAck a) throws IOException; + void writeSubscriptionUpdate(SubscriptionUpdate s) throws IOException; + void writeTransportAck(TransportAck a) throws IOException; + void writeTransportUpdate(TransportUpdate t) throws IOException; void flush() throws IOException; diff --git a/briar-api/src/net/sf/briar/api/protocol/Request.java b/briar-api/src/net/sf/briar/api/protocol/Request.java index 242e59bbd..7e5d86fdd 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Request.java +++ b/briar-api/src/net/sf/briar/api/protocol/Request.java @@ -2,15 +2,30 @@ package net.sf.briar.api.protocol; import java.util.BitSet; -/** A packet requesting some or all of the messages from an offer. */ -public interface Request { +/** + * A packet requesting some or all of the {@link Message}s from an + * {@link Offer}. + */ +public class Request { + + private final BitSet requested; + private final int length; + + public Request(BitSet requested, int length) { + this.requested = requested; + this.length = length; + } /** * Returns a sequence of bits corresponding to the sequence of messages in * the offer, where the i^th bit is set if the i^th message should be sent. */ - BitSet getBitmap(); + public BitSet getBitmap() { + return requested; + } /** Returns the length of the bitmap in bits. */ - int getLength(); + public int getLength() { + return length; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java b/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java new file mode 100644 index 000000000..ba2c41a7c --- /dev/null +++ b/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java @@ -0,0 +1,16 @@ +package net.sf.briar.api.protocol; + +/** A packet acknowledging a {@link SubscriptionUpdate}. */ +public class SubscriptionAck { + + final long version; + + public SubscriptionAck(long version) { + this.version = version; + } + + /** Returns the version number of the acknowledged update. */ + public long getVersionNumber() { + return version; + } +} diff --git a/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java b/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java index 30e0e5639..8b7c1e873 100644 --- a/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java +++ b/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java @@ -1,25 +1,28 @@ package net.sf.briar.api.protocol; -import java.util.Map; +import java.util.Collection; /** A packet updating the sender's subscriptions. */ -public interface SubscriptionUpdate { +public class SubscriptionUpdate { - /** Returns the holes contained in the update. */ - Map getHoles(); + private final Collection subs; + private final long version; - /** Returns the subscriptions contained in the update. */ - Map getSubscriptions(); + public SubscriptionUpdate(Collection subs, long version) { + this.subs = subs; + this.version = version; + } /** - * Returns the expiry time of the contact's database. Messages that are - * older than the expiry time must not be sent to the contact. + * Returns the groups to which the sender subscribes, and which the sender + * has made visible to the recipient. */ - long getExpiryTime(); + public Collection getGroups() { + return subs; + } - /** - * Returns the update's timestamp. Updates that are older than the newest - * update received from the same contact must be ignored. - */ - long getTimestamp(); + /** Returns the update's version number. */ + public long getVersionNumber() { + return version; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Transport.java b/briar-api/src/net/sf/briar/api/protocol/Transport.java deleted file mode 100644 index a8ea4a315..000000000 --- a/briar-api/src/net/sf/briar/api/protocol/Transport.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.util.Map; -import java.util.TreeMap; - -public class Transport { - - private final TransportId id; - private final TreeMap properties; - - public Transport(TransportId id, Map p) { - this.id = id; - properties = new TreeMap(p); - } - - public Transport(TransportId id) { - this.id = id; - properties = new TreeMap(); - } - - public TransportId getId() { - return id; - } - - public Map getProperties() { - return properties; - } - - @Override - public int hashCode() { - return id.hashCode() ^ properties.hashCode(); - } - - @Override - public boolean equals(Object o) { - if(o instanceof Transport) { - Transport t = (Transport) o; - return id.equals(t.id) && properties.equals(t.properties); - } - return false; - } -} diff --git a/briar-api/src/net/sf/briar/api/protocol/TransportAck.java b/briar-api/src/net/sf/briar/api/protocol/TransportAck.java new file mode 100644 index 000000000..11a9dfdc7 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/protocol/TransportAck.java @@ -0,0 +1,23 @@ +package net.sf.briar.api.protocol; + +/** A packet acknowledging a {@link TransportUpdate}. */ +public class TransportAck { + + private final TransportId id; + private final long version; + + public TransportAck(TransportId id, long version) { + this.id = id; + this.version = version; + } + + /** Returns the identifier of the updated transport. */ + public TransportId getId() { + return id; + } + + /** Returns the version number of the acknowledged update. */ + public long getVersionNumber() { + return version; + } +} diff --git a/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java b/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java index 48615ccd7..b36c32eaa 100644 --- a/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java +++ b/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java @@ -1,16 +1,33 @@ package net.sf.briar.api.protocol; -import java.util.Collection; +import net.sf.briar.api.TransportProperties; /** A packet updating the sender's transport properties. */ -public interface TransportUpdate { +public class TransportUpdate { - /** Returns the transports contained in the update. */ - Collection getTransports(); + private final TransportId id; + private final TransportProperties properties; + private final long version; - /** - * Returns the update's timestamp. Updates that are older than the newest - * update received from the same contact must be ignored. - */ - long getTimestamp(); + public TransportUpdate(TransportId id, TransportProperties properties, + long version) { + this.id = id; + this.properties = properties; + this.version = version; + } + + /** Returns the identifier of the updated transport. */ + public TransportId getId() { + return id; + } + + /** Returns the transport's updated properties. */ + public TransportProperties getProperties() { + return properties; + } + + /** Returns the update's version number. */ + public long getVersionNumber() { + return version; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Types.java b/briar-api/src/net/sf/briar/api/protocol/Types.java index 71ec94a39..248d6731b 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Types.java +++ b/briar-api/src/net/sf/briar/api/protocol/Types.java @@ -9,7 +9,8 @@ public interface Types { int MESSAGE = 4; int OFFER = 5; int REQUEST = 6; - int SUBSCRIPTION_UPDATE = 7; - int TRANSPORT = 8; - int TRANSPORT_UPDATE = 9; + int SUBSCRIPTION_ACK = 7; + int SUBSCRIPTION_UPDATE = 8; + int TRANSPORT_ACK = 9; + int TRANSPORT_UPDATE = 10; } diff --git a/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java b/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java index 8fd391d2f..89e30e743 100644 --- a/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java +++ b/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java @@ -1,28 +1,61 @@ package net.sf.briar.api.protocol; +/** A {@link Message} that has not yet had its signatures verified. */ public interface UnverifiedMessage { + /** + * Returns the identifier of the message's parent, or null if this is the + * first message in a thread. + */ MessageId getParent(); + /** + * Returns the {@link Group} to which the message belongs, or null if this + * is a private message. + */ Group getGroup(); + /** + * Returns the message's {@link Author}, or null if this is an anonymous + * message. + */ Author getAuthor(); + /** Returns the message's subject line. */ String getSubject(); + /** Returns the timestamp created by the message's {@link Author}. */ long getTimestamp(); + /** Returns the serialised message. */ byte[] getSerialised(); + /** + * Returns the author's signature, or null if this is an anonymous message. + */ byte[] getAuthorSignature(); + /** + * Returns the group's signature, or null if this is a private message or + * a message belonging to an unrestricted group. + */ byte[] getGroupSignature(); + /** Returns the offset of the message body within the serialised message. */ int getBodyStart(); + /** Returns the length of the message body in bytes. */ int getBodyLength(); + /** + * Returns the length in bytes of the data covered by the author's + * signature. + */ int getLengthSignedByAuthor(); + /** + * Returns the length in bytes of the data covered by the group's + * signature. + */ int getLengthSignedByGroup(); } \ No newline at end of file diff --git a/briar-api/src/net/sf/briar/api/serial/Reader.java b/briar-api/src/net/sf/briar/api/serial/Reader.java index e0610feb8..17146a950 100644 --- a/briar-api/src/net/sf/briar/api/serial/Reader.java +++ b/briar-api/src/net/sf/briar/api/serial/Reader.java @@ -18,7 +18,7 @@ public interface Reader { void addConsumer(Consumer c); void removeConsumer(Consumer c); - void addStructReader(int id, StructReader o); + void addStructReader(int id, StructReader r); void removeStructReader(int id); boolean hasBoolean() throws IOException; diff --git a/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar b/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar index 5bce0d4a2ef4d4c9b686b182f0102d904ff38f00..d9bd9fc00289586406f4630f739f0b2a261d29a6 100644 GIT binary patch delta 13821 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 delta 13530 zcmZWv2Rv2p|L>kVj&-?YXGO@Kl^F>kNyARsrHE)yiU!e?OP>azB@N-msV_<6o3=E+ z4W+%4mUjQoIY*a%|Gux+x6b?hS!W969y0G@>F}t;D7lm%r^*K zxcFC9fRF=+s|BMerNe?k5vBWF&?W%kbAbqcZsF)a`g4H-=!;1Kcw7}|=jnDMa@45* z1ouhstRQ-TFd>hWr#4;{aeB*RrJ@98kD<4aD@j3#w1TXi(>%0DMB4?{S@ekC%E&(^`SBny^%{u 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 V0{R0*pQFz?ncS<_1tSul{tweI$Cdy9 diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index c7a9a51c4..229e912b6 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -15,8 +15,11 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; import net.sf.briar.api.transport.TemporarySecret; @@ -73,7 +76,7 @@ interface Database { /** * Adds a new contact to the database and returns an ID for the contact. *

- * Locking: contact write, subscription write, transport write. + * Locking: contact write, subscription write. */ ContactId addContact(T txn) throws DbException; @@ -85,8 +88,8 @@ interface Database { void addContactTransport(T txn, ContactTransport ct) throws DbException; /** - * Returns false if the given message is already in the database. Otherwise - * stores the message and returns true. + * Stores the given message, or returns false if the message is already in + * the database. *

* Locking: message write. */ @@ -108,8 +111,8 @@ interface Database { throws DbException; /** - * Returns false if the given message is already in the database. Otherwise - * stores the message and returns true. + * Stores the given message, or returns false if the message is already in + * the database. *

* Locking: contact read, message write. */ @@ -132,18 +135,16 @@ interface Database { void addSubscription(T txn, Group g) throws DbException; /** - * Records the given contact's subscription to the given group starting at - * the given time. + * Adds a new transport to the database. *

- * Locking: contact read, subscription write. + * Locking: transport write. */ - void addSubscription(T txn, ContactId c, Group g, long start) - throws DbException; + void addTransport(T txn, TransportId t) throws DbException; /** * Makes the given group visible to the given contact. *

- * Locking: contact read, subscription write. + * Locking: contact write, subscription write. */ void addVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -177,23 +178,13 @@ interface Database { boolean containsSubscription(T txn, GroupId g) throws DbException; /** - * Returns true if the user has been subscribed to the given group since - * the given time. - *

- * Locking: subscription read. - */ - boolean containsSubscription(T txn, GroupId g, long time) - throws DbException; - - /** - * Returns true if the user is subscribed to the given group, the group is - * visible to the given contact, and the subscription has existed since the - * given time. + * Returns true if the user subscribes to the given group and the + * subscription is visible to the given contact. *

* Locking: contact read, subscription read. */ - boolean containsVisibleSubscription(T txn, GroupId g, ContactId c, - long time) throws DbException; + boolean containsVisibleSubscription(T txn, ContactId c, GroupId g) + throws DbException; /** * Returns the configuration for the given transport. @@ -249,13 +240,6 @@ interface Database { TransportProperties getLocalProperties(T txn, TransportId t) throws DbException; - /** - * Returns all local transports. - *

- * Locking: transport read. - */ - Collection getLocalTransports(T txn) throws DbException; - /** * Returns the message identified by the given ID, in serialised form. *

@@ -402,20 +386,42 @@ interface Database { Collection getSubscriptions(T txn, ContactId c) throws DbException; /** - * Returns the time at which the local transports were last modified. + * Returns a subscription ack for the given contact, or null if no ack is + * due. *

- * Locking: transport read. + * Locking: contact read, subscription write. */ - long getTransportsModified(T txn) throws DbException; + SubscriptionAck getSubscriptionAck(T txn, ContactId c) throws DbException; /** - * Returns the time at which a transport update was last sent to the given - * contact. + * Returns a subscription update for the given contact, or null if no + * update is due. *

- * Locking: contact read, transport read. + * Locking: contact read, subscription write. */ - long getTransportsSent(T txn, ContactId c) throws DbException; + SubscriptionUpdate getSubscriptionUpdate(T txn, ContactId c) + throws DbException; + /** + * Returns a collection of transport acks for the given contact, or null if + * no acks are due. + *

+ * Locking: contact read, transport write. + */ + Collection getTransportAcks(T txn, ContactId c) + throws DbException; + + /** + * Returns a collection of transport updates for the given contact, or + * null if no updates are due. + *

+ * Locking: contact read, transport write. + */ + Collection getTransportUpdates(T txn, ContactId c) + throws DbException; + + /** + * Returns the version number of the /** * Returns the number of unread messages in each subscribed group. *

@@ -430,26 +436,6 @@ interface Database { */ Collection getVisibility(T txn, GroupId g) throws DbException; - /** - * Returns any holes covering unsubscriptions that are visible to the given - * contact, occurred strictly before the given timestamp, and have not yet - * been acknowledged. - *

- * Locking: contact read, subscription read. - */ - Map getVisibleHoles(T txn, ContactId c, long timestamp) - throws DbException; - - /** - * Returns any subscriptions that are visible to the given contact, - * occurred strictly before the given timestamp, and have not yet been - * acknowledged. - *

- * Locking: contact read, subscription read. - */ - Map getVisibleSubscriptions(T txn, ContactId c, long timestamp) - throws DbException; - /** * Returns true if any messages are sendable to the given contact. *

@@ -523,25 +509,22 @@ interface Database { * Unsubscribes from the given group. Any messages belonging to the group * are deleted from the database. *

- * Locking: contact read, message write, messageFlag write, + * Locking: contact write, message write, messageFlag write, * messageStatus write, subscription write. */ void removeSubscription(T txn, GroupId g) throws DbException; /** - * Removes any subscriptions for the given contact with IDs between the - * given IDs. If both of the given IDs are null, all subscriptions are - * removed. If only the first is null, all subscriptions with IDs less than - * the second ID are removed. If onlt the second is null, all subscriptions - * with IDs greater than the first are removed. + * Removes a transport (and all associated state) from the database. + *

+ * Locking: contact read, transport write. */ - void removeSubscriptions(T txn, ContactId c, GroupId start, GroupId end) - throws DbException; + void removeTransport(T txn, TransportId t) throws DbException; /** * Makes the given group invisible to the given contact. *

- * Locking: contact read, subscription write. + * Locking: contact write, subscription write. */ void removeVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -557,7 +540,7 @@ interface Database { /** * Sets the given contact's database expiry time. *

- * Locking: contact read, subscription write. + * Locking: contact write. */ void setExpiryTime(T txn, ContactId c, long expiry) throws DbException; @@ -576,6 +559,17 @@ interface Database { */ boolean setRead(T txn, MessageId m, boolean read) throws DbException; + /** + * Updates the remote transport properties for the given contact and the + * given transport, replacing any existing properties, unless an update + * with an equal or higher version number has already been received from + * the contact. + *

+ * Locking: contact read, transport write. + */ + void setRemoteProperties(T txn, ContactId c, TransportUpdate t) + throws DbException; + /** * Sets the sendability score of the given message. *

@@ -612,45 +606,30 @@ interface Database { throws DbException; /** - * Records the time of the latest subscription update acknowledged by the - * given contact. + * Updates the groups to which the given contact subscribes, unless an + * update with an equal or higher version number has already been received + * from the contact. *

* Locking: contact read, subscription write. */ - void setSubscriptionsAcked(T txn, ContactId c, long timestamp) + void setSubscriptions(T txn, ContactId c, SubscriptionUpdate s) throws DbException; /** - * Records the time of the latest subscription update received from the - * given contact. + * Records a subscription ack from the given contact for the given version + * unless the contact has already acked an equal or higher version. *

* Locking: contact read, subscription write. */ - void setSubscriptionsReceived(T txn, ContactId c, long timestamp) + void setSubscriptionUpdateAcked(T txn, ContactId c, long version) throws DbException; /** - * Sets the transports for the given contact, replacing any existing - * transports unless the existing transports have a newer timestamp. + * Records a transport ack from the give contact for the given version + * unless the contact has already acked an equal or higher version. *

* Locking: contact read, transport write. */ - void setTransports(T txn, ContactId c, Collection transports, - long timestamp) throws DbException; - - /** - * Records the time at which the local transports were last modified. - *

- * Locking: contact read, transport write. - */ - void setTransportsModified(T txn, long timestamp) throws DbException; - - /** - * Records the time at which a transport update was last sent to the given - * contact. - *

- * Locking: contact read, transport write. - */ - void setTransportsSent(T txn, ContactId c, long timestamp) - throws DbException; + void setTransportUpdateAcked(T txn, ContactId c, TransportId t, + long version) throws DbException; } diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index d9333bcd2..d1308b3b1 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -5,7 +5,6 @@ import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP; import static net.sf.briar.db.DatabaseConstants.CRITICAL_FREE_SPACE; import static net.sf.briar.db.DatabaseConstants.MAX_BYTES_BETWEEN_SPACE_CHECKS; import static net.sf.briar.db.DatabaseConstants.MAX_MS_BETWEEN_SPACE_CHECKS; -import static net.sf.briar.db.DatabaseConstants.MAX_UPDATE_INTERVAL; import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE; import java.io.IOException; @@ -17,7 +16,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Logger; @@ -36,12 +34,11 @@ import net.sf.briar.api.db.event.ContactAddedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; -import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; +import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; @@ -50,10 +47,10 @@ import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; @@ -98,7 +95,6 @@ DatabaseCleaner.Callback { private final Database db; private final DatabaseCleaner cleaner; private final ShutdownManager shutdown; - private final PacketFactory packetFactory; private final Clock clock; private final Collection listeners = @@ -114,12 +110,10 @@ DatabaseCleaner.Callback { @Inject DatabaseComponentImpl(Database db, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory, - Clock clock) { + ShutdownManager shutdown, Clock clock) { this.db = db; this.cleaner = cleaner; this.shutdown = shutdown; - this.packetFactory = packetFactory; this.clock = clock; } @@ -173,23 +167,13 @@ DatabaseCleaner.Callback { try { subscriptionLock.writeLock().lock(); try { - transportLock.writeLock().lock(); + T txn = db.startTransaction(); try { - windowLock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - c = db.addContact(txn); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - windowLock.writeLock().unlock(); - } - } finally { - transportLock.writeLock().unlock(); + c = db.addContact(txn); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { subscriptionLock.writeLock().unlock(); @@ -243,12 +227,9 @@ DatabaseCleaner.Callback { T txn = db.startTransaction(); try { // Don't store the message if the user has - // unsubscribed from the group or the message - // predates the subscription - if(db.containsSubscription(txn, m.getGroup(), - m.getTimestamp())) { + // unsubscribed from the group + if(db.containsSubscription(txn, m.getGroup())) added = storeGroupMessage(txn, m, null); - } db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -418,11 +399,27 @@ DatabaseCleaner.Callback { } } + public void addTransport(TransportId t) throws DbException { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + db.addTransport(txn, t); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + } + /** - * If the given message is already in the database, returns false. * Otherwise stores the message and marks it as new or seen with respect to * the given contact, depending on whether the message is outgoing or - * incoming, respectively. + * incoming, respectively; or returns false if the message is already in + * the database. *

* Locking: contact read, message write, messageStatus write. */ @@ -478,7 +475,7 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createAck(acked); + return new Ack(acked); } public Collection generateBatch(ContactId c, int maxLength) @@ -627,80 +624,94 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createOffer(offered); + return new Offer(offered); } - public SubscriptionUpdate generateSubscriptionUpdate(ContactId c) + public SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException { - Map holes; - Map subs; - long expiry, timestamp; contactLock.readLock().lock(); try { - subscriptionLock.readLock().lock(); + subscriptionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - timestamp = clock.currentTimeMillis() - 1; - holes = db.getVisibleHoles(txn, c, timestamp); - subs = db.getVisibleSubscriptions(txn, c, timestamp); - expiry = db.getExpiryTime(txn); + SubscriptionAck a = db.getSubscriptionAck(txn, c); db.commitTransaction(txn); + return a; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - subscriptionLock.readLock().unlock(); + subscriptionLock.writeLock().unlock(); } } finally { contactLock.readLock().unlock(); } - return packetFactory.createSubscriptionUpdate(holes, subs, expiry, - timestamp); } - private boolean updateIsDue(long sent) { - long now = clock.currentTimeMillis(); - return now - sent >= MAX_UPDATE_INTERVAL; - } - - public TransportUpdate generateTransportUpdate(ContactId c) + public SubscriptionUpdate generateSubscriptionUpdate(ContactId c) throws DbException { - boolean due; - Collection transports; - long timestamp; contactLock.readLock().lock(); try { - transportLock.readLock().lock(); + subscriptionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - // Work out whether an update is due - long modified = db.getTransportsModified(txn); - long sent = db.getTransportsSent(txn, c); - due = modified >= sent || updateIsDue(sent); + SubscriptionUpdate s = db.getSubscriptionUpdate(txn, c); db.commitTransaction(txn); + return s; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - transportLock.readLock().unlock(); + subscriptionLock.writeLock().unlock(); } - if(!due) return null; + } finally { + contactLock.readLock().unlock(); + } + } + + public Collection generateTransportAcks(ContactId c) + throws DbException { + contactLock.readLock().lock(); + try { transportLock.writeLock().lock(); try { T txn = db.startTransaction(); try { - transports = db.getLocalTransports(txn); - timestamp = clock.currentTimeMillis(); - db.setTransportsSent(txn, c, timestamp); + Collection acks = db.getTransportAcks(txn, c); db.commitTransaction(txn); + return acks; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + + public Collection generateTransportUpdates(ContactId c) + throws DbException { + contactLock.readLock().lock(); + try { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + Collection updates = + db.getTransportUpdates(txn, c); + db.commitTransaction(txn); + return updates; } catch(DbException e) { db.abortTransaction(txn); throw e; @@ -711,7 +722,6 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createTransportUpdate(transports, timestamp); } public TransportConfig getConfig(TransportId t) throws DbException { @@ -766,23 +776,6 @@ DatabaseCleaner.Callback { } } - public Collection getLocalTransports() throws DbException { - transportLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - Collection transports = db.getLocalTransports(txn); - db.commitTransaction(txn); - return transports; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - transportLock.readLock().unlock(); - } - } - public Collection getMessageHeaders(GroupId g) throws DbException { messageLock.readLock().lock(); @@ -1022,7 +1015,6 @@ DatabaseCleaner.Callback { try { if(!p.equals(db.getLocalProperties(txn, t))) { db.mergeLocalProperties(txn, t, p); - db.setTransportsModified(txn, clock.currentTimeMillis()); changed = true; } db.commitTransaction(txn); @@ -1034,7 +1026,7 @@ DatabaseCleaner.Callback { transportLock.writeLock().unlock(); } // Call the listeners outside the lock - if(changed) callListeners(new LocalTransportsUpdatedEvent()); + if(changed) callListeners(new TransportsUpdatedEvent()); } public void receiveAck(ContactId c, Ack a) throws DbException { @@ -1114,8 +1106,7 @@ DatabaseCleaner.Callback { throws DbException { GroupId g = m.getGroup(); if(g == null) return storePrivateMessage(txn, m, c, true); - if(!db.containsVisibleSubscription(txn, g, c, m.getTimestamp())) - return false; + if(!db.containsVisibleSubscription(txn, c, g)) return false; return storeGroupMessage(txn, m, c); } @@ -1161,12 +1152,11 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createRequest(request, offered.size()); + return new Request(request, offered.size()); } - public void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) + public void receiveSubscriptionAck(ContactId c, SubscriptionAck a) throws DbException { - // Update the contact's subscriptions contactLock.readLock().lock(); try { subscriptionLock.writeLock().lock(); @@ -1175,17 +1165,8 @@ DatabaseCleaner.Callback { try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - Map holes = s.getHoles(); - for(Entry e : holes.entrySet()) { - GroupId start = e.getKey(), end = e.getValue(); - db.removeSubscriptions(txn, c, start, end); - } - Map subs = s.getSubscriptions(); - for(Entry e : subs.entrySet()) { - db.addSubscription(txn, c, e.getKey(), e.getValue()); - } - db.setExpiryTime(txn, c, s.getExpiryTime()); - db.setSubscriptionsReceived(txn, c, s.getTimestamp()); + long version = a.getVersionNumber(); + db.setSubscriptionUpdateAcked(txn, c, version); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -1197,15 +1178,34 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - // Call the listeners outside the lock - callListeners(new SubscriptionsUpdatedEvent( - Collections.singletonList(c))); } - public void receiveTransportUpdate(ContactId c, TransportUpdate t) + public void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) + throws DbException { + contactLock.readLock().lock(); + try { + subscriptionLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setSubscriptions(txn, c, s); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + + public void receiveTransportAck(ContactId c, TransportAck a) throws DbException { - Collection transports; - // Update the contact's transport properties contactLock.readLock().lock(); try { transportLock.writeLock().lock(); @@ -1214,8 +1214,33 @@ DatabaseCleaner.Callback { try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - transports = t.getTransports(); - db.setTransports(txn, c, transports, t.getTimestamp()); + TransportId t = a.getId(); + long version = a.getVersionNumber(); + db.setTransportUpdateAcked(txn, c, t, version); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + + public void receiveTransportUpdate(ContactId c, TransportUpdate t) + throws DbException { + contactLock.readLock().lock(); + try { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setRemoteProperties(txn, c, t); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -1227,8 +1252,6 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - // Call the listeners outside the lock - callListeners(new RemoteTransportsUpdatedEvent(c, transports)); } public void removeContact(ContactId c) throws DbException { @@ -1398,8 +1421,7 @@ DatabaseCleaner.Callback { public void setVisibility(GroupId g, Collection visible) throws DbException { - List affected = new ArrayList(); - contactLock.readLock().lock(); + contactLock.writeLock().lock(); try { subscriptionLock.writeLock().lock(); try { @@ -1413,13 +1435,8 @@ DatabaseCleaner.Callback { for(ContactId c : db.getContacts(txn)) { boolean then = oldVisible.contains(c); boolean now = visible.contains(c); - if(!then && now) { - db.addVisibility(txn, c, g); - affected.add(c); - } else if(then && !now) { - db.removeVisibility(txn, c, g); - affected.add(c); - } + if(!then && now) db.addVisibility(txn, c, g); + else if(then && !now) db.removeVisibility(txn, c, g); } db.commitTransaction(txn); } catch(DbException e) { @@ -1430,12 +1447,7 @@ DatabaseCleaner.Callback { subscriptionLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); - } - // Call the listeners outside the lock - if(!affected.isEmpty()) { - affected = Collections.unmodifiableList(affected); - callListeners(new SubscriptionsUpdatedEvent(affected)); + contactLock.writeLock().unlock(); } } @@ -1459,7 +1471,7 @@ DatabaseCleaner.Callback { public void unsubscribe(GroupId g) throws DbException { Collection affected = null; - contactLock.readLock().lock(); + contactLock.writeLock().lock(); try { messageLock.writeLock().lock(); try { @@ -1493,7 +1505,7 @@ DatabaseCleaner.Callback { messageLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); + contactLock.writeLock().unlock(); } // Call the listeners outside the lock if(affected != null && !affected.isEmpty()) diff --git a/briar-core/src/net/sf/briar/db/DatabaseModule.java b/briar-core/src/net/sf/briar/db/DatabaseModule.java index 6ed65d9b8..ce5cdcf6b 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseModule.java +++ b/briar-core/src/net/sf/briar/db/DatabaseModule.java @@ -8,7 +8,6 @@ import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.lifecycle.ShutdownManager; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.util.BoundedExecutor; import com.google.inject.AbstractModule; @@ -41,15 +40,14 @@ public class DatabaseModule extends AbstractModule { } @Provides - Database getDatabase(Clock clock, DatabaseConfig config) { - return new H2Database(clock, config); + Database getDatabase(DatabaseConfig config) { + return new H2Database(config); } @Provides @Singleton DatabaseComponent getDatabaseComponent(Database db, - DatabaseCleaner cleaner, ShutdownManager shutdown, - PacketFactory packetFactory, Clock clock) { + DatabaseCleaner cleaner, ShutdownManager shutdown, Clock clock) { return new DatabaseComponentImpl(db, cleaner, shutdown, - packetFactory, clock); + clock); } } diff --git a/briar-core/src/net/sf/briar/db/H2Database.java b/briar-core/src/net/sf/briar/db/H2Database.java index 4c00ec8c2..471b67910 100644 --- a/briar-core/src/net/sf/briar/db/H2Database.java +++ b/briar-core/src/net/sf/briar/db/H2Database.java @@ -30,8 +30,8 @@ class H2Database extends JdbcDatabase { private final long maxSize; @Inject - H2Database(Clock clock, DatabaseConfig config) { - super(clock, HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE); + H2Database(DatabaseConfig config) { + super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE); home = new File(config.getDataDirectory(), "db"); url = "jdbc:h2:split:" + home.getPath() + ";CIPHER=AES;DB_CLOSE_ON_EXIT=false"; diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index d5a4f5544..708595a0b 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -27,7 +27,6 @@ import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; -import net.sf.briar.api.clock.Clock; import net.sf.briar.api.db.DbClosedException; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; @@ -36,8 +35,11 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; import net.sf.briar.api.transport.TemporarySecret; import net.sf.briar.util.FileUtils; @@ -48,19 +50,14 @@ import net.sf.briar.util.FileUtils; */ abstract class JdbcDatabase implements Database { - private static final String CREATE_SUBSCRIPTIONS = - "CREATE TABLE subscriptions" - + " (groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (groupId))"; - + // Locking: contact private static final String CREATE_CONTACTS = "CREATE TABLE contacts" + " (contactId COUNTER," + + " expiry BIGINT NOT NULL DEFAULT 0," // FIXME: Move this + " PRIMARY KEY (contactId))"; + // Locking: message private static final String CREATE_MESSAGES = "CREATE TABLE messages" + " (messageId HASH NOT NULL," @@ -77,9 +74,10 @@ abstract class JdbcDatabase implements Database { + " contactId INT," // Null for group messages + " PRIMARY KEY (messageId)," + " FOREIGN KEY (groupId)" - + " REFERENCES subscriptions (groupId)" + + " REFERENCES groups (groupId)" + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; private static final String INDEX_MESSAGES_BY_PARENT = @@ -94,58 +92,28 @@ abstract class JdbcDatabase implements Database { private static final String INDEX_MESSAGES_BY_SENDABILITY = "CREATE INDEX messagesBySendability ON messages (sendability)"; - private static final String CREATE_VISIBILITIES = - "CREATE TABLE visibilities" - + " (contactId INT NOT NULL," - + " groupId HASH," // Null for the head of the linked list - + " nextId HASH," // Null for the tail of the linked list - + " deleted BIGINT NOT NULL," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (groupId)" - + " REFERENCES subscriptions (groupId)" - + " ON DELETE CASCADE)"; - - private static final String INDEX_VISIBILITIES_BY_GROUP = - "CREATE INDEX visibilitiesByGroup ON visibilities (groupId)"; - - private static final String INDEX_VISIBILITIES_BY_NEXT = - "CREATE INDEX visibilitiesByNext on visibilities (nextId)"; - + // Locking: contact read, messageStatus private static final String CREATE_MESSAGES_TO_ACK = "CREATE TABLE messagesToAck" + " (messageId HASH NOT NULL," + " contactId INT NOT NULL," + " PRIMARY KEY (messageId, contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - private static final String CREATE_CONTACT_SUBSCRIPTIONS = - "CREATE TABLE contactSubscriptions" - + " (contactId INT NOT NULL," - + " groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (contactId, groupId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_RATINGS = - "CREATE TABLE ratings" - + " (authorId HASH NOT NULL," - + " rating SMALLINT NOT NULL," - + " PRIMARY KEY (authorId))"; - + // Locking: contact read, message read, messageStatus private static final String CREATE_STATUSES = "CREATE TABLE statuses" + " (messageId HASH NOT NULL," + " contactId INT NOT NULL," + " status SMALLINT NOT NULL," + " PRIMARY KEY (messageId, contactId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " FOREIGN KEY (messageId)" + + " REFERENCES messages (messageId)" + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; private static final String INDEX_STATUSES_BY_MESSAGE = @@ -154,30 +122,137 @@ abstract class JdbcDatabase implements Database { private static final String INDEX_STATUSES_BY_CONTACT = "CREATE INDEX statusesByContact ON statuses (contactId)"; + // Locking: message read, messageFlag + private static final String CREATE_FLAGS = + "CREATE TABLE flags" + + " (messageId HASH NOT NULL," + + " read BOOLEAN NOT NULL," + + " starred BOOLEAN NOT NULL," + + " PRIMARY KEY (messageId)," + + " FOREIGN KEY (messageId)" + + " REFERENCES messages (messageId)" + + " ON DELETE CASCADE)"; + + // Locking: rating + private static final String CREATE_RATINGS = + "CREATE TABLE ratings" + + " (authorId HASH NOT NULL," + + " rating SMALLINT NOT NULL," + + " PRIMARY KEY (authorId))"; + + // Locking: subscription + private static final String CREATE_GROUPS = + "CREATE TABLE groups" + + " (groupId HASH NOT NULL," + + " name VARCHAR NOT NULL," + + " key BINARY," // Null for unrestricted groups + + " PRIMARY KEY (groupId))"; + + // Locking: contact read, subscription + private static final String CREATE_GROUP_VISIBILITIES = + "CREATE TABLE groupVisibilities" + + " (contactId INT NOT NULL," + + " groupId HASH NOT NULL," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (groupId)" + + " REFERENCES groups (groupId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, subscription + private static final String CREATE_CONTACT_GROUPS = + "CREATE TABLE contactGroups" + + " (contactId INT NOT NULL," + + " groupId HASH NOT NULL," // Not a foreign key + + " name VARCHAR NOT NULL," + + " key BINARY," // Null for unrestricted groups + + " PRIMARY KEY (contactId, groupId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, subscription + private static final String CREATE_GROUP_VERSIONS = + "CREATE TABLE groupVersions" + + " (contactId INT NOT NULL," + + " localVersion BIGINT NOT NULL," + + " localAcked BIGINT NOT NULL," + + " remoteVersion BIGINT NOT NULL," + + " remoteAcked BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactid)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: transport + private static final String CREATE_TRANSPORTS = + "CREATE TABLE transports" + + " (transportId HASH NOT NULL," + + " PRIMARY KEY (transportId))"; + + // Locking: transport private static final String CREATE_TRANSPORT_CONFIGS = "CREATE TABLE transportConfigs" + " (transportId HASH NOT NULL," + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; + + " PRIMARY KEY (transportId, key)," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + // Locking: transport private static final String CREATE_TRANSPORT_PROPS = "CREATE TABLE transportProperties" + " (transportId HASH NOT NULL," + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; + + " PRIMARY KEY (transportId, key)," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + // Locking: contact read, transport + private static final String CREATE_TRANSPORT_VERSIONS = + "CREATE TABLE transportVersions" + + " (contactId HASH NOT NULL," + + " transportId HASH NOT NULL," + + " localVersion BIGINT NOT NULL," + + " localAcked BIGINT NOT NULL," + + " PRIMARY KEY (contactId, transportId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, transport private static final String CREATE_CONTACT_TRANSPORT_PROPS = "CREATE TABLE contactTransportProperties" + " (contactId INT NOT NULL," - + " transportId HASH NOT NULL," + + " transportId HASH NOT NULL," // Not a foreign key + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," + " PRIMARY KEY (contactId, transportId, key)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; + // Locking: contact read, transport + private static final String CREATE_CONTACT_TRANSPORT_VERSIONS = + "CREATE TABLE contactTransportVersions" + + " (contactId HASH NOT NULL," + + " transportId HASH NOT NULL," // Not a foreign key + + " remoteVersion BIGINT NOT NULL," + + " remoteAcked BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId, transportId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, window private static final String CREATE_CONTACT_TRANSPORTS = "CREATE TABLE contactTransports" + " (contactId INT NOT NULL," @@ -187,9 +262,14 @@ abstract class JdbcDatabase implements Database { + " latency BIGINT NOT NULL," + " alice BOOLEAN NOT NULL," + " PRIMARY KEY (contactId, transportId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; + // Locking: contact read, window private static final String CREATE_SECRETS = "CREATE TABLE secrets" + " (contactId INT NOT NULL," @@ -200,42 +280,16 @@ abstract class JdbcDatabase implements Database { + " centre BIGINT NOT NULL," + " bitmap BINARY NOT NULL," + " PRIMARY KEY (contactId, transportId, period)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_SUBSCRIPTION_TIMES = - "CREATE TABLE subscriptionTimes" - + " (contactId INT NOT NULL," - + " received BIGINT NOT NULL," - + " acked BIGINT NOT NULL," - + " expiry BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_TRANSPORT_TIMESTAMPS = - "CREATE TABLE transportTimestamps" - + " (contactId INT NOT NULL," - + " sent BIGINT NOT NULL," - + " received BIGINT NOT NULL," - + " modified BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_FLAGS = - "CREATE TABLE flags" - + " (messageId HASH NOT NULL," - + " read BOOLEAN NOT NULL," - + " starred BOOLEAN NOT NULL," - + " PRIMARY KEY (messageId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; private static final Logger LOG = Logger.getLogger(JdbcDatabase.class.getName()); - private final Clock clock; // Different database libraries use different names for certain types private final String hashType, binaryType, counterType, secretType; @@ -247,9 +301,8 @@ abstract class JdbcDatabase implements Database { protected abstract Connection createConnection() throws SQLException; - JdbcDatabase(Clock clock, String hashType, String binaryType, - String counterType, String secretType) { - this.clock = clock; + JdbcDatabase(String hashType, String binaryType, String counterType, + String secretType) { this.hashType = hashType; this.binaryType = binaryType; this.counterType = counterType; @@ -286,30 +339,30 @@ abstract class JdbcDatabase implements Database { Statement s = null; try { s = txn.createStatement(); - s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACTS)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES)); s.executeUpdate(INDEX_MESSAGES_BY_PARENT); s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR); s.executeUpdate(INDEX_MESSAGES_BY_TIMESTAMP); s.executeUpdate(INDEX_MESSAGES_BY_SENDABILITY); - s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES)); - s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP); - s.executeUpdate(INDEX_VISIBILITIES_BY_NEXT); s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK)); - s.executeUpdate(insertTypeNames(CREATE_CONTACT_SUBSCRIPTIONS)); - s.executeUpdate(insertTypeNames(CREATE_RATINGS)); s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_CONTACT); + s.executeUpdate(insertTypeNames(CREATE_FLAGS)); + s.executeUpdate(insertTypeNames(CREATE_RATINGS)); + s.executeUpdate(insertTypeNames(CREATE_GROUPS)); + s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS)); + s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_SECRETS)); - s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMES)); - s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS)); - s.executeUpdate(insertTypeNames(CREATE_FLAGS)); s.close(); } catch(SQLException e) { tryToClose(s); @@ -427,7 +480,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - // Create a new contact row + // Create a contact row String sql = "INSERT INTO contacts DEFAULT VALUES"; ps = txn.prepareStatement(sql); int affected = ps.executeUpdate(); @@ -444,31 +497,41 @@ abstract class JdbcDatabase implements Database { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - // Create the head-of-list pointer for the visibility list - sql = "INSERT INTO visibilities (contactId, deleted)" - + " VALUES (?, ZERO())"; + // Create a group version row + sql = "INSERT INTO groupVersions (contactId, localVersion," + + " localAcked, remoteVersion, remoteAcked)" + + " VALUES (?, ?, ZERO(), ZERO(), TRUE)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); + ps.setInt(2, 1); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - // Initialise the subscription timestamps - sql = "INSERT INTO subscriptionTimes" - + " (contactId, received, acked, expiry)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + // Create a transport version row for each local transport + sql = "SELECT transportId FROM transports"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); + rs = ps.executeQuery(); + Collection transports = new ArrayList(); + while(rs.next()) transports.add(rs.getBytes(1)); + rs.close(); ps.close(); - // Initialise the transport timestamps - sql = "INSERT INTO transportTimestamps" - + " (contactId, sent, received, modified)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + if(transports.isEmpty()) return c; + sql = "INSERT INTO transportVersions" + + " (contactId, transportId, localVersion, localAcked)" + + " VALUES (?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); + ps.setInt(3, 1); + for(byte[] t : transports) { + ps.setBytes(2, t); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != transports.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); + } ps.close(); return c; } catch(SQLException e) { @@ -482,9 +545,8 @@ abstract class JdbcDatabase implements Database { throws DbException { PreparedStatement ps = null; try { - String sql = "INSERT INTO contactTransports" - + " (contactId, transportId, epoch, clockDiff, latency," - + " alice)" + String sql = "INSERT INTO contactTransports (contactId," + + " transportId, epoch, clockDiff, latency, alice)" + " VALUES (?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, ct.getContactId().getInt()); @@ -630,15 +692,12 @@ abstract class JdbcDatabase implements Database { public void addSubscription(Connection txn, Group g) throws DbException { PreparedStatement ps = null; try { - String sql = "INSERT INTO subscriptions" - + " (groupId, groupName, groupKey, start)" - + " VALUES (?, ?, ?, ?)"; + String sql = "INSERT INTO groups" + + " (groupId, name, key) VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getId().getBytes()); ps.setString(2, g.getName()); ps.setBytes(3, g.getPublicKey()); - long now = clock.currentTimeMillis(); - ps.setLong(4, now); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -653,9 +712,8 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; try { // Store the new secrets - String sql = "INSERT INTO secrets" - + " (contactId, transportId, period, secret, outgoing," - + " centre, bitmap)" + String sql = "INSERT INTO secrets (contactId, transportId, period," + + " secret, outgoing, centre, bitmap)" + " VALUES (?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); for(TemporarySecret s : secrets) { @@ -698,39 +756,45 @@ abstract class JdbcDatabase implements Database { } } - public void addSubscription(Connection txn, ContactId c, Group g, - long start) throws DbException { + public void addTransport(Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - // Check whether the subscription already exists - String sql = "SELECT NULL FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId = ?"; + // Create a transport row + String sql = "INSERT INTO transports (transportId) VALUES (?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getId().getBytes()); - rs = ps.executeQuery(); - boolean found = rs.next(); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - if(found) return; - // Add the subscription - sql = "INSERT INTO contactSubscriptions" - + " (contactId, groupId, groupName, groupKey, start)" - + " VALUES (?, ?, ?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getId().getBytes()); - ps.setString(3, g.getName()); - ps.setBytes(4, g.getPublicKey()); - ps.setLong(5, start); + ps.setBytes(1, t.getBytes()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); + // Create a transport version row for each contact + sql = "SELECT contactId FROM contacts"; + ps = txn.prepareStatement(sql); + rs = ps.executeQuery(); + Collection contacts = new ArrayList(); + while(rs.next()) contacts.add(rs.getInt(1)); + rs.close(); + ps.close(); + if(contacts.isEmpty()) return; + sql = "INSERT INTO transportVersions" + + " (contactId, transportId, localVersion, localAcked)" + + " VALUES (?, ?, ?, ZERO())"; + ps = txn.prepareStatement(sql); + ps.setBytes(2, t.getBytes()); + ps.setInt(3, 1); + for(Integer c : contacts) { + ps.setInt(1, c); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != contacts.size()) + throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } @@ -738,71 +802,25 @@ abstract class JdbcDatabase implements Database { public void addVisibility(Connection txn, ContactId c, GroupId g) throws DbException { PreparedStatement ps = null; - ResultSet rs = null; try { - // Find the new element's predecessor - byte[] groupId = null, nextId = null; - long deleted = 0L; - String sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId > ?" - + " ORDER BY nextId LIMIT ?"; + String sql = "INSERT INTO groupVisibilities (contactId, groupId)" + + " VALUES (?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - ps.setInt(3, 1); - rs = ps.executeQuery(); - if(!rs.next()) { - // The predecessor has a null nextId so it's at the tail - rs.close(); - ps.close(); - sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId IS NULL"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - } - groupId = rs.getBytes(1); - nextId = rs.getBytes(2); - deleted = rs.getLong(3); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - // Update the predecessor's nextId - if(groupId == null) { - // Inserting at the head of the list - sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId IS NULL"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setInt(2, c.getInt()); - } else { - // Inserting in the middle or at the tail of the list - sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setInt(2, c.getInt()); - ps.setBytes(3, groupId); - } + ps.setBytes(3, g.getBytes()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - // Insert the new element - sql = "INSERT INTO visibilities" - + " (contactId, groupId, nextId, deleted)" - + " VALUES (?, ?, ?, ?)"; + // Bump the subscription version + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - if(nextId == null) ps.setNull(3, BINARY); // At the tail - else ps.setBytes(3, nextId); // In the middle - ps.setLong(4, deleted); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); throw new DbException(e); } @@ -878,7 +896,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT NULL FROM subscriptions WHERE groupId = ?"; + String sql = "SELECT NULL FROM groups WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); @@ -894,48 +912,21 @@ abstract class JdbcDatabase implements Database { } } - public boolean containsSubscription(Connection txn, GroupId g, long time) - throws DbException { + public boolean containsVisibleSubscription(Connection txn, ContactId c, + GroupId g) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT NULL FROM subscriptions" - + " WHERE groupId = ? AND start <= ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setLong(2, time); - rs = ps.executeQuery(); - boolean found = rs.next(); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - return found; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - - public boolean containsVisibleSubscription(Connection txn, GroupId g, - ContactId c, long time) throws DbException { - boolean found = false; - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT start FROM subscriptions AS s" - + " JOIN visibilities AS v" - + " ON s.groupId = v.groupId" - + " WHERE s.groupId = ? AND contactId = ?"; + String sql = "SELECT NULL FROM groups AS g" + + " JOIN groupVisibilities AS gv" + + " ON g.groupId = gv.groupId" + + " WHERE g.groupId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); ps.setInt(2, c.getInt()); rs = ps.executeQuery(); - if(rs.next()) { - long start = rs.getLong(1); - if(start <= time) found = true; - if(rs.next()) throw new DbStateException(); - } + boolean found = rs.next(); + if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); return found; @@ -1100,38 +1091,6 @@ abstract class JdbcDatabase implements Database { } } - public Collection getLocalTransports(Connection txn) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT transportId, key, value" - + " FROM transportProperties" - + " ORDER BY transportId"; - ps = txn.prepareStatement(sql); - rs = ps.executeQuery(); - List transports = new ArrayList(); - TransportId lastId = null; - Transport t = null; - while(rs.next()) { - TransportId id = new TransportId(rs.getBytes(1)); - if(!id.equals(lastId)) { - t = new Transport(id); - transports.add(t); - } - t.getProperties().put(rs.getString(2), rs.getString(3)); - lastId = id; - } - rs.close(); - ps.close(); - return Collections.unmodifiableList(transports); - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public byte[] getMessage(Connection txn, MessageId m) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -1246,19 +1205,18 @@ abstract class JdbcDatabase implements Database { if(raw != null) return raw; // Do we have a sendable group message with the given ID? sql = "SELECT length, raw FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" + + " AND cg.contactId = s.contactId" + " WHERE m.messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()"; @@ -1353,18 +1311,17 @@ abstract class JdbcDatabase implements Database { return Collections.unmodifiableList(ids); // Do we have any sendable group messages? sql = "SELECT m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1401,7 +1358,7 @@ abstract class JdbcDatabase implements Database { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - sql = "SELECT COUNT(messageId) FROM messages" + sql = "SELECT COUNT (messageId) FROM messages" + " WHERE parentId = ? AND groupId = ?" + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); @@ -1509,11 +1466,12 @@ abstract class JdbcDatabase implements Database { TransportProperties p = null; while(rs.next()) { ContactId id = new ContactId(rs.getInt(1)); + String key = rs.getString(2), value = rs.getString(3); if(!id.equals(lastId)) { p = new TransportProperties(); properties.put(id, p); } - p.put(rs.getString(2), rs.getString(3)); + p.put(key, value); } rs.close(); ps.close(); @@ -1614,18 +1572,17 @@ abstract class JdbcDatabase implements Database { if(total == maxLength) return Collections.unmodifiableList(ids); // Do we have any sendable group messages? sql = "SELECT length, m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1676,8 +1633,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, groupName, groupKey" - + " FROM subscriptions"; + String sql = "SELECT groupId, name, key FROM groups"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List subs = new ArrayList(); @@ -1702,8 +1658,7 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, groupName, groupKey" - + " FROM contactSubscriptions" + String sql = "SELECT groupId, name, key FROM contactGroups" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); @@ -1725,45 +1680,153 @@ abstract class JdbcDatabase implements Database { } } - public long getTransportsModified(Connection txn) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT DISTINCT modified FROM transportTimestamps"; - ps = txn.prepareStatement(sql); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbException(); - long modified = rs.getLong(1); - if(rs.next()) throw new DbException(); - rs.close(); - ps.close(); - return modified; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - - public long getTransportsSent(Connection txn, ContactId c) + public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT sent FROM transportTimestamps" - + " WHERE contactId = ?"; + String sql = "SELECT remoteVersion FROM groupVersions" + + " WHERE contactId = ? AND remoteAcked = FALSE"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - if(!rs.next()) throw new DbException(); - long sent = rs.getLong(1); - if(rs.next()) throw new DbException(); + if(!rs.next()) { + rs.close(); + ps.close(); + return null; + } + long version = rs.getLong(1); + if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - return sent; + sql = "UPDATE groupVersions SET remoteAcked = TRUE" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + return new SubscriptionAck(version); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + + public SubscriptionUpdate getSubscriptionUpdate(Connection txn, ContactId c) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT g.groupId, name, key, localVersion" + + " FROM groups AS g" + + " JOIN groupVisibilities as gv" + + " ON g.groupId = gv.groupId" + + " JOIN groupVersions AS v" + + " ON gv.contactId = v.contactId" + + " WHERE gv.contactId = ?" + + " AND localVersion > localAcked"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + rs = ps.executeQuery(); + List subs = new ArrayList(); + long version = 0L; + while(rs.next()) { + byte[] id = rs.getBytes(1); + String name = rs.getString(2); + byte[] key = rs.getBytes(3); + version = rs.getLong(4); + subs.add(new Group(new GroupId(id), name, key)); + } + rs.close(); + ps.close(); + if(subs.isEmpty()) return null; + subs = Collections.unmodifiableList(subs); + return new SubscriptionUpdate(subs, version); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + + public Collection getTransportAcks(Connection txn, + ContactId c) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT transportId, remoteVersion" + + " FROM contactTransportVersions" + + " WHERE contactId = ? AND remoteAcked = FALSE"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + rs = ps.executeQuery(); + List acks = new ArrayList(); + while(rs.next()) { + TransportId id = new TransportId(rs.getBytes(1)); + acks.add(new TransportAck(id, rs.getLong(2))); + } + rs.close(); + ps.close(); + if(acks.isEmpty()) return null; + sql = "UPDATE contactTransportVersions SET remoteAcked = TRUE" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + for(TransportAck a : acks) { + ps.setBytes(2, a.getId().getBytes()); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != acks.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] < 1) throw new DbStateException(); + } + ps.close(); + return Collections.unmodifiableList(acks); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + + public Collection getTransportUpdates(Connection txn, + ContactId c) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT transportId, key, value, localVersion" + + " FROM transportProperties AS tp" + + " JOIN transportVersions as tv" + + " ON tp.transportId = tv.transportId" + + " WHERE tv.contactId = ?" + + " AND localVersion > localAcked"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + rs = ps.executeQuery(); + List updates = new ArrayList(); + TransportId lastId = null; + TransportProperties p = null; + while(rs.next()) { + TransportId id = new TransportId(rs.getBytes(1)); + String key = rs.getString(2), value = rs.getString(3); + long version = rs.getLong(4); + if(!id.equals(lastId)) { + p = new TransportProperties(); + updates.add(new TransportUpdate(id, p, version)); + } + p.put(key, value); + } + rs.close(); + ps.close(); + if(updates.isEmpty()) return null; + return Collections.unmodifiableList(updates); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } @@ -1801,7 +1864,8 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactId FROM visibilities WHERE groupId = ?"; + String sql = "SELECT contactId FROM groupVisibilities" + + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); @@ -1817,77 +1881,6 @@ abstract class JdbcDatabase implements Database { } } - public Map getVisibleHoles(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT groupId, nextId FROM visibilities AS v" - + " JOIN subscriptionTimes AS st" - + " ON v.contactId = st.contactId" - + " WHERE v.contactId = ?" - + " AND deleted > acked AND deleted < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setLong(2, timestamp); - rs = ps.executeQuery(); - Map holes = null; - while(rs.next()) { - byte[] b = rs.getBytes(1); - GroupId groupId = b == null ? null : new GroupId(b); - b = rs.getBytes(2); - GroupId nextId = b == null ? null : new GroupId(b); - if(holes == null) holes = new HashMap(); - holes.put(groupId, nextId); - } - rs.close(); - ps.close(); - if(holes == null) return Collections.emptyMap(); - return Collections.unmodifiableMap(holes); - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - - public Map getVisibleSubscriptions(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT s.groupId, groupName, groupKey, start" - + " FROM subscriptions AS s" - + " JOIN visibilities AS v" - + " ON s.groupId = v.groupId" - + " JOIN subscriptionTimes AS st" - + " ON v.contactId = st.contactId" - + " WHERE v.contactId = ?" - + " AND start > acked AND start < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setLong(2, timestamp); - rs = ps.executeQuery(); - Map subs = null; - while(rs.next()) { - GroupId id = new GroupId(rs.getBytes(1)); - String name = rs.getString(2); - byte[] publicKey = rs.getBytes(3); - long start = rs.getLong(4); - if(subs == null) subs = new HashMap(); - subs.put(new Group(id, name, publicKey), start); - } - rs.close(); - ps.close(); - if(subs == null) return Collections.emptyMap(); - return Collections.unmodifiableMap(subs); - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public boolean hasSendableMessages(Connection txn, ContactId c) throws DbException { PreparedStatement ps = null; @@ -1911,18 +1904,17 @@ abstract class JdbcDatabase implements Database { if(found) return true; // Do we have any sendable group messages? sql = "SELECT m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1967,12 +1959,13 @@ abstract class JdbcDatabase implements Database { rs.close(); ps.close(); // Increment the connection counter - sql = "UPDATE secrets SET outgoing = outgoing + 1" + sql = "UPDATE secrets SET outgoing = outgoing + ?" + " WHERE contactId = ? AND transportId = ? AND period = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, t.getBytes()); - ps.setLong(3, period); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); + ps.setBytes(3, t.getBytes()); + ps.setLong(4, period); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -2070,83 +2063,59 @@ abstract class JdbcDatabase implements Database { public void removeSubscription(Connection txn, GroupId g) throws DbException { - PreparedStatement ps = null, ps1 = null; + PreparedStatement ps = null; ResultSet rs = null; try { - // Remove the group ID from the visibility lists - long now = clock.currentTimeMillis(); - String sql = "SELECT contactId, nextId FROM visibilities" + // Find out which contacts are affected + String sql = "SELECT contactId FROM groupVisibilities" + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); - while(rs.next()) { - int contactId = rs.getInt(1); - byte[] nextId = rs.getBytes(2); - sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; - ps1 = txn.prepareStatement(sql); - if(nextId == null) ps1.setNull(1, BINARY); // At the tail - else ps1.setBytes(1, nextId); // At the head or in the middle - ps1.setLong(2, now); - ps1.setInt(3, contactId); - ps1.setBytes(4, g.getBytes()); - int affected = ps1.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps1.close(); - } + Collection visible = new ArrayList(); + while(rs.next()) visible.add(rs.getInt(1)); rs.close(); ps.close(); - // Remove the group from the subscriptions table - sql = "DELETE FROM subscriptions WHERE groupId = ?"; + // Delete the group + sql = "DELETE FROM groups WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); + if(visible.isEmpty()) return; + // Bump the subscription version for the affected contacts + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, 1); + for(Integer c : visible) { + ps.setInt(2, c); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != visible.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); + } + ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); - tryToClose(ps1); + tryToClose(rs); throw new DbException(e); } } - public void removeSubscriptions(Connection txn, ContactId c, GroupId start, - GroupId end) throws DbException { + public void removeTransport(Connection txn, TransportId t) + throws DbException { PreparedStatement ps = null; try { - if(start == null && end == null) { - // Delete everything - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - } else if(start == null) { - // Delete everything before end - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, end.getBytes()); - } else if(end == null) { - // Delete everything after start - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId > ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, start.getBytes()); - } else { - // Delete everything between start and end - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?" - + " AND groupId > ? AND groupId < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, start.getBytes()); - ps.setBytes(3, end.getBytes()); - } - ps.executeUpdate(); + String sql = "DELETE FROM transports WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { tryToClose(ps); @@ -2157,21 +2126,8 @@ abstract class JdbcDatabase implements Database { public void removeVisibility(Connection txn, ContactId c, GroupId g) throws DbException { PreparedStatement ps = null; - ResultSet rs = null; try { - // Remove the group ID from the linked list - String sql = "SELECT nextId FROM visibilities" - + " WHERE contactId = ? AND groupId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - byte[] nextId = rs.getBytes(1); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - sql = "DELETE FROM visibilities" + String sql = "DELETE FROM groupVisibilities" + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); @@ -2179,19 +2135,16 @@ abstract class JdbcDatabase implements Database { int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; + // Bump the subscription version + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); - if(nextId == null) ps.setNull(1, BINARY); // At the tail - else ps.setBytes(1, nextId); // At the head or in the middle - ps.setLong(2, clock.currentTimeMillis()); - ps.setInt(3, c.getInt()); - ps.setBytes(4, g.getBytes()); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); throw new DbException(e); } @@ -2199,12 +2152,29 @@ abstract class JdbcDatabase implements Database { public void mergeConfig(Connection txn, TransportId t, TransportConfig c) throws DbException { + // Merge the new configuration with the existing one mergeStringMap(txn, t, c, "transportConfigs"); } public void mergeLocalProperties(Connection txn, TransportId t, TransportProperties p) throws DbException { + // Merge the new properties with the existing ones mergeStringMap(txn, t, p, "transportProperties"); + // Bump the transport version + PreparedStatement ps = null; + try { + String sql = "UPDATE transportVersions" + + " SET localVersion = localVersion + ?" + + " WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, 1); + ps.setBytes(2, t.getBytes()); + ps.executeUpdate(); + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + throw new DbException(e); + } } private void mergeStringMap(Connection txn, TransportId t, @@ -2278,7 +2248,7 @@ abstract class JdbcDatabase implements Database { throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE subscriptionTimes SET expiry = ?" + String sql = "UPDATE contacts SET expiry = ?" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, expiry); @@ -2391,6 +2361,85 @@ abstract class JdbcDatabase implements Database { } } + public void setRemoteProperties(Connection txn, ContactId c, + TransportUpdate t) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + // Find the existing version, if any + String sql = "SELECT remoteVersion FROM contactTransportVersions" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + rs = ps.executeQuery(); + long version = rs.next() ? rs.getLong(1) : -1L; + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + // Mark the update as needing to be acked + if(version == -1L) { + // The row doesn't exist - create it + sql = "INSERT INTO contactTransportVersions (contactId," + + " transportId, remoteVersion, remoteAcked)" + + " VALUES (?, ?, ?, FALSE)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + ps.setLong(3, t.getVersionNumber()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + } else { + // The row exists - update it + sql = "UPDATE contactTransportVersions" + + " SET remoteVersion = ?, remoteAcked = FALSE" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setLong(1, Math.max(version, t.getVersionNumber())); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); + ps.close(); + // Return if the update is obsolete + if(t.getVersionNumber() <= version) return; + } + // Delete the existing properties, if any + sql = "DELETE FROM contactTransportProperties" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + ps.executeUpdate(); + ps.close(); + // Store the new properties, if any + TransportProperties p = t.getProperties(); + if(p.isEmpty()) return; + sql = "INSERT INTO contactTransportProperties" + + " (contactId, transportId, key, value)" + + " VALUES (?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + for(Entry e : p.entrySet()) { + ps.setString(1, e.getKey()); + ps.setString(2, e.getValue()); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != p.size()) throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + public void setSendability(Connection txn, MessageId m, int sendability) throws DbException { PreparedStatement ps = null; @@ -2514,16 +2563,15 @@ abstract class JdbcDatabase implements Database { ResultSet rs = null; try { String sql = "SELECT NULL FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " WHERE messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = ?" + " AND timestamp >= expiry"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); @@ -2551,112 +2599,78 @@ abstract class JdbcDatabase implements Database { } } - public void setSubscriptionsAcked(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE subscriptionTimes SET acked = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected > 1) throw new DbStateException(); - ps.close(); - } catch(SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - - public void setSubscriptionsReceived(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE subscriptionTimes SET received = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected > 1) throw new DbStateException(); - ps.close(); - } catch(SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - - public void setTransports(Connection txn, ContactId c, - Collection transports, long timestamp) - throws DbException { + public void setSubscriptions(Connection txn, ContactId c, + SubscriptionUpdate s) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - // Return if the timestamp isn't fresh - String sql = "SELECT received FROM transportTimestamps" + // Find the existing version + String sql = "SELECT remoteVersion FROM groupVersions" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); if(!rs.next()) throw new DbStateException(); - long lastTimestamp = rs.getLong(1); + long version = rs.getLong(1); if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - if(lastTimestamp >= timestamp) return; - // Delete any existing transport properties - sql = "DELETE FROM contactTransportProperties WHERE contactId = ?"; + // Mark the update as needing to be acked + sql = "UPDATE groupVersions" + + " SET remoteVersion = ?, remoteAcked = FALSE" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setLong(1, Math.max(version, s.getVersionNumber())); + ps.setInt(2, c.getInt()); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); + ps.close(); + // Delete the existing subscriptions, if any + sql = "DELETE FROM contactGroups WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.executeUpdate(); - ps.close(); - // Store the new transport properties - sql = "INSERT INTO contactTransportProperties" - + " (contactId, transportId, key, value)" + // Store the new subscriptions, if any + Collection subs = s.getGroups(); + if(subs.isEmpty()) return; + sql = "INSERT INTO contactGroups (contactId, groupId, name, key)" + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - int batchSize = 0; - for(Transport t : transports) { - ps.setBytes(2, t.getId().getBytes()); - for(Entry e1 : t.getProperties().entrySet()) { - ps.setString(3, e1.getKey()); - ps.setString(4, e1.getValue()); - ps.addBatch(); - batchSize++; - } + for(Group g : subs) { + ps.setBytes(2, g.getId().getBytes()); + ps.setString(3, g.getName()); + byte[] key = g.getPublicKey(); + if(key == null) ps.setNull(4, BINARY); + else ps.setBytes(4, key); + ps.addBatch(); } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != batchSize) throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != subs.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); } ps.close(); - // Update the timestamp - sql = "UPDATE transportTimestamps SET received = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); } catch(SQLException e) { + tryToClose(ps); tryToClose(rs); - tryToClose(ps); throw new DbException(e); } } - public void setTransportsModified(Connection txn, long timestamp) - throws DbException { + public void setSubscriptionUpdateAcked(Connection txn, ContactId c, + long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE transportTimestamps set modified = ?"; + String sql = "UPDATE groupVersions SET localAcked = ?" + + " WHERE contactId = ? AND localAcked < ?"; ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.executeUpdate(); + ps.setLong(1, version); + ps.setInt(2, c.getInt()); + ps.setLong(3, version); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { tryToClose(ps); @@ -2664,16 +2678,18 @@ abstract class JdbcDatabase implements Database { } } - public void setTransportsSent(Connection txn, ContactId c, long timestamp) - throws DbException { + public void setTransportUpdateAcked(Connection txn, ContactId c, + TransportId t, long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE transportTimestamps SET sent = ?" - + " WHERE contactId = ? AND sent < ?"; + String sql = "UPDATE transportVersions SET localAcked = ?" + + " WHERE contactId = ? AND transportId = ?" + + " AND localAcked < ?"; ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); + ps.setLong(1, version); ps.setInt(2, c.getInt()); - ps.setLong(3, timestamp); + ps.setBytes(3, t.getBytes()); + ps.setLong(4, version); int affected = ps.executeUpdate(); if(affected > 1) throw new DbStateException(); ps.close(); diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java index 658b3cd40..5ec5f0520 100644 --- a/briar-core/src/net/sf/briar/invitation/Connector.java +++ b/briar-core/src/net/sf/briar/invitation/Connector.java @@ -104,7 +104,7 @@ abstract class Connector extends Thread { protected byte[] receivePublicKeyHash(Reader r) throws IOException { byte[] b = r.readBytes(HASH_LENGTH); - if(b.length != HASH_LENGTH) throw new FormatException(); + if(b.length < HASH_LENGTH) throw new FormatException(); if(LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash"); return b; } diff --git a/briar-core/src/net/sf/briar/protocol/AckImpl.java b/briar-core/src/net/sf/briar/protocol/AckImpl.java deleted file mode 100644 index ea8e92428..000000000 --- a/briar-core/src/net/sf/briar/protocol/AckImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.MessageId; - -class AckImpl implements Ack { - - private final Collection acked; - - AckImpl(Collection acked) { - this.acked = acked; - } - - public Collection getMessageIds() { - return acked; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/AckReader.java b/briar-core/src/net/sf/briar/protocol/AckReader.java index b9e55fc5e..beb073633 100644 --- a/briar-core/src/net/sf/briar/protocol/AckReader.java +++ b/briar-core/src/net/sf/briar/protocol/AckReader.java @@ -12,7 +12,6 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -21,18 +20,11 @@ import net.sf.briar.api.serial.StructReader; class AckReader implements StructReader { - private final PacketFactory packetFactory; - - AckReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Ack readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(ACK); + // Read the message IDs as byte arrays r.setMaxBytesLength(UniqueId.LENGTH); List raw = r.readList(Bytes.class); r.resetMaxBytesLength(); @@ -46,6 +38,6 @@ class AckReader implements StructReader { acked.add(new MessageId(b.getBytes())); } // Build and return the ack - return packetFactory.createAck(Collections.unmodifiableList(acked)); + return new Ack(Collections.unmodifiableList(acked)); } } diff --git a/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java index bced33cb3..41fa3d782 100644 --- a/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java +++ b/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java @@ -27,7 +27,7 @@ class AuthorFactoryImpl implements AuthorFactory { } public Author createAuthor(String name, byte[] publicKey) - throws IOException { + throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Writer w = writerFactory.createWriter(out); w.writeStructId(AUTHOR); @@ -36,10 +36,6 @@ class AuthorFactoryImpl implements AuthorFactory { MessageDigest messageDigest = crypto.getMessageDigest(); messageDigest.update(out.toByteArray()); AuthorId id = new AuthorId(messageDigest.digest()); - return new AuthorImpl(id, name, publicKey); - } - - public Author createAuthor(AuthorId id, String name, byte[] publicKey) { - return new AuthorImpl(id, name, publicKey); + return new Author(id, name, publicKey); } } diff --git a/briar-core/src/net/sf/briar/protocol/AuthorImpl.java b/briar-core/src/net/sf/briar/protocol/AuthorImpl.java deleted file mode 100644 index dfaf022c8..000000000 --- a/briar-core/src/net/sf/briar/protocol/AuthorImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.sf.briar.protocol; - -import net.sf.briar.api.protocol.Author; -import net.sf.briar.api.protocol.AuthorId; - -class AuthorImpl implements Author { - - private final AuthorId id; - private final String name; - private final byte[] publicKey; - - AuthorImpl(AuthorId id, String name, byte[] publicKey) { - this.id = id; - this.name = name; - this.publicKey = publicKey; - } - - public AuthorId getId() { - return id; - } - - public String getName() { - return name; - } - - public byte[] getPublicKey() { - return publicKey; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/AuthorReader.java b/briar-core/src/net/sf/briar/protocol/AuthorReader.java index 9bc177f72..d8d392503 100644 --- a/briar-core/src/net/sf/briar/protocol/AuthorReader.java +++ b/briar-core/src/net/sf/briar/protocol/AuthorReader.java @@ -9,7 +9,6 @@ import java.io.IOException; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.protocol.Author; -import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.serial.DigestingConsumer; import net.sf.briar.api.serial.Reader; @@ -18,15 +17,12 @@ import net.sf.briar.api.serial.StructReader; class AuthorReader implements StructReader { private final MessageDigest messageDigest; - private final AuthorFactory authorFactory; - AuthorReader(CryptoComponent crypto, AuthorFactory authorFactory) { + AuthorReader(CryptoComponent crypto) { messageDigest = crypto.getMessageDigest(); - this.authorFactory = authorFactory; } public Author readStruct(Reader r) throws IOException { - // Initialise the consumer DigestingConsumer digesting = new DigestingConsumer(messageDigest); // Read and digest the data r.addConsumer(digesting); @@ -36,6 +32,6 @@ class AuthorReader implements StructReader { r.removeConsumer(digesting); // Build and return the author AuthorId id = new AuthorId(messageDigest.digest()); - return authorFactory.createAuthor(id, name, publicKey); + return new Author(id, name, publicKey); } } diff --git a/briar-core/src/net/sf/briar/protocol/GroupReader.java b/briar-core/src/net/sf/briar/protocol/GroupReader.java index af1fcfc3c..3cfc9d908 100644 --- a/briar-core/src/net/sf/briar/protocol/GroupReader.java +++ b/briar-core/src/net/sf/briar/protocol/GroupReader.java @@ -23,7 +23,6 @@ class GroupReader implements StructReader { } public Group readStruct(Reader r) throws IOException { - // Initialise the consumer DigestingConsumer digesting = new DigestingConsumer(messageDigest); // Read and digest the data r.addConsumer(digesting); diff --git a/briar-core/src/net/sf/briar/protocol/MessageReader.java b/briar-core/src/net/sf/briar/protocol/MessageReader.java index 9b2f801a1..f86ae4dfa 100644 --- a/briar-core/src/net/sf/briar/protocol/MessageReader.java +++ b/briar-core/src/net/sf/briar/protocol/MessageReader.java @@ -46,7 +46,7 @@ class MessageReader implements StructReader { r.readNull(); } else { byte[] b = r.readBytes(UniqueId.LENGTH); - if(b.length != UniqueId.LENGTH) throw new FormatException(); + if(b.length < UniqueId.LENGTH) throw new FormatException(); parent = new MessageId(b); } // Read the group, if there is one @@ -74,7 +74,7 @@ class MessageReader implements StructReader { if(timestamp < 0L) throw new FormatException(); // Read the salt byte[] salt = r.readBytes(SALT_LENGTH); - if(salt.length != SALT_LENGTH) throw new FormatException(); + if(salt.length < SALT_LENGTH) throw new FormatException(); // Read the message body byte[] body = r.readBytes(MAX_BODY_LENGTH); // Record the offset of the body within the message diff --git a/briar-core/src/net/sf/briar/protocol/OfferImpl.java b/briar-core/src/net/sf/briar/protocol/OfferImpl.java deleted file mode 100644 index de892202e..000000000 --- a/briar-core/src/net/sf/briar/protocol/OfferImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Offer; - -class OfferImpl implements Offer { - - private final Collection offered; - - OfferImpl(Collection offered) { - this.offered = offered; - } - - public Collection getMessageIds() { - return offered; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/OfferReader.java b/briar-core/src/net/sf/briar/protocol/OfferReader.java index 32c0bc5bf..62a8d665a 100644 --- a/briar-core/src/net/sf/briar/protocol/OfferReader.java +++ b/briar-core/src/net/sf/briar/protocol/OfferReader.java @@ -12,7 +12,6 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -21,18 +20,11 @@ import net.sf.briar.api.serial.StructReader; class OfferReader implements StructReader { - private final PacketFactory packetFactory; - - OfferReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Offer readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(OFFER); + // Read the message IDs as byte arrays r.setMaxBytesLength(UniqueId.LENGTH); List raw = r.readList(Bytes.class); r.resetMaxBytesLength(); @@ -46,7 +38,6 @@ class OfferReader implements StructReader { messages.add(new MessageId(b.getBytes())); } // Build and return the offer - return packetFactory.createOffer(Collections.unmodifiableList( - messages)); + return new Offer(Collections.unmodifiableList(messages)); } } diff --git a/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java deleted file mode 100644 index a8e9e32c2..000000000 --- a/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.BitSet; -import java.util.Collection; -import java.util.Map; - -import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; -import net.sf.briar.api.protocol.Request; -import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportUpdate; - -class PacketFactoryImpl implements PacketFactory { - - public Ack createAck(Collection acked) { - return new AckImpl(acked); - } - - public Offer createOffer(Collection offered) { - return new OfferImpl(offered); - } - - public Request createRequest(BitSet requested, int length) { - return new RequestImpl(requested, length); - } - - public SubscriptionUpdate createSubscriptionUpdate( - Map holes, Map subs, long expiry, - long timestamp) { - return new SubscriptionUpdateImpl(holes, subs, expiry, timestamp); - } - - public TransportUpdate createTransportUpdate( - Collection transports, long timestamp) { - return new TransportUpdateImpl(transports, timestamp); - } -} diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolModule.java b/briar-core/src/net/sf/briar/protocol/ProtocolModule.java index 075f4158b..dfce9b987 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolModule.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolModule.java @@ -11,11 +11,12 @@ import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageVerifier; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.protocol.VerificationExecutor; @@ -48,7 +49,6 @@ public class ProtocolModule extends AbstractModule { bind(GroupFactory.class).to(GroupFactoryImpl.class); bind(MessageFactory.class).to(MessageFactoryImpl.class); bind(MessageVerifier.class).to(MessageVerifierImpl.class); - bind(PacketFactory.class).to(PacketFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class); // The executor is bounded, so tasks must be independent and short-lived @@ -59,14 +59,13 @@ public class ProtocolModule extends AbstractModule { } @Provides - StructReader getAckReader(PacketFactory ackFactory) { - return new AckReader(ackFactory); + StructReader getAckReader() { + return new AckReader(); } @Provides - StructReader getAuthorReader(CryptoComponent crypto, - AuthorFactory authorFactory) { - return new AuthorReader(crypto, authorFactory); + StructReader getAuthorReader(CryptoComponent crypto) { + return new AuthorReader(crypto); } @Provides @@ -82,24 +81,33 @@ public class ProtocolModule extends AbstractModule { } @Provides - StructReader getOfferReader(PacketFactory packetFactory) { - return new OfferReader(packetFactory); + StructReader getOfferReader() { + return new OfferReader(); } @Provides - StructReader getRequestReader(PacketFactory packetFactory) { - return new RequestReader(packetFactory); + StructReader getRequestReader() { + return new RequestReader(); } @Provides - StructReader getSubscriptionReader( - StructReader groupReader, PacketFactory packetFactory) { - return new SubscriptionUpdateReader(groupReader, packetFactory); + StructReader getSubscriptionAckReader() { + return new SubscriptionAckReader(); } @Provides - StructReader getTransportReader( - PacketFactory packetFactory) { - return new TransportUpdateReader(packetFactory); + StructReader getSubscriptionUpdateReader( + StructReader groupReader) { + return new SubscriptionUpdateReader(groupReader); + } + + @Provides + StructReader getTransportAckReader() { + return new TransportAckReader(); + } + + @Provides + StructReader getTransportUpdateReader() { + return new TransportUpdateReader(); } } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java index 3ac7649d5..fc42c841d 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java @@ -7,7 +7,9 @@ import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.serial.ReaderFactory; @@ -16,6 +18,7 @@ import net.sf.briar.api.serial.StructReader; import com.google.inject.Inject; import com.google.inject.Provider; +// FIXME: Refactor this package to reduce boilerplate class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { private final ReaderFactory readerFactory; @@ -23,8 +26,10 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { private final Provider> messageProvider; private final Provider> offerProvider; private final Provider> requestProvider; - private final Provider> subscriptionProvider; - private final Provider> transportProvider; + private final Provider> subscriptionAckProvider; + private final Provider> subscriptionUpdateProvider; + private final Provider> transportAckProvider; + private final Provider> transportUpdateProvider; @Inject ProtocolReaderFactoryImpl(ReaderFactory readerFactory, @@ -32,21 +37,26 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { Provider> messageProvider, Provider> offerProvider, Provider> requestProvider, - Provider> subscriptionProvider, - Provider> transportProvider) { + Provider> subscriptionAckProvider, + Provider> subscriptionUpdateProvider, + Provider> transportAckProvider, + Provider> transportUpdateProvider) { this.readerFactory = readerFactory; this.ackProvider = ackProvider; this.messageProvider = messageProvider; this.offerProvider = offerProvider; this.requestProvider = requestProvider; - this.subscriptionProvider = subscriptionProvider; - this.transportProvider = transportProvider; + this.subscriptionAckProvider = subscriptionAckProvider; + this.subscriptionUpdateProvider = subscriptionUpdateProvider; + this.transportAckProvider = transportAckProvider; + this.transportUpdateProvider = transportUpdateProvider; } public ProtocolReader createProtocolReader(InputStream in) { return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(), messageProvider.get(), offerProvider.get(), - requestProvider.get(), subscriptionProvider.get(), - transportProvider.get()); + requestProvider.get(), subscriptionAckProvider.get(), + subscriptionUpdateProvider.get(), transportAckProvider.get(), + transportUpdateProvider.get()); } } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java index 9d5850b15..b13cfe8ad 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java @@ -4,7 +4,9 @@ import static net.sf.briar.api.protocol.Types.ACK; import static net.sf.briar.api.protocol.Types.MESSAGE; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; @@ -14,7 +16,9 @@ import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.serial.Reader; @@ -30,15 +34,19 @@ class ProtocolReaderImpl implements ProtocolReader { StructReader messageReader, StructReader offerReader, StructReader requestReader, - StructReader subscriptionReader, - StructReader transportReader) { + StructReader subscriptionAckReader, + StructReader subscriptionUpdateReader, + StructReader transportAckReader, + StructReader transportUpdateReader) { reader = readerFactory.createReader(in); reader.addStructReader(ACK, ackReader); reader.addStructReader(MESSAGE, messageReader); reader.addStructReader(OFFER, offerReader); reader.addStructReader(REQUEST, requestReader); - reader.addStructReader(SUBSCRIPTION_UPDATE, subscriptionReader); - reader.addStructReader(TRANSPORT_UPDATE, transportReader); + reader.addStructReader(SUBSCRIPTION_ACK, subscriptionAckReader); + reader.addStructReader(SUBSCRIPTION_UPDATE, subscriptionUpdateReader); + reader.addStructReader(TRANSPORT_ACK, transportAckReader); + reader.addStructReader(TRANSPORT_UPDATE, transportUpdateReader); } public boolean eof() throws IOException { @@ -77,6 +85,14 @@ class ProtocolReaderImpl implements ProtocolReader { return reader.readStruct(REQUEST, Request.class); } + public boolean hasSubscriptionAck() throws IOException { + return reader.hasStruct(SUBSCRIPTION_ACK); + } + + public SubscriptionAck readSubscriptionAck() throws IOException { + return reader.readStruct(SUBSCRIPTION_ACK, SubscriptionAck.class); + } + public boolean hasSubscriptionUpdate() throws IOException { return reader.hasStruct(SUBSCRIPTION_UPDATE); } @@ -86,6 +102,14 @@ class ProtocolReaderImpl implements ProtocolReader { SubscriptionUpdate.class); } + public boolean hasTransportAck() throws IOException { + return reader.hasStruct(TRANSPORT_ACK); + } + + public TransportAck readTransportAck() throws IOException { + return reader.readStruct(TRANSPORT_ACK, TransportAck.class); + } + public boolean hasTransportUpdate() throws IOException { return reader.hasStruct(TRANSPORT_UPDATE); } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java index 87ddf36eb..633ec17d8 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java @@ -5,24 +5,24 @@ import static net.sf.briar.api.protocol.Types.ACK; import static net.sf.briar.api.protocol.Types.GROUP; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; -import static net.sf.briar.api.protocol.Types.TRANSPORT; +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; import java.io.OutputStream; import java.util.BitSet; -import java.util.Map.Entry; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.serial.SerialComponent; import net.sf.briar.api.serial.Writer; @@ -103,48 +103,40 @@ class ProtocolWriterImpl implements ProtocolWriter { if(flush) out.flush(); } - public void writeSubscriptionUpdate(SubscriptionUpdate s) - throws IOException { - w.writeStructId(SUBSCRIPTION_UPDATE); - // Holes - w.writeMapStart(); - for(Entry e : s.getHoles().entrySet()) { - w.writeBytes(e.getKey().getBytes()); - w.writeBytes(e.getValue().getBytes()); - } - w.writeMapEnd(); - // Subscriptions - w.writeMapStart(); - for(Entry e : s.getSubscriptions().entrySet()) { - writeGroup(w, e.getKey()); - w.writeInt64(e.getValue()); - } - w.writeMapEnd(); - // Expiry time - w.writeInt64(s.getExpiryTime()); - // Timestamp - w.writeInt64(s.getTimestamp()); + public void writeSubscriptionAck(SubscriptionAck a) throws IOException { + w.writeStructId(SUBSCRIPTION_ACK); + w.writeInt64(a.getVersionNumber()); if(flush) out.flush(); } - private void writeGroup(Writer w, Group g) throws IOException { - w.writeStructId(GROUP); - w.writeString(g.getName()); - byte[] publicKey = g.getPublicKey(); - if(publicKey == null) w.writeNull(); - else w.writeBytes(publicKey); + public void writeSubscriptionUpdate(SubscriptionUpdate s) + throws IOException { + w.writeStructId(SUBSCRIPTION_UPDATE); + w.writeListStart(); + for(Group g : s.getGroups()) { + w.writeStructId(GROUP); + w.writeString(g.getName()); + byte[] publicKey = g.getPublicKey(); + if(publicKey == null) w.writeNull(); + else w.writeBytes(publicKey); + } + w.writeListEnd(); + w.writeInt64(s.getVersionNumber()); + if(flush) out.flush(); + } + + public void writeTransportAck(TransportAck a) throws IOException { + w.writeStructId(TRANSPORT_ACK); + w.writeBytes(a.getId().getBytes()); + w.writeInt64(a.getVersionNumber()); + if(flush) out.flush(); } public void writeTransportUpdate(TransportUpdate t) throws IOException { w.writeStructId(TRANSPORT_UPDATE); - w.writeListStart(); - for(Transport p : t.getTransports()) { - w.writeStructId(TRANSPORT); - w.writeBytes(p.getId().getBytes()); - w.writeMap(p.getProperties()); - } - w.writeListEnd(); - w.writeInt64(t.getTimestamp()); + w.writeBytes(t.getId().getBytes()); + w.writeMap(t.getProperties()); + w.writeInt64(t.getVersionNumber()); if(flush) out.flush(); } diff --git a/briar-core/src/net/sf/briar/protocol/RequestImpl.java b/briar-core/src/net/sf/briar/protocol/RequestImpl.java deleted file mode 100644 index aeae2cd28..000000000 --- a/briar-core/src/net/sf/briar/protocol/RequestImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.BitSet; - -import net.sf.briar.api.protocol.Request; - -class RequestImpl implements Request { - - private final BitSet requested; - private final int length; - - RequestImpl(BitSet requested, int length) { - this.requested = requested; - this.length = length; - } - - public BitSet getBitmap() { - return requested; - } - - public int getLength() { - return length; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/RequestReader.java b/briar-core/src/net/sf/briar/protocol/RequestReader.java index 0514ddc48..8bac06ec7 100644 --- a/briar-core/src/net/sf/briar/protocol/RequestReader.java +++ b/briar-core/src/net/sf/briar/protocol/RequestReader.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.util.BitSet; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -16,20 +15,14 @@ import net.sf.briar.api.serial.StructReader; class RequestReader implements StructReader { - private final PacketFactory packetFactory; - - RequestReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Request readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(REQUEST); + // There may be up to 7 bits of padding at the end of the bitmap int padding = r.readUint7(); if(padding > 7) throw new FormatException(); + // Read the bitmap byte[] bitmap = r.readBytes(MAX_PACKET_LENGTH); r.removeConsumer(counting); // Convert the bitmap into a BitSet @@ -41,6 +34,6 @@ class RequestReader implements StructReader { if((bitmap[i] & bit) != 0) b.set(i * 8 + j); } } - return packetFactory.createRequest(b, length); + return new Request(b, length); } } diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java b/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java new file mode 100644 index 000000000..648fbc7ec --- /dev/null +++ b/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java @@ -0,0 +1,20 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; + +import java.io.IOException; + +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.StructReader; + +class SubscriptionAckReader implements StructReader { + + public SubscriptionAck readStruct(Reader r) throws IOException { + r.readStructId(SUBSCRIPTION_ACK); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new SubscriptionAck(version); + } +} diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java deleted file mode 100644 index a98b7fee1..000000000 --- a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Map; - -import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.SubscriptionUpdate; - -class SubscriptionUpdateImpl implements SubscriptionUpdate { - - private final Map holes; - private final Map subs; - private final long expiry, timestamp; - - SubscriptionUpdateImpl(Map holes, Map subs, - long expiry, long timestamp) { - this.holes = holes; - this.subs = subs; - this.expiry = expiry; - this.timestamp = timestamp; - } - - public Map getHoles() { - return holes; - } - - public Map getSubscriptions() { - return subs; - } - - public long getExpiryTime() { - return expiry; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java index e87c20c7f..35d0e5f42 100644 --- a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java +++ b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java @@ -5,15 +5,12 @@ import static net.sf.briar.api.protocol.Types.GROUP; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; +import java.util.List; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; import net.sf.briar.api.serial.Reader; @@ -22,46 +19,25 @@ import net.sf.briar.api.serial.StructReader; class SubscriptionUpdateReader implements StructReader { private final StructReader groupReader; - private final PacketFactory packetFactory; - SubscriptionUpdateReader(StructReader groupReader, - PacketFactory packetFactory) { + SubscriptionUpdateReader(StructReader groupReader) { this.groupReader = groupReader; - this.packetFactory = packetFactory; } public SubscriptionUpdate readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(SUBSCRIPTION_UPDATE); - // Holes - Map holes = new HashMap(); - r.setMaxBytesLength(UniqueId.LENGTH); - r.readMapStart(); - while(!r.hasMapEnd()) { - byte[] start = r.readBytes(); - if(start.length != UniqueId.LENGTH) throw new FormatException(); - byte[] end = r.readBytes(); - if(end.length != UniqueId.LENGTH)throw new FormatException(); - holes.put(new GroupId(start), new GroupId(end)); - } - r.readMapEnd(); - r.resetMaxBytesLength(); - // Subscriptions + // Read the subscriptions r.addStructReader(GROUP, groupReader); - Map subs = r.readMap(Group.class, Long.class); + List subs = r.readList(Group.class); r.removeStructReader(GROUP); - // Expiry time - long expiry = r.readInt64(); - if(expiry < 0L) throw new FormatException(); - // Timestamp - long timestamp = r.readInt64(); - if(timestamp < 0L) throw new FormatException(); + // Read the version number + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); r.removeConsumer(counting); // Build and return the subscription update - return packetFactory.createSubscriptionUpdate(holes, subs, expiry, - timestamp); + subs = Collections.unmodifiableList(subs); + return new SubscriptionUpdate(subs, version); } } diff --git a/briar-core/src/net/sf/briar/protocol/TransportAckReader.java b/briar-core/src/net/sf/briar/protocol/TransportAckReader.java new file mode 100644 index 000000000..6a7c730d4 --- /dev/null +++ b/briar-core/src/net/sf/briar/protocol/TransportAckReader.java @@ -0,0 +1,24 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; + +import java.io.IOException; + +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.TransportAck; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.StructReader; + +class TransportAckReader implements StructReader { + + public TransportAck readStruct(Reader r) throws IOException { + r.readStructId(TRANSPORT_ACK); + byte[] b = r.readBytes(UniqueId.LENGTH); + if(b.length < UniqueId.LENGTH) throw new FormatException(); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new TransportAck(new TransportId(b), version); + } +} diff --git a/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java b/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java deleted file mode 100644 index b69a2bce7..000000000 --- a/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportUpdate; - -class TransportUpdateImpl implements TransportUpdate { - - private final Collection transports; - private final long timestamp; - - TransportUpdateImpl(Collection transports, - long timestamp) { - this.transports = transports; - this.timestamp = timestamp; - } - - public Collection getTransports() { - return transports; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java b/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java index d95c7d8c1..ee7c4e5cc 100644 --- a/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java +++ b/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java @@ -3,19 +3,13 @@ package net.sf.briar.protocol; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS; -import static net.sf.briar.api.protocol.Types.TRANSPORT; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.TransportProperties; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UniqueId; @@ -26,50 +20,25 @@ import net.sf.briar.api.serial.StructReader; class TransportUpdateReader implements StructReader { - private final PacketFactory packetFactory; - private final StructReader transportReader; - - TransportUpdateReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - transportReader = new TransportReader(); - } - public TransportUpdate readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(TRANSPORT_UPDATE); - r.addStructReader(TRANSPORT, transportReader); - Collection transports = r.readList(Transport.class); - r.removeStructReader(TRANSPORT); - if(transports.size() > MAX_TRANSPORTS) throw new FormatException(); - long timestamp = r.readInt64(); + // Read the transport ID + byte[] b = r.readBytes(UniqueId.LENGTH); + if(b.length < UniqueId.LENGTH) throw new FormatException(); + TransportId id = new TransportId(b); + // Read the transport properties + r.setMaxStringLength(MAX_PROPERTY_LENGTH); + Map m = r.readMap(String.class, String.class); + r.resetMaxStringLength(); + if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) + throw new FormatException(); + // Read the version number + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); r.removeConsumer(counting); - // Check for duplicate IDs - Set ids = new HashSet(); - for(Transport t : transports) { - if(!ids.add(t.getId())) throw new FormatException(); - } // Build and return the transport update - return packetFactory.createTransportUpdate(transports, timestamp); - } - - private static class TransportReader implements StructReader { - - public Transport readStruct(Reader r) throws IOException { - r.readStructId(TRANSPORT); - // Read the ID - byte[] b = r.readBytes(UniqueId.LENGTH); - if(b.length != UniqueId.LENGTH) throw new FormatException(); - TransportId id = new TransportId(b); - // Read the properties - r.setMaxStringLength(MAX_PROPERTY_LENGTH); - Map m = r.readMap(String.class, String.class); - r.resetMaxStringLength(); - if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) - throw new FormatException(); - return new Transport(id, m); - } + return new TransportUpdate(id, new TransportProperties(m), version); } } diff --git a/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java b/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java index 24d084bc8..221dd0027 100644 --- a/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java @@ -27,10 +27,10 @@ import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; +import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Message; @@ -55,6 +55,7 @@ import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.util.ByteUtils; +// FIXME: Read and write subscription and transport acks abstract class DuplexConnection implements DatabaseListener { private static final Logger LOG = @@ -132,7 +133,7 @@ abstract class DuplexConnection implements DatabaseListener { if(affected.contains(contactId)) { dbExecutor.execute(new GenerateSubscriptionUpdate()); } - } else if(e instanceof LocalTransportsUpdatedEvent) { + } else if(e instanceof TransportsUpdatedEvent) { dbExecutor.execute(new GenerateTransportUpdate()); } } @@ -556,8 +557,9 @@ abstract class DuplexConnection implements DatabaseListener { public void run() { try { - TransportUpdate t = db.generateTransportUpdate(contactId); - if(t != null) writerTasks.add(new WriteTransportUpdate(t)); + Collection t = + db.generateTransportUpdates(contactId); + if(t != null) writerTasks.add(new WriteTransportUpdates(t)); } catch(DbException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } @@ -565,18 +567,18 @@ abstract class DuplexConnection implements DatabaseListener { } // This task runs on the writer thread - private class WriteTransportUpdate implements Runnable { + private class WriteTransportUpdates implements Runnable { - private final TransportUpdate update; + private final Collection updates; - private WriteTransportUpdate(TransportUpdate update) { - this.update = update; + private WriteTransportUpdates(Collection updates) { + this.updates = updates; } public void run() { assert writer != null; try { - writer.writeTransportUpdate(update); + for(TransportUpdate t : updates) writer.writeTransportUpdate(t); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); dispose(true, true); diff --git a/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java b/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java index ffc6b8479..e9abf0c16 100644 --- a/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java @@ -30,6 +30,7 @@ import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionRegistry; import net.sf.briar.util.ByteUtils; +// FIXME: Read subscription and transport acks class IncomingSimplexConnection { private static final Logger LOG = diff --git a/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java b/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java index e53facc46..ef9c7c932 100644 --- a/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java @@ -25,6 +25,7 @@ import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.util.ByteUtils; +// FIXME: Write subscription and transport acks class OutgoingSimplexConnection { private static final Logger LOG = @@ -66,15 +67,15 @@ class OutgoingSimplexConnection { // There should be enough space for a packet long capacity = conn.getRemainingCapacity(); if(capacity < MAX_PACKET_LENGTH) throw new EOFException(); - // Write a transport update - TransportUpdate t = db.generateTransportUpdate(contactId); - if(t != null) writer.writeTransportUpdate(t); - // If there's space, write a subscription update - capacity = conn.getRemainingCapacity(); - if(capacity >= MAX_PACKET_LENGTH) { - SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); - if(s != null) writer.writeSubscriptionUpdate(s); + // Write transport updates. FIXME: Check for space + Collection updates = + db.generateTransportUpdates(contactId); + if(updates != null) { + for(TransportUpdate t : updates) writer.writeTransportUpdate(t); } + // Write a subscription update. FIXME: Check for space + SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); + if(s != null) writer.writeSubscriptionUpdate(s); // Write acks until you can't write acks no more capacity = conn.getRemainingCapacity(); int maxMessages = writer.getMaxMessagesForAck(capacity); diff --git a/briar-core/src/net/sf/briar/serial/ReaderImpl.java b/briar-core/src/net/sf/briar/serial/ReaderImpl.java index 9fa9994e4..56fab7d60 100644 --- a/briar-core/src/net/sf/briar/serial/ReaderImpl.java +++ b/briar-core/src/net/sf/briar/serial/ReaderImpl.java @@ -98,7 +98,7 @@ class ReaderImpl implements Reader { if(!consumers.remove(c)) throw new IllegalArgumentException(); } - public void addStructReader(int id, StructReader o) { + public void addStructReader(int id, StructReader r) { if(id < 0 || id > 255) throw new IllegalArgumentException(); if(structReaders.length < id + 1) { int len = Math.min(256, Math.max(id + 1, structReaders.length * 2)); @@ -107,7 +107,7 @@ class ReaderImpl implements Reader { structReaders.length); structReaders = newStructReaders; } - structReaders[id] = o; + structReaders[id] = r; } public void removeStructReader(int id) { diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java index c0be50748..18802f9be 100644 --- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; @@ -23,18 +22,18 @@ import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; -import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageVerifier; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.Subscription; +import net.sf.briar.api.protocol.SubscriptionHole; import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.TransportId; @@ -66,7 +65,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final ConnectionWriterFactory connectionWriterFactory; private final ProtocolReaderFactory protocolReaderFactory; private final ProtocolWriterFactory protocolWriterFactory; - private final PacketFactory packetFactory; private final MessageVerifier messageVerifier; private final ContactId contactId; @@ -80,7 +78,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final String messageBody = "Hello world"; private final Collection messageIds; private final Collection transports; - private final long timestamp = System.currentTimeMillis(); public ProtocolIntegrationTest() throws Exception { super(); @@ -93,7 +90,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); - packetFactory = i.getInstance(PacketFactory.class); messageVerifier = i.getInstance(MessageVerifier.class); contactId = new ContactId(234); transportId = new TransportId(TestUtils.getRandomId()); @@ -149,33 +145,29 @@ public class ProtocolIntegrationTest extends BriarTestCase { ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1, false); - Ack a = packetFactory.createAck(messageIds); - writer.writeAck(a); + writer.writeAck(new Ack(messageIds)); writer.writeMessage(message.getSerialised()); writer.writeMessage(message1.getSerialised()); writer.writeMessage(message2.getSerialised()); writer.writeMessage(message3.getSerialised()); - Offer o = packetFactory.createOffer(messageIds); - writer.writeOffer(o); + writer.writeOffer(new Offer(messageIds)); BitSet requested = new BitSet(4); requested.set(1); requested.set(3); - Request r = packetFactory.createRequest(requested, 4); - writer.writeRequest(r); + writer.writeRequest(new Request(requested, 4)); - // Use a LinkedHashMap for predictable iteration order - Map subs = new LinkedHashMap(); - subs.put(group, 0L); - subs.put(group1, 0L); - SubscriptionUpdate s = packetFactory.createSubscriptionUpdate( - Collections.emptyMap(), subs, 0L, timestamp); + Collection holes = Arrays.asList( + new SubscriptionHole(group.getId(), group1.getId())); + Collection subs = Arrays.asList( + new Subscription(group, 0L), new Subscription(group1, 0L)); + SubscriptionUpdate s = new SubscriptionUpdate(holes, subs, 0L, + subscriptionVersion); writer.writeSubscriptionUpdate(s); - TransportUpdate t = packetFactory.createTransportUpdate(transports, - timestamp); + TransportUpdate t = new TransportUpdate(transports, transportVersion); writer.writeTransportUpdate(t); writer.flush(); @@ -232,17 +224,12 @@ public class ProtocolIntegrationTest extends BriarTestCase { // Read the subscription update assertTrue(reader.hasSubscriptionUpdate()); SubscriptionUpdate s = reader.readSubscriptionUpdate(); - Map subs = s.getSubscriptions(); - assertEquals(2, subs.size()); - assertEquals(Long.valueOf(0L), subs.get(group)); - assertEquals(Long.valueOf(0L), subs.get(group1)); - assertTrue(s.getTimestamp() == timestamp); + // FIXME: Test for equality // Read the transport update assertTrue(reader.hasTransportUpdate()); TransportUpdate t = reader.readTransportUpdate(); - assertEquals(transports, t.getTransports()); - assertTrue(t.getTimestamp() == timestamp); + // FIXME: Test for equality in.close(); } diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java index 389bb46a6..3d4d367ac 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java @@ -9,7 +9,6 @@ import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.lifecycle.ShutdownManager; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.db.DatabaseCleaner.Callback; import org.jmock.Expectations; @@ -29,13 +28,11 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -49,7 +46,6 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -62,8 +58,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -72,13 +67,12 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Test public void testExpiringUnsendableMessageDoesNotTriggerBackwardInclusion() - throws DbException { + throws DbException { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -93,8 +87,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -103,13 +96,12 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Test public void testExpiringSendableMessageTriggersBackwardInclusion() - throws DbException { + throws DbException { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -126,8 +118,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -137,15 +128,14 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Override protected DatabaseComponent createDatabaseComponent( Database database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory) { - return createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + ShutdownManager shutdown) { + return createDatabaseComponentImpl(database, cleaner, shutdown); } private DatabaseComponentImpl createDatabaseComponentImpl( Database database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory) { + ShutdownManager shutdown) { return new DatabaseComponentImpl(database, cleaner, shutdown, - packetFactory, new SystemClock()); + new SystemClock()); } } diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java index 4e03a0341..298503a0c 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java @@ -1,6 +1,5 @@ package net.sf.briar.db; -import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; @@ -29,7 +28,6 @@ import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.Transport; @@ -89,7 +87,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { protected abstract DatabaseComponent createDatabaseComponent( Database database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory); + ShutdownManager shutdown); @Test @SuppressWarnings("unchecked") @@ -99,7 +97,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -167,7 +164,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).close(); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.open(false); db.addListener(listener); @@ -198,7 +195,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) allowing(database).startTransaction(); @@ -217,7 +213,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -231,7 +227,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) oneOf(database).startTransaction(); @@ -254,7 +249,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -269,7 +264,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) oneOf(database).startTransaction(); @@ -295,7 +289,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -310,7 +304,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -320,7 +313,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -334,7 +327,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -346,7 +338,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -360,7 +352,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -381,7 +372,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -396,7 +387,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -420,7 +410,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -434,7 +424,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -446,7 +435,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(false)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalPrivateMessage(privateMessage, contactId); @@ -460,7 +449,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -473,7 +461,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalPrivateMessage(privateMessage, contactId); @@ -488,7 +476,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final Ack ack = context.mock(Ack.class); final Offer offer = context.mock(Offer.class); final SubscriptionUpdate subscriptionUpdate = @@ -504,7 +491,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { exactly(16).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); try { db.addContactTransport(contactTransport); @@ -598,7 +585,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // Check whether the contact transport is in the DB (which it's not) exactly(2).of(database).startTransaction(); @@ -609,7 +595,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { exactly(2).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); try { db.incrementConnectionCounter(contactId, transportId, 0L); @@ -633,8 +619,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Ack ack = context.mock(Ack.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -644,16 +628,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // Get the messages to ack oneOf(database).getMessagesToAck(txn, contactId, 123); will(returnValue(messagesToAck)); - // Create the ack packet - oneOf(packetFactory).createAck(messagesToAck); - will(returnValue(ack)); // Record the messages that were acked oneOf(database).removeMessagesToAck(txn, contactId, messagesToAck); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(ack, db.generateAck(contactId, 123)); + Ack a = db.generateAck(contactId, 123); + assertEquals(messagesToAck, a.getMessageIds()); context.assertIsSatisfied(); } @@ -669,7 +651,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -687,7 +668,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addOutstandingMessages(txn, contactId, sendable); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertEquals(messages, db.generateBatch(contactId, size * 2)); @@ -698,17 +679,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testGenerateBatchFromRequest() throws Exception { final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); final byte[] raw1 = new byte[size]; - final Collection requested = new ArrayList(); - requested.add(messageId); - requested.add(messageId1); - requested.add(messageId2); + final Collection requested = Arrays.asList(messageId, + messageId1, messageId2); final Collection messages = Arrays.asList(raw1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -727,7 +705,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { Collections.singletonList(messageId1)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertEquals(messages, db.generateBatch(contactId, size * 3, requested)); @@ -738,16 +716,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testGenerateOffer() throws Exception { final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); - final Collection offerable = new ArrayList(); - offerable.add(messageId); - offerable.add(messageId1); + final Collection messagesToOffer = Arrays.asList(messageId, + messageId1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Offer offer = context.mock(Offer.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -756,15 +731,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Get the sendable message IDs oneOf(database).getMessagesToOffer(txn, contactId, 123); - will(returnValue(offerable)); - // Create the packet - oneOf(packetFactory).createOffer(offerable); - will(returnValue(offer)); + will(returnValue(messagesToOffer)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(offer, db.generateOffer(contactId, 123)); + Offer o = db.generateOffer(contactId, 123); + assertEquals(messagesToOffer, o.getMessageIds()); context.assertIsSatisfied(); } @@ -776,9 +749,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -786,55 +756,39 @@ public abstract class DatabaseComponentTest extends BriarTestCase { allowing(database).containsContact(txn, contactId); will(returnValue(true)); // Get the visible holes and subscriptions - oneOf(database).getVisibleHoles(with(txn), with(contactId), - with(any(long.class))); - will(returnValue(Collections.emptyMap())); - oneOf(database).getVisibleSubscriptions(with(txn), with(contactId), - with(any(long.class))); - will(returnValue(Collections.singletonMap(group, 0L))); + oneOf(database).getVisibleHoles(txn, contactId); + will(returnValue(Collections.emptyMap())); // FIXME + oneOf(database).getVisibleSubscriptions(txn, contactId); + will(returnValue(Collections.singletonMap(group, 0L))); // FIXME // Get the expiry time oneOf(database).getExpiryTime(txn); will(returnValue(0L)); - // Create the packet - oneOf(packetFactory).createSubscriptionUpdate( - with(Collections.emptyMap()), - with(Collections.singletonMap(group, 0L)), - with(any(long.class)), - with(any(long.class))); - will(returnValue(subscriptionUpdate)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(subscriptionUpdate, - db.generateSubscriptionUpdate(contactId)); + SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); + // FIXME: Check that the update contains the expected data context.assertIsSatisfied(); } @Test public void testTransportUpdateNotSentUnlessDue() throws Exception { - final long now = System.currentTimeMillis(); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); allowing(database).commitTransaction(txn); allowing(database).containsContact(txn, contactId); will(returnValue(true)); - // Check whether an update is due - oneOf(database).getTransportsModified(txn); - will(returnValue(now - 1L)); - oneOf(database).getTransportsSent(txn, contactId); - will(returnValue(now)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertNull(db.generateTransportUpdate(contactId)); @@ -848,34 +802,21 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); allowing(database).commitTransaction(txn); allowing(database).containsContact(txn, contactId); will(returnValue(true)); - // Check whether an update is due - oneOf(database).getTransportsModified(txn); - will(returnValue(0L)); - oneOf(database).getTransportsSent(txn, contactId); - will(returnValue(0L)); // Get the local transport properties - oneOf(database).getLocalTransports(txn); - will(returnValue(transports)); - oneOf(database).setTransportsSent(with(txn), with(contactId), - with(any(long.class))); - // Create the packet - oneOf(packetFactory).createTransportUpdate(with(transports), - with(any(long.class))); - will(returnValue(transportUpdate)); + oneOf(database).getTransports(txn); + will(returnValue(transports)); // FIXME }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(transportUpdate, db.generateTransportUpdate(contactId)); + TransportUpdate t = db.generateTransportUpdate(contactId); + // FIXME: Check that the update contains the expected data context.assertIsSatisfied(); } @@ -887,8 +828,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Ack ack = context.mock(Ack.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -896,15 +835,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { allowing(database).containsContact(txn, contactId); will(returnValue(true)); // Get the acked messages - oneOf(ack).getMessageIds(); - will(returnValue(Collections.singletonList(messageId))); oneOf(database).removeOutstandingMessages(txn, contactId, Collections.singletonList(messageId)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - db.receiveAck(contactId, ack); + db.receiveAck(contactId, new Ack(Collections.singletonList(messageId))); context.assertIsSatisfied(); } @@ -916,7 +853,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -931,7 +867,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, privateMessage); @@ -945,7 +881,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -959,7 +894,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, privateMessage); @@ -974,7 +909,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -989,7 +923,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1004,7 +938,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1023,7 +956,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1037,7 +970,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1065,7 +997,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1080,7 +1012,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1110,7 +1041,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1121,10 +1052,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testReceiveOffer() throws Exception { final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); - final Collection offered = new ArrayList(); - offered.add(messageId); - offered.add(messageId1); - offered.add(messageId2); + final Collection offered = Arrays.asList(messageId, + messageId1, messageId2); final BitSet expectedRequest = new BitSet(3); expectedRequest.set(0); expectedRequest.set(2); @@ -1133,9 +1062,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final Offer offer = context.mock(Offer.class); - final Request request = context.mock(Request.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1151,14 +1078,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Visible - do not request message # 1 oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId2); will(returnValue(false)); // Not visible - request message # 2 - // Create the packet - oneOf(packetFactory).createRequest(expectedRequest, 3); - will(returnValue(request)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(request, db.receiveOffer(contactId, offer)); + Request r = db.receiveOffer(contactId, offer); + assertEquals(expectedRequest, r.getBitmap()); + assertEquals(3, r.getLength()); context.assertIsSatisfied(); } @@ -1167,15 +1093,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testReceiveSubscriptionUpdate() throws Exception { final GroupId start = new GroupId(TestUtils.getRandomId()); final GroupId end = new GroupId(TestUtils.getRandomId()); - final long expiry = 1234L, timestamp = 5678L; + final long expiry = 1234L; Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); + context.mock(SubscriptionUpdate.class); // FIXME: Don't mock context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1184,21 +1109,18 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Get the contents of the update oneOf(subscriptionUpdate).getHoles(); - will(returnValue(Collections.singletonMap(start, end))); - oneOf(subscriptionUpdate).getSubscriptions(); - will(returnValue(Collections.singletonMap(group, 0L))); + will(returnValue(Collections.singletonMap(start, end))); // FIXME + oneOf(subscriptionUpdate).getGroupIds(); + will(returnValue(Collections.singletonMap(group, 0L))); // FIXME oneOf(subscriptionUpdate).getExpiryTime(); will(returnValue(expiry)); - oneOf(subscriptionUpdate).getTimestamp(); - will(returnValue(timestamp)); // Store the contents of the update oneOf(database).removeSubscriptions(txn, contactId, start, end); oneOf(database).addSubscription(txn, contactId, group, 0L); oneOf(database).setExpiryTime(txn, contactId, expiry); - oneOf(database).setSubscriptionsReceived(txn, contactId, timestamp); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveSubscriptionUpdate(contactId, subscriptionUpdate); @@ -1207,15 +1129,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testReceiveTransportUpdate() throws Exception { - final long timestamp = 1234L; Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); + context.mock(TransportUpdate.class); // FIXME: Don't mock context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1225,13 +1145,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // Get the contents of the update oneOf(transportUpdate).getTransports(); will(returnValue(transports)); - oneOf(transportUpdate).getTimestamp(); - will(returnValue(timestamp)); - oneOf(database).setTransports(txn, contactId, transports, - timestamp); + oneOf(database).setTransports(txn, contactId, transports); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveTransportUpdate(contactId, transportUpdate); @@ -1245,7 +1162,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) @@ -1268,7 +1184,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalGroupMessage(message); @@ -1283,7 +1199,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -1299,7 +1214,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalPrivateMessage(privateMessage, contactId); @@ -1315,7 +1230,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) @@ -1329,7 +1243,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message was not added, so the listener should not be called }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalGroupMessage(message); @@ -1345,7 +1259,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -1359,7 +1272,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message was not added, so the listener should not be called }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalPrivateMessage(privateMessage, contactId); @@ -1377,19 +1290,16 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); will(returnValue(txn)); oneOf(database).getLocalProperties(txn, transportId); will(returnValue(new TransportProperties())); oneOf(database).mergeLocalProperties(txn, transportId, properties); - oneOf(database).setTransportsModified(with(txn), - with(any(long.class))); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.mergeLocalProperties(transportId, properties); @@ -1406,7 +1316,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1416,7 +1325,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.mergeLocalProperties(transportId, properties); @@ -1431,7 +1340,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1442,7 +1350,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setSeen(contactId, Collections.singletonList(messageId)); @@ -1458,7 +1366,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1473,7 +1380,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { SubscriptionsUpdatedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.setVisibility(groupId, Collections.singletonList(contactId)); @@ -1490,7 +1397,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1502,7 +1408,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.setVisibility(groupId, both); @@ -1517,7 +1423,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addSecrets() oneOf(database).startTransaction(); @@ -1536,7 +1441,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addSecrets(Collections.singletonList(temporarySecret)); assertEquals(Collections.singletonList(temporarySecret), diff --git a/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java index d99bc010f..82f792ab2 100644 --- a/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java @@ -5,13 +5,11 @@ import static net.sf.briar.api.protocol.Types.ACK; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Collection; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.SerialComponent; @@ -19,8 +17,6 @@ import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -33,7 +29,6 @@ public class AckReaderTest extends BriarTestCase { private final SerialComponent serial; private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final Mockery context; public AckReaderTest() throws Exception { super(); @@ -41,61 +36,39 @@ public class AckReaderTest extends BriarTestCase { serial = i.getInstance(SerialComponent.class); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfAckIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - byte[] b = createAck(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - + reader.addStructReader(ACK, new AckReader()); try { reader.readStruct(ACK, Ack.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test - @SuppressWarnings("unchecked") public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - final Ack ack = context.mock(Ack.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createAck(with(any(Collection.class))); - will(returnValue(ack)); - }}); - byte[] b = createAck(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - - assertEquals(ack, reader.readStruct(ACK, Ack.class)); - context.assertIsSatisfied(); + reader.addStructReader(ACK, new AckReader()); + reader.readStruct(ACK, Ack.class); } @Test public void testEmptyAck() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - byte[] b = createEmptyAck(); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - + reader.addStructReader(ACK, new AckReader()); try { reader.readStruct(ACK, Ack.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } private byte[] createAck(boolean tooBig) throws Exception { diff --git a/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java index 94543fb5f..3b06c4b49 100644 --- a/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java +++ b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java @@ -4,17 +4,13 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_AUTHOR_NAME_LENGTH import static net.sf.briar.api.protocol.ProtocolConstants.MAX_BODY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_GROUP_NAME_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PUBLIC_KEY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_SUBJECT_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS; import java.io.ByteArrayOutputStream; import java.security.PrivateKey; import java.util.ArrayList; import java.util.Collection; -import java.util.Map; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; @@ -28,12 +24,8 @@ import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriterFactory; -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportId; -import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.clock.ClockModule; import net.sf.briar.crypto.CryptoModule; @@ -50,7 +42,6 @@ public class ConstantsTest extends BriarTestCase { private final GroupFactory groupFactory; private final AuthorFactory authorFactory; private final MessageFactory messageFactory; - private final PacketFactory packetFactory; private final ProtocolWriterFactory protocolWriterFactory; public ConstantsTest() throws Exception { @@ -61,7 +52,6 @@ public class ConstantsTest extends BriarTestCase { groupFactory = i.getInstance(GroupFactory.class); authorFactory = i.getInstance(AuthorFactory.class); messageFactory = i.getInstance(MessageFactory.class); - packetFactory = i.getInstance(PacketFactory.class); protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); } @@ -85,8 +75,7 @@ public class ConstantsTest extends BriarTestCase { for(int i = 0; i < maxMessages; i++) { acked.add(new MessageId(TestUtils.getRandomId())); } - Ack a = packetFactory.createAck(acked); - writer.writeAck(a); + writer.writeAck(new Ack(acked)); // Check the size of the serialised ack assertTrue(out.size() <= length); } @@ -136,43 +125,11 @@ public class ConstantsTest extends BriarTestCase { for(int i = 0; i < maxMessages; i++) { offered.add(new MessageId(TestUtils.getRandomId())); } - Offer o = packetFactory.createOffer(offered); - writer.writeOffer(o); + writer.writeOffer(new Offer(offered)); // Check the size of the serialised offer assertTrue(out.size() <= length); } - @Test - public void testTransportsFitIntoUpdate() throws Exception { - // Create the maximum number of plugins, each with the maximum number - // of maximum-length properties - Collection transports = new ArrayList(); - for(int i = 0; i < MAX_TRANSPORTS; i++) { - TransportId id = new TransportId(TestUtils.getRandomId()); - Transport t = new Transport(id); - Map m = t.getProperties(); - for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) { - String key = createRandomString(MAX_PROPERTY_LENGTH); - String value = createRandomString(MAX_PROPERTY_LENGTH); - m.put(key, value); - } - transports.add(t); - } - // Add the transports to an update - ByteArrayOutputStream out = - new ByteArrayOutputStream(MAX_PACKET_LENGTH); - ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, - true); - TransportUpdate t = packetFactory.createTransportUpdate(transports, - Long.MAX_VALUE); - writer.writeTransportUpdate(t); - // Check the size of the serialised update - assertTrue(out.size() > MAX_TRANSPORTS * (UniqueId.LENGTH + 4 - + (MAX_PROPERTIES_PER_TRANSPORT * MAX_PROPERTY_LENGTH * 2)) - + 8); - assertTrue(out.size() <= MAX_PACKET_LENGTH); - } - private static String createRandomString(int length) throws Exception { StringBuilder s = new StringBuilder(length); for(int i = 0; i < length; i++) { diff --git a/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java index 7b89cc8d0..e10da58b0 100644 --- a/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java @@ -5,13 +5,11 @@ import static net.sf.briar.api.protocol.Types.OFFER; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Collection; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.SerialComponent; @@ -19,8 +17,6 @@ import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -33,7 +29,6 @@ public class OfferReaderTest extends BriarTestCase { private final SerialComponent serial; private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final Mockery context; public OfferReaderTest() throws Exception { super(); @@ -41,61 +36,39 @@ public class OfferReaderTest extends BriarTestCase { serial = i.getInstance(SerialComponent.class); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfOfferIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - byte[] b = createOffer(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - + reader.addStructReader(OFFER, new OfferReader()); try { reader.readStruct(OFFER, Offer.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test - @SuppressWarnings("unchecked") public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - final Offer offer = context.mock(Offer.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createOffer(with(any(Collection.class))); - will(returnValue(offer)); - }}); - byte[] b = createOffer(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - - assertEquals(offer, reader.readStruct(OFFER, Offer.class)); - context.assertIsSatisfied(); + reader.addStructReader(OFFER, new OfferReader()); + reader.readStruct(OFFER, Offer.class); } @Test public void testEmptyOffer() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - byte[] b = createEmptyOffer(); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - + reader.addStructReader(OFFER, new OfferReader()); try { reader.readStruct(OFFER, Offer.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } private byte[] createOffer(boolean tooBig) throws Exception { diff --git a/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java index 6c916c3e5..b89b9209d 100644 --- a/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java +++ b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.BitSet; import net.sf.briar.BriarTestCase; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.SerialComponent; @@ -24,7 +23,6 @@ public class ProtocolWriterImplTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test - private final PacketFactory packetFactory; private final SerialComponent serial; private final WriterFactory writerFactory; @@ -32,7 +30,6 @@ public class ProtocolWriterImplTest extends BriarTestCase { super(); Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(), new ProtocolModule(), new SerialModule()); - packetFactory = i.getInstance(PacketFactory.class); serial = i.getInstance(SerialComponent.class); writerFactory = i.getInstance(WriterFactory.class); } @@ -54,8 +51,7 @@ public class ProtocolWriterImplTest extends BriarTestCase { b.set(11); b.set(12); b.set(15); - Request r = packetFactory.createRequest(b, 16); - w.writeRequest(r); + w.writeRequest(new Request(b, 16)); // Short user tag 6, 0 as uint7, short bytes with length 2, 0xD959 byte[] output = out.toByteArray(); assertEquals("C6" + "00" + "92" + "D959", @@ -78,8 +74,7 @@ public class ProtocolWriterImplTest extends BriarTestCase { b.set(9); b.set(11); b.set(12); - Request r = packetFactory.createRequest(b, 13); - w.writeRequest(r); + w.writeRequest(new Request(b, 13)); // Short user tag 6, 3 as uint7, short bytes with length 2, 0x59D8 byte[] output = out.toByteArray(); assertEquals("C6" + "03" + "92" + "59D8", diff --git a/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java index d92b0dc42..ec9d2746d 100644 --- a/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java @@ -9,7 +9,6 @@ import java.util.BitSet; import net.sf.briar.BriarTestCase; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; @@ -19,8 +18,6 @@ import net.sf.briar.clock.ClockModule; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -32,8 +29,6 @@ public class RequestReaderTest extends BriarTestCase { private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final PacketFactory packetFactory; - private final Mockery context; public RequestReaderTest() throws Exception { super(); @@ -41,45 +36,27 @@ public class RequestReaderTest extends BriarTestCase { new ProtocolModule(), new SerialModule()); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - packetFactory = i.getInstance(PacketFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfRequestIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - RequestReader requestReader = new RequestReader(packetFactory); - byte[] b = createRequest(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(REQUEST, requestReader); - + reader.addStructReader(REQUEST, new RequestReader()); try { reader.readStruct(REQUEST, Request.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - RequestReader requestReader = new RequestReader(packetFactory); - final Request request = context.mock(Request.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createRequest(with(any(BitSet.class)), - with(any(int.class))); - will(returnValue(request)); - }}); - byte[] b = createRequest(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(REQUEST, requestReader); - - assertEquals(request, reader.readStruct(REQUEST, Request.class)); - context.assertIsSatisfied(); + reader.addStructReader(REQUEST, new RequestReader()); + reader.readStruct(REQUEST, Request.class); } @Test @@ -104,7 +81,7 @@ public class RequestReaderTest extends BriarTestCase { // Deserialise the request ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - RequestReader requestReader = new RequestReader(packetFactory); + RequestReader requestReader = new RequestReader(); reader.addStructReader(REQUEST, requestReader); Request r = reader.readStruct(REQUEST, Request.class); BitSet decoded = r.getBitmap(); diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java index cffd54b1e..eea453a76 100644 --- a/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java @@ -5,8 +5,6 @@ import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.util.Collection; -import java.util.Collections; import java.util.Random; import net.sf.briar.BriarTestCase; @@ -161,17 +159,9 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { MessageListener listener = new MessageListener(); db.addListener(listener); // Fake a transport update from Alice - TransportUpdate transportUpdate = new TransportUpdate() { - - public Collection getTransports() { - Transport t = new Transport(transportId); - return Collections.singletonList(t); - } - - public long getTimestamp() { - return System.currentTimeMillis(); - } - }; + Transport t = new Transport(transportId); + TransportUpdate transportUpdate = new TransportUpdate(t, + transportVersion); db.receiveTransportUpdate(contactId, transportUpdate); // Create a connection recogniser and recognise the connection ByteArrayInputStream in = new ByteArrayInputStream(b);