org.bitcoinj.net.discovery.MultiplexingDiscovery Maven / Gradle / Ivy
/*
* Copyright 2014 Mike Hearn
* Copyright 2015 Andreas Schildbach
*
* 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 org.bitcoinj.net.discovery;
import com.google.common.collect.Lists;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.VersionMessage;
import org.bitcoinj.net.discovery.HttpDiscovery;
import org.bitcoinj.net.discovery.DnsDiscovery.DnsSeedDiscovery;
import org.bitcoinj.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import okhttp3.OkHttpClient;
import static com.google.common.base.Preconditions.checkArgument;
/**
* MultiplexingDiscovery queries multiple PeerDiscovery objects, shuffles their responses and then returns the results,
* thus selecting randomly between them and reducing the influence of any particular seed. Any that don't respond
* within the timeout are ignored. Backends are queried in parallel. Backends may block.
*/
public class MultiplexingDiscovery implements PeerDiscovery {
private static final Logger log = LoggerFactory.getLogger(MultiplexingDiscovery.class);
protected final List seeds;
protected final NetworkParameters netParams;
private volatile ExecutorService vThreadPool;
/**
* Builds a suitable set of peer discoveries. Will query them in parallel before producing a merged response.
* If specific services are required, DNS is not used as the protocol can't handle it.
* @param params Network to use.
* @param services Required services as a bitmask, e.g. {@link VersionMessage#NODE_NETWORK}.
*/
public static MultiplexingDiscovery forServices(NetworkParameters params, long services) {
List discoveries = Lists.newArrayList();
HttpDiscovery.Details[] httpSeeds = params.getHttpSeeds();
if (httpSeeds != null) {
OkHttpClient httpClient = new OkHttpClient();
for (HttpDiscovery.Details httpSeed : httpSeeds)
discoveries.add(new HttpDiscovery(params, httpSeed, httpClient));
}
// Also use DNS seeds if there is no specific service requirement
if (services == 0) {
String[] dnsSeeds = params.getDnsSeeds();
if (dnsSeeds != null)
for (String dnsSeed : dnsSeeds)
discoveries.add(new DnsSeedDiscovery(params, dnsSeed));
}
return new MultiplexingDiscovery(params, discoveries);
}
/**
* Will query the given seeds in parallel before producing a merged response.
*/
public MultiplexingDiscovery(NetworkParameters params, List seeds) {
checkArgument(!seeds.isEmpty());
this.netParams = params;
this.seeds = seeds;
}
@Override
public InetSocketAddress[] getPeers(final long services, final long timeoutValue, final TimeUnit timeoutUnit) throws PeerDiscoveryException {
vThreadPool = createExecutor();
try {
List> tasks = Lists.newArrayList();
for (final PeerDiscovery seed : seeds) {
tasks.add(new Callable() {
@Override
public InetSocketAddress[] call() throws Exception {
return seed.getPeers(services, timeoutValue, timeoutUnit);
}
});
}
final List> futures = vThreadPool.invokeAll(tasks, timeoutValue, timeoutUnit);
ArrayList addrs = Lists.newArrayList();
for (int i = 0; i < futures.size(); i++) {
Future future = futures.get(i);
if (future.isCancelled()) {
log.warn("Seed {}: timed out", seeds.get(i));
continue; // Timed out.
}
final InetSocketAddress[] inetAddresses;
try {
inetAddresses = future.get();
} catch (ExecutionException e) {
log.warn("Seed {}: failed to look up: {}", seeds.get(i), e.getMessage());
continue;
}
Collections.addAll(addrs, inetAddresses);
}
if (addrs.size() == 0)
throw new PeerDiscoveryException("No peer discovery returned any results in "
+ timeoutUnit.toMillis(timeoutValue) + "ms. Check internet connection?");
Collections.shuffle(addrs);
vThreadPool.shutdownNow();
return addrs.toArray(new InetSocketAddress[addrs.size()]);
} catch (InterruptedException e) {
throw new PeerDiscoveryException(e);
} finally {
vThreadPool.shutdown();
}
}
protected ExecutorService createExecutor() {
return Executors.newFixedThreadPool(seeds.size(), new ContextPropagatingThreadFactory("Multiplexing discovery"));
}
@Override
public void shutdown() {
ExecutorService tp = vThreadPool;
if (tp != null)
tp.shutdown();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy