diff options
Diffstat (limited to 'src/main/java/com/btr/proxy/selector/misc')
3 files changed, 391 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java b/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java new file mode 100644 index 0000000..f006314 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java @@ -0,0 +1,126 @@ +package com.btr.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.Set; +import java.util.Map.Entry; +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 Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class BufferedProxySelector extends ProxySelector { + + private ProxySelector delegate; + + private ConcurrentHashMap<String, CacheEntry> cache; + private int maxSize; + private long ttl; + + private static class CacheEntry { + List<Proxy> result; + long expireAt; + + public CacheEntry(List<Proxy> r, long expireAt) { + super(); + this.result = new ArrayList<Proxy>(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. + ************************************************************************/ + + public BufferedProxySelector(int maxSize, long ttl, ProxySelector delegate) { + super(); + this.cache = new ConcurrentHashMap<String, CacheEntry>(); + this.maxSize = maxSize; + this.delegate = delegate; + this.ttl = ttl; + } + + /************************************************************************* + * 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<Proxy> select(URI uri) { + //String cacheKey = uri.getHost(); // Caching per host may produce wrong results + String cacheKey = uri.toString(); + + CacheEntry entry = this.cache.get(cacheKey); + if (entry == null || entry.isExpired()) { + List<Proxy> 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(); + } + 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<String, CacheEntry> oldest = null; + + Set<Entry<String, CacheEntry>> entries = this.cache.entrySet(); + for (Iterator<Entry<String, CacheEntry>> it = entries.iterator(); it.hasNext();) { + Entry<String, CacheEntry> 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()); + } + } + +} diff --git a/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java b/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java new file mode 100644 index 0000000..5d7f563 --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java @@ -0,0 +1,115 @@ +package com.btr.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.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.btr.proxy.selector.direct.NoProxySelector; + +/***************************************************************************** + * This is a facade for a list of ProxySelecor objects. You can register + * different ProxySelectors per Protocol. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009 + ****************************************************************************/ + +public class ProtocolDispatchSelector extends ProxySelector { + + private Map<String, ProxySelector> selectors; + private ProxySelector fallbackSelector; + + /************************************************************************* + * Constructor + ************************************************************************/ + + public ProtocolDispatchSelector() { + super(); + this.selectors = new ConcurrentHashMap<String, ProxySelector>(); + this.fallbackSelector = NoProxySelector.getInstance(); + } + + /************************************************************************* + * Sets a selector responsible for the given protocol. + * @param protocol the name of the protocol. + * @param selector the selector to use. + ************************************************************************/ + + public void setSelector(String protocol, ProxySelector selector) { + if (protocol == null) { + throw new NullPointerException("Protocol must not be null."); + } + if (selector == null) { + throw new NullPointerException("Selector must not be null."); + } + this.selectors.put(protocol, selector); + } + + /************************************************************************* + * Removes the selector installed for the given protocol. + * @param protocol the protocol name. + * @return the old selector that is removed. + ************************************************************************/ + + public ProxySelector removeSelector(String protocol) { + return this.selectors.remove(protocol); + } + + /************************************************************************* + * Gets the selector installed for the given protocol. + * @param protocol the protocol name. + * @return the selector for that protocol, null if none is currently set. + ************************************************************************/ + + public ProxySelector getSelector(String protocol) { + return this.selectors.get(protocol); + } + + /************************************************************************* + * Sets the fallback selector that is always called when no matching + * protocol selector was found.. + * @param selector the selector to use. + ************************************************************************/ + + public void setFallbackSelector(ProxySelector selector) { + if (selector == null) { + throw new NullPointerException("Selector must not be null."); + } + this.fallbackSelector = selector; + } + + /************************************************************************* + * 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) { + ProxySelector selector = this.fallbackSelector; + String protocol = uri.getScheme(); + if (protocol != null && this.selectors.get(protocol) != null) { + selector = this.selectors.get(protocol); + } + selector.connectFailed(uri, sa, ioe); + } + + /************************************************************************* + * select + * @see java.net.ProxySelector#select(java.net.URI) + ************************************************************************/ + + @Override + public List<Proxy> select(URI uri) { + ProxySelector selector = this.fallbackSelector; + String protocol = uri.getScheme(); + if (protocol != null && this.selectors.get(protocol) != null) { + selector = this.selectors.get(protocol); + } + return selector.select(uri); + } + +} diff --git a/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java b/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java new file mode 100644 index 0000000..41859ec --- /dev/null +++ b/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java @@ -0,0 +1,150 @@ +package com.btr.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.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +/***************************************************************************** + * Implements a fallback selector to warp it around an existing ProxySelector. + * This will remove proxies from a list of proxies and implement an automatic + * retry mechanism. + * + * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2011 + ****************************************************************************/ + +public class ProxyListFallbackSelector extends ProxySelector { + + // Retry a unresponsive proxy after 10 minutes per default. + private static final int DEFAULT_RETRY_DELAY = 1000*60*10; + + private ProxySelector delegate; + private ConcurrentHashMap<SocketAddress, Long> failedDelayCache; + private long retryAfterMs; + + /************************************************************************* + * Constructor + * @param delegate the delegate to use. + ************************************************************************/ + + public ProxyListFallbackSelector(ProxySelector delegate) { + this(DEFAULT_RETRY_DELAY, delegate); + } + + /************************************************************************* + * Constructor + * @param retryAfterMs the "retry delay" as amount of milliseconds. + * @param delegate the delegate to use. + ************************************************************************/ + + public ProxyListFallbackSelector(long retryAfterMs, ProxySelector delegate) { + super(); + this.failedDelayCache = new ConcurrentHashMap<SocketAddress, Long>(); + this.delegate = delegate; + this.retryAfterMs = retryAfterMs; + } + + /************************************************************************* + * 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.failedDelayCache.put(sa, System.currentTimeMillis()); + } + + /************************************************************************* + * select + * @see java.net.ProxySelector#select(java.net.URI) + ************************************************************************/ + + @Override + public List<Proxy> select(URI uri) { + cleanupCache(); + List<Proxy> proxyList = this.delegate.select(uri); + List<Proxy> result = filterUnresponsiveProxiesFromList(proxyList); + return result; + } + + /************************************************************************* + * Cleanup the entries from the cache that are no longer unresponsive. + ************************************************************************/ + + private void cleanupCache() { + Iterator<Entry<SocketAddress, Long>> it + = this.failedDelayCache.entrySet().iterator(); + while (it.hasNext()) { + Entry<SocketAddress, Long> e = it.next(); + Long lastFailTime = e.getValue(); + if (retryDelayHasPassedBy(lastFailTime)) { + it.remove(); + } + } + } + + /************************************************************************* + * @param proxyList + * @return + ************************************************************************/ + + private List<Proxy> filterUnresponsiveProxiesFromList(List<Proxy> proxyList) { + if (this.failedDelayCache.isEmpty()) { + return proxyList; + } + List<Proxy> result = new ArrayList<Proxy>(proxyList.size()); + for (Proxy proxy : proxyList) { + if (isDirect(proxy) || isNotUnresponsive(proxy)) { + result.add(proxy); + } + } + return result; + } + + /************************************************************************* + * @param proxy + * @return + ************************************************************************/ + + private boolean isDirect(Proxy proxy) { + return Proxy.NO_PROXY.equals(proxy); + } + + /************************************************************************* + * @param proxy + * @return + ************************************************************************/ + + private boolean isNotUnresponsive(Proxy proxy) { + Long lastFailTime = this.failedDelayCache.get(proxy.address()); + return retryDelayHasPassedBy(lastFailTime); + } + + /************************************************************************* + * @param lastFailTime + * @return + ************************************************************************/ + + private boolean retryDelayHasPassedBy(Long lastFailTime) { + return lastFailTime == null + || lastFailTime + this.retryAfterMs < System.currentTimeMillis(); + } + + /************************************************************************* + * Only used for unit testing not part of the public API. + * @param retryAfterMs The retryAfterMs to set. + ************************************************************************/ + + final void setRetryAfterMs(long retryAfterMs) { + this.retryAfterMs = retryAfterMs; + } + + + +} |