package se.chalmers.gdcn.control; import net.tomp2p.futures.BaseFutureAdapter; import net.tomp2p.futures.FutureBootstrap; import net.tomp2p.futures.FutureDHT; import net.tomp2p.futures.FutureDiscover; import net.tomp2p.p2p.Peer; import net.tomp2p.p2p.PeerMaker; import net.tomp2p.p2p.builder.BootstrapBuilder; import net.tomp2p.p2p.builder.DiscoverBuilder; import net.tomp2p.peers.Number160; import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerMapChangeListener; import net.tomp2p.storage.Data; import se.chalmers.gdcn.deceitful.*; import se.chalmers.gdcn.communicationToUI.*; import se.chalmers.gdcn.communicationToUI.Operation.OperationBuilder; import se.chalmers.gdcn.files.DataFilesManager; import se.chalmers.gdcn.network.TaskPasser; import se.chalmers.gdcn.replica.ReplicaManager; import se.chalmers.gdcn.replica.ReplicaManager.ReplicaID; import se.chalmers.gdcn.taskbuilder.communicationToClient.TaskListener; import se.chalmers.gdcn.taskbuilder.fileManagement.Install; import java.beans.PropertyChangeListener; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.List; import java.util.Map; /** * Created by Leif on 2014-02-17 */ public class PeerOwner implements se.chalmers.gdcn.communicationToUI.ClientInterface { //Peer implemented by TomP2P private Peer peer = null; private TaskPasser taskPasser = null; private DataFilesManager dataFilesManager; private final String testPath = "TEST"; //Listener used by UI to react to results from commands private final TaskListener taskListener = new TaskListener() { @Override public void taskFinished(String taskName) { notifier.fireOperationFinished(CommandWord.WORK, new OperationBuilder<String>(true).setKey(taskName).create()); } @Override public void taskFailed(String taskName, String reason) { notifier.fireOperationFinished(CommandWord.WORK, new OperationBuilder<String>(false).setKey(taskName).create()); } }; private final TaskManager taskManager = new TaskManager(taskListener, this); private final OperationFinishedSupport notifier = new OperationFinishedSupport(this); @Override public void addListener(PropertyChangeListener listener){ notifier.addListener(listener); } @Override public void removeListener(PropertyChangeListener listener){ notifier.removeListener(listener); } @Override public void start(int port){ dataFilesManager = new DataFilesManager(); startInitiate(port); } /** * Only used by tests. * * */ public void testStart(int port){ dataFilesManager = new DataFilesManager(testPath, port + ""); startInitiate(port); } @Override public void stop(){ //If it can't stop, Will not be any effects if(peer == null || peer.isShutdown()){ notifier.fireOperationFinished(CommandWord.STOP, new OperationBuilder(false).setErrorCode(ErrorCode.NOT_CONNECTED).create()); } else { peer.shutdown(); taskPasser.stopTimer(); notifier.fireOperationFinished(CommandWord.STOP, new OperationBuilder(true).create()); } } @Override public void bootstrap() { List<String[]> bsn = dataFilesManager.getBootstrapNodes(); if(bsn.size() == 0){ System.out.println("No known nodes to bootstrap to."); //TODO attempt default bootstrap! } for(String[] s : bsn) { bootstrap(s[0], Integer.parseInt(s[1])); System.out.println("Attempt bootstrap to "+s[0]+" : "+s[1]); if(getNeighbours().size() > 0) { return; } } } @Override public void bootstrap(final String host, final int port){ try { final InetAddress inetAddress = InetAddress.getByName(host); DiscoverBuilder discoverBuilder = peer.discover().setInetAddress(inetAddress).setPorts(port); FutureDiscover futureDiscover = discoverBuilder.start(); futureDiscover.addListener(new BaseFutureAdapter<FutureDiscover>() { @Override public void operationComplete(FutureDiscover future) throws Exception { if(!future.isSuccess()){ notifier.fireOperationFinished(CommandWord.BOOTSTRAP, new OperationBuilder<InetAddress>(false).setErrorCode(ErrorCode.DISCOVER_FAILURE).create()); return; } BootstrapBuilder bootstrapBuilder = peer.bootstrap().setInetAddress(inetAddress).setPorts(port); FutureBootstrap futureBootstrap = bootstrapBuilder.start(); futureBootstrap.addListener(new BaseFutureAdapter<FutureBootstrap>() { @Override public void operationComplete(FutureBootstrap future) throws Exception { if(!future.isSuccess()){ notifier.fireOperationFinished(CommandWord.BOOTSTRAP, new OperationBuilder<InetAddress>(false).setResult(inetAddress).setErrorCode(ErrorCode.BOOTSTRAP_FAILURE).create()); return; } notifier.fireOperationFinished(CommandWord.BOOTSTRAP, new OperationBuilder<InetAddress>(true).setResult(inetAddress).create()); } }); } }); } catch (UnknownHostException e) { e.printStackTrace(); } } @Override public void put(final String name, final Data value){ //TODO this method might not be used by TaskPasser at all when uploading files... //TODO Remove entirely? Good for debug and is used by JobUploader. FutureDHT futureDHT = peer.put(Number160.createHash(name)).setData(value).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.PUT, new OperationBuilder<Data>(success).setResult(value).setKey(name).create()); } }); } @Override public void put(final Number160 key, final Data value) { FutureDHT futureDHT = peer.put(key).setData(value).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.PUT, new OperationBuilder<Data>(success).setResult(value).setKey(key).create()); } }); } @Override public void put(final Number160 key, final Number160 domain, final Data value){ FutureDHT futureDHT = peer.put(key).setData(value).setDomainKey(domain).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.PUT, new OperationBuilder<Data>(success).setResult(value).setKey(key).create()); } }); } @Override public void get(final String name){ FutureDHT futureDHT = peer.get(Number160.createHash(name)).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.GET, new OperationBuilder<Data>(success).setKey(name).setResult(future.getData()).create()); } }); } @Override public void get(final Number160 key) { FutureDHT futureDHT = peer.get(key).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.GET, new OperationBuilder<Data>(success).setKey(key).setResult(future.getData()).create()); } }); } @Override public void install() { Install.install(); } @Override public void uninstall() { Install.uninstall(); } @Override public void push(String jobName) { //TODO Move to other class? taskManager.uploadJob(jobName, taskPasser.getReplicaManager()); } @Deceitful @Override public void falseWork(String address, int port){ deceit(address, port, new FalseWork(taskPasser, this, peer, taskManager)); } @Deceitful @Override public void spamWork(String address, int port){ deceit(address, port, new SpamWork(1,taskManager, this)); } @Deceitful @Override public void stopWork(String address, int port){ deceit(address, port, new StopWork(this, taskPasser, peer)); } @Deceitful private void deceit(String address, int port, final DeceitfulWork deceitfulWork){ if("self".equals(address)){ deceitfulWork.requestWork(peer.getPeerAddress()); return; } try { DiscoverBuilder discoverBuilder = peer.discover().setInetAddress(InetAddress.getByName(address)).setPorts(port); discoverBuilder.start().addListener(new BaseFutureAdapter<FutureDiscover>(){ @Override public void operationComplete(FutureDiscover future) throws Exception { if(!future.isSuccess()){ notifier.fireOperationFinished(CommandWord.WORK, new OperationBuilder<>(false).setErrorCode(ErrorCode.DISCOVER_FAILURE).create()); return; } PeerAddress jobOwner = future.getReporter(); assert ! jobOwner.equals(peer.getPeerAddress()); deceitfulWork.requestWork(jobOwner); } }); } catch (UnknownHostException e) { e.printStackTrace(); } } @Override public void work(String address, int port, final boolean autoWork) { //TODO might want to continue on already downloaded task? if("self".equals(address)){ taskPasser.requestWork(peer.getPeerAddress(), autoWork); } else { try { System.out.println("Attempt work for "+address+" : "+port); DiscoverBuilder discoverBuilder = peer.discover().setInetAddress(InetAddress.getByName(address)).setPorts(port); discoverBuilder.start().addListener(new BaseFutureAdapter<FutureDiscover>(){ @Override public void operationComplete(FutureDiscover future) throws Exception { if(!future.isSuccess()){ notifier.fireOperationFinished(CommandWord.WORK, new OperationBuilder<>(false).setErrorCode(ErrorCode.DISCOVER_FAILURE) .setReason(future.getFailedReason()).create()); return; } PeerAddress jobOwner = future.getReporter(); assert ! jobOwner.equals(peer.getPeerAddress()); taskPasser.requestWork(jobOwner, autoWork); } }); } catch (UnknownHostException e) { e.printStackTrace(); } } } @Override public List<PeerAddress> getNeighbours(){ return peer.getPeerBean().getPeerMap().getAll(); } @Override public List<PeerAddress> getOldNeighbours() { return null; } @Override public void reBootstrap() { HashSet<PeerAddress> fileNeighbours = dataFilesManager.getFileNeighbours(); for(PeerAddress p : fileNeighbours) { bootstrap(p.getInetAddress().getHostAddress(),p.portTCP()); } if(!enoughNeighbours()) { bootstrap(); } } private boolean enoughNeighbours() { return peer.getPeerBean().getPeerMap().getAll().size() > 0; } @Override public void send(String msg) { //Sends to all known nodes, see what happens for(PeerAddress address : peer.getPeerBean().getPeerMap().getAll()){ taskPasser.sendHello(address, msg); } } @Override public void get(final Number160 key, final Number160 domain){ FutureDHT futureDHT = peer.get(key).setDomainKey(domain).start(); futureDHT.addListener(new BaseFutureAdapter<FutureDHT>() { @Override public void operationComplete(FutureDHT future) throws Exception { boolean success = future.isSuccess(); notifier.fireOperationFinished(CommandWord.GET, new OperationBuilder<Data>(success).setKey(key).setResult(future.getData()).create()); } }); } @Override public void deleteNeighbourFile(){ if(dataFilesManager != null) { dataFilesManager.removeNeighbourFile(); } } public void deleteKeyFile() { if(dataFilesManager != null) { dataFilesManager.removeKeyFile(); } } public void deleteReplicaManager() { if(dataFilesManager != null) { dataFilesManager.removeReplicaManagerFile(); } } public void deleteTestDir() { if(dataFilesManager != null) { dataFilesManager.deleteTestDir(); } } @Override public void requestWork(int index) { int N = getNeighbours().size(); taskPasser.requestWork(getNeighbours().get(index % N), false); } private void startInitiate(int port) { //Stops the peer if it is running to free the port if(peer != null) { stop(); } try { //Initiates the peer KeyPair keyPair = dataFilesManager.getKeypair(); if(keyPair == null) { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); keyPair = generator.generateKeyPair(); dataFilesManager.saveKeyPair(keyPair); } peer = new PeerMaker( keyPair).setPorts(port).makeAndListen(); peer.getPeerBean().getPeerMap().addPeerMapChangeListener(dataFilesManager.getPeerMapListener()); taskPasser = new TaskPasser(peer, taskManager, this, dataFilesManager); } catch (NoSuchAlgorithmException | IOException e) { e.printStackTrace(); } String myAddress = null; if(peer!= null){ try { //peer.getPeerAddress().getInetAddress().getHostAddress() returns 127.0.0.1 instead of local IP //might be useful to return global IP myAddress = InetAddress.getLocalHost().getHostAddress()+" : "+port; } catch (UnknownHostException e) { e.printStackTrace(); //ignore } } boolean success = peer != null; notifier.fireOperationFinished(CommandWord.START, new OperationBuilder<String>(success).setResult(myAddress) .setReason(success? null:"Peer could not be created for some reason! Try another port.") .create()); // downloadEventualResults(); peer.getPeerBean().getPeerMap().addPeerMapChangeListener(new PeerMapChangeListener() { @Override public void peerInserted(PeerAddress peerAddress) { if(getNeighbours().size()<2){ downloadEventualResults(); } } @Override public void peerRemoved(PeerAddress peerAddress) { //ignore } @Override public void peerUpdated(PeerAddress peerAddress) { //ignore } }); } /** * Attempts to download results to previously given keys. * Need to be connected to the DHT in order for this to be useful */ private void downloadEventualResults(){ final ReplicaManager replicaManager = taskPasser.getReplicaManager(); Map<ReplicaID,Number160> pendingResults = replicaManager.pendingResults(); for(final ReplicaID replicaID : pendingResults.keySet()){ System.out.println("See if "+replicaID+" has uploaded something"); Number160 key = pendingResults.get(replicaID); addListener(new OperationFinishedListener(this, key, CommandWord.PUT) { @Override protected void operationFinished(Operation operation) { if(operation.isSuccess()){ Data result = (Data) operation.getResult(); try { byte[] resultObject = (byte[]) result.getObject(); System.out.println("Result downloaded for "+replicaID+" on "+resultObject.length+" bytes."); replicaManager.replicaFinished(replicaID, resultObject); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } }); this.get(key); } } @Override public Number160 getID() { return peer.getPeerID(); } }