package org.limewire.friend.impl; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.limewire.friend.api.Friend; import org.limewire.friend.api.FriendConnectionEvent; import org.limewire.friend.api.FriendEvent; import org.limewire.friend.api.FriendManager; import org.limewire.friend.api.FriendPresence; import org.limewire.friend.api.FriendPresenceEvent; import org.limewire.friend.api.MutableFriendManager; import org.limewire.friend.api.feature.LimewireFeature; import org.limewire.inject.EagerSingleton; import org.limewire.listener.EventBroadcaster; import org.limewire.listener.EventListener; import org.limewire.listener.ListenerSupport; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import com.google.inject.Inject; import com.google.inject.name.Named; /** * Default implementation of {@link FriendManager} and {@link MutableFriendManager}. * <p> * When friends are added and removed, it fires events in the respective broadcasters, * @Named("known") FriendEvent and @Named("available") FriendEvent. * <p> * Also listens to {@link FriendConnectionEvent} and removes all available and known * friends, if it's a disconnect event. This will be have to be revisited when we * start supporting concurrent friend connections to different networks. * <p> * The class is threadsafe. */ @EagerSingleton class MutableFriendManagerImpl implements MutableFriendManager { private static final Log LOG = LogFactory.getLog(MutableFriendManagerImpl.class); private final EventBroadcaster<FriendEvent> knownBroadcaster; private final EventBroadcaster<FriendEvent> availableBroadcaster; private final ConcurrentMap<String, Friend> knownFriends = new ConcurrentHashMap<String, Friend>(); private final ConcurrentMap<String, Friend> availFriends = new ConcurrentHashMap<String, Friend>(); @Inject public MutableFriendManagerImpl(@Named("known") EventBroadcaster<FriendEvent> knownBroadcaster, @Named("available") EventBroadcaster<FriendEvent> availableBroadcaster, EventBroadcaster<FriendPresenceEvent> friendPresenceBroadcaster) { this.knownBroadcaster = knownBroadcaster; this.availableBroadcaster = availableBroadcaster; } @Inject void register(ListenerSupport<FriendConnectionEvent> listenerSupport) { listenerSupport.addListener(new EventListener<FriendConnectionEvent>() { @Override public void handleEvent(FriendConnectionEvent event) { switch(event.getType()) { case DISCONNECTED: for (Friend user : availFriends.values()) { removeAvailableFriend(user); } for (Friend user : knownFriends.values()) { removeKnownFriend(user, false); } break; } } }); } @Override public void addKnownFriend(Friend friend) { if (knownFriends.putIfAbsent(friend.getId(), friend) == null) { LOG.debugf("adding known friend: {0}", friend); knownBroadcaster.broadcast(new FriendEvent(friend, FriendEvent.Type.ADDED)); } else { LOG.debugf("not adding known friend: {0}", friend); } } @Override public void removeKnownFriend(Friend friend, boolean delete) { if (knownFriends.remove(friend.getId()) != null) { LOG.debugf("removed known friend: {0}", friend); if(delete) { knownBroadcaster.broadcast(new FriendEvent(friend, FriendEvent.Type.DELETE)); } knownBroadcaster.broadcast(new FriendEvent(friend, FriendEvent.Type.REMOVED)); } else { LOG.debugf("known friend {0} already removed", friend); } } @Override public FriendPresence getMostRelevantFriendPresence(String id) { Friend friend = availFriends.get(id); if(friend == null) { return null; } else { Collection<FriendPresence> presences = friend.getPresences().values(); //TODO: this is not guarenteed to return the correct FriendPresence // if the user is logged in through two LWs with the same ID // Not really able to fix this without modifying the Browse/File Request for(FriendPresence nextPresence : presences) { if(nextPresence.hasFeatures(LimewireFeature.ID)) { return nextPresence; } } return null; } } Map<String, Friend> getKnownFriends() { return Collections.unmodifiableMap(knownFriends); } Map<String, Friend> getAvailableFriends() { return Collections.unmodifiableMap(availFriends); } Set<String> getAvailableFriendIds() { return Collections.unmodifiableSet(availFriends.keySet()); } @Override public void addAvailableFriend(Friend friend) { if (availFriends.putIfAbsent(friend.getId(), friend) == null) { LOG.debugf("adding avail friend: {0}", friend); availableBroadcaster.broadcast(new FriendEvent(friend, FriendEvent.Type.ADDED)); } else { LOG.debugf("not adding avail friend: {0}", friend); } } @Override public void removeAvailableFriend(Friend friend) { if (availFriends.remove(friend.getId()) != null) { LOG.debugf("removed avail friend: {0}", friend); availableBroadcaster.broadcast(new FriendEvent(friend, FriendEvent.Type.REMOVED)); } else { LOG.debugf("avail friend {0} already removed", friend); } } }