├── nodes
├── start.sh
├── settings.properties
├── src
├── KPack
│ ├── Packets
│ │ ├── PingReply.java
│ │ ├── PingRequest.java
│ │ ├── DeleteRequest.java
│ │ ├── Packet.java
│ │ ├── FindNodeReply.java
│ │ ├── FindValueRequest.java
│ │ ├── FindNodeRequest.java
│ │ ├── FindValueReply.java
│ │ └── StoreRequest.java
│ ├── Files
│ │ ├── KadFileInterf.java
│ │ ├── KadFileMapInterf.java
│ │ ├── KadFile.java
│ │ └── KadFileMap.java
│ ├── Exceptions
│ │ ├── InvalidSettingsException.java
│ │ ├── NotAValidParentException.java
│ │ ├── FileNotKnownException.java
│ │ └── AlreadyInstancedException.java
│ ├── Tree
│ │ ├── Node.java
│ │ ├── TreeNode.java
│ │ ├── Bucket.java
│ │ └── RoutingTree.java
│ ├── KademliaInterf.java
│ ├── FixedKadNode.java
│ ├── KadNode.java
│ ├── UserInterface
│ │ ├── TreeUI.form
│ │ ├── TreeUI.java
│ │ └── UserInterface.java
│ ├── Tupla.java
│ └── Kademlia.java
└── Main.java
├── KademliaFileStorage.iml
├── LICENSE
└── README.md
/nodes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apontini/KademliaFileStorage/HEAD/nodes
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd ./src/
4 | javac Main.java
5 | cd ..
6 | java -classpath ./src/ Main
7 |
8 |
9 |
--------------------------------------------------------------------------------
/settings.properties:
--------------------------------------------------------------------------------
1 | #impostazioni per Kademlia
2 | #i valori sono provvisori, a scopo dimostrativo/di test
3 | port=1337
4 | fileRefreshThreadSleep=20000
5 | fileRefreshWait=100000
6 | bucketRefreshWait=200000
7 | socketTimeout=20000
8 |
--------------------------------------------------------------------------------
/src/KPack/Packets/PingReply.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 | import java.io.Serializable;
5 |
6 | public class PingReply extends Packet implements Serializable {
7 |
8 | public PingReply(KadNode source, KadNode dest)
9 | {
10 | super(source, dest);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/KPack/Files/KadFileInterf.java:
--------------------------------------------------------------------------------
1 | package KPack.Files;
2 |
3 | import java.math.BigInteger;
4 |
5 | public interface KadFileInterf {
6 |
7 | BigInteger getFileID();
8 |
9 | boolean isRedundant();
10 |
11 | String getFileName();
12 |
13 | String getPath();
14 |
15 | long getLastRefresh();
16 | }
17 |
--------------------------------------------------------------------------------
/src/KPack/Packets/PingRequest.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 | import java.io.Serializable;
5 |
6 | public class PingRequest extends Packet implements Serializable {
7 |
8 | public PingRequest(KadNode source, KadNode dest)
9 | {
10 | super(source, dest);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/KPack/Exceptions/InvalidSettingsException.java:
--------------------------------------------------------------------------------
1 | package KPack.Exceptions;
2 |
3 | public class InvalidSettingsException extends Exception
4 | {
5 | public InvalidSettingsException()
6 | {
7 | }
8 |
9 | public InvalidSettingsException(String message)
10 | {
11 | super(message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/KPack/Exceptions/NotAValidParentException.java:
--------------------------------------------------------------------------------
1 | package KPack.Exceptions;
2 |
3 | public class NotAValidParentException extends Exception {
4 |
5 | public NotAValidParentException()
6 | {
7 | }
8 |
9 | public NotAValidParentException(String message)
10 | {
11 | super(message);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/KPack/Exceptions/FileNotKnownException.java:
--------------------------------------------------------------------------------
1 | package KPack.Exceptions;
2 |
3 | public class FileNotKnownException extends Exception {
4 |
5 | // Parameterless Constructor
6 | public FileNotKnownException()
7 | {
8 | }
9 |
10 | // Constructor that accepts a message
11 | public FileNotKnownException(String message)
12 | {
13 | super(message);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/KPack/Files/KadFileMapInterf.java:
--------------------------------------------------------------------------------
1 | package KPack.Files;
2 |
3 | import java.math.BigInteger;
4 | import java.util.Iterator;
5 |
6 | public interface KadFileMapInterf {
7 |
8 | void add(KadFile file);
9 |
10 | void remove(KadFile file);
11 |
12 | void remove(BigInteger ID);
13 |
14 | KadFile get(BigInteger i);
15 |
16 | void clearAll();
17 |
18 | int size();
19 | }
20 |
--------------------------------------------------------------------------------
/src/KPack/Exceptions/AlreadyInstancedException.java:
--------------------------------------------------------------------------------
1 | package KPack.Exceptions;
2 |
3 | public class AlreadyInstancedException extends Exception {
4 |
5 | // Parameterless Constructor
6 | public AlreadyInstancedException()
7 | {
8 | }
9 |
10 | // Constructor that accepts a message
11 | public AlreadyInstancedException(String message)
12 | {
13 | super(message);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/KPack/Packets/DeleteRequest.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.Files.KadFile;
4 | import KPack.KadNode;
5 | import java.io.Serializable;
6 |
7 | public class DeleteRequest extends Packet implements Serializable {
8 |
9 | private KadFile kf;
10 |
11 | public DeleteRequest(KadFile kf, KadNode source, KadNode dest)
12 | {
13 | super(source, dest);
14 | this.kf = kf;
15 | }
16 |
17 | public KadFile getFile()
18 | {
19 | return kf;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/KPack/Tree/Node.java:
--------------------------------------------------------------------------------
1 | package KPack.Tree;
2 |
3 | public abstract class Node {
4 |
5 | private Node parent;
6 |
7 | public synchronized Node getParent()
8 | {
9 | return parent;
10 | }
11 |
12 | public synchronized void setParent(Node parent)
13 | {
14 | this.parent = parent;
15 | }
16 |
17 | public synchronized int getDepth()
18 | {
19 | if (parent == null)
20 | {
21 | return 0;
22 | }
23 | return parent.getDepth() + 1;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/KademliaFileStorage.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/KPack/Tree/TreeNode.java:
--------------------------------------------------------------------------------
1 | package KPack.Tree;
2 |
3 | public class TreeNode extends Node {
4 |
5 | //SOLO nodo interno
6 | private Node left;
7 | private Node right;
8 |
9 | public synchronized Node getLeft()
10 | {
11 | return left;
12 | }
13 |
14 | public synchronized Node getRight()
15 | {
16 | return right;
17 | }
18 |
19 | public synchronized void setLeft(Node left)
20 | {
21 | this.left = left;
22 | }
23 |
24 | public synchronized void setRight(Node right)
25 | {
26 | this.right = right;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/KPack/Packets/Packet.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 | import java.io.Serializable;
5 |
6 | public class Packet implements Serializable{
7 |
8 | private final KadNode source;
9 | private final KadNode dest;
10 |
11 | public Packet(KadNode source, KadNode dest)
12 | {
13 | this.source = source;
14 | this.dest = dest;
15 | }
16 |
17 | public KadNode getSourceKadNode()
18 | {
19 | return source;
20 | }
21 |
22 | public KadNode getDestKadNode()
23 | {
24 | return dest;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/KPack/Packets/FindNodeReply.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 |
5 | import java.io.Serializable;
6 | import java.math.BigInteger;
7 | import java.util.List;
8 |
9 | public class FindNodeReply extends Packet implements Serializable {
10 |
11 | private BigInteger targetID;
12 | private List lkn;
13 |
14 | public FindNodeReply(BigInteger targetID, KadNode sourceKN, KadNode destKN, List lkn)
15 | {
16 | super(sourceKN, destKN);
17 | this.targetID = targetID;
18 | this.lkn = lkn;
19 | }
20 |
21 | public BigInteger getTargetID()
22 | {
23 | return targetID;
24 | }
25 |
26 | public List getList()
27 | {
28 | return lkn;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/KPack/Packets/FindValueRequest.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 | import java.io.Serializable;
5 | import java.math.BigInteger;
6 |
7 | public class FindValueRequest extends Packet implements Serializable {
8 |
9 | private BigInteger fileID;
10 | private boolean contentRequested;
11 |
12 | public FindValueRequest(BigInteger fileID, KadNode source, KadNode dest, boolean contentRequested)
13 | {
14 | super(source, dest);
15 | this.fileID = fileID;
16 | this.contentRequested = contentRequested;
17 | }
18 |
19 | public BigInteger getFileID()
20 | {
21 | return fileID;
22 | }
23 |
24 | public boolean isContentRequested()
25 | {
26 | return contentRequested;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/KPack/KademliaInterf.java:
--------------------------------------------------------------------------------
1 | package KPack;
2 |
3 | import KPack.Exceptions.FileNotKnownException;
4 | import KPack.Files.KadFile;
5 |
6 | import java.io.FileNotFoundException;
7 | import java.math.BigInteger;
8 | import java.security.InvalidParameterException;
9 | import java.util.List;
10 |
11 | public interface KademliaInterf
12 | {
13 |
14 | BigInteger getNodeID();
15 |
16 | List getFileList();
17 |
18 | boolean ping(KadNode node);
19 |
20 | Object findValue(BigInteger fileID, boolean returnContent);
21 |
22 | List findNode(BigInteger nodeID);
23 |
24 | void store(String filepath) throws FileNotFoundException, InvalidParameterException;
25 |
26 | void delete(BigInteger ID) throws FileNotKnownException;
27 |
28 | KadNode getMyNode();
29 | }
30 |
--------------------------------------------------------------------------------
/src/KPack/Packets/FindNodeRequest.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.KadNode;
4 |
5 | import java.io.Serializable;
6 | import java.math.BigInteger;
7 |
8 | public class FindNodeRequest extends Packet implements Serializable {
9 |
10 | private BigInteger targetID;
11 | private boolean track; //se è falsa, il nodo che riceve la richiesta non aggiorna il bucket relativo all'ID
12 |
13 | public FindNodeRequest(BigInteger targetID, KadNode sourceKN, KadNode destKN, boolean track)
14 | {
15 | super(sourceKN, destKN);
16 | this.targetID = targetID;
17 | this.track = track;
18 | }
19 |
20 | public BigInteger getTargetID()
21 | {
22 | return targetID;
23 | }
24 |
25 | public boolean isTracked()
26 | {
27 | return track;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KPack/FixedKadNode.java:
--------------------------------------------------------------------------------
1 | package KPack;
2 |
3 | import java.math.BigInteger;
4 | import java.net.InetAddress;
5 | import java.net.UnknownHostException;
6 |
7 | public class FixedKadNode extends KadNode {
8 |
9 | private String name;
10 | private String indirizzo;
11 |
12 | public FixedKadNode(String indirizzo, int port, BigInteger ID, String name)
13 | {
14 | super(indirizzo, port, ID);
15 | this.name = name;
16 | this.indirizzo = indirizzo;
17 | }
18 |
19 | public String getName()
20 | {
21 | return name;
22 | }
23 |
24 | public KadNode getKadNode()
25 | {
26 | try
27 | {
28 | InetAddress inAddr = InetAddress.getByName(indirizzo);
29 | return new KadNode(inAddr.getHostAddress(), super.getPort(), super.getNodeID());
30 | }
31 | catch (UnknownHostException ex)
32 | {
33 | return null;
34 | }
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Lahace
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/KPack/KadNode.java:
--------------------------------------------------------------------------------
1 | package KPack;
2 |
3 | import java.io.Serializable;
4 | import java.math.BigInteger;
5 | import java.net.InetAddress;
6 | import java.net.UnknownHostException;
7 | import java.util.Objects;
8 |
9 | public class KadNode implements Serializable {
10 |
11 | private InetAddress ip;
12 | private int port;
13 | private BigInteger nodeID;
14 |
15 | public KadNode(String ipString, int port, BigInteger ID)
16 | {
17 | try
18 | {
19 | ip = InetAddress.getByName(ipString);
20 | }
21 | catch (UnknownHostException uoe)
22 | {
23 | uoe.printStackTrace();
24 | }
25 |
26 | this.port = port;
27 | this.nodeID = ID;
28 | }
29 |
30 | public InetAddress getIp()
31 | {
32 | return ip;
33 | }
34 |
35 | public int getPort()
36 | {
37 | return port;
38 | }
39 |
40 | public BigInteger getNodeID()
41 | {
42 | return nodeID;
43 | }
44 |
45 | @Override
46 | public boolean equals(Object o)
47 | {
48 | if (this == o)
49 | {
50 | return true;
51 | }
52 | if (o == null || getClass() != o.getClass())
53 | {
54 | return false;
55 | }
56 | KadNode kadNode = (KadNode) o;
57 | return Objects.equals(nodeID, kadNode.nodeID);
58 | }
59 |
60 | @Override
61 | public String toString()
62 | {
63 | return "KadNode{"
64 | + "ip=" + ip
65 | + ", port=" + port
66 | + ", nodeID=" + nodeID
67 | + '}';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/KPack/Packets/FindValueReply.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.Files.KadFileInterf;
4 | import KPack.KadNode;
5 |
6 | import java.io.IOException;
7 | import java.io.Serializable;
8 | import java.math.BigInteger;
9 | import java.nio.file.Files;
10 | import java.nio.file.Path;
11 | import java.nio.file.Paths;
12 | import java.util.List;
13 |
14 | public class FindValueReply extends Packet implements Serializable {
15 |
16 | private final String fileName;
17 | private BigInteger fileID;
18 | private byte[] content;
19 | private List lkn;
20 |
21 | public FindValueReply(BigInteger fileID, KadFileInterf kf, KadNode source, KadNode dest)
22 | {
23 | super(source, dest);
24 | this.fileID = fileID;
25 | this.lkn = lkn;
26 | if (kf == null)
27 | {
28 | content = null;
29 | fileName = null;
30 | }
31 | else
32 | {
33 | this.fileName = kf.getFileName();
34 |
35 | Path path = Paths.get(kf.getPath()+kf.getFileName());
36 | try
37 | {
38 | content = Files.readAllBytes(path);
39 | }
40 | catch (IOException e)
41 | {
42 | e.printStackTrace();
43 | }
44 | }
45 | }
46 |
47 | public BigInteger getFileID()
48 | {
49 | return fileID;
50 | }
51 |
52 | public byte[] getContent()
53 | {
54 | return content;
55 | }
56 |
57 | public String getFileName()
58 | {
59 | return fileName;
60 | }
61 |
62 | public List getListKadNode()
63 | {
64 | return lkn;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/KPack/Packets/StoreRequest.java:
--------------------------------------------------------------------------------
1 | package KPack.Packets;
2 |
3 | import KPack.Files.KadFileInterf;
4 | import KPack.KadNode;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.io.Serializable;
9 | import java.math.BigInteger;
10 | import java.nio.file.Files;
11 | import java.nio.file.Path;
12 | import java.nio.file.Paths;
13 | import java.security.InvalidParameterException;
14 |
15 | public class StoreRequest extends Packet implements Serializable {
16 |
17 | private BigInteger fileID;
18 | private String fileName;
19 | private byte[] content;
20 |
21 | public StoreRequest(KadFileInterf kf, KadNode source, KadNode dest) throws IOException
22 | {
23 | super(source, dest);
24 | this.fileID = kf.getFileID();
25 | this.fileName = kf.getFileName();
26 | //Orribile ma temporanea
27 | if (!((new File(kf.getPath())).isDirectory()))
28 | {
29 | throw new InvalidParameterException("Errore nella composizione del KadFile, nel path");
30 | }
31 | if ((new File(kf.getPath() + File.separator + kf.getFileName())).isDirectory())
32 | {
33 | throw new InvalidParameterException("Errore nella composizione del KadFile, il file è una directory");
34 | }
35 | Path path = Paths.get(kf.getPath() + File.separator + kf.getFileName());
36 | content = Files.readAllBytes(path);
37 | }
38 |
39 | public BigInteger getFileID()
40 | {
41 | return fileID;
42 | }
43 |
44 | public String getFileName()
45 | {
46 | return fileName;
47 | }
48 |
49 | public byte[] getContent()
50 | {
51 | return content;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/KPack/UserInterface/TreeUI.form:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
--------------------------------------------------------------------------------
/src/KPack/Files/KadFile.java:
--------------------------------------------------------------------------------
1 | package KPack.Files;
2 |
3 | import java.io.Serializable;
4 | import java.math.BigInteger;
5 | import java.util.Objects;
6 |
7 | public class KadFile implements KadFileInterf, Serializable {
8 |
9 | //deve mantenere singolo file
10 | private BigInteger fileID;
11 | private boolean redundant; //false se il file è mio o true se mi è stato dato grazie al protocollo della rete Kademlia
12 | private String fileName;
13 | private String path;
14 | private long lastRefresh;
15 |
16 | public KadFile(BigInteger fileID, boolean redundant, String fileName, String path)
17 | {
18 | this.fileID = fileID;
19 | this.redundant = redundant;
20 | this.fileName = fileName;
21 | this.path = path;
22 | if(redundant) lastRefresh = System.currentTimeMillis();
23 | }
24 |
25 | public BigInteger getFileID()
26 | {
27 | return fileID;
28 | }
29 |
30 | public boolean isRedundant()
31 | {
32 | return redundant;
33 | }
34 |
35 | public String getFileName()
36 | {
37 | return fileName;
38 | }
39 |
40 | public String getPath()
41 | {
42 | return path;
43 | }
44 |
45 | public long getLastRefresh() { return lastRefresh; }
46 |
47 | public void setLastRefresh(long lastRefresh) { this.lastRefresh = lastRefresh;}
48 |
49 | @Override
50 | public String toString()
51 | {
52 | return "KadFile{"
53 | + "fileID=" + fileID
54 | + ", redundant=" + redundant
55 | + ", fileName='" + fileName + '\''
56 | + ", path='" + path + '\''
57 | + '}';
58 | }
59 |
60 | @Override
61 | public boolean equals(Object obj)
62 | {
63 | if (this == obj)
64 | {
65 | return true;
66 | }
67 | if (obj == null)
68 | {
69 | return false;
70 | }
71 | if (getClass() != obj.getClass())
72 | {
73 | return false;
74 | }
75 | final KadFile other = (KadFile) obj;
76 | if (!Objects.equals(this.fileName, other.fileName))
77 | {
78 | return false;
79 | }
80 | if (!Objects.equals(this.fileID, other.fileID))
81 | {
82 | return false;
83 | }
84 | return true;
85 | }
86 |
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kademlia File Storage
2 |
3 | Kademlia File Storage is a project created for the Distributed Systems course in Computer Science Engineering at University of Padua.
4 | It uses *Kademlia* DHT to distribute file to other peers for storage purpose.
5 | Our goal is to achieve the most permanent file storage solution on a p2p network.
6 | *We will not maintain this project once it's completed (with regards to our course objectives)*.
7 |
8 | #### How does it work?
9 | When instantiating a node, a FIND_NODE RPC is performed on a list of a handful nodes (which is serialized in the `nodes` file) to know at most K nodes which are the closest to our node.
10 | This file is serialized by the `writeFixedNodes` method in `Kademlia.java`, if you need to change those nodes for some implementation/change/whatever purposes, please check that.
11 | From then, all other RPCs can be called to interact with the network.
12 | While a node is instantiated, it will receive files from other peers which are performing a *STORE* RPC on the network. These files are frequently refreshed to quickly react to changes occuring in the network, ensuring file persistance for the longest time possible.
13 |
14 | #### How to use it
15 | As you can see in `Main.java` you can simply join a network by creating an instance of the `Kademlia` as follows:
16 | ```
17 | Kademlia myNode = new Kademlia();
18 | ```
19 |
20 | You can then start using Kademlia 4 basic RPCs (*FIND_NODE*, *PING*, *FIND_VALUE* and *STORE*) and an extra RPC (*DELETE*), added by us, to interact with the p2p network and store files.
21 | That's it!
22 |
23 | If you wish to test it with our provided implementation, just run the `start.sh` file found in the main directory.
24 | This will build all Java classes needed and will run `Main.java`.
25 |
26 | #### Settings
27 | For testing purposes, we added some settings inside the `settings.properties` file. These are:
28 | - **port**: the port address which will be used (MUST be forwarded if behind a NAT).
29 | - **fileRefreshThreadSleep**: amount of time (in milliseconds) between one file refresh sequence and another.
30 | - **fileRefreshWait**: amount of time (in milliseconds) to wait before a file can be refreshed.
31 | - **bucketRefreshWait**: amount of time (in milliseconds) between one bucket refresh and another.
32 | - **socketTimeout**: self-explainatory (in milliseconds).
33 |
34 | Based on [Petar Maymounkov and David Mazières paper](https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf).
35 | Clarifications were found in a [presentation](https://docs.google.com/presentation/d/11qGZlPWu6vEAhA7p3qsQaQtWH7KofEC9dMeBFZ1gYeA/edit#slide=id.g1718cc2bc_08645) made by Tristan Slominski (@tristanls)
36 |
--------------------------------------------------------------------------------
/src/KPack/Tupla.java:
--------------------------------------------------------------------------------
1 | package KPack;
2 |
3 | import java.io.Serializable;
4 | import java.math.BigInteger;
5 | import java.util.Objects;
6 |
7 | public class Tupla implements Serializable {
8 |
9 | private K key;
10 | private V value;
11 |
12 | public Tupla(K key, V value)
13 | {
14 | this.key = key;
15 | this.value = value;
16 | }
17 |
18 | public K getKey()
19 | {
20 | return key;
21 | }
22 |
23 | public V getValue()
24 | {
25 | return value;
26 | }
27 |
28 | public void setKey(K key)
29 | {
30 | this.key = key;
31 | }
32 |
33 | public void setValue(V value)
34 | {
35 | this.value = value;
36 | }
37 |
38 | @Override
39 | public boolean equals(Object obj)
40 | {
41 | if (this == obj)
42 | {
43 | return true;
44 | }
45 | if (obj == null)
46 | {
47 | return false;
48 | }
49 | if (obj instanceof Tupla)
50 | {
51 | if (key instanceof BigInteger && ((Tupla) obj).getKey() instanceof BigInteger)
52 | {
53 | if (!((BigInteger) key).equals((BigInteger) (((Tupla) obj).getKey())))
54 | {
55 | return false;
56 | }
57 | }
58 | else
59 | {
60 | return false;
61 | }
62 | if (value instanceof Boolean && ((Tupla) obj).getValue() instanceof Boolean)
63 | {
64 | if (!((Boolean) value).equals((Boolean) (((Tupla) obj).getValue())))
65 | {
66 | return false;
67 | }
68 | }
69 | else
70 | {
71 | return false;
72 | }
73 | return true;
74 | }
75 | if (getClass() != obj.getClass())
76 | {
77 | return false;
78 | }
79 | final Tupla, ?> other = (Tupla, ?>) obj;
80 | if (!Objects.equals(this.key, other.key))
81 | {
82 | return false;
83 | }
84 | if (!Objects.equals(this.value, other.value))
85 | {
86 | return false;
87 | }
88 | return true;
89 | }
90 |
91 | @Override
92 | public int hashCode()
93 | {
94 | int hash = 1;
95 | if (key instanceof BigInteger)
96 | {
97 | hash += ((BigInteger) key).hashCode();
98 | }
99 | else
100 | {
101 | hash += key.hashCode();
102 | }
103 | if (value instanceof Boolean)
104 | {
105 | hash += ((Boolean) value).hashCode();
106 | }
107 | else
108 | {
109 | hash += value.hashCode();
110 | }
111 | return hash;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/KPack/UserInterface/TreeUI.java:
--------------------------------------------------------------------------------
1 | package KPack.UserInterface;
2 |
3 | import KPack.KadNode;
4 | import KPack.Kademlia;
5 | import KPack.Tree.Bucket;
6 | import KPack.Tree.Node;
7 | import KPack.Tree.RoutingTree;
8 | import KPack.Tree.TreeNode;
9 | import java.awt.Dimension;
10 | import static java.lang.Thread.sleep;
11 | import java.util.Iterator;
12 | import javax.swing.JTree;
13 | import javax.swing.tree.DefaultMutableTreeNode;
14 |
15 | public class TreeUI extends javax.swing.JFrame {
16 |
17 | private JTree tree;
18 | private RoutingTree routingTree;
19 | private Kademlia thisNode;
20 |
21 | public TreeUI(RoutingTree rt, Kademlia thisNode)
22 | {
23 | routingTree = rt;
24 | this.thisNode = thisNode;
25 | Node rootNode = routingTree.getRoot();
26 | DefaultMutableTreeNode rootTree = new DefaultMutableTreeNode("");
27 | recursiveTree(rootNode, rootTree);
28 |
29 | tree = new JTree(rootTree);
30 | add(tree);
31 |
32 | this.setPreferredSize(new Dimension(500, 500));
33 | this.setSize(new Dimension(500, 500));
34 | this.setTitle("Routing Tree");
35 | this.pack();
36 | this.setVisible(true);
37 |
38 | new Thread(() ->
39 | {
40 | while (true)
41 | {
42 | try
43 | {
44 | sleep(750);
45 | }
46 | catch (InterruptedException ex)
47 | {
48 | ex.printStackTrace();
49 | }
50 |
51 | updateTree();
52 | }
53 | }).start();
54 | }
55 |
56 | @SuppressWarnings("unchecked")
57 | // //GEN-BEGIN:initComponents
58 | private void initComponents()
59 | {
60 |
61 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
62 | setTitle("Routing Tree");
63 |
64 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
65 | getContentPane().setLayout(layout);
66 | layout.setHorizontalGroup(
67 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
68 | .addGap(0, 855, Short.MAX_VALUE)
69 | );
70 | layout.setVerticalGroup(
71 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
72 | .addGap(0, 629, Short.MAX_VALUE)
73 | );
74 |
75 | pack();
76 | }// //GEN-END:initComponents
77 |
78 | private void updateTree()
79 | {
80 | remove(tree);
81 | Node rootNode = routingTree.getRoot();
82 | DefaultMutableTreeNode rootTree = new DefaultMutableTreeNode("");
83 |
84 | recursiveTree(rootNode, rootTree);
85 |
86 | tree = new JTree(rootTree);
87 | add(tree);
88 |
89 | for (int i = 0; i < tree.getRowCount(); i++)
90 | {
91 | tree.expandRow(i);
92 | }
93 | this.pack();
94 |
95 | }
96 |
97 | private void recursiveTree(Node n, DefaultMutableTreeNode dmt)
98 | {
99 | if (n instanceof Bucket)
100 | {
101 | Bucket b = (Bucket) n;
102 | //System.out.println(b.toString());
103 | synchronized (b)
104 | {
105 | Iterator ikn = b.iterator();
106 | while (ikn.hasNext())
107 | {
108 | KadNode kn = ikn.next();
109 | DefaultMutableTreeNode treeNode = null;
110 | try
111 | {
112 | treeNode = new DefaultMutableTreeNode(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: " + thisNode.distanza(thisNode.getMyNode(), kn) + ")");
113 | }
114 | catch(NullPointerException e)
115 | {
116 | treeNode = new DefaultMutableTreeNode(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: /)");
117 | }
118 | dmt.add(treeNode);
119 | }
120 | }
121 | }
122 | else
123 | {
124 | TreeNode tn = (TreeNode) n;
125 | DefaultMutableTreeNode sx = new DefaultMutableTreeNode("1");
126 | DefaultMutableTreeNode dx = new DefaultMutableTreeNode("0");
127 | dmt.add(sx);
128 | dmt.add(dx);
129 |
130 | recursiveTree(tn.getLeft(), sx);
131 | recursiveTree(tn.getRight(), dx);
132 |
133 | }
134 | }
135 | // Variables declaration - do not modify//GEN-BEGIN:variables
136 | // End of variables declaration//GEN-END:variables
137 | }
138 |
--------------------------------------------------------------------------------
/src/KPack/Files/KadFileMap.java:
--------------------------------------------------------------------------------
1 | package KPack.Files;
2 |
3 | import KPack.Kademlia;
4 |
5 | import java.io.*;
6 | import java.math.BigInteger;
7 | import java.util.HashMap;
8 | import java.util.function.BiConsumer;
9 |
10 | public class KadFileMap implements KadFileMapInterf {
11 |
12 | private HashMap fileMap;
13 | private Kademlia thisNode;
14 | private boolean serialized;
15 |
16 | public KadFileMap(Kademlia thisNode, boolean serialized)
17 | {
18 | fileMap = new HashMap<>();
19 | if(serialized) fileMap = loadListFromFile();
20 | this.thisNode = thisNode;
21 | this.serialized = serialized;
22 | }
23 |
24 | synchronized public void add(KadFile file)
25 | {
26 | fileMap.put(file.getFileID(), file);
27 | if(serialized) serializeMap();
28 | }
29 |
30 | synchronized public void remove(KadFile file)
31 | {
32 | remove(file.getFileID());
33 | }
34 |
35 | synchronized public void remove(BigInteger ID)
36 | {
37 | KadFile temp = fileMap.remove(ID);
38 |
39 | if(serialized)
40 | {
41 | serializeMap();
42 | }
43 | else
44 | {
45 | new File(temp.getPath() + File.separator + temp.getFileName()).delete();
46 | }
47 | }
48 |
49 | public void forEach(BiConsumer function) //Prendere il lock per usare questo metodo!
50 | {
51 | fileMap.forEach(function);
52 | }
53 |
54 | synchronized public void clearAll()
55 | {
56 | fileMap.clear();
57 | }
58 |
59 | synchronized public KadFile get(BigInteger i)
60 | {
61 | return fileMap.get(i);
62 | }
63 |
64 | synchronized public int size() //Prendere il lock per ciclare su size!
65 | {
66 | return fileMap.size();
67 | }
68 |
69 | private void serializeMap()
70 | {
71 | FileOutputStream fout = null;
72 | ObjectOutputStream oos = null;
73 | try
74 | {
75 | File temp = new File(thisNode.FILESPATH);
76 | if (!(temp.exists()))
77 | {
78 | temp.mkdir();
79 | }
80 | File localFiles = new File(thisNode.FILESPATH + "index");
81 | if (!(localFiles.exists()))
82 | {
83 | localFiles.createNewFile();
84 | }
85 |
86 | fout = new FileOutputStream(thisNode.FILESPATH + "index");
87 | oos = new ObjectOutputStream(fout);
88 | oos.writeObject(fileMap);
89 | }
90 | catch (IOException e)
91 | {
92 | e.printStackTrace();
93 | }
94 | finally
95 | {
96 | try
97 | {
98 | if (fout != null)
99 | {
100 | fout.close();
101 | }
102 | if (oos != null)
103 | {
104 | oos.close();
105 | }
106 | }
107 | catch (IOException ioe)
108 | {
109 | ioe.printStackTrace();
110 | }
111 | }
112 | }
113 |
114 | private HashMap loadListFromFile()
115 | {
116 |
117 | HashMap ret = new HashMap<>();
118 | File temp = new File(thisNode.FILESPATH);
119 | if (!(temp.exists()))
120 | {
121 | temp.mkdir();
122 | }
123 |
124 | File localFiles = new File(thisNode.FILESPATH + "index");
125 | if (localFiles.exists())
126 | {
127 | FileInputStream fis = null;
128 | try
129 | {
130 | fis = new FileInputStream(thisNode.FILESPATH + "index");
131 | ObjectInputStream ois = new ObjectInputStream(fis);
132 | while (true)
133 | {
134 | ret = ((HashMap) ois.readObject());
135 | }
136 | }
137 | catch (EOFException | FileNotFoundException | ClassNotFoundException e)
138 | {
139 | //Aspettate o impossibili
140 | }
141 | catch (IOException ioe)
142 | {
143 | ioe.printStackTrace();
144 | }
145 | finally
146 | {
147 | try
148 | {
149 | if (fis != null)
150 | {
151 | fis.close();
152 | }
153 | }
154 | catch (IOException ioe)
155 | {
156 | } //Ignorata
157 | finally
158 | {
159 | return ret;
160 | }
161 | }
162 | }
163 |
164 | try
165 | {
166 | localFiles.createNewFile();
167 | }
168 | catch (IOException ioe)
169 | {
170 | ioe.printStackTrace();
171 | }
172 | finally
173 | {
174 | return ret;
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/KPack/Tree/Bucket.java:
--------------------------------------------------------------------------------
1 | package KPack.Tree;
2 |
3 | import KPack.KadNode;
4 | import KPack.Kademlia;
5 | import static java.lang.Thread.sleep;
6 |
7 | import java.util.Iterator;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 | import java.util.concurrent.atomic.AtomicBoolean;
11 | import java.util.concurrent.atomic.AtomicLong;
12 |
13 | public class Bucket extends Node implements Iterable {
14 |
15 | //SOLO foglia
16 | private int dimensioneMax;
17 | private List listaNodi;
18 | private boolean splittable;
19 | private Kademlia thisKadNode;
20 | //private long timeVisited;
21 |
22 | private AtomicLong lastUse;
23 | private AtomicBoolean isActive;
24 | private Thread refresher;
25 |
26 | public Bucket(Kademlia thisKadNode, boolean splittable)
27 | {
28 | listaNodi = new LinkedList();
29 | dimensioneMax = thisKadNode.K;
30 | this.splittable = splittable;
31 | this.thisKadNode = thisKadNode;
32 |
33 | lastUse = new AtomicLong(System.currentTimeMillis());
34 | isActive = new AtomicBoolean(true);
35 | refresher = null;
36 | }
37 |
38 | public synchronized boolean add(KadNode kn)
39 | {
40 | lastUse.getAndSet(System.currentTimeMillis());
41 | listaNodi.remove(kn);
42 | if (listaNodi.size() == dimensioneMax)
43 | {
44 | if (splittable)
45 | {
46 | return false; //ci pensa l'albero
47 | }
48 | else
49 | {
50 | //pingu 1 solo nodo (il più vecchio)
51 | KadNode node = listaNodi.get(0);
52 | if (node.equals(this.thisKadNode.getMyNode()))
53 | {
54 | node = listaNodi.get(1);
55 | }
56 | if (!thisKadNode.ping(node))
57 | {
58 | listaNodi.remove(node);
59 | listaNodi.add(kn);
60 | return true;
61 | }
62 | //Se non l'ho sostituito, scarto il nuovo nodo
63 | }
64 | }
65 | else
66 | {
67 | //C'è spazio, rimuoviamo il nodo (nel caso sia già presente) e lo riaggiungiamo
68 | //il nodo nuovo è in coda
69 | listaNodi.add(kn);
70 | }
71 | return true; //l'albero non deve gestire niente
72 | }
73 |
74 | public synchronized void removeFromBucket(KadNode kn)
75 | {
76 | listaNodi.remove(kn);
77 | }
78 |
79 | public synchronized int size()
80 | {
81 | return listaNodi.size();
82 | }
83 |
84 | public synchronized KadNode get(int i)
85 | {
86 | return listaNodi.get(i);
87 | }
88 |
89 | public void setSplittable(boolean splittable)
90 | {
91 | this.splittable = splittable;
92 | }
93 |
94 | public boolean isSplittable()
95 | {
96 | return splittable;
97 | }
98 |
99 | public synchronized Iterator iterator()
100 | {
101 | return listaNodi.iterator();
102 | }
103 |
104 | /* public synchronized void setTimeVisited(long now)
105 | {
106 | timeVisited = now;
107 | }*/
108 | /* public synchronized long getTimeVisited()
109 | {
110 | return timeVisited;
111 | }*/
112 | public synchronized String toString()
113 | {
114 | String bu = "{\n";
115 | for (KadNode k : listaNodi)
116 | {
117 | bu += k.toString() + "\n";
118 | }
119 | bu += "}";
120 | return bu;
121 | }
122 |
123 | public void refreshStop()
124 | {
125 | isActive.set(false);
126 | }
127 |
128 | public void refreshStart()
129 | {
130 | if (refresher == null)
131 | {
132 | refresher = new Thread(new BucketRefresher());
133 | refresher.start();
134 | }
135 | }
136 |
137 | private class BucketRefresher implements Runnable {
138 |
139 | @Override
140 | public void run()
141 | {
142 | while (isActive.get())
143 | {
144 | if (!(System.currentTimeMillis() - lastUse.get() >= thisKadNode.bucketRefreshWait))
145 | {
146 | try
147 | {
148 | sleep(thisKadNode.bucketRefreshWait - (System.currentTimeMillis() - lastUse.get()) + 1);
149 | }
150 | catch (InterruptedException ex)
151 | {
152 | break;
153 | }
154 | }
155 | KadNode randomNode;
156 | boolean notDead;
157 | synchronized (Bucket.this)
158 | {
159 | if (size() == 0)
160 | {
161 | lastUse.set(System.currentTimeMillis());
162 | continue;
163 | }
164 | System.out.println("Eseguo il refresh del bucket" + hashCode());
165 | notDead = false;
166 | int randomIndex = (int) (Math.random() * size());
167 | randomNode = get(randomIndex); //prendo un nodo random
168 | }
169 | List knowedNodes = thisKadNode.findNode(randomNode.getNodeID()); //faccio il findNode e mi restituisce una lista
170 | //devo controllare che current ci sia in lista
171 | for (KadNode kn : knowedNodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID
172 | {
173 | if (kn.getNodeID().equals(randomNode.getNodeID())) //ho trovato un nodo con il mio stesso ID
174 | {
175 | notDead = true;
176 | }
177 | }
178 | //se nodo non torna sicuramente è morto (lo elimino), altrimenti non posso dire nulla
179 | if (!notDead)
180 | {
181 | removeFromBucket(randomNode);
182 | }
183 | /*boolean isAlive=thisKadNode.ping(randomNode);
184 | removeFromBucket(randomNode);
185 | if (isAlive) //lo aggiungo in coda ad indicare che l'ho visto vivo di recente
186 | {
187 | listaNodi.add(randomNode);
188 | }*/
189 | System.out.println("Refresh del bucket" + hashCode() + " completato");
190 | lastUse.set(System.currentTimeMillis());
191 | }
192 | System.out.println("Bucket Refresher Thread Morto");
193 | }
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/KPack/Tree/RoutingTree.java:
--------------------------------------------------------------------------------
1 | package KPack.Tree;
2 |
3 | import KPack.KadNode;
4 | import KPack.Kademlia;
5 | import KPack.UserInterface.TreeUI;
6 | import java.awt.HeadlessException;
7 | import java.util.concurrent.locks.Lock;
8 | import java.util.concurrent.locks.ReadWriteLock;
9 | import java.util.concurrent.locks.ReentrantReadWriteLock;
10 | import java.math.BigInteger;
11 |
12 | public class RoutingTree {
13 |
14 | private Node root;
15 | private static boolean instance = false;
16 | private Kademlia thisNode = null;
17 | private int bucketsDim;
18 | private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
19 | private final Lock readLock = readWriteLock.readLock();
20 | private final Lock writeLock = readWriteLock.writeLock();
21 |
22 | public RoutingTree(Kademlia thisNode)
23 | {
24 | if (instance)
25 | {
26 | return;
27 | }
28 | instance = true;
29 | bucketsDim = Kademlia.K;
30 | if (bucketsDim <= 0)
31 | {
32 | throw new java.lang.IllegalArgumentException("La dimensione deve essere maggiore di 0");
33 | }
34 | root = new Bucket(thisNode, true);
35 | this.thisNode = thisNode;
36 | }
37 |
38 | public void add(KadNode nodo)
39 | {
40 | //System.out.println("Prendo il lock dell'add dell'albero");
41 | Bucket toSplitBucket = findNodesBucket(nodo);
42 | if (!toSplitBucket.isSplittable())
43 | {
44 | toSplitBucket.add(nodo);
45 | return;
46 | }
47 |
48 | if (!writeLock.tryLock())
49 | {
50 | add(nodo);
51 | }
52 | else
53 | {
54 | //writeLock.lock();
55 | while (!((toSplitBucket = findNodesBucket(nodo)).add(nodo)))
56 | {
57 | //toSplitBucket.setTimeVisited(now);
58 | toSplitBucket.refreshStop();
59 |
60 | TreeNode temp = new TreeNode();
61 | Bucket bucketSx = new Bucket(thisNode, false);
62 | Bucket bucketDx = new Bucket(thisNode, false);
63 |
64 | temp.setLeft(bucketSx);
65 | temp.setRight(bucketDx);
66 | bucketSx.setParent(temp);
67 | bucketDx.setParent(temp);
68 |
69 | int tempDepth = toSplitBucket.getDepth();
70 | synchronized (toSplitBucket)
71 | {
72 | for (int i = 0; i < toSplitBucket.size(); i++)
73 | {
74 | BigInteger tempNodeID = toSplitBucket.get(i).getNodeID();
75 | if (tempNodeID.testBit(Kademlia.BITID - tempDepth - 1))
76 | {
77 | bucketSx.add(toSplitBucket.get(i));
78 | }
79 | else
80 | {
81 | bucketDx.add(toSplitBucket.get(i));
82 | }
83 | }
84 | Node tempBuckParent = toSplitBucket.getParent();
85 | temp.setParent(tempBuckParent);
86 | //SOLO ORA vado a modificare il nodo che dev'essere splittato
87 | if (tempBuckParent == null) //il genitore di toSplitBucket è null, quindi toSplitBucket è la radice
88 | {
89 | root = temp;
90 | }
91 | else
92 | {
93 | TreeNode tempBuckParentCast = (TreeNode) tempBuckParent;
94 | //Ora il nodo originale che devo sostituire
95 | //temp diventa nodo destro o sinistro di tempBuckParent?
96 | if (tempBuckParentCast.getLeft().equals(toSplitBucket))
97 | {
98 | tempBuckParentCast.setLeft(temp);
99 | }
100 | else
101 | {
102 | tempBuckParentCast.setRight(temp);
103 | }
104 | }
105 |
106 | //aggiorno il flag splittable
107 | if (thisNode.getNodeID().testBit(Kademlia.BITID - tempDepth - 1))
108 | {
109 | bucketSx.setSplittable(true);
110 | }
111 | else
112 | {
113 | bucketDx.setSplittable(true);
114 | }
115 | bucketSx.refreshStart();
116 | bucketDx.refreshStart();
117 | }
118 | }
119 | //System.out.println("Rilascio il lock dell'add dell'albero");
120 | writeLock.unlock();
121 | }
122 | }
123 |
124 | public Bucket findNodesBucket(KadNode node)
125 | {
126 | //System.out.println("Prendo il lock del findNodesBucket dell'albero");
127 | readLock.lock();
128 | //preghiamo gli dei del java e li ringraziamo per la loro benevolenza per la classe BigInteger
129 | Node curNode = root;
130 | int i = Kademlia.BITID - 1;
131 |
132 | while (!(curNode instanceof Bucket))
133 | {
134 | if (node.getNodeID().testBit(i--))
135 | {
136 | curNode = ((TreeNode) curNode).getLeft();
137 | }
138 | else
139 | {
140 | curNode = ((TreeNode) curNode).getRight();
141 | }
142 | }
143 | //System.out.println("Rilascio il lock del findNodesBucket dell'albero");
144 | readLock.unlock();
145 | return (Bucket) curNode;
146 | }
147 |
148 | public Node getRoot()
149 | {
150 | readLock.lock();
151 | try
152 | {
153 | return root;
154 | }
155 | finally
156 | {
157 | readLock.unlock();
158 | }
159 |
160 | }
161 |
162 | /* private class RefreshThread implements Runnable
163 | {
164 | @Override
165 | public void run()
166 | {
167 | long totTime = 0;
168 | while(true)
169 | {
170 | Node curNode = root;
171 | try {
172 | if (5*60*1000-totTime <= 0)
173 | {
174 | throw new java.lang.IllegalArgumentException("Attenzione: tempo per refreshBucket maggiore di 5 minuti!!");
175 | }
176 | Thread.sleep(5*60*1000-totTime);
177 | long timeStart = System.currentTimeMillis();
178 | refreshBucket(curNode);
179 | long timeEnd = System.currentTimeMillis();
180 | totTime = timeEnd - timeStart;
181 | }
182 | catch (InterruptedException ie)
183 | {
184 | System.err.println("Il thread di refresh dei bucket è stato interrotto");
185 | }
186 | }
187 | }*/
188 |
189 | /*private void refreshBucket(Node node)
190 | {
191 | if (node instanceof Bucket)
192 | {
193 | Bucket nodeBucket = (Bucket)node;
194 | int sizeBucket = nodeBucket.size();
195 | boolean notDead = false;
196 | synchronized (nodeBucket)
197 | {
198 | /*Iterator nodeIterator= nodeBucket.iterator();
199 | while(nodeIterator.hasNext())
200 | {
201 | KadNode current = nodeIterator.next();
202 | if(!thisNode.ping(current)) //nodo è morto
203 | {
204 | nodeBucket.removeFromBucket(current);
205 | }
206 | else
207 | { }
208 | }
209 | */
210 | /*if(nodeBucket.getTimeVisited()<= 5*60*1000)
211 | {
212 | return;
213 | }
214 | else
215 | {
216 | int randomIndex = (int) (Math.random() * sizeBucket);
217 | KadNode randomNode = nodeBucket.get(randomIndex); //prendo un nodo random
218 | List knowedNodes = thisNode.findNode(randomNode.getNodeID()); //faccio il findNode e mi restituisce una lista
219 | //devo controllare che current ci sia in lista
220 | for (KadNode kn : knowedNodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID
221 | {
222 | if (kn.getNodeID().equals(randomNode.getNodeID())) //ho trovato un nodo con il mio stesso ID
223 | {
224 | notDead = true;
225 | }
226 | }
227 | //se nodo non torna sicuramente è morto (lo elimino), altrimenti non posso dire nulla
228 | if (!notDead) {
229 | nodeBucket.removeFromBucket(randomNode);
230 | }
231 | }
232 | }
233 |
234 | }
235 | else
236 | {
237 | TreeNode intNode = (TreeNode)node;
238 | refreshBucket(intNode.getLeft());
239 | refreshBucket(intNode.getRight());
240 | }
241 | }*/
242 | // }
243 | }
244 |
--------------------------------------------------------------------------------
/src/Main.java:
--------------------------------------------------------------------------------
1 |
2 | import KPack.Exceptions.AlreadyInstancedException;
3 | import KPack.Exceptions.FileNotKnownException;
4 | import KPack.Files.KadFile;
5 | import KPack.KadNode;
6 | import KPack.Kademlia;
7 | import java.io.File;
8 |
9 | import java.io.IOException;
10 | import java.math.BigInteger;
11 | import java.nio.file.Files;
12 | import java.security.InvalidParameterException;
13 | import java.util.List;
14 | import java.util.Scanner;
15 |
16 | public class Main {
17 |
18 | public static void main(String[] args)
19 | {
20 | Kademlia myNode = null;
21 | try
22 | {
23 | myNode = new Kademlia();
24 | }
25 | catch (AlreadyInstancedException aie)
26 | {
27 | System.exit(1);
28 | }
29 | boolean keep = true;
30 | String in = null;
31 | Scanner reader = new Scanner(System.in);
32 |
33 | System.out.println("I file conosciuti nella lista sono: ");
34 | for (KadFile i : myNode.getFileList())
35 | {
36 | System.out.println(i.toString());
37 | }
38 |
39 | while (keep)
40 | {
41 | try
42 | {
43 | System.out.print("KadNode@ " + myNode.getNodeID() + "-: ");
44 | in = reader.nextLine();
45 | String[] split = in.split(" ");
46 | switch (split[0])
47 | {
48 | case "get":
49 | //Cerca di ottenere un file di cui conosce l'esistenza, se è possibile
50 | if (split.length > 1)
51 | {
52 | try
53 | {
54 | final Kademlia myNodeCopy=myNode;
55 | BigInteger id = new BigInteger(split[1]);
56 | new Thread(() ->
57 | {
58 | KadFile kf=null;
59 | for(KadFile fil:myNodeCopy.getFileList())
60 | {
61 | if(fil.getFileID().equals(id))
62 | {
63 | kf=fil;
64 | break;
65 | }
66 | }
67 | if (kf != null)
68 | {
69 | Object result = myNodeCopy.findValue(id, true);
70 | if (result instanceof byte[])
71 | {
72 | try
73 | {
74 | String path = "." + File.separator + "myDownloadedFile" + File.separator;
75 | File directory = new File(path);
76 | if (!directory.exists())
77 | {
78 | directory.mkdir();
79 | }
80 | Files.write(new File(path + kf.getFileName()).toPath(), (byte[]) result);
81 | }
82 | catch (IOException ex)
83 | {
84 | }
85 | }
86 | }
87 | }).start();
88 | }
89 | catch (Exception e)
90 | {
91 | System.out.println("ID non valido");
92 | }
93 | }
94 | else
95 | {
96 | System.out.println("Please define a file to get");
97 | }
98 | break;
99 | case "ls":
100 | //Lista tutti i file di cui conosco l'esistenza
101 | for (KadFile i : myNode.getFileList())
102 | {
103 | System.out.println(i.toString());
104 | }
105 | break;
106 | case "ip":
107 | System.out.println(myNode.getIP().getHostAddress());
108 | break;
109 | case "store":
110 | if (split.length > 1)
111 | {
112 | try
113 | {
114 | myNode.store(split[1]);
115 | }
116 | catch (IOException | InvalidParameterException ioe)
117 | {
118 | System.out.println("File specificato non valido");
119 | }
120 | }
121 | else
122 | {
123 | System.out.println("Inserisci il percorso del file");
124 | }
125 | break;
126 | case "findnode":
127 | if (split.length > 1)
128 | {
129 | List l = myNode.findNode(new BigInteger(split[1]));
130 | for (int i = 0; i < l.size(); i++)
131 | {
132 | System.out.println("******* " + l.get(i));
133 | }
134 | }
135 | else
136 | {
137 | System.out.println("Inserisci l'ID del nodo");
138 | }
139 | break;
140 | case "delete":
141 | if (split.length > 1)
142 | {
143 | try
144 | {
145 | myNode.delete(new BigInteger(split[1]));
146 | }
147 | catch (FileNotKnownException fnk)
148 | {
149 | System.out.println("Il file non esiste o non ne sei il proprietario");
150 | }
151 | }
152 | else
153 | {
154 | System.out.println("Inserisci L'ID del file");
155 | }
156 | break;
157 | case "ping":
158 | //Funzione di test
159 | if (split.length > 1)
160 | {
161 | try
162 | {
163 | if (myNode.ping(new KadNode(split[1], Short.parseShort(split[2]), new BigInteger(split[3]))))
164 | {
165 | System.out.println("VIVO");
166 | }
167 | }
168 | catch (NumberFormatException nfe)
169 | {
170 | if (myNode.ping(new KadNode(split[1], myNode.getPort(), new BigInteger(split[2]))))
171 | {
172 | System.out.println("VIVO");
173 | }
174 | }
175 | }
176 | else
177 | {
178 | System.out.println("Please define an IP address and ID or IP,Port and ID");
179 | }
180 | break;
181 | case "findvalue":
182 | if (split.length > 1)
183 | {
184 | Object value = myNode.findValue(new BigInteger(split[1]), false);
185 | if (value instanceof List)
186 | {
187 | for (int i = 0; i < ((List) value).size(); i++)
188 | {
189 | System.out.println("******* " + ((List) value).get(i));
190 | }
191 | }
192 | else
193 | {
194 | System.out.println("Il contenuto del file richiesto è stato trovato");
195 | }
196 |
197 | }
198 | else
199 | {
200 | System.out.println("Inserisci l'ID del nodo");
201 | }
202 | break;
203 | case "printtree":
204 | System.out.println(myNode.printTree());
205 | break;
206 | case "exit":
207 | System.exit(0);
208 | break;
209 | case "help":
210 | System.out.println("I possibili comandi da inserire sono i seguenti:");
211 | System.out.println("get: per ottenere uno specifico file");
212 | System.out.println("ls: per ottenere la lista di file di cui si è proprietari presenti nella rete");
213 | System.out.println("store: per memorizzare un file nella rete");
214 | System.out.println("delete: per eliminare un file dalla rete");
215 | System.out.println("ip: per conoscere il proprio ip");
216 | System.out.println("ping: per controllare che un nodo sia attivo o meno nella rete");
217 | System.out.println("findvalue: per ottenere il contenuto del file");
218 | System.out.println("findnode: per restituire una lista contenente fino a K nodi della rete più vicini all’ID specificato.");
219 | System.out.println("printtree: per visualizzare l'albero di routing");
220 | System.out.println("exit: per uscire");
221 | break;
222 | default:
223 | System.out.println("Say what? Digitare \"help\" per visualizzare lista di comandi");
224 | break;
225 | }
226 | }
227 |
228 | catch (ArrayIndexOutOfBoundsException aioobe)
229 | {
230 | System.out.println("Parametri non validi");
231 | }
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/src/KPack/UserInterface/UserInterface.java:
--------------------------------------------------------------------------------
1 | package KPack.UserInterface;
2 |
3 | import KPack.Exceptions.FileNotKnownException;
4 | import KPack.Files.KadFile;
5 | import KPack.Files.KadFileMap;
6 | import KPack.KadNode;
7 | import KPack.Kademlia;
8 | import java.io.File;
9 | import java.io.FileNotFoundException;
10 | import java.io.IOException;
11 | import static java.lang.Thread.sleep;
12 | import java.math.BigInteger;
13 | import java.nio.file.Files;
14 | import java.security.InvalidParameterException;
15 | import java.util.List;
16 | import java.util.logging.Level;
17 | import java.util.logging.Logger;
18 | import javax.swing.DefaultListModel;
19 | import javax.swing.JFileChooser;
20 |
21 | public class UserInterface extends javax.swing.JFrame {
22 |
23 | private Kademlia node;
24 | private KadFileMap fileMap;
25 | private KadFileMap localFileMap;
26 |
27 | DefaultListModel myFileModel = new DefaultListModel<>();
28 | DefaultListModel localFileModel = new DefaultListModel<>();
29 |
30 | public UserInterface(Kademlia node, KadFileMap fileMap, KadFileMap localFileMap)
31 | {
32 | this.node = node;
33 | this.fileMap = fileMap;
34 | this.localFileMap = localFileMap;
35 |
36 | initComponents();
37 |
38 | myFile.setModel(localFileModel);
39 | redundantFile.setModel(myFileModel);
40 |
41 | KadNode thisKadNode = node.getMyNode();
42 | UIID.setText(thisKadNode.getNodeID().intValue() + "");
43 | UIIP.setText(thisKadNode.getIp().getHostAddress());
44 | UIport.setText(thisKadNode.getPort() + "");
45 |
46 | localFileMap.forEach((t, u) ->
47 | {
48 | localFileModel.addElement(t.intValue() + " - " + u.getFileName());
49 | });
50 |
51 | new Thread(new UIRefresher()).start();
52 |
53 | this.setVisible(true);
54 | }
55 |
56 | // //GEN-BEGIN:initComponents
57 | private void initComponents()
58 | {
59 |
60 | jPanel1 = new javax.swing.JPanel();
61 | jLabel1 = new javax.swing.JLabel();
62 | selectedFile = new javax.swing.JTextField();
63 | jButton2 = new javax.swing.JButton();
64 | jButton3 = new javax.swing.JButton();
65 | jPanel2 = new javax.swing.JPanel();
66 | jScrollPane1 = new javax.swing.JScrollPane();
67 | myFile = new javax.swing.JList<>();
68 | jLabel2 = new javax.swing.JLabel();
69 | jButton1 = new javax.swing.JButton();
70 | jPanel3 = new javax.swing.JPanel();
71 | jLabel3 = new javax.swing.JLabel();
72 | jScrollPane2 = new javax.swing.JScrollPane();
73 | redundantFile = new javax.swing.JList<>();
74 | jPanel4 = new javax.swing.JPanel();
75 | jLabel4 = new javax.swing.JLabel();
76 | jLabel5 = new javax.swing.JLabel();
77 | jLabel6 = new javax.swing.JLabel();
78 | jLabel7 = new javax.swing.JLabel();
79 | UIport = new javax.swing.JLabel();
80 | UIIP = new javax.swing.JLabel();
81 | UIID = new javax.swing.JLabel();
82 | jPanel5 = new javax.swing.JPanel();
83 | jLabel8 = new javax.swing.JLabel();
84 | jButton4 = new javax.swing.JButton();
85 | jLabel9 = new javax.swing.JLabel();
86 | findNodeID = new javax.swing.JTextField();
87 | jScrollPane3 = new javax.swing.JScrollPane();
88 | findNodeResult = new javax.swing.JTextArea();
89 | jPanel6 = new javax.swing.JPanel();
90 | jLabel10 = new javax.swing.JLabel();
91 | fileID = new javax.swing.JTextField();
92 | jLabel11 = new javax.swing.JLabel();
93 | jButton5 = new javax.swing.JButton();
94 | filePosition = new javax.swing.JLabel();
95 |
96 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
97 | setTitle("Kademlia");
98 | setResizable(false);
99 |
100 | jPanel1.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
101 |
102 | jLabel1.setText("File Store");
103 |
104 | selectedFile.setEditable(false);
105 |
106 | jButton2.setText("Store");
107 | jButton2.addActionListener(new java.awt.event.ActionListener()
108 | {
109 | public void actionPerformed(java.awt.event.ActionEvent evt)
110 | {
111 | jButton2ActionPerformed(evt);
112 | }
113 | });
114 |
115 | jButton3.setText("Select");
116 | jButton3.addActionListener(new java.awt.event.ActionListener()
117 | {
118 | public void actionPerformed(java.awt.event.ActionEvent evt)
119 | {
120 | jButton3ActionPerformed(evt);
121 | }
122 | });
123 |
124 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
125 | jPanel1.setLayout(jPanel1Layout);
126 | jPanel1Layout.setHorizontalGroup(
127 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
128 | .addGroup(jPanel1Layout.createSequentialGroup()
129 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
130 | .addGroup(jPanel1Layout.createSequentialGroup()
131 | .addGap(250, 250, 250)
132 | .addComponent(jLabel1))
133 | .addGroup(jPanel1Layout.createSequentialGroup()
134 | .addContainerGap()
135 | .addComponent(selectedFile, javax.swing.GroupLayout.PREFERRED_SIZE, 406, javax.swing.GroupLayout.PREFERRED_SIZE)
136 | .addGap(37, 37, 37)
137 | .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 93, javax.swing.GroupLayout.PREFERRED_SIZE))
138 | .addGroup(jPanel1Layout.createSequentialGroup()
139 | .addGap(210, 210, 210)
140 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 169, javax.swing.GroupLayout.PREFERRED_SIZE)))
141 | .addContainerGap(32, Short.MAX_VALUE))
142 | );
143 | jPanel1Layout.setVerticalGroup(
144 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
145 | .addGroup(jPanel1Layout.createSequentialGroup()
146 | .addContainerGap()
147 | .addComponent(jLabel1)
148 | .addGap(51, 51, 51)
149 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
150 | .addComponent(selectedFile, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE)
151 | .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
152 | .addGap(57, 57, 57)
153 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
154 | .addContainerGap(88, Short.MAX_VALUE))
155 | );
156 |
157 | jPanel2.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
158 |
159 | myFile.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
160 | jScrollPane1.setViewportView(myFile);
161 |
162 | jLabel2.setText("My File");
163 |
164 | jButton1.setText("Delete");
165 | jButton1.addActionListener(new java.awt.event.ActionListener()
166 | {
167 | public void actionPerformed(java.awt.event.ActionEvent evt)
168 | {
169 | jButton1ActionPerformed(evt);
170 | }
171 | });
172 |
173 | javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
174 | jPanel2.setLayout(jPanel2Layout);
175 | jPanel2Layout.setHorizontalGroup(
176 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
177 | .addGroup(jPanel2Layout.createSequentialGroup()
178 | .addContainerGap()
179 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 333, Short.MAX_VALUE)
180 | .addContainerGap())
181 | .addGroup(jPanel2Layout.createSequentialGroup()
182 | .addGap(158, 158, 158)
183 | .addComponent(jLabel2)
184 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
185 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
186 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
187 | .addComponent(jButton1)
188 | .addGap(138, 138, 138))
189 | );
190 | jPanel2Layout.setVerticalGroup(
191 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
192 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
193 | .addContainerGap()
194 | .addComponent(jLabel2)
195 | .addGap(18, 18, 18)
196 | .addComponent(jScrollPane1)
197 | .addGap(18, 18, 18)
198 | .addComponent(jButton1)
199 | .addGap(24, 24, 24))
200 | );
201 |
202 | jPanel3.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
203 |
204 | jLabel3.setText("Redundant File");
205 |
206 | redundantFile.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
207 | jScrollPane2.setViewportView(redundantFile);
208 |
209 | javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
210 | jPanel3.setLayout(jPanel3Layout);
211 | jPanel3Layout.setHorizontalGroup(
212 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
213 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup()
214 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
215 | .addComponent(jLabel3)
216 | .addGap(112, 112, 112))
217 | .addGroup(jPanel3Layout.createSequentialGroup()
218 | .addContainerGap()
219 | .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE)
220 | .addContainerGap())
221 | );
222 | jPanel3Layout.setVerticalGroup(
223 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
224 | .addGroup(jPanel3Layout.createSequentialGroup()
225 | .addContainerGap()
226 | .addComponent(jLabel3)
227 | .addGap(18, 18, 18)
228 | .addComponent(jScrollPane2)
229 | .addContainerGap())
230 | );
231 |
232 | jPanel4.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
233 |
234 | jLabel4.setText("Node Info");
235 |
236 | jLabel5.setText("ID:");
237 |
238 | jLabel6.setText("IP:");
239 |
240 | jLabel7.setText("Port:");
241 |
242 | UIport.setText("vvvvvvvvvvvvvvvvvvv");
243 | UIport.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
244 |
245 | UIIP.setText("rffff");
246 | UIIP.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
247 |
248 | UIID.setText("vvvv");
249 | UIID.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
250 |
251 | javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
252 | jPanel4.setLayout(jPanel4Layout);
253 | jPanel4Layout.setHorizontalGroup(
254 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
255 | .addGroup(jPanel4Layout.createSequentialGroup()
256 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
257 | .addGroup(jPanel4Layout.createSequentialGroup()
258 | .addGap(256, 256, 256)
259 | .addComponent(jLabel4))
260 | .addGroup(jPanel4Layout.createSequentialGroup()
261 | .addGap(36, 36, 36)
262 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
263 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
264 | .addComponent(jLabel6, javax.swing.GroupLayout.Alignment.LEADING)
265 | .addComponent(jLabel5))
266 | .addComponent(jLabel7))
267 | .addGap(65, 65, 65)
268 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
269 | .addComponent(UIIP, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
270 | .addComponent(UIport, javax.swing.GroupLayout.DEFAULT_SIZE, 122, Short.MAX_VALUE)
271 | .addComponent(UIID, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
272 | .addGap(0, 269, Short.MAX_VALUE))
273 | );
274 | jPanel4Layout.setVerticalGroup(
275 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
276 | .addGroup(jPanel4Layout.createSequentialGroup()
277 | .addContainerGap()
278 | .addComponent(jLabel4)
279 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
280 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
281 | .addComponent(jLabel5)
282 | .addComponent(UIID))
283 | .addGap(18, 18, 18)
284 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
285 | .addComponent(jLabel6)
286 | .addComponent(UIIP))
287 | .addGap(18, 18, 18)
288 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
289 | .addComponent(jLabel7)
290 | .addComponent(UIport))
291 | .addContainerGap(34, Short.MAX_VALUE))
292 | );
293 |
294 | jPanel5.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
295 |
296 | jLabel8.setText("Find Node");
297 |
298 | jButton4.setText("Find");
299 | jButton4.addActionListener(new java.awt.event.ActionListener()
300 | {
301 | public void actionPerformed(java.awt.event.ActionEvent evt)
302 | {
303 | jButton4ActionPerformed(evt);
304 | }
305 | });
306 |
307 | jLabel9.setText("ID:");
308 |
309 | findNodeResult.setEditable(false);
310 | findNodeResult.setColumns(20);
311 | findNodeResult.setRows(5);
312 | jScrollPane3.setViewportView(findNodeResult);
313 |
314 | javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
315 | jPanel5.setLayout(jPanel5Layout);
316 | jPanel5Layout.setHorizontalGroup(
317 | jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
318 | .addGroup(jPanel5Layout.createSequentialGroup()
319 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
320 | .addGroup(jPanel5Layout.createSequentialGroup()
321 | .addGap(164, 164, 164)
322 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
323 | .addComponent(jButton4)
324 | .addComponent(jLabel8)))
325 | .addGroup(jPanel5Layout.createSequentialGroup()
326 | .addGap(101, 101, 101)
327 | .addComponent(jLabel9)
328 | .addGap(18, 18, 18)
329 | .addComponent(findNodeID)))
330 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
331 | .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 322, javax.swing.GroupLayout.PREFERRED_SIZE)
332 | .addContainerGap())
333 | );
334 | jPanel5Layout.setVerticalGroup(
335 | jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
336 | .addGroup(jPanel5Layout.createSequentialGroup()
337 | .addComponent(jLabel8)
338 | .addGap(35, 35, 35)
339 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
340 | .addComponent(jLabel9)
341 | .addComponent(findNodeID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
342 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 37, Short.MAX_VALUE)
343 | .addComponent(jButton4)
344 | .addGap(24, 24, 24))
345 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel5Layout.createSequentialGroup()
346 | .addGap(0, 0, Short.MAX_VALUE)
347 | .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE)
348 | .addContainerGap())
349 | );
350 |
351 | jPanel6.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
352 |
353 | jLabel10.setText("Get File");
354 |
355 | jLabel11.setText("ID:");
356 |
357 | jButton5.setText("Get");
358 | jButton5.addActionListener(new java.awt.event.ActionListener()
359 | {
360 | public void actionPerformed(java.awt.event.ActionEvent evt)
361 | {
362 | jButton5ActionPerformed(evt);
363 | }
364 | });
365 |
366 | javax.swing.GroupLayout jPanel6Layout = new javax.swing.GroupLayout(jPanel6);
367 | jPanel6.setLayout(jPanel6Layout);
368 | jPanel6Layout.setHorizontalGroup(
369 | jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
370 | .addGroup(jPanel6Layout.createSequentialGroup()
371 | .addGap(211, 211, 211)
372 | .addComponent(jLabel11)
373 | .addGap(18, 18, 18)
374 | .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
375 | .addComponent(jButton5)
376 | .addComponent(fileID, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE)
377 | .addComponent(jLabel10))
378 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
379 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel6Layout.createSequentialGroup()
380 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
381 | .addComponent(filePosition, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE)
382 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
383 | );
384 | jPanel6Layout.setVerticalGroup(
385 | jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
386 | .addGroup(jPanel6Layout.createSequentialGroup()
387 | .addComponent(jLabel10)
388 | .addGap(38, 38, 38)
389 | .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
390 | .addComponent(fileID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
391 | .addComponent(jLabel11))
392 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
393 | .addComponent(jButton5)
394 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
395 | .addComponent(filePosition)
396 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
397 | );
398 |
399 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
400 | getContentPane().setLayout(layout);
401 | layout.setHorizontalGroup(
402 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
403 | .addGroup(layout.createSequentialGroup()
404 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
405 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
406 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
407 | .addComponent(jPanel6, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
408 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
409 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
410 | .addGroup(layout.createSequentialGroup()
411 | .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
412 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
413 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
414 | .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
415 | );
416 | layout.setVerticalGroup(
417 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
418 | .addGroup(layout.createSequentialGroup()
419 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
420 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
421 | .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
422 | .addGroup(layout.createSequentialGroup()
423 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
424 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
425 | .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
426 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
427 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
428 | .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
429 | .addComponent(jPanel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
430 | .addContainerGap())
431 | );
432 |
433 | pack();
434 | }// //GEN-END:initComponents
435 |
436 | private void jButton3ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton3ActionPerformed
437 | {//GEN-HEADEREND:event_jButton3ActionPerformed
438 | JFileChooser fileChooser = new JFileChooser();
439 | int returnVal = fileChooser.showOpenDialog(this);
440 | if (returnVal == JFileChooser.APPROVE_OPTION)
441 | {
442 | File file = fileChooser.getSelectedFile();
443 | selectedFile.setText(file.getAbsolutePath());
444 | }
445 | }//GEN-LAST:event_jButton3ActionPerformed
446 |
447 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton2ActionPerformed
448 | {//GEN-HEADEREND:event_jButton2ActionPerformed
449 | setButtonEnable(false);
450 | if (!selectedFile.getText().equals(""))
451 | {
452 | try
453 | {
454 | node.store(selectedFile.getText());
455 | selectedFile.setText("");
456 | localFileModel.clear();
457 | localFileMap.forEach((t, u) ->
458 | {
459 | localFileModel.addElement(t.intValue() + " - " + u.getFileName());
460 | });
461 | }
462 | catch (FileNotFoundException | InvalidParameterException ex)
463 | {
464 | }
465 | }
466 | setButtonEnable(true);
467 | }//GEN-LAST:event_jButton2ActionPerformed
468 |
469 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton1ActionPerformed
470 | {//GEN-HEADEREND:event_jButton1ActionPerformed
471 | setButtonEnable(false);
472 | myFile.setEnabled(false);
473 | int sel = myFile.getSelectedIndex();
474 | if (sel != -1)
475 | {
476 | String el = myFile.getSelectedValue();
477 | String idS = el.split(" ")[0];
478 | try
479 | {
480 | node.delete(new BigInteger(idS));
481 | localFileModel.remove(sel);
482 | }
483 | catch (FileNotKnownException ex)
484 | {
485 | }
486 | }
487 | myFile.setEnabled(true);
488 | setButtonEnable(true);
489 | }//GEN-LAST:event_jButton1ActionPerformed
490 |
491 | private void jButton4ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton4ActionPerformed
492 | {//GEN-HEADEREND:event_jButton4ActionPerformed
493 | jButton4.setEnabled(false);
494 | String idString = findNodeID.getText();
495 | findNodeResult.setText("");
496 | try
497 | {
498 | BigInteger id = new BigInteger(idString);
499 | new Thread(() ->
500 | {
501 | List nodes = node.findNode(id);
502 | nodes.forEach((t) ->
503 | {
504 | findNodeResult.append(t + "\n");
505 | });
506 | jButton4.setEnabled(true);
507 | }).start();
508 | }
509 | catch (Exception e)
510 | {
511 | findNodeResult.setText("ID non valido");
512 | jButton4.setEnabled(true);
513 | }
514 | }//GEN-LAST:event_jButton4ActionPerformed
515 |
516 | private void jButton5ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton5ActionPerformed
517 | {//GEN-HEADEREND:event_jButton5ActionPerformed
518 | jButton5.setEnabled(false);
519 | findNodeResult.setText("");
520 | String idFile = fileID.getText();
521 | try
522 | {
523 | BigInteger id = new BigInteger(idFile);
524 |
525 | new Thread(() ->
526 | {
527 |
528 | KadFile kf = localFileMap.get(id);
529 | if (kf != null)
530 | {
531 | Object result = node.findValue(id, true);
532 | if (result instanceof byte[])
533 | {
534 | try
535 | {
536 | String path="." + File.separator + "myDownloadedFile" + File.separator;
537 | File directory = new File(path);
538 | if(!directory.exists())
539 | directory.mkdir();
540 | Files.write(new File(path+kf.getFileName()).toPath(), (byte[]) result);
541 | }
542 | catch (IOException ex)
543 | {
544 | }
545 | }
546 | }
547 | jButton5.setEnabled(true);
548 | }).start();
549 | }
550 | catch (Exception e)
551 | {
552 | findNodeResult.setText("ID non valido");
553 | jButton5.setEnabled(true);
554 | }
555 | }//GEN-LAST:event_jButton5ActionPerformed
556 |
557 | private void setButtonEnable(boolean val)
558 | {
559 | jButton2.setEnabled(val);
560 | jButton3.setEnabled(val);
561 | jButton1.setEnabled(val);
562 | jButton4.setEnabled(val);
563 | jButton5.setEnabled(val);
564 | }
565 |
566 | private class UIRefresher implements Runnable {
567 |
568 | @Override
569 | public void run()
570 | {
571 | while (true)
572 | {
573 | try
574 | {
575 | sleep(1000);
576 | }
577 | catch (InterruptedException ex)
578 | {
579 | continue;
580 | }
581 | // int myFileSel = myFile.getSelectedIndex();
582 |
583 | myFileModel.clear();
584 | fileMap.forEach((t, u) ->
585 | {
586 | myFileModel.addElement(t.intValue() + " - " + u.getFileName());
587 | });
588 | }
589 | }
590 |
591 | }
592 | // Variables declaration - do not modify//GEN-BEGIN:variables
593 | private javax.swing.JLabel UIID;
594 | private javax.swing.JLabel UIIP;
595 | private javax.swing.JLabel UIport;
596 | private javax.swing.JTextField fileID;
597 | private javax.swing.JLabel filePosition;
598 | private javax.swing.JTextField findNodeID;
599 | private javax.swing.JTextArea findNodeResult;
600 | private javax.swing.JButton jButton1;
601 | private javax.swing.JButton jButton2;
602 | private javax.swing.JButton jButton3;
603 | private javax.swing.JButton jButton4;
604 | private javax.swing.JButton jButton5;
605 | private javax.swing.JLabel jLabel1;
606 | private javax.swing.JLabel jLabel10;
607 | private javax.swing.JLabel jLabel11;
608 | private javax.swing.JLabel jLabel2;
609 | private javax.swing.JLabel jLabel3;
610 | private javax.swing.JLabel jLabel4;
611 | private javax.swing.JLabel jLabel5;
612 | private javax.swing.JLabel jLabel6;
613 | private javax.swing.JLabel jLabel7;
614 | private javax.swing.JLabel jLabel8;
615 | private javax.swing.JLabel jLabel9;
616 | private javax.swing.JPanel jPanel1;
617 | private javax.swing.JPanel jPanel2;
618 | private javax.swing.JPanel jPanel3;
619 | private javax.swing.JPanel jPanel4;
620 | private javax.swing.JPanel jPanel5;
621 | private javax.swing.JPanel jPanel6;
622 | private javax.swing.JScrollPane jScrollPane1;
623 | private javax.swing.JScrollPane jScrollPane2;
624 | private javax.swing.JScrollPane jScrollPane3;
625 | private javax.swing.JList myFile;
626 | private javax.swing.JList redundantFile;
627 | private javax.swing.JTextField selectedFile;
628 | // End of variables declaration//GEN-END:variables
629 | }
630 |
--------------------------------------------------------------------------------
/src/KPack/Kademlia.java:
--------------------------------------------------------------------------------
1 | package KPack;
2 |
3 | import KPack.Exceptions.AlreadyInstancedException;
4 | import KPack.Exceptions.FileNotKnownException;
5 | import KPack.Exceptions.InvalidSettingsException;
6 | import KPack.Files.KadFile;
7 | import KPack.Files.KadFileMap;
8 | import KPack.Packets.*;
9 | import KPack.Tree.Bucket;
10 | import KPack.Tree.Node;
11 | import KPack.Tree.RoutingTree;
12 | import KPack.Tree.TreeNode;
13 | import KPack.UserInterface.TreeUI;
14 | import KPack.UserInterface.UserInterface;
15 |
16 | import java.awt.*;
17 | import java.io.*;
18 | import static java.lang.Thread.sleep;
19 | import java.math.BigInteger;
20 | import java.net.*;
21 | import java.nio.charset.StandardCharsets;
22 | import java.nio.file.Files;
23 | import java.security.InvalidParameterException;
24 | import java.util.*;
25 | import java.util.List;
26 | import java.util.concurrent.atomic.AtomicReference;
27 | import java.util.concurrent.locks.Lock;
28 | import java.util.concurrent.locks.ReadWriteLock;
29 | import java.util.concurrent.locks.ReentrantReadWriteLock;
30 | import java.util.logging.Level;
31 | import java.util.logging.Logger;
32 |
33 | public class Kademlia implements KademliaInterf {
34 |
35 | private static boolean instance = false;
36 | public final static int BITID = 8;
37 | public final static int K = 4;
38 | public final static int ALPHA = 2;
39 | public final static String FILESPATH = "." + File.separator + "storedFiles" + File.separator;
40 | private KadFileMap fileMap;
41 | private KadFileMap localFileMap;
42 | private BigInteger nodeID;
43 | private RoutingTree routingTree;
44 | private KadNode thisNode;
45 | private ArrayList fixedNodesList = new ArrayList<>();
46 | private Set> recentlyRefreshed;
47 |
48 | //Variabili lette dalle impostazioni
49 | public int port = 1337;
50 | public int fileRefreshWait = 100000;
51 | public int fileRefreshThreadSleep = 20000;
52 | public int bucketRefreshWait = 20000;
53 | private int timeout = 10000;
54 |
55 | private final ReadWriteLock localFileReadWriteLock;
56 | private final Lock localFileReadLock;
57 | private final Lock localFileWriteLock;
58 |
59 | private final ReadWriteLock globalFileReadWriteLock;
60 | private final Lock globalFileReadLock;
61 | private final Lock globalFileWriteLock;
62 |
63 | public Kademlia() throws AlreadyInstancedException
64 | {
65 | if (instance)
66 | {
67 | throw new AlreadyInstancedException();
68 | }
69 | instance = true;
70 | Runtime.getRuntime().addShutdownHook(new Thread() { //Hook per eliminare i file ridondanti allo spegnimento
71 | @Override
72 | public void run()
73 | {
74 | File temp = new File(FILESPATH);
75 | if (temp.listFiles() != null)
76 | {
77 | for (File i : temp.listFiles())
78 | {
79 | if (i.getName().endsWith(".kad"))
80 | {
81 | i.delete();
82 | }
83 | }
84 | }
85 | }
86 | });
87 |
88 | System.out.println("WorkingDir: " + System.getProperty("user.dir"));
89 |
90 | loadSettings();
91 |
92 | //Lo rieseguo, potrebbe non essere stato eseguito in seguito ad un crash della JVM
93 | File temp = new File(FILESPATH);
94 | if (temp.listFiles() != null)
95 | {
96 | for (File i : temp.listFiles())
97 | {
98 | if (i.getName().endsWith(".kad"))
99 | {
100 | i.delete();
101 | }
102 | }
103 | }
104 |
105 | localFileReadWriteLock = new ReentrantReadWriteLock(true);
106 | localFileReadLock = localFileReadWriteLock.readLock();
107 | localFileWriteLock = localFileReadWriteLock.writeLock();
108 |
109 | globalFileReadWriteLock = new ReentrantReadWriteLock(true);
110 | globalFileReadLock = globalFileReadWriteLock.readLock();
111 | globalFileWriteLock = globalFileReadWriteLock.writeLock();
112 |
113 | fileMap = new KadFileMap(this, false);
114 | localFileMap = new KadFileMap(this, true);
115 | String myIP = getIP().getHostAddress().toString();
116 |
117 | routingTree = new RoutingTree(this);
118 |
119 | fixedNodesList = loadFixedNodesFromFile();
120 | recentlyRefreshed = new HashSet<>();
121 |
122 | //Aggiungo all'alberto i nodi noti
123 | for (FixedKadNode fkn : fixedNodesList)
124 | {
125 | routingTree.add(fkn.getKadNode());
126 | }
127 |
128 | if (!isFixedNode())
129 | {
130 | do
131 | {
132 | nodeID = new BigInteger(BITID, new Random());
133 | System.out.println("ID generato: " + nodeID);
134 | }
135 | while (!isUniqueID());
136 | }
137 | System.out.println("ID ottenuto: " + nodeID);
138 |
139 | thisNode = new KadNode(myIP, port, nodeID);
140 |
141 | routingTree.add(thisNode); //Mi aggiungo
142 |
143 | try
144 | {
145 | new TreeUI(routingTree, this);
146 | new UserInterface(this, fileMap, localFileMap);
147 | }
148 | catch (HeadlessException he) //per dispositivi senza schermo
149 | {
150 | }
151 |
152 | new Thread(new ListenerThread(), "Listener").start();
153 |
154 | networkJoin();
155 |
156 | new Thread(new FileRefresh(fileRefreshWait, fileRefreshThreadSleep), "FileRefresh").start();
157 |
158 | synchronized (routingTree.getRoot())
159 | {
160 | if (routingTree.getRoot() instanceof Bucket)
161 | {
162 | ((Bucket) routingTree.getRoot()).refreshStart();
163 | }
164 | }
165 | }
166 |
167 | private boolean isUniqueID()
168 | {
169 | List nodes = findNode(nodeID, false); //richiamo il findnode utilizzando il mio ID
170 | for (KadNode kn : nodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID
171 | {
172 | if (kn.getNodeID().equals(nodeID)) //ho trovato un nodo con il mio stesso ID
173 | {
174 | return false;
175 | }
176 | }
177 | return true; //non ci sono altri nodi con il mio ID, quindi è unico!
178 | }
179 |
180 | private void loadSettings()
181 | {
182 | File temp = new File("./settings.properties");
183 | try
184 | {
185 | if (!temp.exists())
186 | {
187 | throw new InvalidSettingsException("File di configurazione settings.properties non trovato");
188 | }
189 | for (String i : Files.readAllLines(temp.toPath(), StandardCharsets.UTF_8))
190 | {
191 | String sub = i.substring(0, 1);
192 | if (sub.equals("#"))
193 | {
194 | continue;
195 | }
196 | else
197 | {
198 | String[] split = i.split("=");
199 | switch (split[0])
200 | {
201 | case "port":
202 | if (!split[1].isEmpty())
203 | {
204 | if (Integer.valueOf(split[1]) > 1024 && Integer.valueOf(split[1]) < 65535)
205 | {
206 | port = Integer.valueOf(split[1]);
207 | }
208 | else
209 | {
210 | throw new InvalidSettingsException("Porta non valida");
211 | }
212 | }
213 | else
214 | {
215 | throw new InvalidSettingsException("Porta non valida");
216 | }
217 | break;
218 | case "bucketRefreshWait":
219 | if (!split[1].isEmpty())
220 | {
221 | try
222 | {
223 | bucketRefreshWait = Integer.parseInt(split[1]);
224 | System.out.println("\u001B[31mBucketRefreshWait: " + bucketRefreshWait + "\u001B[0m");
225 | }
226 | catch (NumberFormatException | NullPointerException e)
227 | {
228 | throw new InvalidSettingsException("Tempo di refresh dei bucket non valido");
229 | }
230 | }
231 | else
232 | {
233 | throw new InvalidSettingsException("Tempo di refresh dei bucket non valido");
234 | }
235 | break;
236 | case "fileRefreshWait":
237 | if (!split[1].isEmpty())
238 | {
239 | try
240 | {
241 | fileRefreshWait = Integer.parseInt(split[1]);
242 | System.out.println("FileRefreshWait: " + fileRefreshWait);
243 | }
244 | catch (NumberFormatException | NullPointerException e)
245 | {
246 | throw new InvalidSettingsException("Tempo di refresh dei file non valido");
247 | }
248 | }
249 | else
250 | {
251 | throw new InvalidSettingsException("Tempo di refresh dei file non valido");
252 | }
253 | break;
254 | case "socketTimeout":
255 | if (!split[1].isEmpty())
256 | {
257 | try
258 | {
259 | timeout = Integer.parseInt(split[1]);
260 | System.out.println("Timeout Socket: " + timeout);
261 | }
262 | catch (NumberFormatException | NullPointerException e)
263 | {
264 | throw new InvalidSettingsException("Timeout non valido");
265 | }
266 | }
267 | else
268 | {
269 | throw new InvalidSettingsException("Timeout non valido");
270 | }
271 | break;
272 | case "fileRefreshThreadSleep":
273 | if (!split[1].isEmpty())
274 | {
275 | try
276 | {
277 | fileRefreshThreadSleep = Integer.parseInt(split[1]);
278 | System.out.println("fileResfreshThreadSleep: " + fileRefreshThreadSleep);
279 | }
280 | catch (NumberFormatException | NullPointerException e)
281 | {
282 | throw new InvalidSettingsException("Tempo di sleep del thread FileRefresh non valido");
283 | }
284 | }
285 | else
286 | {
287 | throw new InvalidSettingsException("Tempo di sleep del thread FileRefresh non valido");
288 | }
289 | break;
290 | default:
291 | throw new InvalidSettingsException("Parametro non valido: " + split[0]);
292 | }
293 | }
294 | //Check di altri vincoli
295 | if (fileRefreshThreadSleep >= fileRefreshWait)
296 | {
297 | throw new InvalidSettingsException("Il tempo di sleep del thread dev'essere minore del tempo di refresh dei file");
298 | }
299 | }
300 | }
301 | catch (InvalidSettingsException ise)
302 | {
303 | System.err.println("\u001B[31mErrore nelle impostazioni" + ise.getMessage() + "\u001B[0m");
304 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
305 | System.exit(1);
306 | }
307 | catch (IOException ioe)
308 | {
309 | System.err.println("\u001B[31mErrore nelle impostazioni" + ioe.getMessage() + "\u001B[0m");
310 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
311 | ioe.printStackTrace();
312 | System.exit(1);
313 | }
314 | }
315 |
316 | private boolean isFixedNode()
317 | {
318 | String hostname;
319 | InetAddress addr;
320 | try
321 | {
322 | addr = InetAddress.getLocalHost();
323 | hostname = addr.getHostName();
324 |
325 | for (FixedKadNode fkn : fixedNodesList)
326 | {
327 | if (fkn.getName().toLowerCase().equals(hostname.toLowerCase()))
328 | {
329 | nodeID = fkn.getNodeID();
330 | port = fkn.getPort();
331 | return true;
332 | }
333 | }
334 | }
335 | catch (UnknownHostException uhe)
336 | {
337 | System.err.println("\u001B[31mErrore isFixedNode: " + uhe.getMessage() + "\u001B[0m");
338 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
339 | System.exit(1);
340 | }
341 |
342 | return false;
343 | }
344 |
345 | private void networkJoin()
346 | {
347 | //Faccio il findNode su me stesso
348 | List nearestNodes = findNode(nodeID, true);
349 | for (KadNode kn : nearestNodes)
350 | {
351 | routingTree.add(kn);
352 | }
353 | }
354 |
355 | public void writeFixedList() //poi questo sarà da chiamare da qualche parte una sola volta e poi da commentare
356 | {
357 | ArrayList fixNodes = new ArrayList<>();
358 | try
359 | {
360 | InetAddress inAddrPunto = InetAddress.getByName("pintini.ddns.net");
361 | String addressPunto = inAddrPunto.getHostAddress();
362 |
363 | InetAddress inAddrTavo = InetAddress.getByName("tavolino.ddns.net");
364 | String addressTavo = inAddrTavo.getHostAddress();
365 |
366 | FixedKadNode Punto = new FixedKadNode("pintini.ddns.net", (int) 1336, BigInteger.ONE, "pintini");
367 | FixedKadNode Tavolino = new FixedKadNode("tavolino.ddns.net", (int) 1336, BigInteger.valueOf(2), "tavolino");
368 |
369 | fixNodes.add(Punto);
370 | fixNodes.add(Tavolino);
371 | }
372 | catch (UnknownHostException uhe)
373 | {
374 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + uhe.getMessage() + "\u001B[0m");
375 | }
376 | //Scrive file "nodes", inserendoci la lista fixNodes e serializza il file
377 | FileOutputStream fout = null;
378 | ObjectOutputStream oos = null;
379 | try
380 | {
381 | fout = new FileOutputStream("." + File.separator + "nodes");
382 | oos = new ObjectOutputStream(fout);
383 | oos.writeObject(fixNodes);
384 | }
385 | catch (IOException ioe)
386 | {
387 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m");
388 | ioe.printStackTrace();
389 | }
390 | finally
391 | {
392 | try
393 | {
394 | if (fout != null)
395 | {
396 | fout.close();
397 | }
398 | if (oos != null)
399 | {
400 | oos.close();
401 | }
402 | }
403 | catch (IOException ioe)
404 | {
405 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m");
406 | ioe.printStackTrace();
407 | }
408 | }
409 | }
410 |
411 | private ArrayList loadFixedNodesFromFile() //va a leggere il file serializzato e restituisce la lista di KadNodes fissi.
412 | {
413 | ArrayList retFixNodes = new ArrayList<>();
414 |
415 | FileInputStream fis = null;
416 | try
417 | {
418 | fis = new FileInputStream("." + File.separator + "nodes");
419 | ObjectInputStream ois = new ObjectInputStream(fis);
420 | //retFixNodes = new ArrayList<>(); ??
421 | while (true)
422 | {
423 | retFixNodes = ((ArrayList) ois.readObject());
424 | }
425 | }
426 | catch (EOFException e)
427 | {
428 |
429 | }
430 | catch (IOException | ClassNotFoundException ioe)
431 | {
432 | System.err.println("\u001B[31mErrore nella lettura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m");
433 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
434 | ioe.printStackTrace();
435 | System.exit(1);
436 | }
437 | finally
438 | {
439 | try
440 | {
441 | if (fis != null)
442 | {
443 | fis.close();
444 | }
445 | }
446 | catch (IOException ioe)
447 | {
448 | } //Ignorata
449 | finally
450 | {
451 | return retFixNodes;
452 | }
453 | }
454 | }
455 |
456 | public InetAddress getIP()
457 | {
458 | String publicIP = null;
459 | try
460 | {
461 | URL urlForIP = new URL("https://api.ipify.org/");
462 | BufferedReader in = new BufferedReader(new InputStreamReader(urlForIP.openStream()));
463 |
464 | publicIP = in.readLine(); //IP as a String
465 | }
466 | catch (MalformedURLException mue)
467 | {
468 | System.err.println("\u001B[31mErrore nell'URL per l'IP: " + mue.getMessage() + "\u001B[31m");
469 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
470 | System.exit(1);
471 | }
472 | catch (IOException ioe)
473 | {
474 | System.err.println("\u001B[31mEccezione generale nel trovare l'IP del nodo: " + ioe.getMessage() + "\u001B[0m");
475 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
476 | ioe.printStackTrace();
477 | System.exit(1);
478 | }
479 | try
480 | {
481 | return InetAddress.getByName(publicIP);
482 | }
483 | catch (UnknownHostException e)
484 | {
485 | System.err.println("\u001B[31mHost sconosciuto nel trovare l'IP: " + e.getMessage() + "\u001B[0m");
486 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
487 | System.exit(1);
488 | return null;
489 | }
490 | }
491 |
492 | public BigInteger getNodeID()
493 | {
494 | return nodeID;
495 | }
496 |
497 | public boolean ping(KadNode node)
498 | {
499 | PingRequest pr = new PingRequest(thisNode, node);
500 | try
501 | {
502 | Socket s = new Socket();
503 | s.setSoTimeout(timeout);
504 | s.connect(new InetSocketAddress(node.getIp(), node.getPort()), timeout);
505 |
506 | OutputStream os = s.getOutputStream();
507 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
508 | outputStream.writeObject(pr);
509 | outputStream.flush();
510 |
511 | InputStream is = s.getInputStream();
512 | ObjectInputStream inputStream = new ObjectInputStream(is);
513 |
514 | long timeInit = System.currentTimeMillis();
515 | while (true)
516 | {
517 | try
518 | {
519 | Object preply = inputStream.readObject();
520 | if (preply instanceof PingReply)
521 | {
522 | if (((PingReply) preply).getSourceKadNode().equals(pr.getDestKadNode()))
523 | {
524 | is.close();
525 | s.close();
526 | return true;
527 | }
528 | }
529 | s.setSoTimeout(((int) (timeout - (System.currentTimeMillis() - timeInit))));
530 | }
531 | catch (ClassNotFoundException e)
532 | {
533 | System.err.println("\u001B[31mIl nodo pingato mi ha risposto con qualcosa che non conosco\u001B[0m");
534 | }
535 | }
536 | }
537 | catch (SocketTimeoutException soe)
538 | {
539 | System.err.println("\u001B[31mTimeout exception: " + soe.getMessage() + "\u001B[0m");
540 | return false;
541 | }
542 | catch (ConnectException soe)
543 | {
544 | System.err.println("\u001B[31mConnect Exception: " + soe.getMessage() + "\u001B[0m");
545 | return false;
546 | }
547 | catch (EOFException e)
548 | {
549 | return false;
550 | }
551 | catch (IOException ex)
552 | {
553 | ex.printStackTrace();
554 | return false;
555 | }
556 | }
557 |
558 | private Object findValue_lookup(BigInteger fileID) //Object può essere o una List oppure di tipo KadFile
559 | {
560 | /*Verifico se il file richiesto è contenuto nel nodo
561 | Controllo solo sui file ridondanti, questo per evitare che il possessore del file lo abbia modificato
562 | rispetto alla versione presente nella rete*/
563 | KadFile temp = fileMap.get(fileID);
564 | if (temp != null)
565 | {
566 | return temp;
567 | }
568 | else
569 | {
570 | return findNode_lookup(fileID); //se non lo è procedo come per il findnode_lookup
571 | }
572 | }
573 |
574 | public Object findValue(BigInteger fileID, boolean returnContent) //Object può essere di tipo List oppure di tipo byte[] (null se returnContent=false)
575 | {
576 | Bucket bucket = routingTree.findNodesBucket(thisNode);
577 | KadNode target = new KadNode("", (short) 0, fileID);
578 | int depth = ((Node) bucket).getDepth();
579 | BigInteger prefix = thisNode.getNodeID().shiftRight(BITID - depth); // prendo il prefisso relativo al bucket
580 | List lkn = new ArrayList<>(); // lista di tutti i nodi conosciuti
581 | List alphaNode;
582 | AbstractQueue queriedNode = new PriorityQueue<>((o1, o2)
583 | -> distanza(o1, target).compareTo(distanza(o2, target))); // lista dei nodi interrogati
584 | Iterator it = null;
585 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth());
586 | synchronized (bucket)
587 | {
588 | it = bucket.iterator();
589 | while (it.hasNext()) // inserisco l'intero bucket nella lista lkn
590 | {
591 | lkn.add(it.next());
592 | }
593 | //System.out.println("<<< 0 && lkn.size() < K) // se il bucket non contiene K nodi, mi sposto negli altri bucket vicini per prendere i loro nodi fino a raggiungere K
599 | {
600 | if (prefix.testBit(depth - count))
601 | {
602 | bucket = (Bucket) node.getRight();
603 | }
604 | else
605 | {
606 | bucket = (Bucket) node.getLeft();
607 | }
608 | List list = new ArrayList<>();
609 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth());
610 | synchronized (bucket)
611 | {
612 | it = bucket.iterator();
613 |
614 | while (it.hasNext())
615 | {
616 | list.add(it.next());
617 | }
618 | //System.out.println("<<< distanza(o1, thisNode).compareTo(distanza(o2, thisNode)));
622 | if (list.size() >= K - lkn.size())
623 | {
624 | for (KadNode kn : list.subList(0, K - lkn.size()))
625 | {
626 | lkn.add(kn);
627 | }
628 | }
629 | else
630 | {
631 | lkn.addAll(list);
632 | }
633 | node = (TreeNode) node.getParent();
634 | count--;
635 | }
636 | lkn.sort((o1, o2)
637 | -> distanza(o1, target).compareTo(distanza(o2, target)));
638 | if (lkn.size() >= ALPHA)
639 | {
640 | alphaNode = new ArrayList<>();
641 | for (KadNode kn : lkn.subList(0, ALPHA))
642 | {
643 | alphaNode.add(kn);
644 | }
645 |
646 | }
647 | else
648 | {
649 | alphaNode = lkn;
650 | }
651 | //chiedo anche a me stesso
652 | //uso una coppia boolean,byte[] perchè se returnContent è false allora anche se il file è stato trovato comunque il contentuo sarà null,
653 | // boolean mi permette di capire se qualche thread ha trovato il file o meno.
654 | AtomicReference> content = new AtomicReference<>();
655 | content.set(new Tupla(false, null));
656 | while (true)
657 | {
658 | int size = lkn.size(); // per capire se il round di find nodes è fallito o meno
659 | //ad ognuno degli alpha node vado a inviargli un findNode
660 | // for (int i = 0; i < alphaNode.size(); i++)
661 | Thread[] threads = new Thread[alphaNode.size()];
662 | for (int i = 0; i < alphaNode.size(); i++)
663 | {
664 | if (content.get().getKey())
665 | {
666 | return content.get().getValue();
667 | }
668 | KadNode kadNode = alphaNode.get(i);
669 | FindValueRequest fvr = new FindValueRequest(fileID, thisNode, kadNode, returnContent);
670 | threads[i] = new Thread(new Runnable() {
671 | @Override
672 | public void run()
673 | {
674 | try
675 | {
676 | Socket s = new Socket();
677 | s.setSoTimeout(timeout);
678 | s.connect(new InetSocketAddress(kadNode.getIp(), kadNode.getPort()), timeout);
679 |
680 | OutputStream os = s.getOutputStream();
681 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
682 | outputStream.writeObject(fvr);
683 | outputStream.flush();
684 |
685 | InputStream is = s.getInputStream();
686 | ObjectInputStream inputStream = new ObjectInputStream(is);
687 |
688 | long timeInit = System.currentTimeMillis();
689 | boolean state = true;
690 | while (state)
691 | {
692 | if (content.get().getKey())
693 | {
694 | is.close();
695 | s.close();
696 | return;
697 | }
698 | try
699 | {
700 | Object fvreply = inputStream.readObject();
701 | if (fvreply instanceof FindValueReply)
702 | {
703 | if (((FindValueReply) fvreply).getSourceKadNode().equals(fvr.getDestKadNode()))
704 | {
705 | //termino
706 | is.close();
707 | s.close();
708 | content.set(new Tupla(true, ((FindValueReply) fvreply).getContent()));
709 | return;
710 | }
711 | }
712 | else
713 | {
714 | if ((fvreply instanceof FindNodeReply) && ((FindNodeReply) fvreply).getSourceKadNode().equals(fvr.getDestKadNode()))
715 | {
716 | routingTree.add(((FindNodeReply) fvreply).getSourceKadNode());
717 | Iterator it1 = ((FindNodeReply) fvreply).getList().iterator();
718 | while (it1.hasNext())
719 | {
720 | KadNode k = it1.next();
721 | //System.out.println("****Prendo il lock nella lista del findNode ");
722 | synchronized ((lkn))
723 | {
724 | if (!(lkn.contains(k))) // se mi da un nodo che conosco gia, non lo inserisco
725 | {
726 | lkn.add(k);
727 | }
728 | //System.out.println("<<< distanza(o1, target).compareTo(distanza(o2, target)));
796 | if (lkn.size() < K)
797 | {
798 | if (queriedNode.containsAll(lkn))
799 | {
800 | return lkn;
801 | }
802 | }
803 | else
804 | {
805 | if (queriedNode.containsAll(lkn.subList(0, K)))
806 | {
807 | List toRet = new ArrayList<>();
808 | for (KadNode kn : lkn.subList(0, K))
809 | {
810 | toRet.add(kn);
811 | }
812 | return toRet;
813 | }
814 | }
815 | alphaNode.clear();
816 | int alphaSize;
817 | if (size == lkn.size()) //caso in cui il round di find nodes fallisce, cioè nessuno degli alpha node mi da nuovi nodi
818 | {
819 | alphaSize = K;
820 | }
821 | else
822 | {
823 | alphaSize = ALPHA;
824 | }
825 | int i = 0;
826 | while (i < lkn.size() && alphaNode.size() < alphaSize)
827 | {
828 | if (!(queriedNode.contains(lkn.get(i))))
829 | {
830 | alphaNode.add(lkn.get(i));
831 | }
832 | i++;
833 | }
834 | }
835 | }
836 |
837 | private List findNode_lookup(BigInteger targetID)
838 | {
839 | Bucket bucket = routingTree.findNodesBucket(new KadNode("", (short) 0, targetID));
840 | KadNode targetKN = new KadNode("", (short) 0, targetID);
841 | BigInteger currentID = targetID; //mi serve per tenere traccia del percorso che ho fatto nell'albero
842 | int depth = ((Node) bucket).getDepth();
843 | List lkn = new ArrayList<>();
844 | Iterator it = null;
845 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth());
846 | synchronized (bucket)//lista dei K nodi conosciuti più vicini al target
847 | {
848 | it = bucket.iterator();
849 | while (it.hasNext()) //inserisco l'intero bucket nella lista lkn (lkn conterrà i nodi (<=K) più vicini a targetID che conosco)
850 | {
851 | lkn.add(it.next());
852 | }
853 | //System.out.println("<<<= 0 && lkn.size() < K) //ricerco altri nodi vicini al targetID finche non arrivo a K o non ho guardato tutti i nodi nell'albero
858 | {
859 | //sono ad un certo TreeNode node dell'albero, se seguo il targetID, partendo da node, e vado nello stesso ramo che raggiungo seguendo il currentID (partendo da node),
860 | // allora vuol dire che il sottoalbero relativo a quel ramo l'ho già visitato e passo a visitare il sottoalbero fratello. Altrimenti vuol dire che ho già
861 | // visitato entrambi i sottoalberi (sinistro e destro) di node e mi sposto più in su al nodo padre di node.
862 | //Sfrutto il fatto che due ID sono tanto più distanti quanto più verso sinistra è il primo bit diverso. 10111 è più distante da 11111 piuttosto che da 10000
863 | if (!(targetID.testBit((BITID - count) - 1) && currentID.testBit((BITID - count) - 1)
864 | || (!(targetID.testBit((BITID - count) - 1)) && !(currentID.testBit((BITID - count) - 1)))))
865 | {
866 | //qui ho già visitato entrambi i sottoalberi
867 | node = (TreeNode) node.getParent();
868 | count--;
869 | }
870 | else
871 | {
872 | //qui il sottoalbero fratello non l'ho ancora visitato
873 | Node n;
874 | if (targetID.testBit((BITID - count) - 1)) //individuo se sono figlio destro o sinistro di node, poi mi sposto nel fratello per visitarlo
875 | {
876 | n = node.getRight();
877 | }
878 | else
879 | {
880 | n = node.getLeft();
881 | }
882 | //aggiorno currentID perchè il bit alla profondità del nodo fratello è diverso da quello del targetID, questo mi permette, quando risalgo,
883 | //di ricordarmi se ho già visitato o meno quel sottoalbero
884 | currentID = currentID.flipBit((BITID - count) - 1);
885 | if (n instanceof Bucket)
886 | {
887 | //System.out.println("****Prendo il lock del bucket " + n.getDepth());
888 | synchronized (n)
889 | {
890 | it = ((Bucket) n).iterator();
891 | while (it.hasNext())
892 | {
893 | lkn.add(it.next());
894 | }
895 | //System.out.println("<<< K)
931 | {
932 | lkn.sort((o1, o2)
933 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN)));
934 | lkn.removeAll(lkn.subList(K, lkn.size()));
935 | }
936 | return lkn;
937 | }
938 |
939 | public List findNode(BigInteger targetID)
940 | {
941 | return findNodeMethod(targetID, true);
942 | }
943 |
944 | private List findNode(BigInteger targetID, boolean track)
945 | {
946 | return findNodeMethod(targetID, track);
947 | }
948 |
949 | private List findNodeMethod(BigInteger targetID, boolean track)
950 | {
951 | Bucket bucket = routingTree.findNodesBucket(thisNode);
952 | KadNode targetKN = new KadNode("", (short) 0, targetID);
953 | int depth = ((Node) bucket).getDepth();
954 | BigInteger prefix = getNodeID().shiftRight(BITID - depth); // prendo il prefisso relativo al bucket
955 | List lkn = new ArrayList<>(); // lista di tutti i nodi conosciuti
956 | List alphaNode;
957 | AbstractQueue queriedNode = new PriorityQueue<>((o1, o2)
958 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN))); // lista dei nodi interrogati
959 | Iterator it = null;
960 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth());
961 | synchronized (bucket)
962 | {
963 | it = bucket.iterator();
964 | while (it.hasNext()) // inserisco l'intero bucket nella lista lkn
965 | {
966 | lkn.add(it.next());
967 | }
968 | //System.out.println("<<< 0 && lkn.size() < K) // se il bucket non contiene K nodi, mi sposto negli altri bucket vicini per prendere i loro nodi fino a raggiungere K
974 | {
975 | if (prefix.testBit(depth - count))
976 | {
977 | bucket = (Bucket) node.getRight();
978 | }
979 | else
980 | {
981 | bucket = (Bucket) node.getLeft();
982 | }
983 | List list = new ArrayList<>();
984 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth());
985 | synchronized (bucket)
986 | {
987 | it = bucket.iterator();
988 |
989 | while (it.hasNext())
990 | {
991 | list.add(it.next());
992 | }
993 | //System.out.println("<<< distanza(o1, thisNode).compareTo(distanza(o2, thisNode)));
997 | if (list.size() >= K - lkn.size())
998 | {
999 | for (KadNode kn : list.subList(0, K - lkn.size()))
1000 | {
1001 | lkn.add(kn);
1002 | }
1003 | }
1004 | else
1005 | {
1006 | lkn.addAll(list);
1007 | }
1008 | node = (TreeNode) node.getParent();
1009 | count--;
1010 | }
1011 | lkn.sort((o1, o2)
1012 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN)));
1013 | if (lkn.size() >= ALPHA)
1014 | {
1015 | alphaNode = new ArrayList<>();
1016 | for (KadNode kn : lkn.subList(0, ALPHA))
1017 | {
1018 | alphaNode.add(kn);
1019 | }
1020 |
1021 | }
1022 | else
1023 | {
1024 | alphaNode = lkn;
1025 | }
1026 | //chiedo anche a me stesso
1027 | while (true)
1028 | {
1029 | int size = lkn.size(); // per capire se il round di find nodes è fallito o meno
1030 | //ad ognuno degli alpha node vado a inviargli un findNode
1031 | // for (int i = 0; i < alphaNode.size(); i++)
1032 | Thread[] threads = new Thread[alphaNode.size()];
1033 | for (int i = 0; i < alphaNode.size(); i++)
1034 | {
1035 | KadNode kadNode = alphaNode.get(i);
1036 | FindNodeRequest fnr = new FindNodeRequest(targetID, thisNode, kadNode, track);
1037 | threads[i] = new Thread(() ->
1038 | {
1039 | try
1040 | {
1041 | Socket s = new Socket();
1042 | s.setSoTimeout(timeout);
1043 | s.connect(new InetSocketAddress(kadNode.getIp(), kadNode.getPort()), timeout);
1044 |
1045 | OutputStream os = s.getOutputStream();
1046 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1047 | outputStream.writeObject(fnr);
1048 | outputStream.flush();
1049 |
1050 | InputStream is = s.getInputStream();
1051 | ObjectInputStream inputStream = new ObjectInputStream(is);
1052 |
1053 | long timeInit = System.currentTimeMillis();
1054 | boolean state = true;
1055 | while (state)
1056 | {
1057 | try
1058 | {
1059 | Object fnreply = inputStream.readObject();
1060 | if (fnreply instanceof FindNodeReply)
1061 | {
1062 | if (((FindNodeReply) fnreply).getSourceKadNode().equals(fnr.getDestKadNode()))
1063 | {
1064 | routingTree.add(((FindNodeReply) fnreply).getSourceKadNode());
1065 | Iterator it1 = ((FindNodeReply) fnreply).getList().iterator();
1066 | while (it1.hasNext())
1067 | {
1068 | KadNode k = it1.next();
1069 | //System.out.println("****Prendo il lock nella lista del findNode ");
1070 | synchronized ((lkn))
1071 | {
1072 | if (!(lkn.contains(k))) // se mi da un nodo che conosco gia, non lo inserisco
1073 | {
1074 | lkn.add(k);
1075 | }
1076 | //System.out.println("<<< distanza(o1, targetKN).compareTo(distanza(o2, targetKN)));
1136 | if (lkn.size() < K)
1137 | {
1138 | if (queriedNode.containsAll(lkn))
1139 | {
1140 | return lkn;
1141 | }
1142 | }
1143 | else
1144 | {
1145 | if (queriedNode.containsAll(lkn.subList(0, K)))
1146 | {
1147 | List toRet = new ArrayList<>();
1148 | for (KadNode kn : lkn.subList(0, K))
1149 | {
1150 | toRet.add(kn);
1151 | }
1152 | return toRet;
1153 | }
1154 | }
1155 | alphaNode.clear();
1156 | int alphaSize;
1157 | if (size == lkn.size()) //caso in cui il round di find nodes fallisce, cioè nessuno dei alpha node mi da nuovi nodi
1158 | {
1159 | alphaSize = K;
1160 | }
1161 | else
1162 | {
1163 | alphaSize = ALPHA;
1164 | }
1165 | int i = 0;
1166 | while (i < lkn.size() && alphaNode.size() < alphaSize)
1167 | {
1168 | if (!(queriedNode.contains(lkn.get(i))))
1169 | {
1170 | alphaNode.add(lkn.get(i));
1171 | }
1172 | i++;
1173 | }
1174 | }
1175 | }
1176 |
1177 | public List getFileList()
1178 | {
1179 | List temp = new ArrayList<>();
1180 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa");
1181 | globalFileReadLock.lock();
1182 | localFileReadLock.lock();
1183 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa");
1184 | fileMap.forEach((k, v) -> temp.add(v));
1185 | System.out.println(fileMap.size());
1186 | localFileMap.forEach((k, v) -> temp.add(v));
1187 | System.out.println(localFileMap.size());
1188 | localFileReadLock.unlock();
1189 | globalFileReadLock.unlock();
1190 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa");
1191 | return temp;
1192 | }
1193 |
1194 | public KadNode getMyNode()
1195 | {
1196 | return thisNode;
1197 | }
1198 |
1199 | public int getPort()
1200 | {
1201 | return port;
1202 | }
1203 |
1204 | public void store(String filepath) throws FileNotFoundException, InvalidParameterException
1205 | {
1206 | File temp = new File(filepath);
1207 | if (!temp.exists())
1208 | {
1209 | throw new FileNotFoundException();
1210 | }
1211 | if (temp.isDirectory())
1212 | {
1213 | throw new InvalidParameterException("Non posso memorizzare una directory");
1214 | }
1215 |
1216 | //Genero un ID per il file e controllo se esiste
1217 | BigInteger fileID = null;
1218 | do
1219 | {
1220 | fileID = new BigInteger(BITID, new Random());
1221 | System.out.println("Cerco se esistono file con ID: " + fileID + "...");
1222 | }
1223 | while ((findValue(fileID, false)) instanceof byte[]);
1224 |
1225 | List closestK = findNode(fileID);
1226 |
1227 | localFileWriteLock.lock();
1228 | System.out.println("Il file avrà ID: " + fileID);
1229 | KadFile tempfile = new KadFile(fileID, false, temp.getName(), temp.getParent());
1230 | localFileMap.add(tempfile);
1231 | System.out.println("Invio il file a: ");
1232 | for (KadNode i : closestK)
1233 | {
1234 | System.out.println(i.getNodeID() + " (Distanza: " + distanza(new KadNode("0.0.0.0", 0, fileID), i) + ")");
1235 | }
1236 | // List closestK = findNode_lookup(fileID); togliere il commento per i test veri
1237 | for (KadNode i : closestK)
1238 | {
1239 | try
1240 | {
1241 | StoreRequest sr = new StoreRequest(new KadFile(fileID, true, temp.getName(), temp.getParent()), thisNode, i);
1242 | new Thread(() ->
1243 | {
1244 | try
1245 | {
1246 | System.out.println("Contatto " + i.getNodeID() + "(" + i.getIp() + ") per lo store");
1247 | Socket s = new Socket(i.getIp(), i.getPort());
1248 | OutputStream os = s.getOutputStream();
1249 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1250 | outputStream.writeObject(sr);
1251 | outputStream.flush();
1252 | }
1253 | catch (IOException ex)
1254 | {
1255 | System.err.println("\u001B[31mErrore nell'invio dello store: " + ex.getMessage() + "\u001B[0m");
1256 | //ex.printStackTrace();
1257 | }
1258 | }).start();
1259 |
1260 | }
1261 | catch (IOException ioe)
1262 | {
1263 | System.err.println("\u001B[31mErrore nell'eseguire lo store: " + ioe.getMessage() + "\u001B[0m");
1264 | ioe.printStackTrace();
1265 | }
1266 | }
1267 | localFileWriteLock.unlock();
1268 |
1269 | }
1270 |
1271 | public void delete(BigInteger id) throws FileNotKnownException
1272 | {
1273 | localFileWriteLock.lock();
1274 | KadFile temp = localFileMap.get(id); //Non si possono eliminare file ridondanti dalla rete, solo il proprietario può
1275 |
1276 | if (temp != null)
1277 | {
1278 | List closestK = findNode(temp.getFileID());
1279 | System.out.println("Elimino il file " + temp.getFileName() + " (" + temp.getFileID() + ") da: ");
1280 | for (KadNode i : closestK)
1281 | {
1282 | System.out.println(i.getNodeID() + " (Distanza: " + distanza(thisNode, i) + ")");
1283 | }
1284 | for (KadNode k : closestK)
1285 | {
1286 | DeleteRequest dr = new DeleteRequest(temp, thisNode, k);
1287 | new Thread(() ->
1288 | {
1289 | try
1290 | {
1291 | System.out.println("Contatto " + k.getNodeID() + "(" + k.getIp() + ") per il delete");
1292 | Socket s = new Socket();
1293 | s.setSoTimeout(timeout);
1294 | s.connect(new InetSocketAddress(k.getIp(), k.getPort()), timeout);
1295 |
1296 | OutputStream os = s.getOutputStream();
1297 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1298 | outputStream.writeObject(dr);
1299 | outputStream.flush();
1300 | }
1301 | catch (IOException ioe)
1302 | {
1303 | System.err.println("\u001B[31mErrore generale nell'eseguire il delete per " + k.getNodeID() + ": " + ioe.getMessage() + "\u001B[0m");
1304 | ioe.printStackTrace();
1305 | }
1306 | }).start();
1307 |
1308 | }
1309 | localFileMap.remove(temp);
1310 | }
1311 | else
1312 | {
1313 | throw new FileNotKnownException();
1314 | }
1315 | localFileWriteLock.unlock();
1316 | }
1317 |
1318 | private class ListenerThread implements Runnable {
1319 |
1320 | private ServerSocket listener;
1321 |
1322 | @Override
1323 | public void run()
1324 | {
1325 | try
1326 | {
1327 | listener = new ServerSocket(port);
1328 | System.out.println("Thread Server avviato\n" + "IP: " + getIP() + "\nPorta: " + port);
1329 | }
1330 | catch (IOException ex)
1331 | {
1332 | System.err.println("\u001B[31mErrore nell'apertura del socket del Thread Server: " + ex.getMessage() + "\u001B[0m");
1333 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m");
1334 | ex.printStackTrace();
1335 | System.exit(1);
1336 | }
1337 |
1338 | while (true)
1339 | {
1340 | Socket connection = null;
1341 | try
1342 | {
1343 | connection = listener.accept();
1344 |
1345 | connection.setSoTimeout(timeout);
1346 |
1347 | Object responseObject = null;
1348 |
1349 | //Analizzo la richiesta ricevuta
1350 | InputStream is = connection.getInputStream();
1351 | ObjectInputStream inStream = new ObjectInputStream(is);
1352 |
1353 | Object received = inStream.readObject();
1354 | System.out.println(received.getClass() + " received from " + connection.getInetAddress().getHostAddress());
1355 |
1356 | //se non ho ricevuto un pacchetto o non sono io il destinatario, chiudo la connessione
1357 | if (!(received instanceof Packet) || !((Packet) received).getDestKadNode().equals(thisNode))
1358 | {
1359 | connection.close();
1360 | continue;
1361 | }
1362 |
1363 | Packet p = (Packet) received;
1364 | //aggiungo il nodo sorgente all'albero di routing
1365 | if (!(received instanceof FindNodeRequest && !((FindNodeRequest) received).isTracked()))
1366 | {
1367 | new Thread(() ->
1368 | {
1369 | routingTree.add(p.getSourceKadNode());
1370 | }).start();
1371 | }
1372 |
1373 | //Elaboro la risposta
1374 | if (received instanceof FindNodeRequest)
1375 | {
1376 | FindNodeRequest fnr = (FindNodeRequest) received;
1377 |
1378 | System.out.println("Received FindNodeRequest from: " + fnr.getSourceKadNode());
1379 |
1380 | List lkn = findNode_lookup(fnr.getTargetID());
1381 |
1382 | FindNodeReply fnrep = new FindNodeReply(fnr.getTargetID(), thisNode, fnr.getSourceKadNode(), lkn);
1383 |
1384 | responseObject = fnrep;
1385 | }
1386 | else if (received instanceof FindValueRequest)
1387 | {
1388 | FindValueRequest fvr = (FindValueRequest) received;
1389 |
1390 | globalFileReadLock.lock();
1391 | Object value = findValue_lookup(fvr.getFileID());
1392 | globalFileReadLock.unlock();
1393 |
1394 | FindValueReply fvrep = null;
1395 | if (value instanceof KadFile)
1396 | {
1397 | if (fvr.isContentRequested())
1398 | {
1399 | fvrep = new FindValueReply(fvr.getFileID(), (KadFile) value, thisNode, fvr.getSourceKadNode());
1400 | }
1401 | else
1402 | {
1403 | fvrep = new FindValueReply(fvr.getFileID(), null, thisNode, fvr.getSourceKadNode());
1404 | }
1405 | responseObject = fvrep;
1406 | }
1407 | else //È una lista di KadNode tornata da findnode_lookup
1408 | {
1409 | responseObject = new FindNodeReply(fvr.getFileID(), thisNode, fvr.getSourceKadNode(), (List) value);
1410 | }
1411 |
1412 | }
1413 | else if (received instanceof StoreRequest)
1414 | {
1415 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa");
1416 | globalFileWriteLock.lock();
1417 |
1418 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa");
1419 |
1420 | StoreRequest rq = (StoreRequest) received;
1421 |
1422 | //i file ridondanti vengono salvati con estensione .FILEID.kad
1423 | System.out.println("Ho ricevuto uno store di " + rq.getFileName() + " da " + rq.getSourceKadNode().getIp());
1424 | String extension = rq.getFileName().contains("." + rq.getFileID() + ".kad") ? "" : "." + rq.getFileID() + ".kad";
1425 | File toStore = new File(FILESPATH + rq.getFileName() + extension);
1426 | toStore.delete();
1427 | toStore.createNewFile();
1428 | Files.write(toStore.toPath(), rq.getContent());
1429 | fileMap.add(new KadFile(rq.getFileID(), true, rq.getFileName() + extension, FILESPATH));
1430 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa");
1431 | globalFileWriteLock.unlock();
1432 | }
1433 | else if (received instanceof DeleteRequest)
1434 | {
1435 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa");
1436 | globalFileWriteLock.lock();
1437 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa");
1438 |
1439 | DeleteRequest dr = (DeleteRequest) received;
1440 |
1441 | System.out.println("Ho ricevuto un delete di " + dr.getFile().getFileName() + " (" + dr.getFile().getFileID() + ") da " + dr.getSourceKadNode().getIp());
1442 |
1443 | try
1444 | {
1445 | fileMap.remove(dr.getFile().getFileID());
1446 | }
1447 | catch (NullPointerException npe)
1448 | {
1449 | System.out.println("..Ma non ce l'ho!");
1450 | }
1451 | //Controllo di non averlo refreshato di recente
1452 | if (recentlyRefreshed.contains(new Tupla<>(dr.getFile().getFileID(), dr.getFile().getFileName())))
1453 | {
1454 | List temp = findNode(dr.getFile().getFileID(), true);
1455 | for (KadNode i : temp)
1456 | {
1457 | //DA PARALLELIZZARE
1458 | new Thread(() ->
1459 | {
1460 | try
1461 | {
1462 | Socket tempS = new Socket();
1463 | tempS.setSoTimeout(timeout);
1464 | tempS.connect(new InetSocketAddress(i.getIp(), i.getPort()), timeout);
1465 | OutputStream os = tempS.getOutputStream();
1466 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1467 | outputStream.writeObject(new DeleteRequest(dr.getFile(), thisNode, i));
1468 | outputStream.flush();
1469 | }
1470 | catch (IOException ex)
1471 | {
1472 | Logger.getLogger(Kademlia.class.getName()).log(Level.SEVERE, null, ex);
1473 | }
1474 | }).start();
1475 | recentlyRefreshed.remove(new Tupla<>(dr.getFile().getFileID(), dr.getFile().getFileName()));
1476 | }
1477 | }
1478 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa");
1479 | globalFileWriteLock.unlock();
1480 | }
1481 | else if (received instanceof PingRequest)
1482 | {
1483 | PingRequest pr = (PingRequest) received;
1484 |
1485 | KadNode sourceKadNode = pr.getSourceKadNode();
1486 |
1487 | System.out.println("Received PingRequest from: " + pr.getSourceKadNode().toString());
1488 |
1489 | PingReply reply = new PingReply(thisNode, sourceKadNode);
1490 |
1491 | responseObject = reply;
1492 | }
1493 |
1494 | if (responseObject != null)
1495 | {
1496 | OutputStream os = connection.getOutputStream();
1497 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1498 | try
1499 | {
1500 | System.out.println("Rispondo a " + connection.getInetAddress().getHostAddress() + " (" + ((Packet) responseObject).getDestKadNode().getNodeID() + ") con una " + responseObject.getClass());
1501 | }
1502 | catch (NullPointerException npe)
1503 | {
1504 | System.out.println("Rispondo a " + connection.getInetAddress().getHostAddress() + " con una " + responseObject.getClass());
1505 | }
1506 | outputStream.writeObject(responseObject);
1507 | outputStream.flush();
1508 | os.close();
1509 | }
1510 | }
1511 | catch (ClassNotFoundException ex)
1512 | {
1513 | System.err.println("\u001B[31mClassNotFound nel thread server: " + ex.getMessage() + "\u001B[0m");
1514 | ex.printStackTrace();
1515 | }
1516 | catch (IOException ex)
1517 | {
1518 | System.err.println("\u001B[31mIOException nel thread server: " + ex.getMessage() + "\u001B[0m");
1519 | ex.printStackTrace();
1520 | }
1521 | finally
1522 | {
1523 | try
1524 | {
1525 | if (connection != null)
1526 | {
1527 | connection.close();
1528 | }
1529 | }
1530 | catch (IOException ex)
1531 | {
1532 | System.err.println("\u001B[31mIOException nel thread server: " + ex.getMessage() + "\u001B[0m");
1533 | ex.printStackTrace();
1534 | }
1535 | }
1536 | System.out.println("Connessione conclusa");
1537 | }
1538 | }
1539 | }
1540 |
1541 | private class FileRefresh implements Runnable {
1542 |
1543 | private int refreshWait;
1544 | private int refreshThreadSleep;
1545 |
1546 | public FileRefresh(int refreshWait, int refreshThreadSleep)
1547 | {
1548 | this.refreshWait = refreshWait;
1549 | this.refreshThreadSleep = refreshThreadSleep;
1550 | }
1551 |
1552 | public void run()
1553 | {
1554 | while (true)
1555 | {
1556 | try
1557 | {
1558 | Thread.sleep(refreshThreadSleep);
1559 | System.out.println("Inizio il refresh dei file..");
1560 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa");
1561 | List toBeDeleted = new ArrayList<>();
1562 |
1563 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa");
1564 | //si refreshano solo i file ridondanti
1565 | globalFileReadLock.lock();
1566 | recentlyRefreshed.clear();
1567 | List copyList = new ArrayList<>();
1568 | fileMap.forEach((k, v) ->
1569 | {
1570 | copyList.add(v);
1571 | });
1572 | globalFileReadLock.unlock();
1573 | copyList.forEach((v) ->
1574 | {
1575 | try
1576 | {
1577 | sleep(5000); //aspetto 5 secondi tra un file e il successivo per lasciare libero il lock per la write
1578 | }
1579 | catch (InterruptedException ex)
1580 | {
1581 | }
1582 | globalFileReadLock.lock();
1583 | if (fileMap.get(v.getFileID()) == null)
1584 | {
1585 | globalFileReadLock.unlock();
1586 | return;
1587 | }
1588 | globalFileReadLock.unlock();
1589 | if ((System.currentTimeMillis() - v.getLastRefresh()) >= refreshWait)
1590 | {
1591 | System.out.println("++++Refresho " + v.getFileName());
1592 | List temp = findNode(v.getFileID(), true);
1593 | //Se non sono tra i K nodi più vicini a quell'ID, elimino il file da me e passo al prossimo file
1594 | boolean state = true;
1595 | for (KadNode n : temp)
1596 | {
1597 | if (thisNode.getNodeID().equals(n.getNodeID()))
1598 | {
1599 | state = false;
1600 | break;
1601 | }
1602 | }
1603 | if (state)
1604 | {
1605 | System.out.println("+++Non sono tra i nodi più vicini! Elimino il file");
1606 | toBeDeleted.add(v);
1607 | return;
1608 | }
1609 |
1610 | recentlyRefreshed.add(new Tupla<>(v.getFileID(), v.getFileName()));
1611 | //Ora passo al refresh vero e proprio
1612 | for (KadNode n : temp)
1613 | {
1614 | System.out.println("++++Tento di contattare " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ")");
1615 | if (n.getNodeID().equals(thisNode.getNodeID()))
1616 | {
1617 | System.out.println("Ma sono io, dunque proseguo");
1618 | continue;
1619 | }
1620 | Socket tempS = null;
1621 | try
1622 | {
1623 | tempS = new Socket();
1624 | tempS.setSoTimeout(timeout);
1625 | tempS.connect(new InetSocketAddress(n.getIp(), n.getPort()), timeout);
1626 | OutputStream os = tempS.getOutputStream();
1627 | ObjectOutputStream outputStream = new ObjectOutputStream(os);
1628 | outputStream.writeObject(new FindValueRequest(v.getFileID(), thisNode, n, false));
1629 | outputStream.flush();
1630 |
1631 | InputStream is = tempS.getInputStream();
1632 | ObjectInputStream ois = new ObjectInputStream(is);
1633 | try
1634 | {
1635 | Object resp = ois.readObject();
1636 | if (resp instanceof FindNodeReply)
1637 | {
1638 | KadFile toSend = new KadFile(v.getFileID(), true, v.getFileName(), v.getPath());
1639 | System.out.println("\u001B[32m ++++Invio a " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ") \u001B[0m");
1640 |
1641 | try
1642 | {
1643 | tempS.close();
1644 | }
1645 | catch (IOException e)
1646 | {
1647 | e.printStackTrace();
1648 | }
1649 | tempS = new Socket();
1650 | tempS.setSoTimeout(timeout);
1651 | tempS.connect(new InetSocketAddress(n.getIp(), n.getPort()), timeout);
1652 | os = tempS.getOutputStream();
1653 | outputStream = new ObjectOutputStream(os);
1654 | outputStream.writeObject(new StoreRequest(toSend, thisNode, n));
1655 | outputStream.flush();
1656 |
1657 | System.out.println("\u001B[32m ++++Invio a " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ") completato \u001B[0m");
1658 | }
1659 | }
1660 | catch (EOFException eofe)
1661 | {
1662 | //Aspettata, ignoro
1663 | }
1664 | catch (ClassNotFoundException cnfe)
1665 | {
1666 | System.err.println("\u001B[31mErrore nella risposta durante il refresh: " + cnfe.getMessage() + "\u001B[0m");
1667 | //Gli invio comunque il file
1668 | KadFile toSend = new KadFile(v.getFileID(), true, v.getFileName(), v.getPath());
1669 | outputStream.writeObject(new StoreRequest(toSend, thisNode, n));
1670 | }
1671 | }
1672 | catch (SocketException se)
1673 | {
1674 | System.err.println("\u001B[31mImpossibile aprire il socket verso " + n.getIp().toString() + ": " + se.getMessage() + "\u001B[0m");
1675 | }
1676 | catch (EOFException eofe)
1677 | {
1678 | System.err.println("\u001B[31mEOF di: " + eofe.getCause() + "\u001B[0m");
1679 | }
1680 | catch (IOException ioe)
1681 | {
1682 | System.err.println("\u001B[31mErrore nel refresh: " + ioe.getMessage() + "\u001B[0m");
1683 | ioe.printStackTrace();
1684 | }
1685 | finally
1686 | {
1687 | try
1688 | {
1689 | if (tempS != null)
1690 | {
1691 | tempS.close();
1692 | }
1693 | }
1694 | catch (IOException ioe)
1695 | {
1696 | System.err.println("\u001B[31mErrore generale nel chiudere il socket del refresh: " + ioe.getMessage() + "\u001B[0m");
1697 | ioe.printStackTrace();
1698 | }
1699 | }
1700 | }
1701 | v.setLastRefresh(System.currentTimeMillis());
1702 | }
1703 | });
1704 |
1705 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa");
1706 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa");
1707 | globalFileWriteLock.lock();
1708 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa");
1709 | //Elimino qua i file per evitare ConcurrentModificationExceptions
1710 | for (KadFile i : toBeDeleted)
1711 | {
1712 | fileMap.remove(i.getFileID());
1713 | }
1714 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa");
1715 | globalFileWriteLock.unlock();
1716 | System.out.println("Refresh dei file finito");
1717 | }
1718 | catch (InterruptedException ie)
1719 | {
1720 | System.err.println("\u001B[31mThread di refresh dei file interrotto\u001B[0m");
1721 | }
1722 | }
1723 | }
1724 | }
1725 |
1726 | public static BigInteger distanza(KadNode o1, KadNode o2)
1727 | {
1728 | return o1.getNodeID().xor(o2.getNodeID());
1729 | }
1730 |
1731 | public static String intToBinary(BigInteger n)
1732 | {
1733 | String num = "";
1734 | for (int i = 0; i < Kademlia.BITID; i++)
1735 | {
1736 | num = (n.testBit(i) ? 1 : 0) + num;
1737 | }
1738 | return num;
1739 | }
1740 |
1741 | public String printTree()
1742 | {
1743 | StringBuilder sb = new StringBuilder();
1744 | printTree(routingTree.getRoot(), 0, sb);
1745 | return sb.toString();
1746 | }
1747 |
1748 | private void printTree(Node n, int indent, StringBuilder sb)
1749 | {
1750 | if (n instanceof Bucket)
1751 | {
1752 | Bucket b = (Bucket) n;
1753 | Iterator ikn = b.iterator();
1754 | while (ikn.hasNext())
1755 | {
1756 | StringBuilder s1 = new StringBuilder();
1757 | for (int i = 0; i < indent; i++)
1758 | {
1759 | s1.append("| ");
1760 | }
1761 | sb.append(s1);
1762 |
1763 | sb.append("+ ");
1764 | KadNode kn = ikn.next();
1765 | sb.append(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: " + distanza(thisNode, kn) + ")");
1766 | sb.append("\n");
1767 | }
1768 | return;
1769 | }
1770 | else
1771 | {
1772 | TreeNode tn = (TreeNode) n;
1773 |
1774 | StringBuilder s1 = new StringBuilder();
1775 | for (int i = 0; i < indent; i++)
1776 | {
1777 | s1.append("| ");
1778 | }
1779 | sb.append(s1);
1780 | sb.append("+--");
1781 | sb.append("1");
1782 | sb.append("\n");
1783 |
1784 | printTree(tn.getLeft(), indent + 1, sb);
1785 | s1 = new StringBuilder();
1786 | for (int i = 0; i < indent; i++)
1787 | {
1788 | s1.append("| ");
1789 | }
1790 | sb.append(s1);
1791 | sb.append("+--");
1792 | sb.append("0");
1793 | sb.append("\n");
1794 | printTree(tn.getRight(), indent + 1, sb);
1795 | }
1796 | }
1797 | }
1798 |
--------------------------------------------------------------------------------