Handle IOException within ViewLoader#load

Prior to this change, all callers of ViewLoader#load were forced to
handle the (checked) IOException declared by the #load method signature.
This resulted in a significant amount of duplicate handling logic where
nothing more than logging the error occured.

Failing to load a view represents a catastrophic error in the
application; i.e. it is not something that can be handled in any way
other than shutting the application down, fixing what is broken and
restarting the application. For this reason, any IOException raised
within ViewLoader#load is now caught, wrapped and re-thrown as an
(unchecked) RuntimeException. This will be handled by the platform's
UncaughtExceptionHandler, and a dialog will be raised to inform the user
that a fatal error has occured.

As a result all try/catch blocks around calls to ViewLoader#load have
now been removed, making for tighter, more readable, and easier to test
code.

In the future, the distinction between errors that are programmatically
recoverable and those that are catastrophic (typically developer errors)
will be used to determine whether methods in the Bitsquare API throw
checked or unchecked exceptions.
This commit is contained in:
Chris Beams 2014-11-03 16:52:11 +01:00
parent 77044d5cfe
commit e08c2bb564
No known key found for this signature in database
GPG Key ID: 3D214F8F5BC5ED73
12 changed files with 168 additions and 247 deletions

View File

@ -104,28 +104,23 @@ public class BitsquareUI extends Application {
ViewLoader.setInjector(injector);
ViewLoader loader = new ViewLoader(Navigation.Item.MAIN, false);
try {
Parent view = loader.load();
Parent view = loader.load();
Scene scene = new Scene(view, 1000, 600);
scene.getStylesheets().setAll(
"/io/bitsquare/gui/bitsquare.css",
"/io/bitsquare/gui/images.css");
Scene scene = new Scene(view, 1000, 600);
scene.getStylesheets().setAll(
"/io/bitsquare/gui/bitsquare.css",
"/io/bitsquare/gui/images.css");
setupCloseHandlers(primaryStage, scene);
setupCloseHandlers(primaryStage, scene);
primaryStage.setScene(scene);
primaryStage.setScene(scene);
primaryStage.setMinWidth(75);
primaryStage.setMinHeight(50);
primaryStage.setMinWidth(75);
primaryStage.setMinHeight(50);
Profiler.initScene(primaryStage.getScene());
Profiler.initScene(primaryStage.getScene());
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
}
primaryStage.show();
}
private void setupCloseHandlers(Stage primaryStage, Scene scene) {

View File

@ -30,8 +30,6 @@ import io.bitsquare.settings.Settings;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -147,20 +145,14 @@ public class MainViewCB extends ViewCB<MainPM> {
protected Initializable loadView(Navigation.Item navigationItem) {
super.loadView((navigationItem));
final ViewLoader loader = new ViewLoader(navigationItem);
try {
final Node view = loader.load();
contentContainer.getChildren().setAll(view);
childController = loader.getController();
final Node view = loader.load();
contentContainer.getChildren().setAll(view);
childController = loader.getController();
if (childController instanceof ViewCB)
((ViewCB) childController).setParent(this);
if (childController instanceof ViewCB)
((ViewCB) childController).setParent(this);
return childController;
} catch (IOException e) {
e.printStackTrace();
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
}
return null;
return childController;
}

View File

@ -22,8 +22,6 @@ import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.ViewCB;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -137,37 +135,32 @@ public class AccountViewCB extends CachedViewCB<AccountPM> {
super.loadView(navigationItem);
final ViewLoader loader = new ViewLoader(navigationItem);
try {
Node view = loader.load();
Tab tab = null;
switch (navigationItem) {
case ACCOUNT_SETTINGS:
tab = accountSettingsTab;
tab.setText("Account settings");
arbitratorSettingsTab.setDisable(false);
break;
case ACCOUNT_SETUP:
tab = accountSettingsTab;
tab.setText("Account setup");
arbitratorSettingsTab.setDisable(true);
break;
case ARBITRATOR_SETTINGS:
tab = arbitratorSettingsTab;
break;
}
// for IRC demo we deactivate the arbitratorSettingsTab
arbitratorSettingsTab.setDisable(true);
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + Navigation.Item.ACCOUNT_SETUP.getFxmlUrl());
e.printStackTrace();
Node view = loader.load();
Tab tab = null;
switch (navigationItem) {
case ACCOUNT_SETTINGS:
tab = accountSettingsTab;
tab.setText("Account settings");
arbitratorSettingsTab.setDisable(false);
break;
case ACCOUNT_SETUP:
tab = accountSettingsTab;
tab.setText("Account setup");
arbitratorSettingsTab.setDisable(true);
break;
case ARBITRATOR_SETTINGS:
tab = arbitratorSettingsTab;
break;
}
// for IRC demo we deactivate the arbitratorSettingsTab
arbitratorSettingsTab.setDisable(true);
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
return childController;
}

View File

@ -23,8 +23,6 @@ import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.main.account.arbitrator.registration.ArbitratorRegistrationViewCB;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -98,30 +96,26 @@ public class ArbitratorSettingsViewCB extends CachedViewCB {
protected Initializable loadView(Navigation.Item navigationItem) {
// don't use caching here, cause exc. -> need to investigate and is rarely called so no caching is better
final ViewLoader loader = new ViewLoader(navigationItem, false);
try {
final Parent view = loader.load();
arbitratorRegistrationViewCB = loader.getController();
final Stage rootStage = BitsquareUI.getPrimaryStage();
final Stage stage = new Stage();
stage.setTitle("Arbitrator");
stage.setMinWidth(800);
stage.setMinHeight(400);
stage.setWidth(800);
stage.setHeight(600);
stage.setX(rootStage.getX() + 50);
stage.setY(rootStage.getY() + 50);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(rootStage);
Scene scene = new Scene(view, 800, 600);
stage.setScene(scene);
stage.show();
final Parent view = loader.load();
arbitratorRegistrationViewCB = loader.getController();
return arbitratorRegistrationViewCB;
} catch (IOException e) {
e.printStackTrace();
}
return null;
final Stage rootStage = BitsquareUI.getPrimaryStage();
final Stage stage = new Stage();
stage.setTitle("Arbitrator");
stage.setMinWidth(800);
stage.setMinHeight(400);
stage.setWidth(800);
stage.setHeight(600);
stage.setX(rootStage.getX() + 50);
stage.setY(rootStage.getY() + 50);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(rootStage);
Scene scene = new Scene(view, 800, 600);
stage.setScene(scene);
stage.show();
return arbitratorRegistrationViewCB;
}

View File

@ -29,8 +29,6 @@ import io.bitsquare.persistence.Persistence;
import io.bitsquare.settings.Settings;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
@ -141,16 +139,11 @@ public class ArbitratorBrowserViewCB extends CachedViewCB implements ArbitratorL
super.loadView(navigationItem);
final ViewLoader loader = new ViewLoader(navigationItem);
try {
Node view = loader.load();
((Pane) root).getChildren().set(0, view);
Initializable childController = arbitratorProfileViewCB = loader.getController();
((ViewCB) childController).setParent(this);
Node view = loader.load();
((Pane) root).getChildren().set(0, view);
Initializable childController = arbitratorProfileViewCB = loader.getController();
((ViewCB) childController).setParent(this);
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
e.printStackTrace();
}
return childController;
}

View File

@ -30,8 +30,6 @@ import io.bitsquare.locale.Country;
import io.bitsquare.locale.Region;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;
@ -190,36 +188,31 @@ public class RestrictionsViewCB extends CachedViewCB<RestrictionsPM> implements
protected Initializable loadView(Navigation.Item navigationItem) {
// TODO caching causes exception
final ViewLoader loader = new ViewLoader(navigationItem, false);
try {
final Node view = loader.load();
//TODO Resolve type problem...
Initializable childController = loader.getController();
//childController.setParentController(this);
final Node view = loader.load();
//TODO Resolve type problem...
Initializable childController = loader.getController();
//childController.setParentController(this);
final Stage rootStage = BitsquareUI.getPrimaryStage();
final Stage stage = new Stage();
stage.setTitle("Arbitrator selection");
stage.setMinWidth(800);
stage.setMinHeight(500);
stage.setWidth(800);
stage.setHeight(600);
stage.setX(rootStage.getX() + 50);
stage.setY(rootStage.getY() + 50);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(rootStage);
Scene scene = new Scene((Parent) view, 800, 600);
stage.setScene(scene);
stage.setOnHidden(windowEvent -> {
if (navigationItem == Navigation.Item.ARBITRATOR_BROWSER)
updateArbitratorList();
});
stage.show();
final Stage rootStage = BitsquareUI.getPrimaryStage();
final Stage stage = new Stage();
stage.setTitle("Arbitrator selection");
stage.setMinWidth(800);
stage.setMinHeight(500);
stage.setWidth(800);
stage.setHeight(600);
stage.setX(rootStage.getX() + 50);
stage.setY(rootStage.getY() + 50);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(rootStage);
Scene scene = new Scene((Parent) view, 800, 600);
stage.setScene(scene);
stage.setOnHidden(windowEvent -> {
if (navigationItem == Navigation.Item.ARBITRATOR_BROWSER)
updateArbitratorList();
});
stage.show();
return childController;
} catch (IOException e) {
e.printStackTrace();
}
return null;
return childController;
}

View File

@ -25,8 +25,6 @@ import io.bitsquare.gui.main.account.content.ContextAware;
import io.bitsquare.gui.util.Colors;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -149,18 +147,12 @@ public class AccountSettingsViewCB extends CachedViewCB {
@Override
protected Initializable loadView(Navigation.Item navigationItem) {
final ViewLoader loader = new ViewLoader(navigationItem);
try {
final Pane view = loader.load();
content.getChildren().setAll(view);
childController = loader.getController();
((ViewCB<? extends PresentationModel>) childController).setParent(this);
((ContextAware) childController).useSettingsContext(true);
return childController;
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
e.printStackTrace();
}
return null;
final Pane view = loader.load();
content.getChildren().setAll(view);
childController = loader.getController();
((ViewCB<? extends PresentationModel>) childController).setParent(this);
((ContextAware) childController).useSettingsContext(true);
return childController;
}

View File

@ -29,8 +29,6 @@ import io.bitsquare.gui.main.account.content.restrictions.RestrictionsViewCB;
import io.bitsquare.gui.main.account.content.seedwords.SeedWordsViewCB;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -189,18 +187,12 @@ public class AccountSetupViewCB extends ViewCB implements MultiStepNavigation {
@Override
protected Initializable loadView(Navigation.Item navigationItem) {
final ViewLoader loader = new ViewLoader(navigationItem);
try {
final Pane view = loader.load();
content.getChildren().setAll(view);
childController = loader.getController();
((ViewCB<? extends PresentationModel>) childController).setParent(this);
((ContextAware) childController).useSettingsContext(false);
return childController;
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
e.printStackTrace();
}
return null;
final Pane view = loader.load();
content.getChildren().setAll(view);
childController = loader.getController();
((ViewCB<? extends PresentationModel>) childController).setParent(this);
((ContextAware) childController).useSettingsContext(false);
return childController;
}
}

View File

@ -22,8 +22,6 @@ import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.ViewCB;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -121,26 +119,21 @@ public class FundsViewCB extends CachedViewCB {
super.loadView(navigationItem);
final ViewLoader loader = new ViewLoader(navigationItem);
try {
Node view = loader.load();
Tab tab = null;
switch (navigationItem) {
case WITHDRAWAL:
tab = withdrawalTab;
break;
case TRANSACTIONS:
tab = transactionsTab;
break;
}
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
e.printStackTrace();
Node view = loader.load();
Tab tab = null;
switch (navigationItem) {
case WITHDRAWAL:
tab = withdrawalTab;
break;
case TRANSACTIONS:
tab = transactionsTab;
break;
}
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
return childController;
}

View File

@ -23,8 +23,6 @@ import io.bitsquare.gui.ViewCB;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.util.ViewLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -127,29 +125,24 @@ public class PortfolioViewCB extends CachedViewCB {
super.loadView(navigationItem);
final ViewLoader loader = new ViewLoader(navigationItem);
try {
Parent view = loader.load();
Tab tab = null;
switch (navigationItem) {
case OFFERS:
tab = offersTab;
break;
case PENDING_TRADES:
tab = pendingTradesTab;
break;
case CLOSED_TRADES:
tab = closedTradesTab;
break;
}
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
} catch (IOException e) {
log.error("Loading view failed. FxmlUrl = " + navigationItem.getFxmlUrl());
e.printStackTrace();
Parent view = loader.load();
Tab tab = null;
switch (navigationItem) {
case OFFERS:
tab = offersTab;
break;
case PENDING_TRADES:
tab = pendingTradesTab;
break;
case CLOSED_TRADES:
tab = closedTradesTab;
break;
}
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
return childController;
}
}

View File

@ -30,8 +30,6 @@ import io.bitsquare.util.ViewLoader;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import java.io.IOException;
import java.net.URL;
import java.util.List;
@ -169,61 +167,49 @@ public class TradeViewCB extends CachedViewCB implements TradeNavigator {
if (navigationItem == Navigation.Item.OFFER_BOOK && offerBookViewCB == null) {
// Offerbook must not be cached by ViewLoader as we use 2 instances for sell and buy screens.
ViewLoader offerBookLoader = new ViewLoader(navigationItem, false);
try {
final Parent view = offerBookLoader.load();
final Tab tab = new Tab(direction == Direction.BUY ? "Buy Bitcoin" : "Sell Bitcoin");
tab.setClosable(false);
tab.setContent(view);
tabPane.getTabs().add(tab);
offerBookViewCB = offerBookLoader.getController();
offerBookViewCB.setParent(this);
final Parent view = offerBookLoader.load();
final Tab tab = new Tab(direction == Direction.BUY ? "Buy Bitcoin" : "Sell Bitcoin");
tab.setClosable(false);
tab.setContent(view);
tabPane.getTabs().add(tab);
offerBookViewCB = offerBookLoader.getController();
offerBookViewCB.setParent(this);
offerBookViewCB.setDirection(direction);
// offerBookViewCB.setNavigationListener(n -> loadView(n));
offerBookViewCB.setDirection(direction);
// offerBookViewCB.setNavigationListener(n -> loadView(n));
return offerBookViewCB;
} catch (IOException e) {
log.error(e.getMessage());
}
return offerBookViewCB;
}
else if (navigationItem == Navigation.Item.CREATE_OFFER && createOfferViewCB == null) {
// CreateOffer and TakeOffer must not be cached by ViewLoader as we cannot use a view multiple times
// in different graphs
final ViewLoader loader = new ViewLoader(navigationItem, false);
try {
createOfferView = loader.load();
createOfferViewCB = loader.getController();
createOfferViewCB.setParent(this);
createOfferViewCB.initWithData(direction, amount, price);
createOfferViewCB.setCloseListener(this::onCreateOfferViewRemoved);
final Tab tab = new Tab("Create offer");
tab.setContent(createOfferView);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
return createOfferViewCB;
} catch (IOException e) {
log.error(e.getMessage());
}
createOfferView = loader.load();
createOfferViewCB = loader.getController();
createOfferViewCB.setParent(this);
createOfferViewCB.initWithData(direction, amount, price);
createOfferViewCB.setCloseListener(this::onCreateOfferViewRemoved);
final Tab tab = new Tab("Create offer");
tab.setContent(createOfferView);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
return createOfferViewCB;
}
else if (navigationItem == Navigation.Item.TAKE_OFFER && takeOfferViewCB == null &&
offer != null) {
// CreateOffer and TakeOffer must not be cached by ViewLoader as we cannot use a view multiple times
// in different graphs
ViewLoader loader = new ViewLoader(Navigation.Item.TAKE_OFFER, false);
try {
takeOfferView = loader.load();
takeOfferViewCB = loader.getController();
takeOfferViewCB.setParent(this);
takeOfferViewCB.initWithData(direction, amount, offer);
takeOfferViewCB.setCloseListener(this::onCreateOfferViewRemoved);
final Tab tab = new Tab("Take offer");
tab.setContent(takeOfferView);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
return takeOfferViewCB;
} catch (IOException e) {
log.error(e.getMessage());
}
takeOfferView = loader.load();
takeOfferViewCB = loader.getController();
takeOfferViewCB.setParent(this);
takeOfferViewCB.initWithData(direction, amount, offer);
takeOfferViewCB.setCloseListener(this::onCreateOfferViewRemoved);
final Tab tab = new Tab("Take offer");
tab.setContent(takeOfferView);
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
return takeOfferViewCB;
}
return null;
}

View File

@ -22,6 +22,8 @@ import io.bitsquare.locale.BSResources;
import com.google.inject.Injector;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
@ -69,18 +71,21 @@ public class ViewLoader {
}
@SuppressWarnings("unchecked")
public <T> T load() throws java.io.IOException {
public <T> T load() {
if (isCached) {
item = cachedGUIItems.get(url);
log.debug("loaded from cache " + url);
return (T) cachedGUIItems.get(url).view;
}
else {
log.debug("load from disc " + url);
log.debug("load from disc " + url);
try {
T result = loader.load();
item = new Item(result, loader.getController());
cachedGUIItems.put(url, item);
return result;
} catch (IOException e) {
throw new RuntimeException("Failed to load view at " + url, e);
}
}