All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.markusbernhardt.proxy.selector.misc.BufferedProxySelector Maven / Gradle / Ivy

package com.github.markusbernhardt.proxy.selector.misc;

import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/*****************************************************************************
 * Implements a cache that can be used to warp it around an existing
 * ProxySelector. You can specify a maximum cache size and a "time to live" for
 * positive resolves.
 * 
 * @author Markus Bernhardt, Copyright 2016
 * @author Bernd Rosstauscher, Copyright 2009
 ****************************************************************************/

public class BufferedProxySelector extends ProxySelector {

    /*************************************************************************
     * Define the available scopes of the cache key generation
     ************************************************************************/

    public enum CacheScope {

        /*********************************************************************
         * Cache keys are generated by uri.getHost().
         ********************************************************************/

        CACHE_SCOPE_HOST,

        /*********************************************************************
		 * Cache keys are generated by
		 * uri.getHost() + ":" + uri.getPort().
         ********************************************************************/

        CACHE_SCOPE_HOST_PORT,

        /*********************************************************************
         * Cache keys are generated by uri.toString().
         ********************************************************************/

        CACHE_SCOPE_URL
    }

    @Override
    public String toString() {
		return "BufferedProxySelector{" +
				"delegate=" + delegate +
				", cache=" + cache +
				", maxSize=" + maxSize +
				", ttl=" + ttl +
				", cacheScope=" + cacheScope +
				'}';
    }

    private ProxySelector delegate;

    private ConcurrentHashMap cache;
    private int maxSize;
    private long ttl;
    private CacheScope cacheScope;

    private static class CacheEntry {
        List result;
        long expireAt;

        public CacheEntry(List r, long expireAt) {
            super();
            this.result = new ArrayList(r.size());
            this.result.addAll(r);
            this.result = Collections.unmodifiableList(this.result);
            this.expireAt = expireAt;
        }

        public boolean isExpired() {
            return System.nanoTime() >= this.expireAt;
        }
    }

    /*************************************************************************
     * Constructor
     * 
     * @param maxSize
     *            the max size for the cache.
     * @param ttl
	 *            the "time to live" for cache entries as amount in
	 *            milliseconds.
     * @param delegate
     *            the delegate to use.
     * @param cacheScope
     *            the desired cache scope.
     ************************************************************************/

    public BufferedProxySelector(int maxSize, long ttl, ProxySelector delegate, CacheScope cacheScope) {
        super();
        this.cache = new ConcurrentHashMap();
        this.maxSize = maxSize;
        this.delegate = delegate;
        this.ttl = ttl;
        this.cacheScope = cacheScope;
    }

    /*************************************************************************
     * connectFailed
     * 
	 * @see java.net.ProxySelector#connectFailed(java.net.URI,
	 *      java.net.SocketAddress, java.io.IOException)
     ************************************************************************/

    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        this.delegate.connectFailed(uri, sa, ioe);
    }

    /*************************************************************************
     * select
     * 
     * @see java.net.ProxySelector#select(java.net.URI)
     ************************************************************************/

    @Override
    public List select(URI uri) {
        String cacheKey;
        switch (cacheScope) {
            case CACHE_SCOPE_HOST:
                cacheKey = uri.getHost();
                break;
            case CACHE_SCOPE_HOST_PORT:
                cacheKey = uri.getHost() + ":" + uri.getPort();
                break;
            case CACHE_SCOPE_URL:
                cacheKey = uri.toString();
                break;
            default:
                throw new RuntimeException("FixMe: Unhandled CacheScope enum constant.");
        }

        CacheEntry entry = null;
        if (cacheKey != null) {
            entry = this.cache.get(cacheKey);
        }

        if (entry == null || entry.isExpired()) {
            List result = this.delegate.select(uri);
            entry = new CacheEntry(result, System.nanoTime() + this.ttl * 1000 * 1000);

            synchronized (this.cache) {
                if (this.cache.size() >= this.maxSize) {
                    purgeCache();
                }
                if (cacheKey != null) {
                    this.cache.put(cacheKey, entry);
                }
            }
        }

        return entry.result;
    }

    /*************************************************************************
     * Purge cache to get some free space for a new entry.
     ************************************************************************/

    private void purgeCache() {

        // Remove all expired entries and find the oldest.
        boolean removedOne = false;
        Entry oldest = null;

        Set> entries = this.cache.entrySet();
        for (Iterator> it = entries.iterator(); it.hasNext();) {
            Entry entry = it.next();
            if (entry.getValue().isExpired()) {
                it.remove();
                removedOne = true;
			} else if (oldest == null || entry.getValue().expireAt < oldest.getValue().expireAt) {
                oldest = entry;
            }
        }

        // Remove oldest if no expired entries were found.
        if (!removedOne && oldest != null) {
            this.cache.remove(oldest.getKey());
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy