diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 7fc3908bf..4bbbaec88 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -58,7 +58,8 @@ public interface DatabaseComponent { * Adds a new contact to the database with the given transport properties * and returns an ID for the contact. */ - ContactId addContact(Map transports) throws DbException; + ContactId addContact(Map> transports) + throws DbException; /** Adds a locally generated message to the database. */ void addLocallyGeneratedMessage(Message m) throws DbException; @@ -111,10 +112,11 @@ public interface DatabaseComponent { Collection getSubscriptions() throws DbException; /** Returns the local transport properties. */ - Map getTransports() throws DbException; + Map> getTransports() throws DbException; /** Returns the transport properties for the given contact. */ - Map getTransports(ContactId c) throws DbException; + Map> getTransports(ContactId c) + throws DbException; /** Returns the contacts to which the given group is visible. */ Collection getVisibility(GroupId g) throws DbException; @@ -152,9 +154,11 @@ public interface DatabaseComponent { void setRating(AuthorId a, Rating r) throws DbException; /** - * Sets the local transport properties, replacing any existing properties. + * Sets the local transport properties for the transport with the given + * name, replacing any existing properties for that transport. */ - void setTransports(Map transports) throws DbException; + void setTransports(String name, Map transports) + throws DbException; /** * Makes the given group visible to the given set of contacts and invisible diff --git a/api/net/sf/briar/api/protocol/Transports.java b/api/net/sf/briar/api/protocol/Transports.java index d18f5ccd3..8af5d09ac 100644 --- a/api/net/sf/briar/api/protocol/Transports.java +++ b/api/net/sf/briar/api/protocol/Transports.java @@ -12,7 +12,7 @@ public interface Transports { static final int MAX_SIZE = (1024 * 1024) - 100; /** Returns the transports contained in the update. */ - Map getTransports(); + Map> getTransports(); /** * Returns the update's timestamp. Updates that are older than the newest diff --git a/api/net/sf/briar/api/protocol/writers/TransportWriter.java b/api/net/sf/briar/api/protocol/writers/TransportWriter.java index 6e03d2f56..f835e0059 100644 --- a/api/net/sf/briar/api/protocol/writers/TransportWriter.java +++ b/api/net/sf/briar/api/protocol/writers/TransportWriter.java @@ -7,5 +7,6 @@ import java.util.Map; public interface TransportWriter { /** Writes the contents of the update. */ - void writeTransports(Map transports) throws IOException; + void writeTransports(Map> transports) + throws IOException; } diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index 02eabb4dc..f60389303 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -82,7 +82,7 @@ interface Database { *

* Locking: contacts write, transports write. */ - ContactId addContact(T txn, Map transports) + ContactId addContact(T txn, Map> transports) throws DbException; /** @@ -272,14 +272,15 @@ interface Database { *

* Locking: transports read. */ - Map getTransports(T txn) throws DbException; + Map> getTransports(T txn) throws DbException; /** * Returns the transport properties for the given contact. *

* Locking: contacts read, transports read. */ - Map getTransports(T txn, ContactId c) throws DbException; + Map> getTransports(T txn, ContactId c) + throws DbException; /** * Returns the contacts to which the given group is visible. @@ -397,11 +398,12 @@ interface Database { long timestamp) throws DbException; /** - * Sets the local transport properties, replacing any existing properties. + * Sets the local transport properties for the transport with the given + * name, replacing any existing properties for that transport. *

* Locking: transports write. */ - void setTransports(T txn, Map transports) + void setTransports(T txn, String name, Map transports) throws DbException; /** @@ -411,8 +413,9 @@ interface Database { *

* Locking: contacts write, transports write. */ - void setTransports(T txn, ContactId c, Map transports, - long timestamp) throws DbException; + void setTransports(T txn, ContactId c, + Map> transports, long timestamp) + throws DbException; /** * Makes the given group visible to the given set of contacts and invisible diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index bb09069af..f31fefffd 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -159,17 +159,19 @@ abstract class JdbcDatabase implements Database { private static final String CREATE_CONTACT_TRANSPORTS = "CREATE TABLE contactTransports" + " (contactId INT NOT NULL," + + " transportName VARCHAR NOT NULL," + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (contactId, key)," + + " PRIMARY KEY (contactId, transportName, key)," + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - private static final String CREATE_LOCAL_TRANSPORTS = - "CREATE TABLE localTransports" - + " (key VARCHAR NOT NULL," + private static final String CREATE_TRANSPORTS = + "CREATE TABLE transports" + + " (transportName VARCHAR NOT NULL," + + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (key))"; + + " PRIMARY KEY (transportName, key))"; private static final Logger LOG = Logger.getLogger(JdbcDatabase.class.getName()); @@ -252,7 +254,7 @@ abstract class JdbcDatabase implements Database { s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_CONTACT); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); - s.executeUpdate(insertTypeNames(CREATE_LOCAL_TRANSPORTS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.close(); } catch(SQLException e) { tryToClose(s); @@ -406,7 +408,8 @@ abstract class JdbcDatabase implements Database { } } - public ContactId addContact(Connection txn, Map transports) + public ContactId addContact(Connection txn, + Map> transports) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -434,24 +437,27 @@ abstract class JdbcDatabase implements Database { if(affected != 1) throw new DbStateException(); ps.close(); // Store the contact's transport properties - if(transports != null) { - sql = "INSERT INTO contactTransports (contactId, key, value)" - + " VALUES (?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - for(Entry e : transports.entrySet()) { - ps.setString(2, e.getKey()); - ps.setString(3, e.getValue()); + sql = "INSERT INTO contactTransports" + + " (contactId, transportName, key, value)" + + " VALUES (?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + int batchSize = 0; + for(Entry> e : transports.entrySet()) { + ps.setString(2, e.getKey()); + for(Entry e1 : e.getValue().entrySet()) { + ps.setString(3, e1.getKey()); + ps.setString(4, e1.getValue()); ps.addBatch(); + batchSize++; } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != transports.size()) - throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } - ps.close(); } + 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(); + } + ps.close(); return c; } catch(SQLException e) { tryToClose(ps); @@ -1119,19 +1125,31 @@ abstract class JdbcDatabase implements Database { } } - public Map getTransports(Connection txn) + public Map> getTransports(Connection txn) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT key, value FROM localTransports"; + String sql = "SELECT transportName, key, value" + + " FROM transports" + + " ORDER BY transportName"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); - Map transports = new TreeMap(); - while(rs.next()) transports.put(rs.getString(1), rs.getString(2)); + Map> outer = + new TreeMap>(); + Map inner = null; + String lastName = null; + while(rs.next()) { + String name = rs.getString(1); + if(!name.equals(lastName)) { + inner = new TreeMap(); + outer.put(name, inner); + } + inner.put(rs.getString(2), rs.getString(3)); + } rs.close(); ps.close(); - return transports; + return outer; } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1139,21 +1157,33 @@ abstract class JdbcDatabase implements Database { } } - public Map getTransports(Connection txn, ContactId c) - throws DbException { + public Map> getTransports(Connection txn, + ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT key, value FROM contactTransports" - + " WHERE contactId = ?"; + String sql = "SELECT transportName, key, value" + + " FROM contactTransports" + + " WHERE contactId = ?" + + " ORDER BY transportName"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - Map transports = new TreeMap(); - while(rs.next()) transports.put(rs.getString(1), rs.getString(2)); + Map> outer = + new TreeMap>(); + Map inner = null; + String lastName = null; + while(rs.next()) { + String name = rs.getString(1); + if(!name.equals(lastName)) { + inner = new TreeMap(); + outer.put(name, inner); + } + inner.put(rs.getString(2), rs.getString(3)); + } rs.close(); ps.close(); - return transports; + return outer; } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1579,33 +1609,33 @@ abstract class JdbcDatabase implements Database { } } - public void setTransports(Connection txn, Map transports) - throws DbException { + public void setTransports(Connection txn, String name, + Map transports) throws DbException { PreparedStatement ps = null; try { - // Delete any existing transports - String sql = "DELETE FROM localTransports"; + // Delete any existing properties for the named transport + String sql = "DELETE FROM transports WHERE transportName = ?"; ps = txn.prepareStatement(sql); + ps.setString(1, name); ps.executeUpdate(); ps.close(); - // Store the new transports - if(!transports.isEmpty()) { - sql = "INSERT INTO localTransports (key, value)" - + " VALUES (?, ?)"; - ps = txn.prepareStatement(sql); - for(Entry e : transports.entrySet()) { - ps.setString(1, e.getKey()); - ps.setString(2, e.getValue()); - ps.addBatch(); - } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != transports.size()) - throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } - ps.close(); + // Store the new properties + sql = "INSERT INTO transports (transportName, key, value)" + + " VALUES (?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setString(1, name); + for(Entry e : transports.entrySet()) { + ps.setString(2, e.getKey()); + ps.setString(3, e.getValue()); + ps.addBatch(); } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != transports.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); throw new DbException(e); @@ -1613,7 +1643,8 @@ abstract class JdbcDatabase implements Database { } public void setTransports(Connection txn, ContactId c, - Map transports, long timestamp) throws DbException { + Map> transports, long timestamp) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -1636,24 +1667,27 @@ abstract class JdbcDatabase implements Database { ps.executeUpdate(); ps.close(); // Store the new transports - if(transports != null) { - sql = "INSERT INTO contactTransports (contactId, key, value)" - + " VALUES (?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - for(Entry e : transports.entrySet()) { - ps.setString(2, e.getKey()); - ps.setString(3, e.getValue()); + sql = "INSERT INTO contactTransports" + + " (contactId, transportName, key, value)" + + " VALUES (?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + int batchSize = 0; + for(Entry> e : transports.entrySet()) { + ps.setString(2, e.getKey()); + for(Entry e1 : e.getValue().entrySet()) { + ps.setString(3, e1.getKey()); + ps.setString(4, e1.getValue()); ps.addBatch(); + batchSize++; } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != transports.size()) - throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); - } - ps.close(); } + 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(); + } + ps.close(); // Update the timestamp sql = "UPDATE contacts SET transportsTimestamp = ?" + " WHERE contactId = ?"; diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index 2e7aca33c..9d85dbe78 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -125,7 +125,7 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { } } - public ContactId addContact(Map transports) + public ContactId addContact(Map> transports) throws DbException { if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact"); contactLock.writeLock().lock(); @@ -497,7 +497,8 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { try { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn); + Map> transports = + db.getTransports(txn); t.writeTransports(transports); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + transports.size() + " transports"); @@ -568,12 +569,13 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { } } - public Map getTransports() throws DbException { + public Map> getTransports() throws DbException { transportLock.readLock().lock(); try { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn); + Map> transports = + db.getTransports(txn); db.commitTransaction(txn); return transports; } catch(DbException e) { @@ -585,7 +587,8 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { } } - public Map getTransports(ContactId c) throws DbException { + public Map> getTransports(ContactId c) + throws DbException { contactLock.readLock().lock(); try { if(!containsContact(c)) throw new NoSuchContactException(); @@ -593,7 +596,8 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { try { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn, c); + Map> transports = + db.getTransports(txn, c); db.commitTransaction(txn); return transports; } catch(DbException e) { @@ -826,7 +830,8 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { try { Txn txn = db.startTransaction(); try { - Map transports = t.getTransports(); + Map> transports = + t.getTransports(); db.setTransports(txn, c, transports, t.getTimestamp()); if(LOG.isLoggable(Level.FINE)) LOG.fine("Received " + transports.size() @@ -902,15 +907,15 @@ class ReadWriteLockDatabaseComponent extends DatabaseComponentImpl { } } - public void setTransports(Map transports) + public void setTransports(String name, Map transports) throws DbException { boolean changed = false; transportLock.writeLock().lock(); try { Txn txn = db.startTransaction(); try { - if(!transports.equals(db.getTransports(txn))) { - db.setTransports(txn, transports); + if(!transports.equals(db.getTransports(txn).get(name))) { + db.setTransports(txn, name, transports); changed = true; } db.commitTransaction(txn); diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java index d0e09670c..1d7a48350 100644 --- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java +++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java @@ -96,7 +96,7 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { } } - public ContactId addContact(Map transports) + public ContactId addContact(Map> transports) throws DbException { if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact"); synchronized(contactLock) { @@ -366,7 +366,8 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { synchronized(transportLock) { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn); + Map> transports = + db.getTransports(txn); t.writeTransports(transports); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added " + transports.size() + " transports"); @@ -424,11 +425,12 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { } } - public Map getTransports() throws DbException { + public Map> getTransports() throws DbException { synchronized(transportLock) { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn); + Map> transports = + db.getTransports(txn); db.commitTransaction(txn); return transports; } catch(DbException e) { @@ -438,13 +440,15 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { } } - public Map getTransports(ContactId c) throws DbException { + public Map> getTransports(ContactId c) + throws DbException { synchronized(contactLock) { if(!containsContact(c)) throw new NoSuchContactException(); synchronized(transportLock) { Txn txn = db.startTransaction(); try { - Map transports = db.getTransports(txn, c); + Map> transports = + db.getTransports(txn, c); db.commitTransaction(txn); return transports; } catch(DbException e) { @@ -614,7 +618,8 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { synchronized(transportLock) { Txn txn = db.startTransaction(); try { - Map transports = t.getTransports(); + Map> transports = + t.getTransports(); db.setTransports(txn, c, transports, t.getTimestamp()); if(LOG.isLoggable(Level.FINE)) LOG.fine("Received " + transports.size() @@ -668,14 +673,14 @@ class SynchronizedDatabaseComponent extends DatabaseComponentImpl { } } - public void setTransports(Map transports) + public void setTransports(String name, Map transports) throws DbException { boolean changed = false; synchronized(transportLock) { Txn txn = db.startTransaction(); try { - if(!transports.equals(db.getTransports(txn))) { - db.setTransports(txn, transports); + if(!transports.equals(db.getTransports(txn).get(name))) { + db.setTransports(txn, name, transports); changed = true; } db.commitTransaction(txn); diff --git a/components/net/sf/briar/invitation/InvitationWorker.java b/components/net/sf/briar/invitation/InvitationWorker.java index 9b05d6329..dc6d5615d 100644 --- a/components/net/sf/briar/invitation/InvitationWorker.java +++ b/components/net/sf/briar/invitation/InvitationWorker.java @@ -71,7 +71,7 @@ class InvitationWorker implements Runnable { File invitationDat = new File(dir, "invitation.dat"); callback.encryptingFile(invitationDat); // FIXME: Create a real invitation - Map transports; + Map> transports; try { transports = databaseComponent.getTransports(); } catch(DbException e) { diff --git a/components/net/sf/briar/protocol/TransportFactory.java b/components/net/sf/briar/protocol/TransportFactory.java index 8a2b39725..d56c0c582 100644 --- a/components/net/sf/briar/protocol/TransportFactory.java +++ b/components/net/sf/briar/protocol/TransportFactory.java @@ -6,5 +6,6 @@ import net.sf.briar.api.protocol.Transports; interface TransportFactory { - Transports createTransports(Map transports, long timestamp); + Transports createTransports(Map> transports, + long timestamp); } diff --git a/components/net/sf/briar/protocol/TransportFactoryImpl.java b/components/net/sf/briar/protocol/TransportFactoryImpl.java index 85ceefc19..1cabeed06 100644 --- a/components/net/sf/briar/protocol/TransportFactoryImpl.java +++ b/components/net/sf/briar/protocol/TransportFactoryImpl.java @@ -6,7 +6,7 @@ import net.sf.briar.api.protocol.Transports; class TransportFactoryImpl implements TransportFactory { - public Transports createTransports(Map transports, + public Transports createTransports(Map> transports, long timestamp) { return new TransportsImpl(transports, timestamp); } diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java index bec0dd0f6..b945c1b65 100644 --- a/components/net/sf/briar/protocol/TransportReader.java +++ b/components/net/sf/briar/protocol/TransportReader.java @@ -2,6 +2,7 @@ package net.sf.briar.protocol; import java.io.IOException; import java.util.Map; +import java.util.TreeMap; import net.sf.briar.api.protocol.Tags; import net.sf.briar.api.protocol.Transports; @@ -26,10 +27,26 @@ class TransportReader implements ObjectReader { // Read the data r.addConsumer(counting); r.readUserDefinedTag(Tags.TRANSPORTS); - Map transports = r.readMap(String.class, String.class); + // Transport maps are always written in delimited form + Map> outer = + new TreeMap>(); + r.readMapStart(); + while(!r.hasMapEnd()) { + String name = r.readString(Transports.MAX_SIZE); + Map inner = new TreeMap(); + r.readMapStart(); + while(!r.hasMapEnd()) { + String key = r.readString(Transports.MAX_SIZE); + String value = r.readString(Transports.MAX_SIZE); + inner.put(key, value); + } + r.readMapEnd(); + outer.put(name, inner); + } + r.readMapEnd(); long timestamp = r.readInt64(); r.removeConsumer(counting); // Build and return the transports update - return transportFactory.createTransports(transports, timestamp); + return transportFactory.createTransports(outer, timestamp); } } diff --git a/components/net/sf/briar/protocol/TransportsImpl.java b/components/net/sf/briar/protocol/TransportsImpl.java index 247f4aa5f..157b1d2af 100644 --- a/components/net/sf/briar/protocol/TransportsImpl.java +++ b/components/net/sf/briar/protocol/TransportsImpl.java @@ -6,15 +6,16 @@ import net.sf.briar.api.protocol.Transports; class TransportsImpl implements Transports { - private final Map transports; + private final Map> transports; private final long timestamp; - TransportsImpl(Map transports, long timestamp) { + TransportsImpl(Map> transports, + long timestamp) { this.transports = transports; this.timestamp = timestamp; } - public Map getTransports() { + public Map> getTransports() { return transports; } diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java index 495e001b9..89dc7ac0d 100644 --- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java @@ -3,6 +3,7 @@ package net.sf.briar.protocol.writers; import java.io.IOException; import java.io.OutputStream; import java.util.Map; +import java.util.Map.Entry; import net.sf.briar.api.protocol.Tags; import net.sf.briar.api.protocol.writers.TransportWriter; @@ -19,10 +20,21 @@ class TransportWriterImpl implements TransportWriter { w = writerFactory.createWriter(out); } - public void writeTransports(Map transports) + public void writeTransports(Map> transports) throws IOException { w.writeUserDefinedTag(Tags.TRANSPORTS); - w.writeMap(transports); + // Transport maps are always written in delimited form + w.writeMapStart(); + for(Entry> e : transports.entrySet()) { + w.writeString(e.getKey()); + w.writeMapStart(); + for(Entry e1 : e.getValue().entrySet()) { + w.writeString(e1.getKey()); + w.writeString(e1.getValue()); + } + w.writeMapEnd(); + } + w.writeMapEnd(); w.writeInt64(System.currentTimeMillis()); out.flush(); } diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 2ddcdfd13..cf8db1128 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -52,7 +52,7 @@ public abstract class DatabaseComponentTest extends TestCase { private final byte[] raw; private final Message message; private final Group group; - private final Map transports; + private final Map> transports; public DatabaseComponentTest() { super(); @@ -68,7 +68,8 @@ public abstract class DatabaseComponentTest extends TestCase { message = new TestMessage(messageId, MessageId.NONE, groupId, authorId, timestamp, raw); group = new TestGroup(groupId, "The really exciting group", null); - transports = Collections.singletonMap("foo", "bar"); + transports = Collections.singletonMap("foo", + Collections.singletonMap("bar", "baz")); } protected abstract DatabaseComponent createDatabaseComponent( @@ -1112,9 +1113,9 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(database).startTransaction(); will(returnValue(txn)); oneOf(database).getTransports(txn); - will(returnValue(Collections.singletonMap("foo", "bar"))); - oneOf(database).setTransports(txn, - Collections.singletonMap("bar", "baz")); + will(returnValue(transports)); + oneOf(database).setTransports(txn, "bar", + Collections.singletonMap("baz", "bam")); oneOf(database).commitTransaction(txn); oneOf(listener).eventOccurred( DatabaseListener.Event.TRANSPORTS_UPDATED); @@ -1122,7 +1123,7 @@ public abstract class DatabaseComponentTest extends TestCase { DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addListener(listener); - db.setTransports(Collections.singletonMap("bar", "baz")); + db.setTransports("bar", Collections.singletonMap("baz", "bam")); context.assertIsSatisfied(); } @@ -1138,13 +1139,13 @@ public abstract class DatabaseComponentTest extends TestCase { oneOf(database).startTransaction(); will(returnValue(txn)); oneOf(database).getTransports(txn); - will(returnValue(Collections.singletonMap("bar", "baz"))); + will(returnValue(transports)); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addListener(listener); - db.setTransports(Collections.singletonMap("bar", "baz")); + db.setTransports("foo", transports.get("foo")); context.assertIsSatisfied(); } diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index ea40cab10..7de9d7953 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -60,6 +60,7 @@ public class H2DatabaseTest extends TestCase { private final byte[] raw; private final Message message; private final Group group; + private final Map> transports; public H2DatabaseTest() throws Exception { super(); @@ -78,6 +79,8 @@ public class H2DatabaseTest extends TestCase { message = new TestMessage(messageId, MessageId.NONE, groupId, authorId, timestamp, raw); group = groupFactory.createGroup(groupId, "Group name", null); + transports = Collections.singletonMap("foo", + Collections.singletonMap("bar", "baz")); } @Before @@ -91,7 +94,6 @@ public class H2DatabaseTest extends TestCase { Database db = open(false); Connection txn = db.startTransaction(); assertFalse(db.containsContact(txn, contactId)); - Map transports = Collections.singletonMap("foo", "bar"); assertEquals(contactId, db.addContact(txn, transports)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsSubscription(txn, groupId)); @@ -107,8 +109,7 @@ public class H2DatabaseTest extends TestCase { db = open(true); txn = db.startTransaction(); assertTrue(db.containsContact(txn, contactId)); - transports = db.getTransports(txn, contactId); - assertEquals(Collections.singletonMap("foo", "bar"), transports); + assertEquals(transports, db.getTransports(txn, contactId)); assertTrue(db.containsSubscription(txn, groupId)); assertTrue(db.containsMessage(txn, messageId)); byte[] raw1 = db.getMessage(txn, messageId); @@ -141,20 +142,20 @@ public class H2DatabaseTest extends TestCase { // Create three contacts assertFalse(db.containsContact(txn, contactId)); - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId1)); - assertEquals(contactId1, db.addContact(txn, null)); + assertEquals(contactId1, db.addContact(txn, transports)); assertTrue(db.containsContact(txn, contactId1)); assertFalse(db.containsContact(txn, contactId2)); - assertEquals(contactId2, db.addContact(txn, null)); + assertEquals(contactId2, db.addContact(txn, transports)); assertTrue(db.containsContact(txn, contactId2)); // Delete one of the contacts db.removeContact(txn, contactId1); assertFalse(db.containsContact(txn, contactId1)); // Add another contact - a new ID should be created assertFalse(db.containsContact(txn, contactId3)); - assertEquals(contactId3, db.addContact(txn, null)); + assertEquals(contactId3, db.addContact(txn, transports)); assertTrue(db.containsContact(txn, contactId3)); db.commitTransaction(txn); @@ -201,7 +202,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -239,7 +240,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -281,7 +282,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.addMessage(txn, message); @@ -318,7 +319,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -349,7 +350,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); db.addMessage(txn, message); @@ -382,7 +383,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and some batches to ack - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId1); db.commitTransaction(txn); @@ -410,7 +411,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and receive the same batch twice - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId); db.commitTransaction(txn); @@ -437,7 +438,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -474,7 +475,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -517,7 +518,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { db.addOutstandingBatch(txn, contactId, ids[i], @@ -556,7 +557,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { db.addOutstandingBatch(txn, contactId, ids[i], @@ -784,24 +785,28 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact with some transport properties - Map transports = Collections.singletonMap("foo", "bar"); assertEquals(contactId, db.addContact(txn, transports)); assertEquals(transports, db.getTransports(txn, contactId)); // Replace the transport properties - transports = new TreeMap(); - transports.put("foo", "bar baz"); - transports.put("bar", "baz quux"); - db.setTransports(txn, contactId, transports, 1); - assertEquals(transports, db.getTransports(txn, contactId)); + Map> transports1 = + new TreeMap>(); + transports1.put("foo", Collections.singletonMap("bar", "baz")); + transports1.put("bar", Collections.singletonMap("baz", "quux")); + db.setTransports(txn, contactId, transports1, 1); + assertEquals(transports1, db.getTransports(txn, contactId)); // Remove the transport properties db.setTransports(txn, contactId, - Collections.emptyMap(), 2); + Collections.>emptyMap(), 2); assertEquals(Collections.emptyMap(), db.getTransports(txn, contactId)); // Set the local transport properties - db.setTransports(txn, transports); + for(String s : transports.keySet()) { + db.setTransports(txn, s, transports.get(s)); + } assertEquals(transports, db.getTransports(txn)); // Remove the local transport properties - db.setTransports(txn, Collections.emptyMap()); + for(String s : transports.keySet()) { + db.setTransports(txn, s, Collections.emptyMap()); + } assertEquals(Collections.emptyMap(), db.getTransports(txn)); db.commitTransaction(txn); @@ -814,19 +819,20 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact with some transport properties - Map transports = Collections.singletonMap("foo", "bar"); assertEquals(contactId, db.addContact(txn, transports)); assertEquals(transports, db.getTransports(txn, contactId)); // Replace the transport properties using a timestamp of 2 - Map transports1 = new TreeMap(); - transports1.put("foo", "bar baz"); - transports1.put("bar", "baz quux"); + Map> transports1 = + new TreeMap>(); + transports1.put("foo", Collections.singletonMap("bar", "baz")); + transports1.put("bar", Collections.singletonMap("baz", "quux")); db.setTransports(txn, contactId, transports1, 2); assertEquals(transports1, db.getTransports(txn, contactId)); // Try to replace the transport properties using a timestamp of 1 - Map transports2 = new TreeMap(); - transports2.put("bar", "baz"); - transports2.put("quux", "fnord"); + Map> transports2 = + new TreeMap>(); + transports2.put("bar", Collections.singletonMap("baz", "quux")); + transports2.put("baz", Collections.singletonMap("quux", "fnord")); db.setTransports(txn, contactId, transports2, 1); // The old properties should still be there assertEquals(transports1, db.getTransports(txn, contactId)); @@ -844,7 +850,6 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - Map transports = Collections.emptyMap(); assertEquals(contactId, db.addContact(txn, transports)); // Add some subscriptions Collection subs = Collections.singletonList(group); @@ -869,7 +874,6 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - Map transports = Collections.emptyMap(); assertEquals(contactId, db.addContact(txn, transports)); // Add some subscriptions Collection subs = Collections.singletonList(group); @@ -892,7 +896,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -910,7 +914,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); db.addMessage(txn, message); @@ -933,7 +937,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); db.addMessage(txn, message); @@ -955,7 +959,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -980,7 +984,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -999,7 +1003,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and a neighbour subscription - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); // There's no local subscription for the group @@ -1016,7 +1020,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.addMessage(txn, message); db.setStatus(txn, contactId, messageId, Status.NEW); @@ -1035,7 +1039,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.addMessage(txn, message); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -1055,7 +1059,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -1076,7 +1080,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, Collections.singleton(group), 1); @@ -1096,7 +1100,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, null)); + assertEquals(contactId, db.addContact(txn, transports)); db.addSubscription(txn, group); // The group should not be visible to the contact diff --git a/test/net/sf/briar/protocol/FileReadWriteTest.java b/test/net/sf/briar/protocol/FileReadWriteTest.java index 9f8304687..52516a380 100644 --- a/test/net/sf/briar/protocol/FileReadWriteTest.java +++ b/test/net/sf/briar/protocol/FileReadWriteTest.java @@ -10,6 +10,7 @@ import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.Map; import junit.framework.TestCase; import net.sf.briar.TestUtils; @@ -72,6 +73,7 @@ public class FileReadWriteTest extends TestCase { private final Message message, message1, message2, message3; private final String authorName = "Alice"; private final String messageBody = "Hello world"; + private final Map> transports; public FileReadWriteTest() throws Exception { super(); @@ -111,6 +113,8 @@ public class FileReadWriteTest extends TestCase { message3 = messageEncoder.encodeMessage(MessageId.NONE, group1, groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(), messageBody.getBytes("UTF-8")); + transports = Collections.singletonMap("foo", + Collections.singletonMap("bar", "baz")); } @Before @@ -154,7 +158,7 @@ public class FileReadWriteTest extends TestCase { s.writeSubscriptions(subs); TransportWriter t = packetWriterFactory.createTransportWriter(out); - t.writeTransports(Collections.singletonMap("foo", "bar")); + t.writeTransports(transports); out.close(); assertTrue(file.exists()); @@ -229,7 +233,7 @@ public class FileReadWriteTest extends TestCase { assertTrue(reader.hasUserDefined(Tags.TRANSPORTS)); Transports t = reader.readUserDefined(Tags.TRANSPORTS, Transports.class); - assertEquals(Collections.singletonMap("foo", "bar"), t.getTransports()); + assertEquals(transports, t.getTransports()); assertTrue(t.getTimestamp() > start); assertTrue(t.getTimestamp() <= System.currentTimeMillis());