com.robonobo.eon.ConnectionHolder.java Source code

Java tutorial

Introduction

Here is the source code for com.robonobo.eon.ConnectionHolder.java

Source

package com.robonobo.eon;
/*
 * Eye-Of-Needle
 * Copyright (C) 2008 Will Morton (macavity@well.com) & Ray Hilton (ray@wirestorm.net)
    
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

import java.net.*;
import java.util.*;

import org.apache.commons.logging.Log;

public class ConnectionHolder {
    private Map<Integer, EONConnection> defaultConns;
    private Map<Integer, Map<EonSocketAddress, EONConnection>> specificConns;
    private List<EONConnection> connsToWaitFor = null;
    private Log log;
    private EONManager mgr;

    public ConnectionHolder(EONManager mgr) {
        this.mgr = mgr;
        defaultConns = new HashMap<Integer, EONConnection>();
        specificConns = new HashMap<Integer, Map<EonSocketAddress, EONConnection>>();
        log = mgr.getLogger(getClass());
    }

    int numConnsWaitingFor() {
        List<EONConnection> tmpList = connsToWaitFor;
        if (tmpList == null)
            return 0;
        return tmpList.size();
    }

    void closeAllConns(int waitTimeMs) {
        log.debug("Closing all EON connections");
        Date startWaitingTime = new Date();
        Date endWaitingTime = new Date(startWaitingTime.getTime() + waitTimeMs);
        List<EONConnection> connsToClose = new ArrayList<EONConnection>();
        connsToWaitFor = new ArrayList<EONConnection>();
        // Come out of this sync block before closing anything, else EONConnection.close() may cause a race condition
        synchronized (this) {
            ArrayList<EONConnection> allConns = new ArrayList<EONConnection>(defaultConns.values());
            Iterator<Map<EonSocketAddress, EONConnection>> spIter = specificConns.values().iterator();
            while (spIter.hasNext()) {
                Map<EonSocketAddress, EONConnection> thisMap = spIter.next();
                allConns.addAll(thisMap.values());
            }
            Iterator<EONConnection> allIter = allConns.iterator();
            while (allIter.hasNext()) {
                EONConnection conn = allIter.next();
                if (conn instanceof SEONConnection)
                    connsToWaitFor.add(conn);
                connsToClose.add(conn);
            }
        }
        Iterator<EONConnection> i = connsToClose.iterator();
        while (i.hasNext()) {
            EONConnection conn = i.next();
            conn.close();
        }
        synchronized (this) {
            while (connsToWaitFor.size() > 0) {
                SEONConnection thisConn = (SEONConnection) connsToWaitFor.get(0);
                if (!thisConn.isOpen())
                    connsToWaitFor.remove(0);
                else {
                    try {
                        Date nowTime = new Date();
                        long msToWait = endWaitingTime.getTime() - nowTime.getTime();
                        if (msToWait <= 0) {
                            log.debug("EON finished waiting for connections");
                            break;
                        } else {
                            log.debug("EON waiting for " + connsToWaitFor + " conns, " + msToWait + "ms");
                            wait(msToWait);
                        }
                    } catch (InterruptedException e) {
                        log.debug("EON caught InterruptedException while closing down", e);
                    }
                }
            }
        }
        for (EONConnection eonConn : connsToWaitFor) {
            SEONConnection seonConn = (SEONConnection) eonConn;
            seonConn.cancelTimeouts();
        }
        connsToWaitFor.clear();
        log.debug("All EON conns closed");
    }

    synchronized boolean requestPort(int localEONPort, EonSocketAddress addressMask, EONConnection thisConn) {
        Integer eonp = new Integer(localEONPort);
        if (addressMask.getAddress().equals(EONManager.getWildcardAddress())) {
            log.debug("EONManager: Port " + localEONPort + " requested for listening");
            if (defaultConns.containsKey(eonp)) {
                return false;
            } else {
                defaultConns.put(eonp, thisConn);
                return true;
            }
        } else {
            log.debug("EONManager: Port " + localEONPort + " requested for address mask " + addressMask);
            // This is a child connection caused by a remote process
            // connecting to a local listening socket
            if (specificConns.containsKey(eonp)) {
                Map<EonSocketAddress, EONConnection> portMap = specificConns.get(eonp);
                if (portMap.containsKey(addressMask)) {
                    return false;
                } else {
                    portMap.put(addressMask, thisConn);
                    return true;
                }
            } else {
                Map<EonSocketAddress, EONConnection> map = new HashMap<EonSocketAddress, EONConnection>();
                map.put(addressMask, thisConn);
                specificConns.put(new Integer(localEONPort), map);
                return true;
            }
        }
    }

    synchronized int getPort(EonSocketAddress addressMask, EONConnection thisConn) {
        for (int i = 1025; i < 65535; i++) {
            Integer thisInt = new Integer(i);
            if (!defaultConns.containsKey(thisInt) && !specificConns.containsKey(thisInt)) {
                if (!requestPort(i, addressMask, thisConn))
                    throw new RuntimeException("RequestPort failed after being called from GetPort");
                return i;
            }
        }
        // We've run out of ports! Something pretty bad is going on...
        throw new RuntimeException("No more EON ports available");
    }

    synchronized void returnPort(int localEONPort, EonSocketAddress addressMask, EONConnection thisConn) {
        Integer eonp = new Integer(localEONPort);
        if (addressMask.getAddress().equals(EONManager.getWildcardAddress())) {
            log.debug("EONManager: Port " + localEONPort + " returned for listening");
            if (defaultConns.containsKey(eonp)) {
                if (defaultConns.get(eonp).equals(thisConn))
                    defaultConns.remove(eonp);
                else
                    throw new IllegalArgumentException(
                            "This connection does not own this address mask on this port");
            } else
                log.debug("No such address mask/port when returning port " + localEONPort + " for address mask "
                        + addressMask);
        } else {
            log.debug("EONManager: Port " + localEONPort + " returned for address mask " + addressMask);
            if (specificConns.containsKey(eonp)) {
                Map<EonSocketAddress, EONConnection> portMap = specificConns.get(eonp);
                if (portMap.containsKey(addressMask)) {
                    if (portMap.get(addressMask) == thisConn) {
                        portMap.remove(addressMask);
                        if (portMap.size() == 0)
                            specificConns.remove(eonp);
                    } else
                        throw new IllegalArgumentException(
                                "This connection does not own this address mask on this port");
                } else
                    log.debug("No such address mask/port when returning port " + localEONPort + " for address mask "
                            + addressMask);
            } else
                log.debug("No such address mask/port when returning port " + localEONPort + " for address mask "
                        + addressMask);
        }
        // We might be waiting for connections to close();
        notifyAll();
    }

    void killAllConnsAssociatedWith(InetSocketAddress addr) {
        List<EONConnection> toDie = new ArrayList<EONConnection>();
        synchronized (this) {
            Iterator<Map<EonSocketAddress, EONConnection>> i = specificConns.values().iterator();
            while (i.hasNext()) {
                Map<EonSocketAddress, EONConnection> map = i.next();
                Iterator<EonSocketAddress> j = map.keySet().iterator();
                while (j.hasNext()) {
                    EonSocketAddress ep = j.next();
                    if (ep.getInetSocketAddress().equals(addr))
                        toDie.add(map.get(ep));
                }
            }
        }
        Iterator<EONConnection> i = toDie.iterator();
        while (i.hasNext()) {
            EONConnection conn = i.next();
            conn.abort();
        }
    }

    synchronized EONConnection getLocalConnForIncoming(EonSocketAddress destSockAddr,
            EonSocketAddress sourceSockAddr) {
        EONConnection resultConn = null;
        Integer eonp = new Integer(destSockAddr.getEonPort());
        // We try and match the packet to an endpoint-specific
        // connection.
        // If this fails, chuck it to the default connection on that
        // port.
        if (specificConns.containsKey(eonp)) {
            Map<EonSocketAddress, EONConnection> portMap = specificConns.get(eonp);
            Iterator<EonSocketAddress> i = portMap.keySet().iterator();
            while (i.hasNext()) {
                EonSocketAddress thisEP = (EonSocketAddress) i.next();
                if (thisEP.equals(sourceSockAddr)) {
                    resultConn = (EONConnection) portMap.get(thisEP);
                    break;
                }
            }
        }
        if (resultConn == null) {
            if (defaultConns.containsKey(eonp))
                resultConn = (EONConnection) defaultConns.get(eonp);
        }
        return resultConn;
    }

    synchronized boolean haveConnection(EONConnection conn, int localEonPort) {
        Integer eonPort = new Integer(localEonPort);
        if (defaultConns.containsValue(conn))
            return true;
        if (specificConns.containsKey(eonPort)) {
            Map<EonSocketAddress, EONConnection> portMap = specificConns.get(eonPort);
            if (portMap.containsValue(conn))
                return true;
        }
        return false;
    }

    synchronized int getLowestMaxObservedRtt(SEONConnection exceptConn) {
        // TODO: Optimise me!  This will perform horribly with lots of connections
        int result = -1;
        for (Map<EonSocketAddress, EONConnection> connMap : specificConns.values()) {
            for (EONConnection conn : connMap.values()) {
                if (conn == exceptConn)
                    continue;
                int thisMax = ((SEONConnection) conn).getMaxObservedRtt();
                if (thisMax > 0 && ((thisMax < result) || result < 0))
                    result = thisMax;
            }
        }
        return result;
    }

}