package challengetask.group02.helpers; import challengetask.group02.fsstructure.Block; import challengetask.group02.fsstructure.Directory; import challengetask.group02.fsstructure.Entry; import challengetask.group02.fsstructure.File; import net.tomp2p.dht.FutureGet; import net.tomp2p.dht.FuturePut; import net.tomp2p.dht.FutureRemove; import net.tomp2p.dht.PeerDHT; import net.tomp2p.peers.Number160; import net.tomp2p.peers.Number640; import net.tomp2p.peers.PeerAddress; import net.tomp2p.storage.Data; import net.tomp2p.utils.Pair; import java.io.IOException; import java.util.Map; import java.util.Random; /** * Created by anvar on 25/04/15. */ /** * This is the class to modify the tree, * some methods use simple put, others like addNewEntry and removeEntry * utilize vDHT mechanisms. * * The code for vDHT is mostly copied from ExampleVDHT class from tomp2p examples. * * Requires further refactoring. * */ public class FSModifyHelper { private PeerDHT peer; private Random RND = new Random(42L); private SimpleCache<Entry> cache = new SimpleCache<>(1); public FSModifyHelper(PeerDHT peer) { this.peer = peer; } public int addNewEntry(Directory parentDir, Entry child) { try { put(child); vUpdateParentAddChild(parentDir, child); } catch (Exception e) { } return 0; } public Entry getEntryByID(Number160 ID) throws IOException, ClassNotFoundException { FutureGet futureGet = peer.get(ID).getLatest().start(); futureGet.awaitUninterruptibly(); if (futureGet.isEmpty()) { System.out.println("getEntryFromID did not get a result -> faulty fs"); } return (Entry) futureGet.data().object(); } public void removeEntry(Directory parent, Entry entry) throws ClassNotFoundException { try { /*entry.setDirtyBit(true); put(entry);*/ removeEntry(entry); vUpdateParentRemoveChild(parent, entry); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public void removeEntryFromParent(Directory parent, Entry entry) throws ClassNotFoundException { try { /*entry.setDirtyBit(true); put(entry);*/ vUpdateParentRemoveChild(parent, entry); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } private void put(Entry entry) throws IOException { FuturePut futurePut = peer.put(entry.getID()).data(new Data(entry)).start(); futurePut.awaitUninterruptibly(); } private void put(Block block) throws IOException { FuturePut futurePut = peer.put(block.getID()).data(new Data(block)).start(); futurePut.awaitUninterruptibly(); } public int updateEntryName(Directory parent, Entry entry, String newName) { try { parent.renameChild(entry.getEntryName(), newName); xPut(parent); entry.setEntryName(newName); put(entry); } catch (Exception e) { } return 0; } private void xPut(Entry entry) throws IOException { FutureGet fg = peer.get(entry.getID()).getLatest().start() .awaitUninterruptibly(); Pair<Number640, Data> pair = checkVersions(fg.rawData()); Data newData = new Data(entry); Number160 v = pair.element0().versionKey(); long version = v.timestamp() + 1; newData.addBasedOn(v); Pair<Number160, Data> pair3 = new Pair<Number160, Data>(new Number160(version, newData.hash()), newData); FuturePut fp1 = peer.put(entry.getID()).data(Number160.ZERO, pair3.element1().prepareFlag(), pair3.element0()).start().awaitUninterruptibly(); Pair<Number640, Byte> pair2 = checkVersions(fp1.rawResult()); FuturePut fp = peer.put(entry.getID()) .versionKey(pair2.element0().versionKey()).putConfirm() .data(new Data()).start().awaitUninterruptibly(); } public void putFile(Number160 ID, File file) { try { put(file); } catch (IOException e) { e.printStackTrace(); } } public void putBlock(Number160 ID, Block block) { try { FuturePut futurePut = peer.put(block.getID()).data(new Data(block)).start(); futurePut.awaitUninterruptibly(); } catch (IOException e) { e.printStackTrace(); } } public Block getBlockDHT(Number160 ID) { Block block; try { FutureGet futureGet = peer.get(ID).start(); futureGet.awaitUninterruptibly(); block = (Block) futureGet.data().object(); return block; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } public int moveEntry(Directory newParent, Directory oldParent, Entry entry, String newName) { try { removeEntryFromParent(oldParent, entry); } catch (ClassNotFoundException e) { e.printStackTrace(); } entry.setEntryName(newName); addNewEntry(newParent, entry); return 0; } public void removeEntry(Entry entry) { //TODO asynchronous FutureRemove future = peer.remove(entry.getID()).all().start(); future.awaitUninterruptibly(); } /** * This method deletes (removes from DHT) all the blocks of the file, so that the file object can be safely deleted. * * @param file The file to be cleared. */ public void clearAndDeleteFile(File file) { file.setDirtyBit(true); try { put(file); for (Number160 number160 : file.getBlocks()) { //TODO asynchronous: callbacks are counted, after all deletions called back, resume with deleting the file FutureRemove future = peer.remove(number160).start(); future.awaitUninterruptibly(); } } catch (IOException e) { e.printStackTrace(); } } public void flushFile(File file) { file.setReadOnly(false); file.setModifierPeer(null); try { put(file); } catch (IOException e) { e.printStackTrace(); } } private void vUpdateParentAddChild(Directory parentDir, Entry child) throws ClassNotFoundException, InterruptedException, IOException { Pair<Number640, Byte> pair2 = null; for (int i = 0; i < 5; i++) { Pair<Number160, Data> pair = getAndUpdate(parentDir, child); if (pair == null) { System.out .println("we cannot handle this kind of inconsistency automatically, handing over the the API dev"); return; } FuturePut fp = peer .put(parentDir.getID()) .data(Number160.ZERO, pair.element1().prepareFlag(), pair.element0()).start().awaitUninterruptibly(); pair2 = checkVersions(fp.rawResult()); // 1 is PutStatus.OK_PREPARED if (pair2 != null && pair2.element1() == 1) { break; } System.out.println("get delay or fork - put"); // if not removed, a low ttl will eventually get rid of it peer.remove(parentDir.getID()).versionKey(pair.element0()).start() .awaitUninterruptibly(); Thread.sleep(RND.nextInt(500)); } if (pair2 != null && pair2.element1() == 1) { //stored FuturePut fp = peer.put(parentDir.getID()) .versionKey(pair2.element0().versionKey()).putConfirm() .data(new Data()).start().awaitUninterruptibly(); } else { System.out .println("we cannot handle this kind of inconsistency automatically, handing over the the API dev"); } } private Pair<Number160, Data> getAndUpdate(Directory parentDir, Entry child) throws InterruptedException, ClassNotFoundException, IOException { Random RND = new Random(42L); Pair<Number640, Data> pair = tryToGet(parentDir.getID()); // we got the latest data if (pair != null) { // update operation is append parentDir.addChild(child.getEntryName(), child.getID(), child.getType()); Data newData = new Data(parentDir); Number160 v = pair.element0().versionKey(); long version = v.timestamp() + 1; newData.addBasedOn(v); //since we create a new version, we can access old versions as well return new Pair<Number160, Data>(new Number160(version, newData.hash()), newData); } return null; } private void vUpdateParentRemoveChild(Directory parentDir, Entry child) throws InterruptedException, IOException, ClassNotFoundException { Pair<Number640, Byte> pair2 = null; for (int i = 0; i < 5; i++) { Pair<Number160, Data> pair = getAndUpdate_remove(parentDir, child); if (pair == null) { System.out .println("we cannot handle this kind of inconsistency automatically, handing over the the API dev"); return; } FuturePut fp = peer .put(parentDir.getID()) .data(Number160.ZERO, pair.element1().prepareFlag(), pair.element0()).start().awaitUninterruptibly(); pair2 = checkVersions(fp.rawResult()); // 1 is PutStatus.OK_PREPARED if (pair2 != null && pair2.element1() == 1) { break; } System.out.println("get delay or fork - put"); // if not removed, a low ttl will eventually get rid of it peer.remove(parentDir.getID()).versionKey(pair.element0()).start() .awaitUninterruptibly(); try { Thread.sleep(RND.nextInt(500)); } catch (InterruptedException e) { e.printStackTrace(); } } if (pair2 != null && pair2.element1() == 1) { //stored FuturePut fp = peer.put(parentDir.getID()) .versionKey(pair2.element0().versionKey()).putConfirm() .data(new Data()).start().awaitUninterruptibly(); } else { System.out .println("we cannot handle this kind of inconsistency automatically, handing over the the API dev"); } } private Pair<Number160, Data> getAndUpdate_remove(Directory parentDir, Entry child) throws InterruptedException, ClassNotFoundException, IOException { Random RND = new Random(42L); Pair<Number640, Data> pair = tryToGet(parentDir.getID()); // we got the latest data if (pair != null) { // update operation is append parentDir.removeChild(child.getEntryName()); Data newData = new Data(parentDir); Number160 v = pair.element0().versionKey(); long version = v.timestamp() + 1; newData.addBasedOn(v); //since we create a new version, we can access old versions as well return new Pair<Number160, Data>(new Number160(version, newData.hash()), newData); } return null; } private Pair<Number640, Data> tryToGet(Number160 key) { Pair<Number640, Data> pair = null; for (int i = 0; i < 5; i++) { FutureGet fg = peer.get(key).getLatest().start() .awaitUninterruptibly(); // check if all the peers agree on the same latest version, if not // wait a little and try again pair = checkVersions(fg.rawData()); if (pair != null) { break; } // something went wrong, have to wait try { Thread.sleep(RND.nextInt(500)); } catch (InterruptedException e) { //TODO find out what is it e.printStackTrace(); } } return pair; } private static <K> Pair<Number640, K> checkVersions( Map<PeerAddress, Map<Number640, K>> rawData) { Number640 latestKey = null; K latestData = null; for (Map.Entry<PeerAddress, Map<Number640, K>> entry : rawData .entrySet()) { if (latestData == null && latestKey == null) { latestData = entry.getValue().values().iterator().next(); latestKey = entry.getValue().keySet().iterator().next(); } else { if (!latestKey.equals(entry.getValue().keySet().iterator() .next()) || !latestData.equals(entry.getValue().values() .iterator().next())) { return null; } } } return new Pair<Number640, K>(latestKey, latestData); } }