
io.datakernel.dns.BlockingDnsResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http Show documentation
Show all versions of http Show documentation
High-performance asynchronous HTTP client and server.
The newest version!
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.dns;
import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import io.datakernel.async.ResultCallback;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.eventloop.NioEventloop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newConcurrentMap;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
/**
* It is abstract class which represents non-asynchronous resolving. It resolves each domain
* in the executor.
*/
public abstract class BlockingDnsResolver {
static final Logger logger = LoggerFactory.getLogger(BlockingDnsResolver.class);
protected final Map cache = newConcurrentMap();
protected final Map> pendings = newHashMap();
protected long positiveKeepMillis = TimeUnit.MINUTES.toMillis(60);
protected long negativeKeepMillis = TimeUnit.MINUTES.toMillis(10);
protected final Executor executor;
public BlockingDnsResolver(Executor executor) {
this.executor = executor;
}
/**
* Returns a DNS client which resolves domains in other thread and result callback handles in thread of eventloop.
*
* @param resolver DNS resolver for client
* @param eventloop eventloop in which it will calls callback
* @return new DNS Client
*/
public static DnsClient getAsDnsClient(final BlockingDnsResolver resolver, final NioEventloop eventloop) {
return new DnsClient() {
public void resolve(String domainName, final ResultCallback callback, boolean ipv6) {
final Eventloop.ConcurrentOperationTracker concurrentOperationTracker = eventloop.startConcurrentOperation();
final ListenableFuture future = ipv6 ? resolver.resolve6(domainName) : resolver.resolve4(domainName);
future.addListener(new Runnable() {
@Override
public void run() {
try {
final InetAddress[] inetAddresses = future.get();
eventloop.postConcurrently(new Runnable() {
@Override
public void run() {
concurrentOperationTracker.complete();
callback.onResult(inetAddresses);
}
});
} catch (final InterruptedException e) {
eventloop.postConcurrently(new Runnable() {
@Override
public void run() {
concurrentOperationTracker.complete();
callback.onException(e);
}
});
} catch (final ExecutionException e) {
eventloop.postConcurrently(new Runnable() {
@Override
public void run() {
concurrentOperationTracker.complete();
if (e.getCause() instanceof Exception)
callback.onException((Exception) e.getCause());
}
});
}
}
}, sameThreadExecutor());
}
@Override
public void resolve4(String domainName, ResultCallback callback) {
resolve(domainName, callback, false);
}
@Override
public void resolve6(String domainName, final ResultCallback callback) {
resolve(domainName, callback, true);
}
};
}
public void setPositiveKeepMillis(long positiveKeepMillis) {
this.positiveKeepMillis = positiveKeepMillis;
}
public void setNegativeKeepMillis(long negativeKeepMillis) {
this.negativeKeepMillis = negativeKeepMillis;
}
protected static class ResolvedAddress {
private final InetAddress[] addresses;
private final long updateTimestamp;
private volatile int counter;
ResolvedAddress(InetAddress[] address, long ttl) {
this.addresses = address;
this.updateTimestamp = System.currentTimeMillis() + ttl;
}
boolean isInvalid() {
return (System.currentTimeMillis() > updateTimestamp);
}
InetAddress getAddress() {
if (addresses == null)
return null;
InetAddress address = addresses[counter];
counter = (counter + 1) % addresses.length;
return address;
}
InetAddress[] getAllAddresses() {
return addresses;
}
public long getUpdateTimestamp() {
return updateTimestamp;
}
}
/**
* Resolves a IP for the IPv4 addresses.
*
* @param host domain name to resolve
* @return future from which you can get result of resolving
*/
public ListenableFuture resolve4(String host) {
return resolve(host, false);
}
/**
* Resolves a IP for the IPv6 addresses.
*
* @param host domain name to resolve
* @return future from which you can get result of resolving
*/
public ListenableFuture resolve6(String host) {
return resolve(host, true);
}
protected ListenableFuture resolve(String host, boolean ipv6) {
checkNotNull(host);
if (InetAddresses.isInetAddress(host)) {
InetAddress ipAddress = InetAddresses.forString(host);
return Futures.immediateFuture(new InetAddress[]{ipAddress});
}
host = host.toLowerCase();
ResolvedAddress resolvedAddress = cache.get(host);
if (resolvedAddress != null && !resolvedAddress.isInvalid()) {
return Futures.immediateFuture(resolvedAddress.getAllAddresses());
}
return resolvePending(host, ipv6);
}
synchronized protected ListenableFuture resolvePending(final String host, final boolean ipv6) {
ListenableFuture pending = pendings.get(host);
if (pending != null) {
return pending;
} else {
final SettableFuture future = SettableFuture.create();
pendings.put(host, future);
executor.execute(new Runnable() {
@Override
public void run() {
final ResolvedAddress resolvedAddress = doResolve(host, ipv6);
synchronized (BlockingDnsResolver.this) {
cache.put(host, resolvedAddress);
pendings.remove(host);
}
InetAddress[] inetAddresses = resolvedAddress.getAllAddresses();
if (inetAddresses == null) {
future.setException(new DnsException(host, DnsMessage.ResponseErrorCode.UNKNOWN));
} else {
future.set(resolvedAddress.getAllAddresses());
}
}
});
return future;
}
}
protected abstract ResolvedAddress doResolve(String host, boolean ipv6);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy