Expose network information to GUI cleanly

This commit introduces io.bitsquare.network.ClientNode--an interface
whose name and structure will surely change--as a simplistic abstraction
over TomP2PNode that allows for exposing information to the "Network"
tab of the Preferences section of the GUI without actually requiring the
injection of TomP2PNode and other tomp2p internals into the GUI layer.

Changes to 'network' and 'msg' packages:
----------------------------------------

 - Move ConnectionType enum from test into main tree, and expose
   ClientNode#getConnectionType.

 - Both ClientNode and TomP2P are now available for injection. Both
   types are bound to the same TomP2P singleton instance. Note
   especially how NetworkPreferencesViewCB now receives a ClientNode
   instead of a TomP2PNode.

 - Restore package-private visibility to BootstrappedPeerFactory

 - Remove no longer necessary TomP2PNode#getPeerDHT

 - Expose getter for BootstrappedPeerFactory#bootstrapState

Changes to 'gui' package:
-------------------------

 - NetworkPreferencesViewCB has been simplified. All no-op methods have
   been removed, and the class now simply implements JavaFX's
   Initializable interface as opposed to Bitsquare's own ViewCB
   hierarchy, because the latter is not actually necessary (no caching
   is required for the data handled by this controller, etc.

 - In order to make the above possible, PreferencesViewCB now tolerates
   adding non-ViewCB child controllers.

 - NetworkPreferencesPM has been removed (perhaps temporarily), in an
   experiment to see "just how simple" CB controller classes can be.

 - Text fields in NetworkPreferencesView have been renamed.

Notes:
------

The data that now shows up in the "Network" tab is no longer formatted
as it once was; values are essentially nothing more than their #toString
representations. Again, this can be tweaked further, but leaving things
in this raw state provides an opportunity to discuss the current
presentation model approach, ViewCB hierarchy, etc.
This commit is contained in:
Chris Beams 2014-11-12 18:39:55 +01:00
parent d657763596
commit c7f7b37572
No known key found for this signature in database
GPG Key ID: 3D214F8F5BC5ED73
10 changed files with 123 additions and 202 deletions

View File

@ -141,7 +141,8 @@ public class PreferencesViewCB extends CachedViewCB {
tab.setContent(view);
((TabPane) root).getSelectionModel().select(tab);
Initializable childController = loader.getController();
((ViewCB) childController).setParent(this);
if (childController instanceof ViewCB)
((ViewCB) childController).setParent(this);
return childController;
}

View File

@ -1,136 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.main.preferences.network;
import io.bitsquare.BitsquareException;
import io.bitsquare.gui.PresentationModel;
import io.bitsquare.msg.tomp2p.BootstrappedPeerFactory;
import io.bitsquare.msg.tomp2p.TomP2PNode;
import io.bitsquare.network.BootstrapState;
import io.bitsquare.network.Node;
import org.bitcoinj.core.NetworkParameters;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import net.tomp2p.peers.PeerSocketAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetworkPreferencesPM extends PresentationModel {
private static final Logger log = LoggerFactory.getLogger(NetworkPreferencesPM.class);
final String bitcoinNetworkType;
final String p2pNetworkConnection;
final String p2pNetworkAddress;
final String bootstrapAddress;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
NetworkPreferencesPM(NetworkParameters networkParameters,
BootstrappedPeerFactory bootstrappedPeerFactory,
TomP2PNode tomP2PNode,
@Named(BootstrappedPeerFactory.BOOTSTRAP_NODE_KEY) Node bootstrapNode) {
switch (networkParameters.getId()) {
case NetworkParameters.ID_REGTEST:
bitcoinNetworkType = "Regtest";
break;
case NetworkParameters.ID_TESTNET:
bitcoinNetworkType = "Testnet";
break;
case NetworkParameters.ID_MAINNET:
bitcoinNetworkType = "Mainnet";
break;
default:
bitcoinNetworkType = "Undefined";
throw new BitsquareException("Invalid networkParameters " + networkParameters.getId());
}
PeerSocketAddress socketAddress = tomP2PNode.getPeerDHT().peerAddress().peerSocketAddress();
p2pNetworkAddress = "IP: " + socketAddress.inetAddress().getHostAddress()
+ ", TCP port: " + socketAddress.tcpPort()
+ ", UDP port: " + socketAddress.udpPort();
bootstrapAddress = "ID: " + bootstrapNode.getName()
+ ", IP: " + bootstrapNode.getIp()
+ ", Port: " + bootstrapNode.getPortAsString();
BootstrapState state = bootstrappedPeerFactory.bootstrapState.get();
if (state == BootstrapState.DIRECT_SUCCESS)
p2pNetworkConnection = "Direct connection";
else if (state == BootstrapState.NAT_SUCCESS)
p2pNetworkConnection = "Connected with automatic port forwarding";
else if (state == BootstrapState.RELAY_SUCCESS)
p2pNetworkConnection = "Relayed by other peers";
else
throw new BitsquareException("Invalid BootstrapState " + state);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("EmptyMethod")
@Override
public void initialize() {
super.initialize();
}
@SuppressWarnings("EmptyMethod")
@Override
public void activate() {
super.activate();
}
@SuppressWarnings("EmptyMethod")
@Override
public void deactivate() {
super.deactivate();
}
@SuppressWarnings("EmptyMethod")
@Override
public void terminate() {
super.terminate();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
}

View File

@ -38,7 +38,7 @@
<Insets top="10"/>
</GridPane.margin>
</Label>
<TextField fx:id="bitcoinNetworkType" GridPane.rowIndex="0" GridPane.columnIndex="1"
<TextField fx:id="bitcoinNetwork" GridPane.rowIndex="0" GridPane.columnIndex="1"
mouseTransparent="true" editable="false" focusTraversable="false">
<GridPane.margin>
<Insets top="10"/>
@ -46,11 +46,11 @@
</TextField>
<Label text="P2P network connection:" GridPane.rowIndex="1"/>
<TextField fx:id="p2pNetworkConnection" GridPane.rowIndex="1" GridPane.columnIndex="1"
<TextField fx:id="connectionType" GridPane.rowIndex="1" GridPane.columnIndex="1"
mouseTransparent="true" editable="false" focusTraversable="false"/>
<Label text="My external visible P2P network address:" GridPane.rowIndex="2"/>
<TextField fx:id="p2pNetworkAddress" GridPane.rowIndex="2" GridPane.columnIndex="1"
<TextField fx:id="nodeAddress" GridPane.rowIndex="2" GridPane.columnIndex="1"
mouseTransparent="true" editable="false" focusTraversable="false"/>
<Label text="P2P bootstrap node address:" GridPane.rowIndex="3">
@ -58,7 +58,7 @@
<Insets bottom="-15"/>
</GridPane.margin>
</Label>
<TextField fx:id="bootstrapAddress" GridPane.rowIndex="3" GridPane.columnIndex="1"
<TextField fx:id="bootstrapNodeAddress" GridPane.rowIndex="3" GridPane.columnIndex="1"
mouseTransparent="true" editable="false" focusTraversable="false">
<GridPane.margin>
<Insets bottom="-15"/>

View File

@ -17,7 +17,9 @@
package io.bitsquare.gui.main.preferences.network;
import io.bitsquare.gui.CachedViewCB;
import io.bitsquare.network.ClientNode;
import org.bitcoinj.core.NetworkParameters;
import java.net.URL;
@ -26,62 +28,28 @@ import java.util.ResourceBundle;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetworkPreferencesViewCB implements Initializable {
/**
* This UI is not cached as it is normally only needed once.
*/
public class NetworkPreferencesViewCB extends CachedViewCB<NetworkPreferencesPM> {
private final NetworkParameters networkParameters;
private final ClientNode clientNode;
private static final Logger log = LoggerFactory.getLogger(NetworkPreferencesViewCB.class);
@FXML TextField bitcoinNetworkType, p2pNetworkConnection, p2pNetworkAddress, bootstrapAddress;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@FXML TextField bitcoinNetwork, connectionType, nodeAddress, bootstrapNodeAddress;
@Inject
private NetworkPreferencesViewCB(NetworkPreferencesPM presentationModel) {
super(presentationModel);
public NetworkPreferencesViewCB(NetworkParameters networkParameters, ClientNode clientNode) {
this.networkParameters = networkParameters;
this.clientNode = clientNode;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("EmptyMethod")
@Override
public void initialize(URL url, ResourceBundle rb) {
super.initialize(url, rb);
bitcoinNetwork.setText(networkParameters.getId());
nodeAddress.setText(clientNode.getAddress().toString());
bootstrapNodeAddress.setText(clientNode.getBootstrapNodeAddress().toString());
connectionType.setText(clientNode.getConnectionType().toString());
}
@Override
public void activate() {
super.activate();
bitcoinNetworkType.setText(presentationModel.bitcoinNetworkType);
p2pNetworkConnection.setText(presentationModel.p2pNetworkConnection);
p2pNetworkAddress.setText(presentationModel.p2pNetworkAddress);
bootstrapAddress.setText(presentationModel.bootstrapAddress);
}
@SuppressWarnings("EmptyMethod")
@Override
public void deactivate() {
super.deactivate();
}
@SuppressWarnings("EmptyMethod")
@Override
public void terminate() {
super.terminate();
}
}

View File

@ -70,10 +70,10 @@ import org.slf4j.LoggerFactory;
/**
* Creates a DHT peer and bootstraps to the network via a bootstrap node
*/
public class BootstrappedPeerFactory {
class BootstrappedPeerFactory {
private static final Logger log = LoggerFactory.getLogger(BootstrappedPeerFactory.class);
public static final String BOOTSTRAP_NODE_KEY = "bootstrapNode";
static final String BOOTSTRAP_NODE_KEY = "bootstrapNode";
static final String NETWORK_INTERFACE_KEY = "interface";
static final String NETWORK_INTERFACE_UNSPECIFIED = "<unspecified>";
@ -83,7 +83,8 @@ public class BootstrappedPeerFactory {
private final Persistence persistence;
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
public final ObjectProperty<BootstrapState> bootstrapState = new SimpleObjectProperty<>();
private final ObjectProperty<BootstrapState> bootstrapState = new SimpleObjectProperty<>();
private Peer peer;
private PeerDHT peerDHT;
@ -333,6 +334,14 @@ public class BootstrappedPeerFactory {
}
}
public Node getBootstrapNode() {
return bootstrapNode;
}
public ObjectProperty<BootstrapState> getBootstrapState() {
return bootstrapState;
}
private void setState(BootstrapState bootstrapState, String message) {
setState(bootstrapState, message, true);
}

View File

@ -20,10 +20,13 @@ package io.bitsquare.msg.tomp2p;
import io.bitsquare.msg.MessageModule;
import io.bitsquare.msg.MessageService;
import io.bitsquare.network.BootstrapNodes;
import io.bitsquare.network.ClientNode;
import io.bitsquare.network.Node;
import com.google.inject.name.Names;
import javax.inject.Singleton;
import org.springframework.core.env.Environment;
import static io.bitsquare.msg.tomp2p.BootstrappedPeerFactory.*;
@ -43,7 +46,8 @@ public class TomP2PMessageModule extends MessageModule {
protected void doConfigure() {
bind(int.class).annotatedWith(Names.named(Node.PORT_KEY)).toInstance(
env.getProperty(Node.PORT_KEY, Integer.class, Node.DEFAULT_PORT));
bind(TomP2PNode.class).asEagerSingleton();
bind(TomP2PNode.class).in(Singleton.class);
bind(ClientNode.class).to(TomP2PNode.class);
bind(Node.class).annotatedWith(Names.named(BOOTSTRAP_NODE_KEY)).toInstance(
Node.at(

View File

@ -17,8 +17,12 @@
package io.bitsquare.msg.tomp2p;
import io.bitsquare.BitsquareException;
import io.bitsquare.msg.MessageBroker;
import io.bitsquare.msg.listeners.BootstrapListener;
import io.bitsquare.network.BootstrapState;
import io.bitsquare.network.ClientNode;
import io.bitsquare.network.ConnectionType;
import io.bitsquare.network.Node;
import io.bitsquare.network.tomp2p.TomP2PPeer;
@ -53,6 +57,7 @@ import net.tomp2p.futures.FutureDirect;
import net.tomp2p.futures.FuturePeerConnection;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.peers.PeerSocketAddress;
import net.tomp2p.storage.Data;
import net.tomp2p.utils.Utils;
@ -69,7 +74,7 @@ import static io.bitsquare.util.tomp2p.BaseFutureUtil.isSuccess;
* This class is offering generic functionality of TomP2P needed for Bitsquare, like data and domain protection.
* It does not handle any domain aspects of Bitsquare.
*/
public class TomP2PNode {
public class TomP2PNode implements ClientNode {
private static final Logger log = LoggerFactory.getLogger(TomP2PNode.class);
private KeyPair keyPair;
@ -134,7 +139,7 @@ public class TomP2PNode {
}
});
bootstrappedPeerFactory.bootstrapState.addListener((ov, oldValue, newValue) ->
bootstrappedPeerFactory.getBootstrapState().addListener((ov, oldValue, newValue) ->
bootstrapListener.onBootstrapStateChanged(newValue));
}
@ -144,10 +149,6 @@ public class TomP2PNode {
peerDHT.peer().shutdown();
}
public PeerDHT getPeerDHT() {
return peerDHT;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Generic DHT methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -380,4 +381,33 @@ public class TomP2PNode {
log.debug("storePeerAddress " + peerDHT.peerAddress().toString());
return putDomainProtectedData(locationKey, data);
}
@Override
public ConnectionType getConnectionType() {
BootstrapState bootstrapState = bootstrappedPeerFactory.getBootstrapState().get();
switch (bootstrapState) {
case DIRECT_SUCCESS:
return ConnectionType.DIRECT;
case NAT_SUCCESS:
return ConnectionType.NAT;
case RELAY_SUCCESS:
return ConnectionType.RELAY;
default:
throw new BitsquareException("Invalid bootstrap state: %s", bootstrapState);
}
}
@Override
public Node getAddress() {
PeerSocketAddress socketAddress = peerDHT.peerAddress().peerSocketAddress();
return Node.at(
peerDHT.peerID().toString(),
socketAddress.inetAddress().getHostAddress(),
socketAddress.tcpPort());
}
@Override
public Node getBootstrapNodeAddress() {
return bootstrappedPeerFactory.getBootstrapNode();
}
}

View File

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.network;
public interface ClientNode {
ConnectionType getConnectionType();
Node getAddress();
Node getBootstrapNodeAddress();
}

View File

@ -0,0 +1,22 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.network;
public enum ConnectionType {
UNKNOWN, DIRECT, NAT, RELAY
}

View File

@ -18,6 +18,7 @@
package io.bitsquare.msg;
import io.bitsquare.network.BootstrapNodes;
import io.bitsquare.network.ConnectionType;
import io.bitsquare.network.Node;
import io.bitsquare.util.Repeat;
import io.bitsquare.util.RepeatRule;
@ -80,10 +81,6 @@ import static org.junit.Assert.*;
public class TomP2PTests {
private static final Logger log = LoggerFactory.getLogger(TomP2PTests.class);
private enum ConnectionType {
UNKNOWN, DIRECT, NAT, RELAY
}
// If you want to test in one specific connection mode define it directly, otherwise use UNKNOWN
private static final ConnectionType FORCED_CONNECTION_TYPE = ConnectionType.DIRECT;