package edu.washington.cs.oneswarm.f2f.servicesharing; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.logging.Logger; import com.aelitis.azureus.core.networkmanager.NetworkConnection; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FHashSearch; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FHashSearchResp; import edu.washington.cs.oneswarm.f2f.network.FriendConnection; import edu.washington.cs.oneswarm.f2f.network.FriendConnection.OverlayRegistrationError; /** * This class manages active service connections (ServiceChannelEndpoints.) * Each channel can be used for multiple connections, such that a client can * initiate follow-on connections to a service without re-executing a search. * * @author willscott * */ public class ServiceConnectionManager implements ServiceChannelEndpointDelegate { // Singleton Pattern. private final static ServiceConnectionManager instance = new ServiceConnectionManager(); public final static Logger logger = Logger.getLogger(ServiceConnectionManager.class.getName()); private ServiceConnectionManager() { } public static ServiceConnectionManager getInstance() { return instance; } // Both are lists are keyed on service key. 1 endpoint per overlay path, 1 // serviceconnection per local connection. private final HashMap<Long, List<ServiceChannelEndpoint>> connections = new HashMap<Long, List<ServiceChannelEndpoint>>(); private final HashMap<Long, List<ServiceConnection>> services = new HashMap<Long, List<ServiceConnection>>(); public ServiceChannelEndpoint createChannel(FriendConnection nextHop, OSF2FHashSearch search, OSF2FHashSearchResp response, boolean outgoing) { ServiceChannelEndpoint channel = new ServiceChannelEndpoint(nextHop, search, response, outgoing); try { nextHop.registerOverlayTransport(channel); } catch (OverlayRegistrationError e) { logger.warning("got an error when registering outgoing transport: " + e.getMessage()); return channel; } this.addChannel(channel); return channel; } private void addChannel(ServiceChannelEndpoint channel) { logger.fine("Network Channel registered with Connection Manager"); Long key = channel.getServiceKey(); if (!this.connections.containsKey(key)) { registerKey(key); } if (this.connections.get(key).contains(channel)) { logger.info("Attempting to register existing channel:" + channel); return; } this.connections.get(key).add(channel); channel.addDelegate(this, (short) -1); if (this.services.get(key).size() > 0) { for (ServiceConnection service : this.services.get(key)) { logger.finest("Channel added to existing service: " + service.getDescription()); service.addChannel(channel); } } } private void registerKey(Long key) { List<ServiceChannelEndpoint> channel = new ArrayList<ServiceChannelEndpoint>(); this.connections.put(key, channel); List<ServiceConnection> service = new ArrayList<ServiceConnection>(); this.services.put(key, service); } public Collection<ServiceChannelEndpoint> getChannelsForService(long key) { return this.connections.get(Long.valueOf(key)); } /* ServiceChannelEndpointDelegate implementation. */ @Override public void channelDidClose(ServiceChannelEndpoint sender) { Long key = sender.getServiceKey(); if (!this.connections.containsKey(key)) { logger.info("Attempting to deregister channel for unknown service."); return; } synchronized (this.connections) { List<ServiceChannelEndpoint> list = this.connections.get(key); if (list == null) { return; } list.remove(sender); if (list.size() == 0) { logger.fine("All service connections closed for key " + key); this.connections.remove(key); } } } @Override public void channelDidConnect(ServiceChannelEndpoint sender) { } @Override public void channelIsReady(ServiceChannelEndpoint sender) { } @Override public boolean channelGotMessage(ServiceChannelEndpoint sender, OSF2FServiceDataMsg msg) { // Alert the service manager when a new flow is established. if (msg.isSyn()) { logger.fine("New Flow Established over " + sender.getChannelId()); long serviceKey = sender.getServiceKey(); SharedService ss = ServiceSharingManager.getInstance().getSharedService(serviceKey); List<ServiceConnection> existing = services.get(serviceKey); short subchannel = 0; if (existing == null) { services.put(serviceKey, new ArrayList<ServiceConnection>()); } else { for (ServiceConnection c : existing) { if (c.subchannelId == msg.getSubchannel()) { // Ignore duplicate syn messages - the connection will // handle it directly. return false; } } subchannel = msg.getSubchannel(); } NetworkConnection outgoingConnection = ss.createConnection(); ServiceConnection c = new ServiceConnection(false, subchannel, outgoingConnection); this.services.get(serviceKey).add(c); for (ServiceChannelEndpoint channel : this.getChannelsForService(serviceKey)) { c.addChannel(channel); } c.channelGotMessage(sender, msg); return true; } if (msg.isRst()) { logger.fine("RST message received."); List<ServiceConnection> existing = services.get(sender.getServiceKey()); if (existing != null) { for (ServiceConnection c : existing) { // TODO(willscott): Also need check here to differentiate // distinct clients. if (c.subchannelId == msg.getSubchannel()) { c.closeUponReading(msg.getSequenceNumber()); break; } } } return true; } return false; } public boolean requestService(NetworkConnection incomingConnection, long serverSearchKey) { // Create a new sub flow if channels exist, or note the request for when // one does. Collection<ServiceChannelEndpoint> channels = this.getChannelsForService(serverSearchKey); if (channels != null && channels.size() > 0) { short subchannel = (short) services.get(serverSearchKey).size(); ServiceConnection c = new ServiceConnection(true, subchannel, incomingConnection); for (ServiceChannelEndpoint channel : channels) { c.addChannel(channel); } services.get(serverSearchKey).add(c); logger.fine("Service requested - existing channel found. Search Skipped."); return true; } else { registerKey(serverSearchKey); ServiceConnection c = new ServiceConnection(true, (short)0, incomingConnection); services.get(serverSearchKey).add(c); logger.fine("Service requested - existing channel not present. Search Needed."); return false; } } @Override public boolean writesMessages() { return false; } }