diff --git a/briar-api/src/net/sf/briar/api/transport/ConnectionListener.java b/briar-api/src/net/sf/briar/api/transport/ConnectionListener.java new file mode 100644 index 000000000..0d9a027c8 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/transport/ConnectionListener.java @@ -0,0 +1,13 @@ +package net.sf.briar.api.transport; + +import net.sf.briar.api.ContactId; + +/** An interface for listening for connection and disconnection events. */ +public interface ConnectionListener { + + /** Called when a contact connects and has no existing connections. */ + void contactConnected(ContactId c); + + /** Called when a contact disconnects and has no remaining connections. */ + void contactDisconnected(ContactId c); +} diff --git a/briar-api/src/net/sf/briar/api/transport/ConnectionRegistry.java b/briar-api/src/net/sf/briar/api/transport/ConnectionRegistry.java index 2983fff39..fd3759613 100644 --- a/briar-api/src/net/sf/briar/api/transport/ConnectionRegistry.java +++ b/briar-api/src/net/sf/briar/api/transport/ConnectionRegistry.java @@ -10,9 +10,15 @@ import net.sf.briar.api.messaging.TransportId; */ public interface ConnectionRegistry { + void addListener(ConnectionListener c); + + void removeListener(ConnectionListener c); + void registerConnection(ContactId c, TransportId t); void unregisterConnection(ContactId c, TransportId t); Collection getConnectedContacts(TransportId t); + + boolean isConnected(ContactId c); } diff --git a/briar-core/src/net/sf/briar/transport/ConnectionRegistryImpl.java b/briar-core/src/net/sf/briar/transport/ConnectionRegistryImpl.java index ffa0c2c97..d1a5b48a6 100644 --- a/briar-core/src/net/sf/briar/transport/ConnectionRegistryImpl.java +++ b/briar-core/src/net/sf/briar/transport/ConnectionRegistryImpl.java @@ -6,41 +6,81 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; import net.sf.briar.api.ContactId; import net.sf.briar.api.messaging.TransportId; +import net.sf.briar.api.transport.ConnectionListener; import net.sf.briar.api.transport.ConnectionRegistry; class ConnectionRegistryImpl implements ConnectionRegistry { // Locking: this private final Map> connections; + // Locking: this + private final Map contactCounts; + private final List listeners; ConnectionRegistryImpl() { connections = new HashMap>(); + contactCounts = new HashMap(); + listeners = new CopyOnWriteArrayList(); } - public synchronized void registerConnection(ContactId c, TransportId t) { - Map m = connections.get(t); - if(m == null) { - m = new HashMap(); - connections.put(t, m); - } - Integer count = m.get(c); - if(count == null) m.put(c, 1); - else m.put(c, count + 1); + public void addListener(ConnectionListener c) { + listeners.add(c); } - public synchronized void unregisterConnection(ContactId c, TransportId t) { - Map m = connections.get(t); - if(m == null) throw new IllegalArgumentException(); - Integer count = m.remove(c); - if(count == null) throw new IllegalArgumentException(); - if(count == 1) { - if(m.isEmpty()) connections.remove(t); - } else { - m.put(c, count - 1); + public void removeListener(ConnectionListener c) { + listeners.remove(c); + } + + public void registerConnection(ContactId c, TransportId t) { + boolean firstConnection = false; + synchronized(this) { + Map m = connections.get(t); + if(m == null) { + m = new HashMap(); + connections.put(t, m); + } + Integer count = m.get(c); + if(count == null) m.put(c, 1); + else m.put(c, count + 1); + count = contactCounts.get(c); + if(count == null) { + firstConnection = true; + contactCounts.put(c, 1); + } else { + contactCounts.put(c, count + 1); + } } + if(firstConnection) + for(ConnectionListener l : listeners) l.contactConnected(c); + } + + public void unregisterConnection(ContactId c, TransportId t) { + boolean lastConnection = false; + synchronized(this) { + Map m = connections.get(t); + if(m == null) throw new IllegalArgumentException(); + Integer count = m.remove(c); + if(count == null) throw new IllegalArgumentException(); + if(count == 1) { + if(m.isEmpty()) connections.remove(t); + } else { + m.put(c, count - 1); + } + count = contactCounts.get(c); + if(count == null) throw new IllegalArgumentException(); + if(count == 1) { + lastConnection = true; + contactCounts.remove(c); + } else { + contactCounts.put(c, count - 1); + } + } + if(lastConnection) + for(ConnectionListener l : listeners) l.contactDisconnected(c); } public synchronized Collection getConnectedContacts( @@ -50,4 +90,8 @@ class ConnectionRegistryImpl implements ConnectionRegistry { List keys = new ArrayList(m.keySet()); return Collections.unmodifiableList(keys); } + + public synchronized boolean isConnected(ContactId c) { + return contactCounts.containsKey(c); + } }