unregister closed tab and dialog subscribers, fix connection error reset behaviour

This commit is contained in:
Craig Raw 2020-08-20 11:25:34 +02:00
parent d5aba35184
commit 77dd8ca5d7
22 changed files with 188 additions and 15 deletions

View file

@ -188,6 +188,18 @@ public class AppController implements Initializable {
EventManager.get().post(new OpenWalletsEvent(getOpenWallets())); EventManager.get().post(new OpenWalletsEvent(getOpenWallets()));
} }
List<WalletTabData> closedWalletTabs = c.getRemoved().stream().map(tab -> (TabData)tab.getUserData())
.filter(tabData -> tabData.getType() == TabData.TabType.WALLET).map(tabData -> (WalletTabData)tabData).collect(Collectors.toList());
if(!closedWalletTabs.isEmpty()) {
EventManager.get().post(new WalletTabsClosedEvent(closedWalletTabs));
}
List<TransactionTabData> closedTransactionTabs = c.getRemoved().stream().map(tab -> (TabData)tab.getUserData())
.filter(tabData -> tabData.getType() == TabData.TabType.TRANSACTION).map(tabData -> (TransactionTabData)tabData).collect(Collectors.toList());
if(!closedTransactionTabs.isEmpty()) {
EventManager.get().post(new TransactionTabsClosedEvent(closedTransactionTabs));
}
if(tabs.getTabs().isEmpty()) { if(tabs.getTabs().isEmpty()) {
Stage tabStage = (Stage)tabs.getScene().getWindow(); Stage tabStage = (Stage)tabs.getScene().getWindow();
tabStage.setTitle("Sparrow"); tabStage.setTitle("Sparrow");
@ -268,10 +280,14 @@ public class AppController implements Initializable {
} }
}); });
connectionService.setOnFailed(failEvent -> { connectionService.setOnFailed(failEvent -> {
//Close connection here to create a new transport next time we try
connectionService.resetConnection();
changeMode = false; changeMode = false;
onlineProperty.setValue(false); onlineProperty.setValue(false);
changeMode = true; changeMode = true;
log.debug("Connection failed", failEvent.getSource().getException());
EventManager.get().post(new ConnectionFailedEvent(failEvent.getSource().getException())); EventManager.get().post(new ConnectionFailedEvent(failEvent.getSource().getException()));
}); });

View file

@ -12,6 +12,10 @@ public class WalletTabData extends TabData {
this.walletForm = walletForm; this.walletForm = walletForm;
} }
public WalletForm getWalletForm() {
return walletForm;
}
public Wallet getWallet() { public Wallet getWallet() {
return walletForm.getWallet(); return walletForm.getWallet();
} }

View file

@ -19,6 +19,9 @@ public class DeviceAddressDialog extends DeviceDialog<String> {
this.keyDerivation = keyDerivation; this.keyDerivation = keyDerivation;
EventManager.get().register(this); EventManager.get().register(this);
setOnCloseRequest(event -> {
EventManager.get().unregister(this);
});
} }
@Override @Override
@ -28,7 +31,6 @@ public class DeviceAddressDialog extends DeviceDialog<String> {
@Subscribe @Subscribe
public void addressDisplayed(AddressDisplayedEvent event) { public void addressDisplayed(AddressDisplayedEvent event) {
EventManager.get().unregister(this);
setResult(event.getAddress()); setResult(event.getAddress());
this.close(); this.close();
} }

View file

@ -15,6 +15,9 @@ public class DeviceSignDialog extends DeviceDialog<PSBT> {
super(devices); super(devices);
this.psbt = psbt; this.psbt = psbt;
EventManager.get().register(this); EventManager.get().register(this);
setOnCloseRequest(event -> {
EventManager.get().unregister(this);
});
setResultConverter(dialogButton -> dialogButton.getButtonData().isCancelButton() ? null : psbt); setResultConverter(dialogButton -> dialogButton.getButtonData().isCancelButton() ? null : psbt);
} }
@ -26,7 +29,6 @@ public class DeviceSignDialog extends DeviceDialog<PSBT> {
@Subscribe @Subscribe
public void psbtSigned(PSBTSignedEvent event) { public void psbtSigned(PSBTSignedEvent event) {
if(psbt == event.getPsbt()) { if(psbt == event.getPsbt()) {
EventManager.get().unregister(this);
setResult(event.getSignedPsbt()); setResult(event.getSignedPsbt());
this.close(); this.close();
} }

View file

@ -21,6 +21,9 @@ public class WalletExportDialog extends Dialog<Wallet> {
public WalletExportDialog(Wallet wallet) { public WalletExportDialog(Wallet wallet) {
EventManager.get().register(this); EventManager.get().register(this);
setOnCloseRequest(event -> {
EventManager.get().unregister(this);
});
final DialogPane dialogPane = getDialogPane(); final DialogPane dialogPane = getDialogPane();
@ -64,7 +67,6 @@ public class WalletExportDialog extends Dialog<Wallet> {
@Subscribe @Subscribe
public void walletExported(WalletExportEvent event) { public void walletExported(WalletExportEvent event) {
EventManager.get().unregister(this);
wallet = event.getWallet(); wallet = event.getWallet();
setResult(wallet); setResult(wallet);
this.close(); this.close();

View file

@ -16,6 +16,9 @@ public class WalletImportDialog extends Dialog<Wallet> {
public WalletImportDialog() { public WalletImportDialog() {
EventManager.get().register(this); EventManager.get().register(this);
setOnCloseRequest(event -> {
EventManager.get().unregister(this);
});
final DialogPane dialogPane = getDialogPane(); final DialogPane dialogPane = getDialogPane();
@ -51,7 +54,6 @@ public class WalletImportDialog extends Dialog<Wallet> {
@Subscribe @Subscribe
public void walletImported(WalletImportEvent event) { public void walletImported(WalletImportEvent event) {
EventManager.get().unregister(this);
wallet = event.getWallet(); wallet = event.getWallet();
setResult(wallet); setResult(wallet);
this.close(); this.close();

View file

@ -0,0 +1,17 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.sparrow.TransactionTabData;
import java.util.List;
public class TransactionTabsClosedEvent {
private final List<TransactionTabData> closedTransactionTabData;
public TransactionTabsClosedEvent(List<TransactionTabData> closedTransactionTabData) {
this.closedTransactionTabData = closedTransactionTabData;
}
public List<TransactionTabData> getClosedTransactionTabData() {
return closedTransactionTabData;
}
}

View file

@ -0,0 +1,17 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.sparrow.WalletTabData;
import java.util.List;
public class WalletTabsClosedEvent {
private final List<WalletTabData> closedWalletTabData;
public WalletTabsClosedEvent(List<WalletTabData> closedWalletTabData) {
this.closedWalletTabData = closedWalletTabData;
}
public List<WalletTabData> getClosedWalletTabData() {
return closedWalletTabData;
}
}

View file

@ -27,6 +27,10 @@ public class KeystoreImportDialog extends Dialog<Keystore> {
public KeystoreImportDialog(Wallet wallet, KeystoreSource initialSource) { public KeystoreImportDialog(Wallet wallet, KeystoreSource initialSource) {
EventManager.get().register(this); EventManager.get().register(this);
setOnCloseRequest(event -> {
EventManager.get().unregister(this);
});
final DialogPane dialogPane = getDialogPane(); final DialogPane dialogPane = getDialogPane();
try { try {

View file

@ -579,7 +579,6 @@ public class ElectrumServer {
private final boolean subscribe; private final boolean subscribe;
private boolean firstCall = true; private boolean firstCall = true;
private Thread reader; private Thread reader;
private Throwable lastReaderException;
private long feeRatesRetrievedAt; private long feeRatesRetrievedAt;
public ConnectionService() { public ConnectionService() {
@ -598,7 +597,7 @@ public class ElectrumServer {
if(firstCall) { if(firstCall) {
electrumServer.connect(); electrumServer.connect();
reader = new Thread(new ReadRunnable()); reader = new Thread(new ReadRunnable(), "ElectrumServerReadThread");
reader.setDaemon(true); reader.setDaemon(true);
reader.setUncaughtExceptionHandler(ConnectionService.this); reader.setUncaughtExceptionHandler(ConnectionService.this);
reader.start(); reader.start();
@ -639,8 +638,7 @@ public class ElectrumServer {
return new FeeRatesUpdatedEvent(blockTargetFeeRates); return new FeeRatesUpdatedEvent(blockTargetFeeRates);
} }
} else { } else {
firstCall = true; resetConnection();
throw new ServerException("Connection to server failed", lastReaderException);
} }
} }
@ -649,12 +647,21 @@ public class ElectrumServer {
}; };
} }
public void resetConnection() {
try {
closeActiveConnection();
firstCall = true;
} catch (ServerException e) {
log.error("Error closing connection during connection reset", e);
}
}
@Override @Override
public boolean cancel() { public boolean cancel() {
try { try {
closeActiveConnection(); closeActiveConnection();
} catch (ServerException e) { } catch (ServerException e) {
log.error("Eror closing connection", e); log.error("Error closing connection", e);
} }
return super.cancel(); return super.cancel();
@ -664,12 +671,11 @@ public class ElectrumServer {
public void reset() { public void reset() {
super.reset(); super.reset();
firstCall = true; firstCall = true;
lastReaderException = null;
} }
@Override @Override
public void uncaughtException(Thread t, Throwable e) { public void uncaughtException(Thread t, Throwable e) {
this.lastReaderException = e; log.error("Uncaught error in ConnectionService", e);
} }
} }
@ -679,8 +685,9 @@ public class ElectrumServer {
try { try {
TcpTransport tcpTransport = (TcpTransport)getTransport(); TcpTransport tcpTransport = (TcpTransport)getTransport();
tcpTransport.readInputLoop(); tcpTransport.readInputLoop();
} catch (ServerException e) { } catch(ServerException e) {
throw new RuntimeException(e.getCause() != null ? e.getCause() : e); //Only debug logging here as the exception has been passed on to the ConnectionService thread via TcpTransport
log.debug("Read thread terminated", e);
} }
} }
} }

View file

@ -28,6 +28,8 @@ public class TcpTransport implements Transport, Closeable {
private final JsonRpcServer jsonRpcServer = new JsonRpcServer(); private final JsonRpcServer jsonRpcServer = new JsonRpcServer();
private final SubscriptionService subscriptionService = new SubscriptionService(); private final SubscriptionService subscriptionService = new SubscriptionService();
private Exception lastException;
public TcpTransport(HostAndPort server) { public TcpTransport(HostAndPort server) {
this.server = server; this.server = server;
this.socketFactory = SocketFactory.getDefault(); this.socketFactory = SocketFactory.getDefault();
@ -50,7 +52,7 @@ public class TcpTransport implements Transport, Closeable {
out.flush(); out.flush();
} }
private synchronized String readResponse() { private synchronized String readResponse() throws IOException {
while(reading) { while(reading) {
try { try {
wait(); wait();
@ -60,6 +62,10 @@ public class TcpTransport implements Transport, Closeable {
} }
} }
if(lastException != null) {
throw new IOException("Error reading response", lastException);
}
reading = true; reading = true;
notifyAll(); notifyAll();
@ -83,8 +89,12 @@ public class TcpTransport implements Transport, Closeable {
} catch(InterruptedException e) { } catch(InterruptedException e) {
//Restore interrupt status and continue //Restore interrupt status and continue
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} catch(IOException e) { } catch(Exception e) {
if(running) { if(running) {
lastException = e;
reading = false;
notifyAll();
//Allow this thread to terminate as we will need to reconnect with a new transport anyway
throw new ServerException(e); throw new ServerException(e);
} }
} }

View file

@ -196,6 +196,11 @@ public class HeadersController extends TransactionFormController implements Init
initializeView(); initializeView();
} }
@Override
protected TransactionForm getTransactionForm() {
return headersForm;
}
private void initializeView() { private void initializeView() {
Transaction tx = headersForm.getTransaction(); Transaction tx = headersForm.getTransaction();

View file

@ -461,6 +461,11 @@ public class InputController extends TransactionFormController implements Initia
initializeView(); initializeView();
} }
@Override
protected TransactionForm getTransactionForm() {
return inputForm;
}
@Override @Override
protected String describeScriptChunk(ScriptChunk chunk) { protected String describeScriptChunk(ScriptChunk chunk) {
String chunkString = super.describeScriptChunk(chunk); String chunkString = super.describeScriptChunk(chunk);

View file

@ -49,6 +49,11 @@ public class InputsController extends TransactionFormController implements Initi
initialiseView(); initialiseView();
} }
@Override
protected TransactionForm getTransactionForm() {
return inputsForm;
}
private void initialiseView() { private void initialiseView() {
Transaction tx = inputsForm.getTransaction(); Transaction tx = inputsForm.getTransaction();
count.setText(Integer.toString(tx.getInputs().size())); count.setText(Integer.toString(tx.getInputs().size()));

View file

@ -147,6 +147,11 @@ public class OutputController extends TransactionFormController implements Initi
initializeView(); initializeView();
} }
@Override
protected TransactionForm getTransactionForm() {
return outputForm;
}
@Subscribe @Subscribe
public void blockTransactionOutputsFetched(BlockTransactionOutputsFetchedEvent event) { public void blockTransactionOutputsFetched(BlockTransactionOutputsFetchedEvent event) {
if(event.getTxId().equals(outputForm.getTransaction().getTxId()) && outputForm.getPsbt() == null && outputForm.getIndex() >= event.getPageStart() && outputForm.getIndex() < event.getPageEnd()) { if(event.getTxId().equals(outputForm.getTransaction().getTxId()) && outputForm.getPsbt() == null && outputForm.getIndex() >= event.getPageStart() && outputForm.getIndex() < event.getPageEnd()) {

View file

@ -51,6 +51,11 @@ public class OutputsController extends TransactionFormController implements Init
} }
} }
@Override
protected TransactionForm getTransactionForm() {
return outputsForm;
}
@Subscribe @Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) { public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
total.refresh(event.getBitcoinUnit()); total.refresh(event.getBitcoinUnit());

View file

@ -8,6 +8,7 @@ import com.sparrowwallet.drongo.psbt.PSBTOutput;
import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.AppController;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.TransactionTabData;
import com.sparrowwallet.sparrow.control.TransactionHexArea; import com.sparrowwallet.sparrow.control.TransactionHexArea;
import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.net.ElectrumServer; import com.sparrowwallet.sparrow.net.ElectrumServer;
@ -489,4 +490,13 @@ public class TransactionController implements Initializable {
highlightTxHex(); highlightTxHex();
} }
} }
@Subscribe
public void transactionTabsClosed(TransactionTabsClosedEvent event) {
for(TransactionTabData tabData : event.getClosedTransactionTabData()) {
if(tabData.getTransactionData() == txdata) {
EventManager.get().unregister(this);
}
}
}
} }

View file

@ -23,6 +23,10 @@ public abstract class TransactionForm {
this.txdata = txdata; this.txdata = txdata;
} }
public TransactionData getTransactionData() {
return txdata;
}
public Transaction getTransaction() { public Transaction getTransaction() {
return txdata.getTransaction(); return txdata.getTransaction();
} }

View file

@ -1,9 +1,13 @@
package com.sparrowwallet.sparrow.transaction; package com.sparrowwallet.sparrow.transaction;
import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.protocol.NonStandardScriptException; import com.sparrowwallet.drongo.protocol.NonStandardScriptException;
import com.sparrowwallet.drongo.protocol.TransactionOutput; import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.sparrow.BaseController; import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.TransactionTabData;
import com.sparrowwallet.sparrow.event.TransactionTabsClosedEvent;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.scene.chart.PieChart; import javafx.scene.chart.PieChart;
@ -18,6 +22,8 @@ import java.util.List;
public abstract class TransactionFormController extends BaseController { public abstract class TransactionFormController extends BaseController {
private static final int MAX_PIE_SEGMENTS = 200; private static final int MAX_PIE_SEGMENTS = 200;
protected abstract TransactionForm getTransactionForm();
protected void addPieData(PieChart pie, List<TransactionOutput> outputs) { protected void addPieData(PieChart pie, List<TransactionOutput> outputs) {
ObservableList<PieChart.Data> outputsPieData = FXCollections.observableArrayList(); ObservableList<PieChart.Data> outputsPieData = FXCollections.observableArrayList();
@ -64,6 +70,15 @@ public abstract class TransactionFormController extends BaseController {
}); });
} }
@Subscribe
public void transactionTabsClosed(TransactionTabsClosedEvent event) {
for(TransactionTabData tabData : event.getClosedTransactionTabData()) {
if(tabData.getTransactionData() == getTransactionForm().getTransactionData()) {
EventManager.get().unregister(this);
}
}
}
public static class TransactionReferenceContextMenu extends ContextMenu { public static class TransactionReferenceContextMenu extends ContextMenu {
public TransactionReferenceContextMenu(String reference) { public TransactionReferenceContextMenu(String reference) {
MenuItem referenceItem = new MenuItem("Copy Reference"); MenuItem referenceItem = new MenuItem("Copy Reference");

View file

@ -7,8 +7,10 @@ import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData;
import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent; import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent;
import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent; import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent;
import com.sparrowwallet.sparrow.event.WalletTabsClosedEvent;
import javafx.beans.property.IntegerProperty; import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase; import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
@ -204,4 +206,13 @@ public class TransactionEntry extends Entry implements Comparable<TransactionEnt
} }
} }
} }
@Subscribe
public void walletTabsClosed(WalletTabsClosedEvent event) {
for(WalletTabData tabData : event.getClosedWalletTabData()) {
if(tabData.getWalletForm().getWallet() == wallet) {
EventManager.get().unregister(this);
}
}
}
} }

View file

@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.AppController;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData;
import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.net.ElectrumServer; import com.sparrowwallet.sparrow.net.ElectrumServer;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
@ -216,4 +217,13 @@ public class WalletForm {
refreshHistory(AppController.getCurrentBlockHeight()); refreshHistory(AppController.getCurrentBlockHeight());
} }
} }
@Subscribe
public void walletTabsClosed(WalletTabsClosedEvent event) {
for(WalletTabData tabData : event.getClosedWalletTabData()) {
if(tabData.getWalletForm() == this) {
EventManager.get().unregister(this);
}
}
}
} }

View file

@ -1,6 +1,10 @@
package com.sparrowwallet.sparrow.wallet; package com.sparrowwallet.sparrow.wallet;
import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.sparrow.BaseController; import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.WalletTabData;
import com.sparrowwallet.sparrow.event.WalletTabsClosedEvent;
public abstract class WalletFormController extends BaseController { public abstract class WalletFormController extends BaseController {
public WalletForm walletForm; public WalletForm walletForm;
@ -15,4 +19,15 @@ public abstract class WalletFormController extends BaseController {
} }
public abstract void initializeView(); public abstract void initializeView();
@Subscribe
public void walletTabsClosed(WalletTabsClosedEvent event) {
for(WalletTabData tabData : event.getClosedWalletTabData()) {
if(tabData.getWalletForm() == walletForm) {
EventManager.get().unregister(this);
} else if(walletForm instanceof SettingsWalletForm && tabData.getStorage() == walletForm.getStorage()) {
EventManager.get().unregister(this);
}
}
}
} }