Merge pull request #162 from stevemyers/master

Added built in dht seed node
This commit is contained in:
Manfred Karrer 2014-10-05 18:59:37 +02:00
commit 0d09b2baf6
13 changed files with 357 additions and 16 deletions

2
.gitignore vendored
View File

@ -2,7 +2,7 @@
/log
/bin
/out
.idea
.idea/*
!.idea/copyright/Bitsquare_Affero_GPLv3.xml
!.idea/copyright/profiles_settings.xml
!.idea/codeStyleSettings.xml

View File

@ -30,6 +30,7 @@ repositories {
dependencies {
compile 'org.bitcoinj:bitcoinj-core:0.12'
compile 'net.tomp2p:tomp2p-all:5.0-Alpha24.805623c-SNAPSHOT'
compile 'com.typesafe.akka:akka-actor_2.10:2.3.4'
compile 'org.slf4j:slf4j-api:1.7.7'
compile 'ch.qos.logback:logback-core:1.1.2'
compile 'ch.qos.logback:logback-classic:1.1.2'

View File

@ -48,6 +48,7 @@ import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import akka.actor.ActorSystem;
import lighthouse.files.AppDirectory;
public class BitSquare extends Application {
@ -118,11 +119,11 @@ public class BitSquare extends Application {
log.error(e.getMessage());
}
// currently there is not SystemTray support for java fx (planned for version 3) so we use the old AWT
AWTSystemTray.createSystemTray(primaryStage);
final Injector injector = Guice.createInjector(new BitSquareModule());
// currently there is not SystemTray support for java fx (planned for version 3) so we use the old AWT
AWTSystemTray.createSystemTray(primaryStage, injector.getInstance(ActorSystem.class));
walletFacade = injector.getInstance(WalletFacade.class);
messageFacade = injector.getInstance(MessageFacade.class);
Profiler.printMsgWithTime("BitSquare: messageFacade, walletFacade created");

View File

@ -18,6 +18,7 @@
package io.bitsquare.di;
import io.bitsquare.BitSquare;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade;
@ -32,9 +33,11 @@ import io.bitsquare.gui.util.validation.FiatValidator;
import io.bitsquare.gui.util.validation.InputValidator;
import io.bitsquare.gui.util.validation.PasswordValidator;
import io.bitsquare.msg.BootstrappedPeerFactory;
import io.bitsquare.msg.DHTSeedService;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.P2PNode;
import io.bitsquare.msg.SeedNodeAddress;
import io.bitsquare.msg.actor.DHTManager;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.TradeManager;
@ -55,6 +58,8 @@ import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import akka.actor.ActorSystem;
public class BitSquareModule extends AbstractModule {
private static final Logger log = LoggerFactory.getLogger(BitSquareModule.class);
@ -95,6 +100,11 @@ public class BitSquareModule extends AbstractModule {
bind(SeedNodeAddress.StaticSeedNodeAddresses.class).annotatedWith(Names.named("defaultSeedNode"))
.toProvider(StaticSeedNodeAddressesProvider.class).asEagerSingleton();
// Actor Related Classes to Inject
bind(ActorSystem.class).toProvider(ActorSystemProvider.class).asEagerSingleton();
bind(DHTSeedService.class);
}
}
@ -155,4 +165,17 @@ class NetworkParametersProvider implements Provider<NetworkParameters> {
}
return result;
}
}
class ActorSystemProvider implements Provider<ActorSystem> {
@Override
public ActorSystem get() {
ActorSystem system = ActorSystem.create(BitSquare.getAppName());
// create top level actors
system.actorOf(DHTManager.getProps(), DHTManager.SEED_NAME);
return system;
}
}

View File

@ -23,6 +23,8 @@ import io.bitsquare.gui.util.ImageUtil;
import java.awt.*;
import java.util.concurrent.TimeoutException;
import javax.swing.*;
import javafx.application.Platform;
@ -31,6 +33,9 @@ import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import akka.actor.ActorSystem;
import scala.concurrent.duration.Duration;
/**
* There is no JavaFX support yet, so we need to use AWT.
* TODO research more
@ -40,10 +45,13 @@ public class AWTSystemTray {
private static boolean isStageVisible = true;
private static MenuItem showGuiItem;
private static Stage stage;
private static ActorSystem actorSystem;
private static TrayIcon trayIcon;
public static void createSystemTray(Stage stage) {
public static void createSystemTray(Stage stage, ActorSystem actorSystem) {
AWTSystemTray.stage = stage;
AWTSystemTray.actorSystem = actorSystem;
if (SystemTray.isSupported()) {
// prevent exiting the app when the last window get closed
Platform.setImplicitExit(false);
@ -82,6 +90,15 @@ public class AWTSystemTray {
});
exitItem.addActionListener(e -> {
systemTray.remove(trayIcon);
actorSystem.shutdown();
try {
actorSystem.awaitTermination(Duration.create(5L, "seconds"));
} catch (Exception ex) {
if (ex instanceof TimeoutException)
log.error("ActorSystem did not shutdown properly.");
else
log.error(ex.getMessage());
}
System.exit(0);
});

View File

@ -21,7 +21,9 @@ import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.UIModel;
import io.bitsquare.gui.util.Profiler;
import io.bitsquare.msg.DHTSeedService;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.actor.event.PeerInitialized;
import io.bitsquare.msg.listeners.BootstrapListener;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.Trade;
@ -47,6 +49,7 @@ class MainModel extends UIModel {
private static final Logger log = LoggerFactory.getLogger(MainModel.class);
private final User user;
private final DHTSeedService dhtSeedService;
private final WalletFacade walletFacade;
private final MessageFacade messageFacade;
private final TradeManager tradeManager;
@ -66,9 +69,10 @@ class MainModel extends UIModel {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private MainModel(User user, WalletFacade walletFacade, MessageFacade messageFacade,
private MainModel(User user, DHTSeedService dhtSeedService, WalletFacade walletFacade, MessageFacade messageFacade,
TradeManager tradeManager, Persistence persistence) {
this.user = user;
this.dhtSeedService = dhtSeedService;
this.walletFacade = walletFacade;
this.messageFacade = messageFacade;
this.tradeManager = tradeManager;
@ -98,20 +102,30 @@ class MainModel extends UIModel {
///////////////////////////////////////////////////////////////////////////////////////////
void initBackend() {
Profiler.printMsgWithTime("MainModel.initFacades");
messageFacade.init(new BootstrapListener() {
@Override
public void onCompleted() {
messageFacadeInited = true;
if (walletFacadeInited) onFacadesInitialised();
}
@Override
public void onFailed(Throwable throwable) {
log.error(throwable.toString());
dhtSeedService.setHandler(m -> {
if (m instanceof PeerInitialized) {
log.debug("dht seed initialized. ");
// init messageFacade after seed node initialized
messageFacade.init(new BootstrapListener() {
@Override
public void onCompleted() {
messageFacadeInited = true;
if (walletFacadeInited) onFacadesInitialised();
}
@Override
public void onFailed(Throwable throwable) {
log.error(throwable.toString());
}
});
}
});
dhtSeedService.initializePeer();
Profiler.printMsgWithTime("MainModel.initFacades");
walletFacade.initialize(() -> {
walletFacadeInited = true;
if (messageFacadeInited)

View File

@ -0,0 +1,30 @@
package io.bitsquare.msg;
import io.bitsquare.msg.actor.DHTManager;
import io.bitsquare.msg.actor.command.InitializePeer;
import io.bitsquare.util.ActorService;
import com.google.inject.Inject;
import java.util.List;
import net.tomp2p.peers.Number160;
import akka.actor.ActorSystem;
public class DHTSeedService extends ActorService {
private static final List<SeedNodeAddress.StaticSeedNodeAddresses> staticSedNodeAddresses = SeedNodeAddress
.StaticSeedNodeAddresses.getAllSeedNodeAddresses();
@Inject
public DHTSeedService(ActorSystem system) {
super(system, "/user/" + DHTManager.SEED_NAME);
}
public void initializePeer() {
// TODO hard coded seed peer config for now, should read from config properties file
send(new InitializePeer(new Number160(5001), 5001, null));
}
}

View File

@ -0,0 +1,69 @@
package io.bitsquare.msg.actor;
import io.bitsquare.msg.actor.command.InitializePeer;
import io.bitsquare.msg.actor.event.PeerInitialized;
import net.tomp2p.connection.Ports;
import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.futures.FutureBootstrap;
import net.tomp2p.p2p.Peer;
import net.tomp2p.p2p.PeerBuilder;
import akka.actor.AbstractActor;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.pf.ReceiveBuilder;
public class DHTManager extends AbstractActor {
public static final String PEER_NAME = "peerDhtManager";
public static final String SEED_NAME = "seedDhtManager";
private final LoggingAdapter log = Logging.getLogger(context().system(), this);
// TODO move into app setup
// timeout in ms
private final Long bootstrapTimeout = 10000L;
public static Props getProps() {
return Props.create(DHTManager.class);
}
private Peer peer;
private PeerDHT peerDHT;
public DHTManager() {
receive(ReceiveBuilder
.match(InitializePeer.class, ip -> {
log.debug("Received message: {}", ip);
peer = new PeerBuilder(ip.getPeerId())
.ports(ip.getPort() != null ? ip.getPort() : new Ports().tcpPort()).start();
peerDHT = new PeerBuilderDHT(peer).start();
// TODO add code to discover non-local peers
// FutureDiscover futureDiscover = peer.discover().peerAddress(bootstrapPeers.).start();
// futureDiscover.awaitUninterruptibly();
if (ip.getBootstrapPeers() != null) {
FutureBootstrap futureBootstrap = peer.bootstrap()
.bootstrapTo(ip.getBootstrapPeers()).start();
futureBootstrap.awaitUninterruptibly(bootstrapTimeout);
}
sender().tell(new PeerInitialized(peer.peerID()), self());
})
.matchAny(o -> log.info("received unknown message")).build()
);
}
@Override
public void postStop() throws Exception {
log.debug("postStop");
peerDHT.shutdown();
super.postStop();
}
}

View File

@ -0,0 +1,35 @@
package io.bitsquare.msg.actor.command;
import java.util.Collection;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
/**
* <p>Command to initialize TomP2P Peer.</p>
*/
public class InitializePeer {
private final Number160 peerId;
private final Integer port;
private final Collection<PeerAddress> bootstrapPeers;
public InitializePeer(Number160 peerId, Integer port, Collection<PeerAddress> bootstrapPeers) {
this.peerId = peerId;
this.port = port;
this.bootstrapPeers = bootstrapPeers;
}
public Number160 getPeerId() {
return peerId;
}
public Integer getPort() {
return port;
}
public Collection<PeerAddress> getBootstrapPeers() {
return bootstrapPeers;
}
}

View File

@ -0,0 +1,21 @@
package io.bitsquare.msg.actor.event;
import net.tomp2p.peers.Number160;
/**
* <p>TomP2P Peer Initialized event.</p>
*/
public class PeerInitialized {
private final Number160 peerId;
public PeerInitialized(Number160 peerId) {
this.peerId = peerId;
}
public Number160 getPeerId() {
return peerId;
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.util;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.actor.Inbox;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import com.sun.glass.ui.Application;
import scala.concurrent.duration.FiniteDuration;
public abstract class ActorService extends Service<String> {
private final LoggingAdapter log;
private final ActorSystem system;
private final Inbox inbox;
private ActorSelection actor;
private MessageHandler handler;
protected ActorService(ActorSystem system, String actorPath) {
this.log = Logging.getLogger(system, this);
this.system = system;
this.inbox = Inbox.create(system);
this.actor = system.actorSelection(actorPath);
log.debug(actor.pathString());
this.start();
}
public void setHandler(MessageHandler handler) {
this.handler = handler;
}
public void send(Object command) {
if (actor != null) {
actor.tell(command, inbox.getRef());
}
}
protected Task<String> createTask() {
return new Task<String>() {
protected String call() throws Exception {
while (!isCancelled()) {
if (inbox != null) {
try {
Object result = inbox.receive(FiniteDuration.create(1l, "minute"));
if (result != null) {
System.out.println(result.toString());
if (handler != null) {
Application.invokeLater(new Runnable() {
@Override
public void run() {
handler.handle(result);
}
});
}
}
} catch (Exception e) {
//System.out.println(e.toString());
}
}
}
return null;
}
};
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.util;
public interface MessageHandler {
public void handle(Object message);
}

View File

@ -0,0 +1,17 @@
akka {
# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs
# to STDOUT)
#loggers = ["akka.event.slf4j.Slf4jLogger"]
# Log level used by the configured loggers (see "loggers") as soon
# as they have been started; before that, see "stdout-loglevel"
# Options: OFF, ERROR, WARNING, INFO, DEBUG
loglevel = "DEBUG"
# Log level for the very basic logger activated during ActorSystem startup.
# This logger prints the log messages to stdout (System.out).
# Options: OFF, ERROR, WARNING, INFO, DEBUG
stdout-loglevel = "DEBUG"
}