diff --git a/src/main/java/io/bitsquare/gui/main/trade/orderbook/OrderBookListItem.java b/src/main/java/io/bitsquare/gui/main/trade/orderbook/OrderBookListItem.java index 078ceee9b9..4134237769 100644 --- a/src/main/java/io/bitsquare/gui/main/trade/orderbook/OrderBookListItem.java +++ b/src/main/java/io/bitsquare/gui/main/trade/orderbook/OrderBookListItem.java @@ -23,7 +23,7 @@ import io.bitsquare.trade.Offer; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -class OrderBookListItem { +public class OrderBookListItem { private final Offer offer; private final ObjectProperty bankAccountCountry = new SimpleObjectProperty<>(); @@ -32,7 +32,7 @@ class OrderBookListItem { // Constructor /////////////////////////////////////////////////////////////////////////////////////////// - OrderBookListItem(Offer offer, Country bankAccountCountry) { + public OrderBookListItem(Offer offer, Country bankAccountCountry) { this.offer = offer; setBankAccountCountry(bankAccountCountry); } @@ -51,7 +51,7 @@ class OrderBookListItem { // Getters /////////////////////////////////////////////////////////////////////////////////////////// - Offer getOffer() { + public Offer getOffer() { return offer; } diff --git a/src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java b/src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java index 8214207a7a..fc56855367 100644 --- a/src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java +++ b/src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java @@ -35,6 +35,10 @@ import javax.annotation.concurrent.Immutable; import javax.inject.Inject; +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + import net.tomp2p.connection.Ports; import net.tomp2p.dht.PeerBuilderDHT; import net.tomp2p.dht.PeerDHT; @@ -76,6 +80,7 @@ public class BootstrappedPeerFactory { private final Persistence persistence; private final SettableFuture settableFuture = SettableFuture.create(); + public final StringProperty connectionState = new SimpleStringProperty(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -194,6 +199,9 @@ public class BootstrappedPeerFactory { log.debug("We are not behind a NAT and reachable to other peers: My address visible to the " + "outside is " + futureDiscover.peerAddress()); requestBootstrapPeerMap(); + setConnectionState("We are not behind a NAT and reachable to other peers: My address visible to " + + "the " + + "outside is " + futureDiscover.peerAddress()); settableFuture.set(peerDHT); persistence.write(ref, "lastSuccessfulBootstrap", "default"); @@ -203,6 +211,10 @@ public class BootstrappedPeerFactory { log.warn("We are probably behind a NAT and not reachable to other peers. We try port forwarding " + "as next step."); + setConnectionState("We are probably behind a NAT and not reachable to other peers. We try port " + + "forwarding " + + "as next step."); + bootstrapWithPortForwarding(peerDHT, futureDiscover); } } @@ -230,6 +242,8 @@ public class BootstrappedPeerFactory { log.debug("Port forwarding was successful. My address visible to the outside is " + futureNAT.peerAddress()); requestBootstrapPeerMap(); + setConnectionState("Port forwarding was successful. My address visible to the outside is " + + futureNAT.peerAddress()); settableFuture.set(peerDHT); persistence.write(ref, "lastSuccessfulBootstrap", "portForwarding"); @@ -238,6 +252,8 @@ public class BootstrappedPeerFactory { log.warn("Port forwarding has failed. Reason: " + futureNAT.failedReason()); log.warn("We try to use a relay as next step."); + setConnectionState("We try to use a relay as next step."); + bootstrapWithRelay(peerDHT, nodeBehindNat); } } @@ -324,6 +340,8 @@ public class BootstrappedPeerFactory { if (future.isSuccess()) { log.debug("Final bootstrap was successful. bootstrapTo = " + futureBootstrap2.bootstrapTo()); requestBootstrapPeerMap(); + setConnectionState("Final bootstrap was successful. bootstrapTo = " + futureBootstrap2 + .bootstrapTo()); settableFuture.set(peerDHT); persistence.write(ref, "lastSuccessfulBootstrap", "relay"); @@ -348,4 +366,8 @@ public class BootstrappedPeerFactory { private void requestBootstrapPeerMap() { log.debug("getBootstrapPeerMap"); } + + private void setConnectionState(String state) { + Platform.runLater(() -> connectionState.set(state)); + } } diff --git a/src/main/java/io/bitsquare/msg/MessageFacade.java b/src/main/java/io/bitsquare/msg/MessageFacade.java index 8b795aa9d0..4cfe1e06cf 100644 --- a/src/main/java/io/bitsquare/msg/MessageFacade.java +++ b/src/main/java/io/bitsquare/msg/MessageFacade.java @@ -177,6 +177,7 @@ public class MessageFacade implements MessageBroker { try { Object offerDataObject = offerData.object(); if (offerDataObject instanceof Offer) { + log.error("Added offer to DHT with ID: " + ((Offer) offerDataObject).getId()); listener.onOfferAdded((Offer) offerDataObject); } } catch (ClassNotFoundException | IOException e) { diff --git a/src/test/java/io/bitsquare/msg/dhttest/DHTTestController.java b/src/test/java/io/bitsquare/msg/dhttest/DHTTestController.java new file mode 100644 index 0000000000..a367ec0008 --- /dev/null +++ b/src/test/java/io/bitsquare/msg/dhttest/DHTTestController.java @@ -0,0 +1,182 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.msg.dhttest; + +import io.bitsquare.bank.BankAccountType; +import io.bitsquare.gui.main.trade.orderbook.OrderBookListItem; +import io.bitsquare.locale.CountryUtil; +import io.bitsquare.msg.BootstrappedPeerFactory; +import io.bitsquare.msg.MessageFacade; +import io.bitsquare.msg.listeners.AddOfferListener; +import io.bitsquare.msg.listeners.BootstrapListener; +import io.bitsquare.msg.listeners.OrderBookListener; +import io.bitsquare.trade.Direction; +import io.bitsquare.trade.Offer; +import io.bitsquare.user.User; + +import org.bitcoinj.core.Coin; + +import java.net.URL; + +import java.util.ArrayList; +import java.util.Currency; +import java.util.List; +import java.util.ResourceBundle; + +import javax.inject.Inject; + +import javafx.beans.property.ReadOnlyObjectWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.util.Callback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DHTTestController implements Initializable { + private static final Logger log = LoggerFactory.getLogger(DHTTestController.class); + + private final MessageFacade messageFacade; + private BootstrappedPeerFactory bootstrappedPeerFactory; + private User user; + private final ObservableList orderBookListItems = FXCollections.observableArrayList(); + + @FXML TableView table; + @FXML TextArea stateLabel; + @FXML TextField toSaveTextField; + @FXML TableColumn idColumn; + + @Inject + private DHTTestController(MessageFacade messageFacade, User user, + BootstrappedPeerFactory bootstrappedPeerFactory) { + this.user = user; + this.messageFacade = messageFacade; + this.bootstrappedPeerFactory = bootstrappedPeerFactory; + + user.applyPersistedUser(null); + } + + @Override + public void initialize(URL url, ResourceBundle rb) { + messageFacade.init(new BootstrapListener() { + @Override + public void onCompleted() { + onMessageFacadeInitialised(); + } + + @Override + public void onFailed(Throwable throwable) { + log.error(throwable.toString()); + } + }); + + messageFacade.addOrderBookListener(new OrderBookListener() { + @Override + public void onOfferAdded(Offer offer) { + log.debug("offer added " + offer.getId()); + } + + @Override + public void onOffersReceived(List offers) { + //TODO use deltas instead replacing the whole list + orderBookListItems.clear(); + offers.stream().forEach(offer -> { + if (offer != null) { + orderBookListItems.add(new OrderBookListItem(offer, CountryUtil.getDefaultCountry())); + } + }); + } + + @Override + public void onOfferRemoved(Offer offer) { + orderBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId())); + } + }); + + setIDColumnCellFactory(); + table.setItems(orderBookListItems); + + bootstrappedPeerFactory.connectionState.addListener((ov, oldValue, newValue) -> { + stateLabel.setText(newValue); + }); + } + + @FXML + public void onAddOffer() { + Offer offer = new Offer(toSaveTextField.getText(), + user.getMessagePublicKey(), + Direction.BUY, + 500, + Coin.COIN, + Coin.COIN, + BankAccountType.SEPA, + Currency.getInstance("EUR"), + CountryUtil.getDefaultCountry(), + "bankAccountUID", + new ArrayList<>(), + 10, + new ArrayList<>(), + new ArrayList<>()); + + messageFacade.addOffer(offer, new AddOfferListener() { + @Override + public void onComplete() { + log.debug("onAddOffer onComplete"); + } + + @Override + public void onFailed(String reason, Throwable throwable) { + log.debug("onAddOffer onFailed"); + } + }); + } + + public void onGetOffers() { + log.debug("onLoad"); + messageFacade.getOffers("EUR"); + } + + private void onMessageFacadeInitialised() { + log.debug("onMessageFacadeInitialised"); + } + + private void setIDColumnCellFactory() { + idColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + idColumn.setCellFactory( + new Callback, TableCell>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell() { + @Override + public void updateItem(final OrderBookListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && item.getOffer() != null && item.getOffer().getAmount() != null) + setText(item.getOffer().getId()); + } + }; + } + }); + } + +} + diff --git a/src/test/java/io/bitsquare/msg/dhttest/DHTTestRunner.java b/src/test/java/io/bitsquare/msg/dhttest/DHTTestRunner.java new file mode 100644 index 0000000000..6c1131aec9 --- /dev/null +++ b/src/test/java/io/bitsquare/msg/dhttest/DHTTestRunner.java @@ -0,0 +1,102 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bitsquare is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bitsquare. If not, see . + */ + +package io.bitsquare.msg.dhttest; + +import io.bitsquare.di.BitSquareModule; +import io.bitsquare.util.ViewLoader; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +import java.io.IOException; + +import java.net.MalformedURLException; +import java.net.URL; + +import javafx.application.Application; +import javafx.scene.*; +import javafx.scene.input.*; +import javafx.scene.layout.*; +import javafx.stage.Stage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DHTTestRunner extends Application { + private static final Logger log = LoggerFactory.getLogger(DHTTestRunner.class); + private Scene scene; + private Parent view; + private Pane pane; + private boolean devTest = true; + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) throws IOException { + Injector injector = Guice.createInjector(new BitSquareModule()); + ViewLoader.setInjector(injector); + + pane = new StackPane(); + scene = new Scene(pane, 1000, 630); + scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+S"), this::loadMainWindow); + loadMainWindow(); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public void loadMainWindow() { + log.debug("re load"); + pane.getChildren().removeAll(); + ViewLoader loader = new ViewLoader( + getUrl("/io/bitsquare/msg/dhttest/DHTTestView.fxml") + , false); + + try { + view = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + pane.getChildren().setAll(view); + refreshStylesheets(); + } + + private void refreshStylesheets() { + scene.getStylesheets().clear(); + scene.getStylesheets().setAll(getUrl("/io/bitsquare/gui/bitsquare.css").toExternalForm()); + } + + private URL getUrl(String subPath) { + if (devTest) { + try { + // load from file system location to make a reload possible. makes dev process easier with hot reload + return new URL("file:///Users/mk/Documents/_intellij/bitsquare/src/test/java" + subPath); + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + else { + return getClass().getResource(subPath); + } + } + + +} diff --git a/src/test/java/io/bitsquare/msg/dhttest/DHTTestView.fxml b/src/test/java/io/bitsquare/msg/dhttest/DHTTestView.fxml new file mode 100644 index 0000000000..c2c8883113 --- /dev/null +++ b/src/test/java/io/bitsquare/msg/dhttest/DHTTestView.fxml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + +