phex.common.AlternateLocationContainer.java Source code

Java tutorial

Introduction

Here is the source code for phex.common.AlternateLocationContainer.java

Source

/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2005 Phex Development Group
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- CVS Information ---
 *  $Id: AlternateLocationContainer.java,v 1.19 2005/11/03 16:33:45 gregork Exp $
 */
package phex.common;

import java.util.*;

import javax.xml.bind.JAXBException;

import org.apache.commons.collections.SequencedHashMap;

import phex.common.address.DestAddress;
import phex.http.HTTPHeader;
import phex.utils.Logger;
import phex.utils.NLogger;
import phex.utils.NLoggerNames;
import phex.xml.ObjectFactory;
import phex.xml.XJBAlternateLocation;

/**
 * A AlternateLocationContainer that helps holder to add, sort and access
 * AlternateLocations.
 *
 * @author Gregor Koukkoullis
 */
public class AlternateLocationContainer {
    public static final int MAX_ALT_LOC_COUNT = 100;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * The urn each alternate location must match to be accepted.
     */
    private URN urn;

    /**
     * The map of AlternateLocations. It is lazy initialized on first use and
     * sorted by timestamp.<br>
     * We are using a SequencedHashMap that allows use to remove the alt loc
     * which has not been seen (put) into the map for the longest time.
     */
    private SequencedHashMap altLocationMap;

    public AlternateLocationContainer(URN urn) {
        if (urn == null) {
            throw new NullPointerException("URN must be provided");
        }
        this.urn = urn;
    }

    public void addContainer(AlternateLocationContainer cont) {
        if (cont.urn == null || !cont.urn.equals(urn)) {
            // dont add container with wrong or not existing sha1.
            // This is a implementation error.
            throw new IllegalArgumentException("Trying to add container with not matching urns");

        }

        // this check prevents a NullPointerException during putAll();
        if (cont.altLocationMap == null) {// nothing to add..
            return;
        }
        initMap();
        synchronized (cont.altLocationMap) {
            Iterator iterator = cont.altLocationMap.keySet().iterator();
            AlternateLocation tempAltLoc;
            while (iterator.hasNext()) {
                tempAltLoc = (AlternateLocation) iterator.next();
                addAlternateLocation(tempAltLoc);
            }
        }
    }

    public void addAlternateLocation(AlternateLocation altLoc) {
        URN altLocURN = altLoc.getURN();
        if (altLocURN == null || !altLocURN.equals(urn)) {// dont add alt location with wrong or not existing sha1
            Logger.logMessage(Logger.WARNING, Logger.DOWNLOAD,
                    "Cant add alt-location with not matching URN to container ");
            return;
        }

        initMap();
        synchronized (altLocationMap) {
            // thanks to AlternateLocation hashCode implementation the map ensures
            // that each alt loc is only once present. The put operation of a 
            // duplicate alt loc is required here to update the SequencedHashMap
            // internal order of elements. This ensures that alt loc not seen
            // for a long time get removed from the map.
            altLocationMap.put(altLoc, PRESENT);
            // make sure we have not more then 100 alt locations.
            if (altLocationMap.size() > MAX_ALT_LOC_COUNT) {// drop last element
                Object firstKey = altLocationMap.getFirstKey();
                altLocationMap.remove(firstKey);
            }
        }
    }

    public void removeAlternateLocation(AlternateLocation altLoc) {
        URN altLocURN = altLoc.getURN();
        if (altLocURN == null || !altLocURN.equals(urn)) {// dont add alt location with wrong or not existing sha1
            Logger.logMessage(Logger.WARNING, Logger.DOWNLOAD,
                    "Cant remove alt-location with not matching URN to container ");
            return;
        }
        initMap();
        synchronized (altLocationMap) {
            altLocationMap.remove(altLoc);
        }
    }

    public void addFromUriResHTTPHeaders(HTTPHeader[] headers) {
        List altLocList = parseUriResAltLocFromHTTPHeaders(headers);
        Iterator iterator = altLocList.iterator();
        while (iterator.hasNext()) {
            addAlternateLocation((AlternateLocation) iterator.next());
        }
    }

    public void addFromCompactIpHTTPHeaders(HTTPHeader[] headers, URN urn) {
        List altLocList = parseCompactIpAltLocFromHTTPHeaders(headers, urn);
        Iterator iterator = altLocList.iterator();
        while (iterator.hasNext()) {
            addAlternateLocation((AlternateLocation) iterator.next());
        }
    }

    /**
     * Returns a HTTPHeader array for this host address. This will include all
     * alternate locations except the one that equals the host address and the
     * one contained in the sendAltLocList. The newly send alt locs are added
     * to the sendAltLocList inside this call.
     * 
     * @param the name of the http header to generate, use X-ALT or X-NAlt.
     * @param hostAddress the host address that gets the alternate locations.
     * @param sendAltLocList a list of already send alt locs.
     * @return a HTTPHeader array for this host address
     */
    public HTTPHeader getAltLocHTTPHeaderForAddress(String headerName, DestAddress hostAddress, Set sendAltLocSet) {
        if (isEmpty()) {
            return null;
        }
        int count = 0;
        StringBuffer headerValue = new StringBuffer();
        synchronized (altLocationMap) {
            Iterator iterator = altLocationMap.keySet().iterator();
            while (iterator.hasNext()) {
                AlternateLocation altLoc = (AlternateLocation) iterator.next();

                // filter out alt locations to given host address..
                if (hostAddress.getHostName().equals(altLoc.getHostAddress().getHostName())) {
                    continue;
                }
                // filter out already send alt locs...
                if (sendAltLocSet.contains(altLoc)) {
                    continue;
                }

                if (count > 0) {
                    headerValue.append(",");
                }
                headerValue.append(altLoc.getHTTPString());
                sendAltLocSet.add(altLoc);
                count++;
                if (count == 10) {
                    break;
                }
            }
        }
        if (headerValue.length() > 0) {
            HTTPHeader altLocHeader = new HTTPHeader(headerName, headerValue.toString());
            return altLocHeader;
        } else {
            return null;
        }
    }

    /**
     * Returns the number of alternate locations in the container.
     * @return the number of alternate locations in the container.
     */
    public synchronized int getSize() {
        if (altLocationMap == null) {
            return 0;
        } else {
            return altLocationMap.size();
        }
    }

    /**
     * Indicates whether the container is empty or not.
     * @return <code>true</code> if the container is empty, <code>false</code>
     *         otherwise.
     */
    public synchronized boolean isEmpty() {
        if (altLocationMap == null) {
            return true;
        } else {
            return altLocationMap.isEmpty();
        }
    }

    public synchronized void createXJBAlternateLocationList(List list) throws JAXBException {
        if (altLocationMap == null) {
            return;
        }
        Iterator iterator = altLocationMap.keySet().iterator();
        ObjectFactory objFactory = new ObjectFactory();
        while (iterator.hasNext()) {
            AlternateLocation altLoc = (AlternateLocation) iterator.next();
            XJBAlternateLocation xjbAltLoc = objFactory.createXJBAlternateLocation();
            xjbAltLoc.setHostAddress(altLoc.getHostAddress().getFullHostName());
            xjbAltLoc.setURN(altLoc.getURN().getAsString());
            list.add(xjbAltLoc);
        }
    }

    /**
     * Returns a string representation of the object.
     * @return a string representation of the object.
     */
    public synchronized String toString() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("Alt-Locations(SHA1: ");
        stringBuffer.append(urn.getAsString());
        stringBuffer.append(")=[ ");
        if (altLocationMap != null) {
            Iterator iterator = altLocationMap.keySet().iterator();
            AlternateLocation altLoc;
            while (iterator.hasNext()) {
                altLoc = (AlternateLocation) iterator.next();
                stringBuffer.append(altLoc.toString());
                stringBuffer.append(", ");
            }
        }
        stringBuffer.append(" ]");
        return stringBuffer.toString();
    }

    private void initMap() {
        if (altLocationMap == null) {
            altLocationMap = new SequencedHashMap();
        }
    }

    /**
     * Parses AlternateLocations from the values of the given http headers and
     * returns a List containing the results. 
     * @param headers HTTPHeaderGroup to parse the AlternateLocations from
     * @return List containing all AlternateLocations found.
     */
    public static List parseUriResAltLocFromHTTPHeaders(HTTPHeader[] headers) {
        if (headers.length == 0) {
            return Collections.EMPTY_LIST;
        }
        List altLocList = new ArrayList();
        StringTokenizer tokenizer;
        for (int i = 0; i < headers.length; i++) {
            HTTPHeader header = headers[i];
            tokenizer = new StringTokenizer(header.getValue(), ",");

            while (tokenizer.hasMoreTokens()) {
                try {
                    String altLocationStr = tokenizer.nextToken().trim();
                    AlternateLocation altLocation = AlternateLocation.parseUriResAltLoc(altLocationStr);
                    if (altLocation == null) {
                        continue;
                    }
                    altLocList.add(altLocation);
                }
                // TODO filter this out
                // this is currently just to make sure we have no untested error cases.
                // and the download will not fail because of this.
                catch (Exception exp) {
                    NLogger.error(NLoggerNames.GLOBAL, exp, exp);
                }
            }
        }
        return altLocList;
    }

    /**
     * Parses AlternateLocations from the values of the given http headers and
     * returns a List containing the results. 
     * @param headers HTTPHeaderGroup to parse the AlternateLocations from
     * @return List containing all AlternateLocations found.
     */
    public static List parseCompactIpAltLocFromHTTPHeaders(HTTPHeader[] headers, URN urn) {
        if (headers.length == 0) {
            return Collections.EMPTY_LIST;
        }
        List altLocList = new ArrayList();
        StringTokenizer tokenizer;
        for (int i = 0; i < headers.length; i++) {
            HTTPHeader header = headers[i];
            tokenizer = new StringTokenizer(header.getValue(), ",");

            while (tokenizer.hasMoreTokens()) {
                try {
                    String altLocationStr = tokenizer.nextToken().trim();
                    AlternateLocation altLocation = AlternateLocation.parseCompactIpAltLoc(altLocationStr, urn);
                    if (altLocation == null) {
                        continue;
                    }
                    altLocList.add(altLocation);
                }
                // TODO filter this out
                // this is currently just to make sure we have no untested error cases.
                // and the download will not fail because of this.
                catch (Exception exp) {
                    NLogger.error(NLoggerNames.GLOBAL, exp, exp);
                }
            }
        }
        return altLocList;
    }
}