// Runs a story of FILIUS' Gnutella program (PeerToPeerAnwendung with its // PeerToPeerServer on port 6346 and PeerToPeerClient) on terminal-net in Java // FILIUS and records what really happens: every frame each card saw, Java's // tables of them, each program's known peers and search results whenever it // tells its window, and at the end its peers, results and files in /peer2peer. // // GnutellaRun // // terminal-net: the program on 192.168.0.20, .30, .31 and 10.0.0.2 (behind // the router), each keeping at most three peers unless the story says // otherwise; .10 has none. A call comes as the program's window makes it // (beitretenNetzwerk, resetNetwork, sucheDatei, herunterladenDatei, // loescheSuchergebnisse); the acts are in the output. // pair .30 joins .31 // chain .30 joins .31, then .20 joins .30 (the ping goes on to .31) // twoPaths .31 joins 10.0.0.2, .30 joins .31 and 10.0.0.2, then .20 joins // .30: the ping reaches .31 and 10.0.0.2 each on two paths // full .31 keeps two peers, .30 and .20 one; .30, .20 and 10.0.0.2 join .31 // reset .30 joins .31, resets, joins again // errors .20 joins itself, .10 (no program), .99 (no one) // search a line .20 - .30 - .31 - 10.0.0.2 (by their numbers of peers); // .31 shares notizen.txt, 10.0.0.2 "Lied der Woche.txt"; .20 // searches "woche" (10.0.0.2's hit comes back through .31 and // .30) and downloads it, searches "notiz" and downloads it, // again after .31 removed it, searches "xyz", clears // searchBoth the line, .31 also shares lied.txt; .20 searches "lied" import filius.software.dateiaustausch.PeerToPeerAnwendung; import filius.software.system.Datei; import filius.software.system.Dateisystem; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.tree.DefaultMutableTreeNode; public class GnutellaRun { static final String CLIENT = "00000000-0000-0000-0000-000000000401"; static final String WEB = "00000000-0000-0000-0000-000000000403"; static final String ECHO = "00000000-0000-0000-0000-000000000404"; static final String FAR = "00000000-0000-0000-0000-000000000406"; static final String[] PEERS = { CLIENT, WEB, ECHO, FAR }; static final String CLASS = "filius.software.dateiaustausch.PeerToPeerAnwendung"; static ScenarioNetwork network; static final List events = new ArrayList<>(); static synchronized void event(String host, String kind, String text) { events.add(new Object[] { System.currentTimeMillis() - network.start, host, kind, text }); } static void sleepUntil(long milliseconds) throws InterruptedException { long rest = network.start + milliseconds - System.currentTimeMillis(); if (rest > 0) Thread.sleep(rest); } /** The story's calls: time, host, call ("join
" or "reset"). */ static List acts(String story) { List acts = new ArrayList<>(); switch (story) { case "pair": acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" }); break; case "chain": acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 3000L, CLIENT, "join 192.168.0.30" }); break; case "twoPaths": acts.add(new Object[] { 500L, ECHO, "join 10.0.0.2" }); acts.add(new Object[] { 3000L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 5500L, WEB, "join 10.0.0.2" }); acts.add(new Object[] { 8000L, CLIENT, "join 192.168.0.30" }); break; case "full": acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 3000L, CLIENT, "join 192.168.0.31" }); acts.add(new Object[] { 5500L, FAR, "join 192.168.0.31" }); break; case "reset": acts.add(new Object[] { 500L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 3000L, WEB, "reset" }); acts.add(new Object[] { 3500L, WEB, "join 192.168.0.31" }); break; case "search": acts.add(new Object[] { 500L, CLIENT, "join 192.168.0.30" }); acts.add(new Object[] { 3000L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 5500L, FAR, "join 192.168.0.31" }); acts.add(new Object[] { 8000L, CLIENT, "search woche" }); acts.add(new Object[] { 11000L, CLIENT, "download 0" }); acts.add(new Object[] { 14000L, CLIENT, "search notiz" }); acts.add(new Object[] { 17000L, CLIENT, "download 0" }); acts.add(new Object[] { 20000L, ECHO, "remove notizen.txt" }); acts.add(new Object[] { 20500L, CLIENT, "download 0" }); acts.add(new Object[] { 23000L, CLIENT, "search xyz" }); acts.add(new Object[] { 26000L, CLIENT, "clear" }); break; case "searchBoth": acts.add(new Object[] { 500L, CLIENT, "join 192.168.0.30" }); acts.add(new Object[] { 3000L, WEB, "join 192.168.0.31" }); acts.add(new Object[] { 5500L, FAR, "join 192.168.0.31" }); acts.add(new Object[] { 8000L, CLIENT, "search lied" }); break; case "errors": acts.add(new Object[] { 500L, CLIENT, "join 192.168.0.20" }); acts.add(new Object[] { 1000L, CLIENT, "join 192.168.0.10" }); acts.add(new Object[] { 20000L, CLIENT, "join 192.168.0.99" }); break; default: throw new IllegalArgumentException("unknown story " + story); } return acts; } public static void main(String[] args) throws Exception { network = ScenarioNetwork.read(args[0]); String story = args[1]; long end = Long.parseLong(args[2]); List acts = acts(story); Map programs = new LinkedHashMap<>(); for (String host : PEERS) { network.system(host).installApp(CLASS, false); PeerToPeerAnwendung program = (PeerToPeerAnwendung) network.system(host).holeSoftware(CLASS); if (story.equals("full")) program.setMaxTeilnehmerZahl(host.equals(ECHO) ? 2 : host.equals(FAR) ? 3 : 1); else if (story.startsWith("search")) program.setMaxTeilnehmerZahl(host.equals(CLIENT) || host.equals(FAR) ? 1 : 2); else program.setMaxTeilnehmerZahl(3); program.hinzuBeobachter((observable, data) -> { event(host, "peers", String.join(",", program.holeBekanntePeerToPeerTeilnehmer())); event(host, "results", String.join("|", program.holeErgebnisse())); }); programs.put(host, program); } if (story.startsWith("search")) { Dateisystem echoFiles = network.system(ECHO).getDateisystem(); if (story.equals("searchBoth")) echoFiles.speicherDatei("/peer2peer", new Datei("lied.txt", "txt", "Hallo Welt")); echoFiles.speicherDatei("/peer2peer", new Datei("notizen.txt", "txt", "Notiz")); network.system(FAR).getDateisystem().speicherDatei("/peer2peer", new Datei("Lied der Woche.txt", "txt", "La la la")); } network.start(); for (Object[] act : acts) { sleepUntil((Long) act[0]); String host = (String) act[1]; String call = (String) act[2]; event(host, "call", call); PeerToPeerAnwendung program = programs.get(host); if (call.startsWith("join ")) program.beitretenNetzwerk(call.substring("join ".length())); else if (call.startsWith("search ")) program.sucheDatei(call.substring("search ".length())); else if (call.startsWith("download ")) { // The window offers only the rows it shows. int row = Integer.parseInt(call.substring("download ".length())); if (row < program.holeErgebnisse().size()) program.herunterladenDatei(row); else event(host, "skipped", call); } else if (call.startsWith("remove ")) { // The file explorer's "delete" on the peer that has the file. Dateisystem files = network.system(host).getDateisystem(); DefaultMutableTreeNode folder = files.verzeichnisKnoten("/peer2peer"); folder.remove(Dateisystem.verzeichnisKnoten(folder, call.substring("remove ".length()))); } else if (call.equals("clear")) program.loescheSuchergebnisse(); else program.resetNetwork(); } sleepUntil(end); ScenarioNetwork.Json out = new ScenarioNetwork.Json(); out.open('{'); network.writeFrames(out); out.key("acts").open('['); for (Object[] act : acts) out.open('[').number((Long) act[0]).string((String) act[1]).string((String) act[2]).close(']'); out.close(']'); synchronized (GnutellaRun.class) { out.key("events").open('['); for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).string((String) each[3]).close(']'); out.close(']'); } out.key("programs").open('{'); for (Map.Entry entry : programs.entrySet()) { PeerToPeerAnwendung program = entry.getValue(); out.key(entry.getKey()).open('{'); out.key("peers").open('['); for (String peer : program.holeBekanntePeerToPeerTeilnehmer()) out.string(peer); out.close(']'); out.key("results").open('['); for (String result : program.holeErgebnisse()) out.string(result); out.close(']'); out.key("files").open('['); for (Datei file : network.system(entry.getKey()).getDateisystem().holeDateien(program.holeVerzeichnis())) { out.open('[').string(file.getName()).string(file.getDateiTyp()).string(file.getDateiInhalt()).close(']'); } out.close(']'); out.close('}'); } out.close('}'); out.close('}'); network.stop(); ScenarioNetwork.write(args[3], out); System.exit(0); } }