add exception handling to runnables

This commit is contained in:
Manfred Karrer 2015-11-03 20:56:08 +01:00
parent 4d82467fa3
commit 0b7e45ca55
15 changed files with 463 additions and 385 deletions

View file

@ -39,7 +39,6 @@ import com.google.common.io.Files;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import org.bitcoinj.utils.Threading;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -49,7 +48,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -62,8 +60,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
public class FileManager<T> { public class FileManager<T> {
private static final Logger log = LoggerFactory.getLogger(FileManager.class); private static final Logger log = LoggerFactory.getLogger(FileManager.class);
private static final ReentrantLock lock = Threading.lock("FileManager");
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
private final File dir; private final File dir;
private final File storageFile; private final File storageFile;
@ -85,7 +81,7 @@ public class FileManager<T> {
ThreadFactoryBuilder builder = new ThreadFactoryBuilder() ThreadFactoryBuilder builder = new ThreadFactoryBuilder()
.setDaemon(true) .setDaemon(true)
.setNameFormat("FileManager thread") .setNameFormat("FileManager-%d")
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread. .setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
// An executor that starts up threads when needed and shuts them down later. // An executor that starts up threads when needed and shuts them down later.
@ -144,25 +140,20 @@ public class FileManager<T> {
executor.schedule(saver, delay, delayTimeUnit); executor.schedule(saver, delay, delayTimeUnit);
} }
public T read(File file) { public synchronized T read(File file) {
log.debug("read" + file); log.debug("read" + file);
lock.lock();
try (final FileInputStream fileInputStream = new FileInputStream(file); try (final FileInputStream fileInputStream = new FileInputStream(file);
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) { final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
return (T) objectInputStream.readObject(); return (T) objectInputStream.readObject();
} catch (Throwable t) { } catch (Throwable t) {
log.error("Exception at read: " + t.getMessage()); log.error("Exception at read: " + t.getMessage());
return null; return null;
} finally {
lock.unlock();
} }
} }
public void removeFile(String fileName) { public synchronized void removeFile(String fileName) {
log.debug("removeFile" + fileName); log.debug("removeFile" + fileName);
File file = new File(dir, fileName); File file = new File(dir, fileName);
lock.lock();
try {
boolean result = file.delete(); boolean result = file.delete();
if (!result) if (!result)
log.warn("Could not delete file: " + file.toString()); log.warn("Could not delete file: " + file.toString());
@ -176,9 +167,6 @@ public class FileManager<T> {
log.warn("Could not delete backupFile: " + file.toString()); log.warn("Could not delete backupFile: " + file.toString());
} }
} }
} finally {
lock.unlock();
}
} }
@ -200,9 +188,7 @@ public class FileManager<T> {
} }
} }
public void removeAndBackupFile(String fileName) throws IOException { public synchronized void removeAndBackupFile(String fileName) throws IOException {
lock.lock();
try {
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "corrupted").toString()); File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "corrupted").toString());
if (!corruptedBackupDir.exists()) if (!corruptedBackupDir.exists())
if (!corruptedBackupDir.mkdir()) if (!corruptedBackupDir.mkdir())
@ -210,14 +196,9 @@ public class FileManager<T> {
File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "corrupted", fileName).toString()); File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "corrupted", fileName).toString());
renameTempFileToFile(storageFile, corruptedFile); renameTempFileToFile(storageFile, corruptedFile);
} finally {
lock.unlock();
}
} }
public void backupFile(String fileName) throws IOException { public synchronized void backupFile(String fileName) throws IOException {
lock.lock();
try {
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString()); File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
if (!backupDir.exists()) if (!backupDir.exists())
if (!backupDir.mkdir()) if (!backupDir.mkdir())
@ -225,9 +206,6 @@ public class FileManager<T> {
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString()); File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
Files.copy(storageFile, backupFile); Files.copy(storageFile, backupFile);
} finally {
lock.unlock();
}
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -240,8 +218,7 @@ public class FileManager<T> {
UserThread.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now)); UserThread.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now));
} }
private void saveToFile(T serializable, File dir, File storageFile) { private synchronized void saveToFile(T serializable, File dir, File storageFile) {
lock.lock();
File tempFile = null; File tempFile = null;
FileOutputStream fileOutputStream = null; FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null; ObjectOutputStream objectOutputStream = null;
@ -292,13 +269,10 @@ public class FileManager<T> {
e.printStackTrace(); e.printStackTrace();
log.error("Cannot close resources." + e.getMessage()); log.error("Cannot close resources." + e.getMessage());
} }
lock.unlock();
} }
} }
private void renameTempFileToFile(File tempFile, File file) throws IOException { private synchronized void renameTempFileToFile(File tempFile, File file) throws IOException {
lock.lock();
try {
if (Utils.isWindows()) { if (Utils.isWindows()) {
// Work around an issue on Windows whereby you can't rename over existing files. // Work around an issue on Windows whereby you can't rename over existing files.
final File canonical = file.getCanonicalFile(); final File canonical = file.getCanonicalFile();
@ -311,8 +285,5 @@ public class FileManager<T> {
} else if (!tempFile.renameTo(file)) { } else if (!tempFile.renameTo(file)) {
throw new IOException("Failed to rename " + tempFile + " to " + file); throw new IOException("Failed to rename " + tempFile + " to " + file);
} }
} finally {
lock.unlock();
}
} }
} }

View file

@ -245,9 +245,18 @@ public class WalletService {
} }
public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
Context ctx = Context.get();
new Thread(() -> {
try {
Context.propagate(ctx);
walletAppKit.stopAsync(); walletAppKit.stopAsync();
walletAppKit.awaitTerminated(); walletAppKit.awaitTerminated();
initialize(seed, resultHandler, exceptionHandler); initialize(seed, resultHandler, exceptionHandler);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
}, "RestoreWallet-%d").start();
} }

View file

@ -1,5 +1,6 @@
package io.bitsquare.crypto; package io.bitsquare.crypto;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
@ -8,6 +9,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import java.util.concurrent.*;
//TODO: Borrowed form BitcoinJ/Lighthouse. Remove Protos dependency, check complete code logic. //TODO: Borrowed form BitcoinJ/Lighthouse. Remove Protos dependency, check complete code logic.
public class ScryptUtil { public class ScryptUtil {
private static final Logger log = LoggerFactory.getLogger(ScryptUtil.class); private static final Logger log = LoggerFactory.getLogger(ScryptUtil.class);
@ -27,14 +30,31 @@ public class ScryptUtil {
} }
public static void deriveKeyWithScrypt(KeyCrypterScrypt keyCrypterScrypt, String password, DeriveKeyResultHandler resultHandler) { public static void deriveKeyWithScrypt(KeyCrypterScrypt keyCrypterScrypt, String password, DeriveKeyResultHandler resultHandler) {
new Thread(() -> { final ThreadFactory threadFactory = new ThreadFactoryBuilder()
log.info("Doing key derivation"); .setNameFormat("Routing-%d")
.setDaemon(true)
.build();
ExecutorService executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
executorService.submit(() -> {
try {
log.info("Doing key derivation");
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password); KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
long duration = System.currentTimeMillis() - start; long duration = System.currentTimeMillis() - start;
log.info("Key derivation took {} msec", duration); log.info("Key derivation took {} msec", duration);
UserThread.execute(() -> resultHandler.handleResult(aesKey)); UserThread.execute(() -> {
}).start(); try {
resultHandler.handleResult(aesKey);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
});
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
});
} }
} }

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.offer; package io.bitsquare.trade.offer;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
@ -47,10 +46,11 @@ import javax.inject.Named;
import java.io.File; import java.io.File;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.inject.internal.util.$Preconditions.checkNotNull; import static com.google.inject.internal.util.$Preconditions.checkNotNull;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
@ -70,7 +70,7 @@ public class OpenOfferManager {
private boolean shutDownRequested; private boolean shutDownRequested;
private ScheduledThreadPoolExecutor executor; private ScheduledThreadPoolExecutor executor;
private P2PServiceListener p2PServiceListener; private P2PServiceListener p2PServiceListener;
private final Timer timer = new Timer();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -96,9 +96,13 @@ public class OpenOfferManager {
openOffersStorage = new Storage<>(storageDir); openOffersStorage = new Storage<>(storageDir);
this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers"); this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
init();
}
private void init() {
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook // In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
Thread shutDownHookThread = new Thread(OpenOfferManager.this::shutDown, "OpenOfferManager.ShutDownHook"); Runtime.getRuntime().addShutdownHook(new Thread(OpenOfferManager.this::shutDown,
Runtime.getRuntime().addShutdownHook(shutDownHookThread); "OpenOfferManager.ShutDownHook"));
// Handler for incoming offer availability requests // Handler for incoming offer availability requests
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peerAddress) -> { p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peerAddress) -> {
@ -155,25 +159,27 @@ public class OpenOfferManager {
} }
private void startRePublishThread() { private void startRePublishThread() {
if (p2PServiceListener != null) p2PService.removeP2PServiceListener(p2PServiceListener); if (p2PServiceListener != null)
p2PService.removeP2PServiceListener(p2PServiceListener);
ThreadFactoryBuilder builder = new ThreadFactoryBuilder() long period = (long) (Offer.TTL * 0.8);
.setDaemon(true) TimerTask timerTask = new TimerTask() {
.setNameFormat("Re-publish offers thread") @Override
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread. public void run() {
Thread.currentThread().setName("RepublishOffers-%d");
// An executor that starts up threads when needed and shuts them down later. rePublishOffers();
executor = new ScheduledThreadPoolExecutor(1, builder.build()); try {
executor.setKeepAliveTime(5, TimeUnit.SECONDS); } catch (Throwable t) {
executor.allowCoreThreadTimeOut(true); t.printStackTrace();
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); log.error("Executing task failed. " + t.getMessage());
}
checkArgument(Offer.TTL > 120000, "Offer.TTL <= 120"); }
long period = Offer.TTL - 120000; // 2 min before TTL expires };
executor.scheduleAtFixedRate(this::rePublishOffers, 500, period, TimeUnit.MILLISECONDS); timer.scheduleAtFixedRate(timerTask, 500, period);
} }
private void rePublishOffers() { private void rePublishOffers() {
log.trace("rePublishOffers");
for (OpenOffer openOffer : openOffers) { for (OpenOffer openOffer : openOffers) {
offerBookService.addOffer(openOffer.getOffer(), offerBookService.addOffer(openOffer.getOffer(),
() -> log.debug("Successful added offer to P2P network"), () -> log.debug("Successful added offer to P2P network"),

View file

@ -51,9 +51,10 @@ public class SetupPayoutTxLockTimeReachedListener extends TradeTask {
() -> { () -> {
try { try {
log.debug("Block height reached " + blockHeightFuture.get().getHeight()); log.debug("Block height reached " + blockHeightFuture.get().getHeight());
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} catch (ExecutionException e) {
e.printStackTrace();
} }
broadcastTx(); broadcastTx();
}, },

View file

@ -33,7 +33,6 @@ import javafx.scene.control.Button;
import javafx.scene.control.DatePicker; import javafx.scene.control.DatePicker;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import org.bitcoinj.core.Context;
import org.bitcoinj.core.Wallet; import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.crypto.MnemonicCode; import org.bitcoinj.crypto.MnemonicCode;
@ -198,9 +197,6 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
log.info("Attempting wallet restore using seed '{}' from date {}", seedWordsTextArea.getText(), datePicker.getValue()); log.info("Attempting wallet restore using seed '{}' from date {}", seedWordsTextArea.getText(), datePicker.getValue());
long date = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC); long date = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date); DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
Context ctx = Context.get();
new Thread(() -> {
Context.propagate(ctx);
walletService.restoreSeedWords(seed, walletService.restoreSeedWords(seed,
() -> UserThread.execute(() -> { () -> UserThread.execute(() -> {
log.debug("Wallet restored with seed words"); log.debug("Wallet restored with seed words");
@ -223,6 +219,5 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
"Error message: " + throwable.getMessage()) "Error message: " + throwable.getMessage())
.show(); .show();
})); }));
}, "Restore wallet thread").start();
} }
} }

View file

@ -3,6 +3,7 @@ package io.bitsquare.p2p;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import io.bitsquare.app.ProgramArguments; import io.bitsquare.app.ProgramArguments;
@ -35,8 +36,7 @@ import java.io.File;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -47,15 +47,20 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class P2PService { public class P2PService {
private static final Logger log = LoggerFactory.getLogger(P2PService.class); private static final Logger log = LoggerFactory.getLogger(P2PService.class);
private final SeedNodesRepository seedNodesRepository;
private final int port;
private final File torDir;
private final boolean useLocalhost;
@Nullable @Nullable
private final EncryptionService encryptionService; private final EncryptionService encryptionService;
private final SetupListener setupListener; private SetupListener setupListener;
private KeyRing keyRing; private KeyRing keyRing;
private final File storageDir;
private final NetworkStatistics networkStatistics; private final NetworkStatistics networkStatistics;
private final NetworkNode networkNode; private NetworkNode networkNode;
private final Routing routing; private Routing routing;
private final ProtectedExpirableDataStorage dataStorage; private ProtectedExpirableDataStorage dataStorage;
private final List<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArrayList<>(); private final List<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArrayList<>();
private final List<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArrayList<>(); private final List<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArrayList<>();
private final List<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArrayList<>(); private final List<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArrayList<>();
@ -73,7 +78,7 @@ public class P2PService {
private boolean allSeedNodesRequested; private boolean allSeedNodesRequested;
private Timer sendGetAllDataMessageTimer; private Timer sendGetAllDataMessageTimer;
private volatile boolean hiddenServiceReady; private volatile boolean hiddenServiceReady;
private final ExecutorService executorService;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -87,11 +92,27 @@ public class P2PService {
@Nullable EncryptionService encryptionService, @Nullable EncryptionService encryptionService,
KeyRing keyRing, KeyRing keyRing,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
this.seedNodesRepository = seedNodesRepository;
this.port = port;
this.torDir = torDir;
this.useLocalhost = useLocalhost;
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
this.keyRing = keyRing; this.keyRing = keyRing;
this.storageDir = storageDir;
networkStatistics = new NetworkStatistics(); networkStatistics = new NetworkStatistics();
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("P2PService-%d")
.setDaemon(true)
.build();
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
init();
}
private void init() {
// network layer // network layer
if (useLocalhost) { if (useLocalhost) {
networkNode = new LocalhostNetworkNode(port); networkNode = new LocalhostNetworkNode(port);
@ -578,7 +599,12 @@ public class P2PService {
sendGetAllDataMessageTimer.schedule(new TimerTask() { sendGetAllDataMessageTimer.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
try {
sendGetAllDataMessage(remainingSeedNodeAddresses); sendGetAllDataMessage(remainingSeedNodeAddresses);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
}, new Random().nextInt(2000) + 1000); }, new Random().nextInt(2000) + 1000);
} else { } else {

View file

@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -37,11 +38,13 @@ public class Utils {
public static void shutDownExecutorService(ExecutorService executorService, long waitBeforeShutDown) { public static void shutDownExecutorService(ExecutorService executorService, long waitBeforeShutDown) {
executorService.shutdown(); executorService.shutdown();
try { try {
executorService.awaitTermination(waitBeforeShutDown, TimeUnit.MILLISECONDS); boolean done = executorService.awaitTermination(waitBeforeShutDown, TimeUnit.MILLISECONDS);
if (!done) log.trace("Not all tasks completed at shutdown.");
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
executorService.shutdownNow(); final List<Runnable> rejected = executorService.shutdownNow();
log.debug("Rejected tasks: {}", rejected.size());
} }
public static byte[] compress(Serializable input) { public static byte[] compress(Serializable input) {

View file

@ -1,10 +1,13 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.common.util.concurrent.Uninterruptibles;
import io.bitsquare.common.ByteArrayUtils; import io.bitsquare.common.ByteArrayUtils;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Utils; import io.bitsquare.p2p.Utils;
import io.bitsquare.p2p.network.messages.CloseConnectionMessage; import io.bitsquare.p2p.network.messages.CloseConnectionMessage;
import javafx.concurrent.Task;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -16,9 +19,7 @@ import java.net.SocketTimeoutException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Connection { public class Connection {
private static final Logger log = LoggerFactory.getLogger(Connection.class); private static final Logger log = LoggerFactory.getLogger(Connection.class);
@ -37,18 +38,18 @@ public class Connection {
private final String uid; private final String uid;
private final Map<IllegalRequest, Integer> illegalRequests = new ConcurrentHashMap<>(); private final Map<IllegalRequest, Integer> illegalRequests = new ConcurrentHashMap<>();
private final ExecutorService executorService = Executors.newCachedThreadPool(); private final ExecutorService executorService;
private ObjectOutputStream out; private ObjectOutputStream out;
private ObjectInputStream in; private ObjectInputStream in;
@Nullable
private Address peerAddress;
private boolean isAuthenticated;
private volatile boolean stopped; private volatile boolean stopped;
private volatile boolean shutDownInProgress; private volatile boolean shutDownInProgress;
private volatile boolean inputHandlerStopped; private volatile boolean inputHandlerStopped;
private volatile Date lastActivityDate; private volatile Date lastActivityDate;
@Nullable
private Address peerAddress;
private boolean isAuthenticated;
//TODO got java.util.zip.DataFormatException: invalid distance too far back //TODO got java.util.zip.DataFormatException: invalid distance too far back
@ -69,6 +70,17 @@ public class Connection {
uid = UUID.randomUUID().toString(); uid = UUID.randomUUID().toString();
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("Connection-%d")
.setDaemon(true)
.build();
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
init();
}
private void init() {
try { try {
socket.setSoTimeout(SOCKET_TIMEOUT); socket.setSoTimeout(SOCKET_TIMEOUT);
// Need to access first the ObjectOutputStream otherwise the ObjectInputStream would block // Need to access first the ObjectOutputStream otherwise the ObjectInputStream would block
@ -203,12 +215,8 @@ public class Connection {
if (sendCloseConnectionMessage) { if (sendCloseConnectionMessage) {
sendMessage(new CloseConnectionMessage()); sendMessage(new CloseConnectionMessage());
try {
// give a bit of time for closing gracefully // give a bit of time for closing gracefully
Thread.sleep(100); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
} }
try { try {
@ -296,6 +304,7 @@ public class Connection {
private class InputHandler implements Runnable { private class InputHandler implements Runnable {
@Override @Override
public void run() { public void run() {
try {
Thread.currentThread().setName("InputHandler-" + socket.getLocalPort()); Thread.currentThread().setName("InputHandler-" + socket.getLocalPort());
while (!inputHandlerStopped) { while (!inputHandlerStopped) {
try { try {
@ -334,7 +343,20 @@ public class Connection {
inputHandlerStopped = true; inputHandlerStopped = true;
shutDown(false); shutDown(false);
} else { } else {
executorService.submit(() -> messageListener.onMessage(message, Connection.this)); Task task = new Task() {
@Override
protected Object call() throws Exception {
return null;
}
};
executorService.submit(() -> {
try {
messageListener.onMessage(message, Connection.this);
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
});
} }
} else { } else {
reportIllegalRequest(IllegalRequest.InvalidDataType); reportIllegalRequest(IllegalRequest.InvalidDataType);
@ -352,6 +374,10 @@ public class Connection {
handleConnectionException(e); handleConnectionException(e);
} }
} }
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
} }
} }

View file

@ -1,9 +1,6 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.*;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
@ -19,6 +16,7 @@ import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
public class LocalhostNetworkNode extends NetworkNode { public class LocalhostNetworkNode extends NetworkNode {
@ -92,8 +90,9 @@ public class LocalhostNetworkNode extends NetworkNode {
private void createTorNode(final Consumer<TorNode> resultHandler) { private void createTorNode(final Consumer<TorNode> resultHandler) {
Callable<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> task = () -> { Callable<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> task = () -> {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.trace("[simulation] Create TorNode"); if (simulateTorDelayTorNode > 0)
if (simulateTorDelayTorNode > 0) Thread.sleep(simulateTorDelayTorNode); Uninterruptibles.sleepUninterruptibly(simulateTorDelayTorNode, TimeUnit.MILLISECONDS);
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"TorNode created [simulation]:" + "TorNode created [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms" "\nTook " + (System.currentTimeMillis() - ts) + " ms"
@ -115,8 +114,9 @@ public class LocalhostNetworkNode extends NetworkNode {
private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) { private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) {
Callable<HiddenServiceDescriptor> task = () -> { Callable<HiddenServiceDescriptor> task = () -> {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.debug("[simulation] Create hidden service"); if (simulateTorDelayHiddenService > 0)
if (simulateTorDelayHiddenService > 0) Thread.sleep(simulateTorDelayHiddenService); Uninterruptibles.sleepUninterruptibly(simulateTorDelayHiddenService, TimeUnit.MILLISECONDS);
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"Hidden service created [simulation]:" + "Hidden service created [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms" "\nTook " + (System.currentTimeMillis() - ts) + " ms"

View file

@ -58,6 +58,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
final SettableFuture<Connection> resultFuture = SettableFuture.create(); final SettableFuture<Connection> resultFuture = SettableFuture.create();
Callable<Connection> task = () -> { Callable<Connection> task = () -> {
try {
Thread.currentThread().setName("Outgoing-connection-to-" + peerAddress); Thread.currentThread().setName("Outgoing-connection-to-" + peerAddress);
Optional<Connection> outboundConnectionOptional = getOutboundConnection(peerAddress); Optional<Connection> outboundConnectionOptional = getOutboundConnection(peerAddress);
@ -123,6 +124,11 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
connection.sendMessage(message); connection.sendMessage(message);
return connection; return connection;
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
throw t;
}
}; };
ListenableFuture<Connection> future = executorService.submit(task); ListenableFuture<Connection> future = executorService.submit(task);

View file

@ -1,6 +1,5 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import io.bitsquare.p2p.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -10,8 +9,6 @@ import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server implements Runnable { public class Server implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Server.class); private static final Logger log = LoggerFactory.getLogger(Server.class);
@ -19,7 +16,6 @@ public class Server implements Runnable {
private final ServerSocket serverSocket; private final ServerSocket serverSocket;
private final MessageListener messageListener; private final MessageListener messageListener;
private final ConnectionListener connectionListener; private final ConnectionListener connectionListener;
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final List<Connection> connections = new CopyOnWriteArrayList<>(); private final List<Connection> connections = new CopyOnWriteArrayList<>();
private volatile boolean stopped; private volatile boolean stopped;
@ -32,6 +28,7 @@ public class Server implements Runnable {
@Override @Override
public void run() { public void run() {
try {
Thread.currentThread().setName("Server-" + serverSocket.getLocalPort()); Thread.currentThread().setName("Server-" + serverSocket.getLocalPort());
while (!stopped) { while (!stopped) {
try { try {
@ -52,6 +49,10 @@ public class Server implements Runnable {
e.printStackTrace(); e.printStackTrace();
} }
} }
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
public void shutDown() { public void shutDown() {
@ -67,7 +68,6 @@ public class Server implements Runnable {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
Utils.shutDownExecutorService(executorService);
log.debug("Server shutdown complete"); log.debug("Server shutdown complete");
} }
} }

View file

@ -21,6 +21,7 @@ import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -60,18 +61,28 @@ public class TorNetworkNode extends NetworkNode {
this.torDir = torDir; this.torDir = torDir;
init();
}
private void init() {
selfTestTimeoutTask = new TimerTask() { selfTestTimeoutTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
try {
log.error("A timeout occurred at self test"); log.error("A timeout occurred at self test");
stopSelfTestTimer(); stopSelfTestTimer();
selfTestFailed(); selfTestFailed();
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
}; };
selfTestTask = new TimerTask() { selfTestTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
try {
stopTimeoutTimer(); stopTimeoutTimer();
if (selfTestRunning.get()) { if (selfTestRunning.get()) {
log.debug("running self test"); log.debug("running self test");
@ -103,6 +114,10 @@ public class TorNetworkNode extends NetworkNode {
} }
} }
} }
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
}; };
@ -144,11 +159,7 @@ public class TorNetworkNode extends NetworkNode {
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor; TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
startServer(hiddenServiceDescriptor.getServerSocket()); startServer(hiddenServiceDescriptor.getServerSocket());
try { Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
Thread.sleep(500);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
setupListeners.stream().forEach(e -> e.onHiddenServiceReady()); setupListeners.stream().forEach(e -> e.onHiddenServiceReady());
@ -252,12 +263,7 @@ public class TorNetworkNode extends NetworkNode {
restartCounter++; restartCounter++;
if (restartCounter <= MAX_RESTART_ATTEMPTS) { if (restartCounter <= MAX_RESTART_ATTEMPTS) {
shutDown(() -> { shutDown(() -> {
try { Uninterruptibles.sleepUninterruptibly(WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS);
Thread.sleep(WAIT_BEFORE_RESTART);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
log.warn("We restart tor as too many self tests failed."); log.warn("We restart tor as too many self tests failed.");
start(null); start(null);
}); });

View file

@ -1,8 +1,6 @@
package io.bitsquare.p2p.routing; package io.bitsquare.p2p.routing;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.*;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Utils; import io.bitsquare.p2p.Utils;
@ -15,10 +13,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Routing { public class Routing {
@ -47,7 +42,7 @@ public class Routing {
private final List<Address> reportedNeighborAddresses = new CopyOnWriteArrayList<>(); private final List<Address> reportedNeighborAddresses = new CopyOnWriteArrayList<>();
private final Map<Address, Runnable> authenticationCompleteHandlers = new ConcurrentHashMap<>(); private final Map<Address, Runnable> authenticationCompleteHandlers = new ConcurrentHashMap<>();
private final Timer maintenanceTimer = new Timer(); private final Timer maintenanceTimer = new Timer();
private final ExecutorService executorService = Executors.newCachedThreadPool(); private final ExecutorService executorService;
private volatile boolean shutDownInProgress; private volatile boolean shutDownInProgress;
@ -61,6 +56,17 @@ public class Routing {
// We copy it as we remove ourselves later from the list if we are a seed node // We copy it as we remove ourselves later from the list if we are a seed node
this.seedNodes = new CopyOnWriteArrayList<>(seeds); this.seedNodes = new CopyOnWriteArrayList<>(seeds);
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("Routing-%d")
.setDaemon(true)
.build();
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
init(networkNode);
}
private void init(NetworkNode networkNode) {
networkNode.addMessageListener((message, connection) -> { networkNode.addMessageListener((message, connection) -> {
if (message instanceof AuthenticationMessage) if (message instanceof AuthenticationMessage)
processAuthenticationMessage((AuthenticationMessage) message, connection); processAuthenticationMessage((AuthenticationMessage) message, connection);
@ -110,8 +116,13 @@ public class Routing {
maintenanceTimer.scheduleAtFixedRate(new TimerTask() { maintenanceTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
try {
disconnectOldConnections(); disconnectOldConnections();
pingNeighbors(); pingNeighbors();
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
}, MAINTENANCE_INTERVAL, MAINTENANCE_INTERVAL); }, MAINTENANCE_INTERVAL, MAINTENANCE_INTERVAL);
} }
@ -126,11 +137,7 @@ public class Routing {
Connection connection = authenticatedConnections.remove(0); Connection connection = authenticatedConnections.remove(0);
log.info("Shutdown oldest connection with last activity date=" + connection.getLastActivityDate() + " / connection=" + connection); log.info("Shutdown oldest connection with last activity date=" + connection.getLastActivityDate() + " / connection=" + connection);
connection.shutDown(() -> disconnectOldConnections()); connection.shutDown(() -> disconnectOldConnections());
try { Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
Thread.sleep(200);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
} }
} }
@ -153,11 +160,7 @@ public class Routing {
removeNeighbor(e.address); removeNeighbor(e.address);
} }
}); });
try { Uninterruptibles.sleepUninterruptibly(new Random().nextInt(5000) + 5000, TimeUnit.MILLISECONDS);
Thread.sleep(new Random().nextInt(5000) + 5000);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}); });
} }
@ -247,16 +250,16 @@ public class Routing {
public void startAuthentication(List<Address> connectedSeedNodes) { public void startAuthentication(List<Address> connectedSeedNodes) {
connectedSeedNodes.forEach(connectedSeedNode -> { connectedSeedNodes.forEach(connectedSeedNode -> {
executorService.submit(() -> { executorService.submit(() -> {
sendRequestAuthenticationMessage(seedNodes, connectedSeedNode);
try { try {
sendRequestAuthenticationMessage(seedNodes, connectedSeedNode);
// give a random pause of 3-5 sec. before using the next // give a random pause of 3-5 sec. before using the next
Thread.sleep(new Random().nextInt(2000) + 3000); Uninterruptibles.sleepUninterruptibly(new Random().nextInt(2000) + 3000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) { } catch (Throwable t) {
Thread.currentThread().interrupt(); t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
} }
}); });
}); });
} }
private void sendRequestAuthenticationMessage(final List<Address> remainingSeedNodes, final Address address) { private void sendRequestAuthenticationMessage(final List<Address> remainingSeedNodes, final Address address) {
@ -315,17 +318,11 @@ public class Routing {
connection.shutDown(() -> { connection.shutDown(() -> {
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to // we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections) // inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
try { Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
Thread.sleep(100);
} catch (InterruptedException e) { if (simulateAuthTorNode > 0)
Thread.currentThread().interrupt(); Uninterruptibles.sleepUninterruptibly(simulateAuthTorNode, TimeUnit.MILLISECONDS);
}
try {
if (simulateAuthTorNode > 0) Thread.sleep(simulateAuthTorNode);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + getAddress()); log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + getAddress());
long nonce = addToMapAndGetNonce(peerAddress); long nonce = addToMapAndGetNonce(peerAddress);
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new ChallengeMessage(getAddress(), requestAuthenticationMessage.nonce, nonce)); SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new ChallengeMessage(getAddress(), requestAuthenticationMessage.nonce, nonce));
@ -413,25 +410,25 @@ public class Routing {
ArrayList<Address> neighborAddresses = ((GetNeighborsMessage) message).neighborAddresses; ArrayList<Address> neighborAddresses = ((GetNeighborsMessage) message).neighborAddresses;
log.trace("Received neighbors: " + neighborAddresses); log.trace("Received neighbors: " + neighborAddresses);
// remove ourselves // remove ourselves
neighborAddresses.remove(getAddress());
addToReportedNeighbors(neighborAddresses, connection); addToReportedNeighbors(neighborAddresses, connection);
} }
} else if (message instanceof NeighborsMessage) { } else if (message instanceof NeighborsMessage) {
log.trace("NeighborsMessage from " + connection.getPeerAddress() + " at " + getAddress());
NeighborsMessage neighborsMessage = (NeighborsMessage) message; NeighborsMessage neighborsMessage = (NeighborsMessage) message;
Address peerAddress = neighborsMessage.address;
log.trace("NeighborsMessage from " + peerAddress + " at " + getAddress());
ArrayList<Address> neighborAddresses = neighborsMessage.neighborAddresses; ArrayList<Address> neighborAddresses = neighborsMessage.neighborAddresses;
log.trace("Received neighbors: " + neighborAddresses); log.trace("Received neighbors: " + neighborAddresses);
// remove ourselves // remove ourselves
neighborAddresses.remove(getAddress());
addToReportedNeighbors(neighborAddresses, connection); addToReportedNeighbors(neighborAddresses, connection);
log.info("\n\nAuthenticationComplete\nPeer with address " + connection.getPeerAddress().toString()
+ " authenticated (" + connection.getObjectId() + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
// we wait until the handshake is completed before setting the authenticate flag // we wait until the handshake is completed before setting the authenticate flag
// authentication at both sides of the connection // authentication at both sides of the connection
setAuthenticated(connection, neighborsMessage.address);
log.info("\n\nAuthenticationComplete\nPeer with address " + peerAddress
+ " authenticated (" + connection.getObjectId() + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
setAuthenticated(connection, peerAddress);
Runnable authenticationCompleteHandler = authenticationCompleteHandlers.remove(connection.getPeerAddress()); Runnable authenticationCompleteHandler = authenticationCompleteHandlers.remove(connection.getPeerAddress());
if (authenticationCompleteHandler != null) if (authenticationCompleteHandler != null)
@ -442,18 +439,21 @@ public class Routing {
} }
private void addToReportedNeighbors(ArrayList<Address> neighborAddresses, Connection connection) { private void addToReportedNeighbors(ArrayList<Address> neighborAddresses, Connection connection) {
log.trace("addToReportedNeighbors");
// we disconnect misbehaving nodes trying to send too many neighbors // we disconnect misbehaving nodes trying to send too many neighbors
// reported neighbors include the peers connected neighbors which is normally max. 8 but we give some headroom // reported neighbors include the peers connected neighbors which is normally max. 8 but we give some headroom
// for safety // for safety
if (neighborAddresses.size() > 1100) { if (neighborAddresses.size() > 1100) {
connection.shutDown(); connection.shutDown();
} else { } else {
neighborAddresses.remove(getAddress());
reportedNeighborAddresses.addAll(neighborAddresses); reportedNeighborAddresses.addAll(neighborAddresses);
purgeReportedNeighbors(); purgeReportedNeighbors();
} }
} }
private void purgeReportedNeighbors() { private void purgeReportedNeighbors() {
log.trace("purgeReportedNeighbors");
int all = getAllNeighborAddresses().size(); int all = getAllNeighborAddresses().size();
if (all > 1000) { if (all > 1000) {
int diff = all - 100; int diff = all - 100;
@ -478,10 +478,7 @@ public class Routing {
private void authenticateToNextRandomNeighbor() { private void authenticateToNextRandomNeighbor() {
executorService.submit(() -> { executorService.submit(() -> {
try { try {
Thread.sleep(new Random().nextInt(200) + 200); Uninterruptibles.sleepUninterruptibly(new Random().nextInt(200) + 200, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
if (getConnectedNeighbors().size() <= MAX_CONNECTIONS) { if (getConnectedNeighbors().size() <= MAX_CONNECTIONS) {
Address randomNotConnectedNeighborAddress = getRandomNotConnectedNeighborAddress(); Address randomNotConnectedNeighborAddress = getRandomNotConnectedNeighborAddress();
if (randomNotConnectedNeighborAddress != null) { if (randomNotConnectedNeighborAddress != null) {
@ -493,6 +490,10 @@ public class Routing {
} else { } else {
log.info("We have already enough connections."); log.info("We have already enough connections.");
} }
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
}); });
} }
@ -531,9 +532,8 @@ public class Routing {
private boolean verifyNonceAndAuthenticatePeerAddress(long peersNonce, Address peerAddress) { private boolean verifyNonceAndAuthenticatePeerAddress(long peersNonce, Address peerAddress) {
log.trace("verifyNonceAndAuthenticatePeerAddress nonceMap=" + nonceMap + " / peerAddress=" + peerAddress); log.trace("verifyNonceAndAuthenticatePeerAddress nonceMap=" + nonceMap + " / peerAddress=" + peerAddress);
long nonce = nonceMap.remove(peerAddress); Long nonce = nonceMap.remove(peerAddress);
boolean result = nonce == peersNonce; return nonce != null && nonce == peersNonce;
return result;
} }
private void setAuthenticated(Connection connection, Address peerAddress) { private void setAuthenticated(Connection connection, Address peerAddress) {

View file

@ -52,6 +52,10 @@ public class ProtectedExpirableDataStorage {
storage = new Storage<>(storageDir); storage = new Storage<>(storageDir);
init();
}
private void init() {
ConcurrentHashMap<BigInteger, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "sequenceNumberMap"); ConcurrentHashMap<BigInteger, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "sequenceNumberMap");
if (persisted != null) { if (persisted != null) {
sequenceNumberMap = persisted; sequenceNumberMap = persisted;
@ -79,9 +83,14 @@ public class ProtectedExpirableDataStorage {
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
try {
log.info("removeExpiredEntries called "); log.info("removeExpiredEntries called ");
map.entrySet().stream().filter(entry -> entry.getValue().isExpired()) map.entrySet().stream().filter(entry -> entry.getValue().isExpired())
.forEach(entry -> map.remove(entry.getKey())); .forEach(entry -> map.remove(entry.getKey()));
} catch (Throwable t) {
t.printStackTrace();
log.error("Executing task failed. " + t.getMessage());
}
} }
}, },
CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL,