package com.theotherian.dns;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutionException;
import org.apache.log4j.Logger;
import org.xbill.DNS.spi.DNSJavaNameService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* Name service that uses both thread local and global representations of DNS lookups.<br>
* <p>
* Order of resolution is as follows:
* <ul>
* <li>First, see if the thread local cache has an ip for a host</li>
* <li>Next, see if there was an ip for that host in the hosts file</li>
* <li>Otherwise, look up from a global cache</li>
* </ul>
* Entries do not expire from cache, which is the default behavior in the JVM.
* </p>
* @author isimpson
*
*/
public class ThreadLocalNameService extends DNSJavaNameService {
private static Logger LOGGER = Logger.getLogger(ThreadLocalNameService.class);
static {
LOGGER.info("Thread Local DNS Name Service loaded");
}
private LoadingCache<String, InetAddress[]> dnsCache = CacheBuilder.newBuilder()
.build(new CacheLoader<String, InetAddress[]>() {
@Override
public InetAddress[] load(String key) throws Exception {
return normalLookup(key);
}
});
@VisibleForTesting
static boolean isLocal(InetAddress ip) {
return ip.isLinkLocalAddress() || ip.isLoopbackAddress() || ip.isSiteLocalAddress();
}
public ThreadLocalNameService() {
super();
}
private InetAddress[] normalLookup(String hostname) throws UnknownHostException {
return super.lookupAllHostAddr(hostname);
}
@Override
public InetAddress[] lookupAllHostAddr(String hostname) throws UnknownHostException {
try {
InetAddress[] addresses = OverrideNameServiceManager.lookup(hostname);
if (addresses.length > 0) {
return addresses;
}
return dnsCache.get(hostname);
}
catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}