Index: project/src/restore.sh
===================================================================
--- project/src/restore.sh	(nonexistent)
+++ project/src/restore.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj RESTORE test1.pdf
\ No newline at end of file
Index: project/src/files/test1.pdf
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: project/src/files/test1.pdf
===================================================================
--- project/src/files/test1.pdf	(nonexistent)
+++ project/src/files/test1.pdf	(revision 1)

Property changes on: project/src/files/test1.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: project/src/rmipeer.sh
===================================================================
--- project/src/rmipeer.sh	(nonexistent)
+++ project/src/rmipeer.sh	(revision 1)
@@ -0,0 +1,5 @@
+find . -type f -name '*.class' -delete
+
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/peer/Peer.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.server.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/peer/Peer 1.0 0 192.168.0.1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
\ No newline at end of file
Index: project/src/files/test2.txt
===================================================================
--- project/src/files/test2.txt	(nonexistent)
+++ project/src/files/test2.txt	(revision 1)
@@ -0,0 +1 @@
+FICHEIRO DE TESTE
\ No newline at end of file
Index: project/src/main/java/database/Database.java
===================================================================
--- project/src/main/java/database/Database.java	(nonexistent)
+++ project/src/main/java/database/Database.java	(revision 1)
@@ -0,0 +1,312 @@
+package main.java.database;
+
+
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.peer.Peer;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class Database implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+
+
+    private ConcurrentHashMap<FileChunkID, ArrayList<Integer>> perceivedRepDeg;
+    private ConcurrentHashMap<FileChunkID, Integer> desiredRepDeg;
+    private List<FileID> storedFiles;
+
+
+    public Database() {
+
+        storedFiles = new ArrayList<>();
+        perceivedRepDeg = new ConcurrentHashMap<FileChunkID, ArrayList<Integer>>();
+        desiredRepDeg = new ConcurrentHashMap<FileChunkID, Integer>();
+    }
+
+
+
+
+    public synchronized void insertChunkInfo(FileID fileID, int replicationDegree, int chunkNo, int peerID) {
+        FileChunkID id = new FileChunkID(fileID.toString(), chunkNo);
+
+        perceivedRepDeg.put(id, new ArrayList<>());
+        perceivedRepDeg.get(id).add(peerID);
+        desiredRepDeg.put(id, replicationDegree);
+
+        Peer.saveDBToDisk();
+
+    }
+
+    public synchronized void removeChunkInfo(FileChunkID chunkID){
+        desiredRepDeg.remove(chunkID);
+        perceivedRepDeg.remove(chunkID);
+        Peer.saveDBToDisk();
+
+    }
+
+    public void printDatabase() {
+        /*
+        For each file whose backup it has initiated:
+                The file pathname
+                The backup service id of the file
+                The desired replication degree
+                    For each chunk of the file:
+                        Its id
+                        Its perceived replication degree
+         */
+
+
+
+        System.out.println("----------------------------------------------------");
+
+        System.out.println("Printing Database Info...\n\n");
+
+        if(!storedFiles.isEmpty()){
+            System.out.println("Files I have backed up: ");
+
+
+
+            for (FileID fid: storedFiles){
+
+                if(fid.getNumChunks()!=-1){
+                    System.out.println("\t id: " + fid.toString());
+                    System.out.println("\t Desired rep degree: " + fid.getDesiredRepDeg());
+                    System.out.println("\t Chunks: ");
+
+                    Iterator<Map.Entry<FileChunkID, ArrayList<Integer>>> it = perceivedRepDeg.entrySet().iterator();
+                    while (it.hasNext()) {
+                        Map.Entry<FileChunkID, ArrayList<Integer>> pair = it.next();
+
+                        FileChunkID cID = pair.getKey();
+
+                        if(cID.getFileID().equals(fid.toString())){
+
+                            ArrayList<Integer> replications = pair.getValue();
+
+                            System.out.println("\t\t ChunkNo:" + cID.getChunkNumber()+ " Perceived Replication Degree:"
+                            + replications.size());
+                        }
+
+
+                        it.remove(); // avoids a ConcurrentModificationException
+                    }
+
+                }
+
+            }
+
+            System.out.println("----------------------------------------------------");
+            System.out.println("Chunks on my system: ");
+            for (FileID fid: storedFiles){
+
+                if(fid.getNumChunks()==-1){
+
+                    Iterator<Map.Entry<FileChunkID, ArrayList<Integer>>> it = perceivedRepDeg.entrySet().iterator();
+                    while (it.hasNext()) {
+                        Map.Entry<FileChunkID, ArrayList<Integer>> pair = it.next();
+
+                        FileChunkID cID = pair.getKey();
+
+                        if(cID.getFileID().equals(fid.toString())){
+
+                            ArrayList<Integer> replications = pair.getValue();
+
+                            File f = new File("peer"+Peer.getID()+"/Backup/"+fid.toString().split("\\.")[0]+
+                                    "/"+cID.toString());
+
+                            System.out.println("\t\t ChunkNo:" + cID.getChunkNumber()+ " Perceived Replication Degree:"
+                                    + replications.size() + " Size:"+ f.length());
+                        }
+
+
+                        it.remove(); // avoids a ConcurrentModificationException
+                    }
+
+                }
+
+            }
+
+            System.out.println("----------------------------------------------------");
+
+
+
+        }
+
+    }
+
+    public synchronized void insertFile(FileID fileID) {
+
+        if(!storedFiles.contains(fileID)){
+            storedFiles.add(fileID);
+            Peer.saveDBToDisk();
+        }
+        else {
+            int i = storedFiles.indexOf(fileID);
+            storedFiles.get(i).setNumChunks(fileID.getNumChunks());
+        }
+
+    }
+
+    public synchronized void removeFile(FileID fileID){
+        storedFiles.remove(fileID);
+
+        Peer.saveDBToDisk();
+
+
+    }
+
+    public synchronized int getNumChunksOfFile(FileID fID){
+        int index = storedFiles.indexOf(fID);
+        if(index!=-1)
+            return storedFiles.get(index).getNumChunks();
+        else
+            return -1;
+
+    }
+
+    public String printStoredFiles() {
+        return "\t Stored Files: \n " +
+                storedFiles.toString() + "\n";
+    }
+
+    public boolean isFileStored(FileID fID) {
+
+
+        return storedFiles.contains(fID);
+    }
+
+
+
+    public List<String> getFileChunksofFileID(FileID fileID) {
+
+
+        List<String> chunksOfFile = new ArrayList<>();
+
+        printDatabase();
+        for(FileID fid : storedFiles) {
+            if(fid.equals(fileID)) {
+
+                for (int i = 0; i < fid.getNumChunks(); i++){
+                    chunksOfFile.add(fid + "-" + i);
+                }
+            }
+
+        }
+
+        return chunksOfFile;
+    }
+
+
+    public synchronized void addNewRepDegCounter(FileChunkID chunkID, Integer repDeg){
+
+        if (!perceivedRepDeg.containsKey(chunkID)) {
+            perceivedRepDeg.put(chunkID, new ArrayList<>());
+        }
+
+
+        if (!desiredRepDeg.containsKey(chunkID)) {
+            desiredRepDeg.put(chunkID, repDeg);
+        }
+
+        System.out.println("SAVING DB");
+        Peer.saveDBToDisk();
+    }
+
+    public void removeRepDegCounter(FileChunkID chunkID){
+        perceivedRepDeg.remove(chunkID);
+        desiredRepDeg.remove(chunkID);
+    }
+
+    public synchronized void increasePerceivedRepDeg(FileChunkID chunkID, int senderID){
+
+        if(perceivedRepDeg.containsKey(chunkID)) {
+
+            if (!perceivedRepDeg.get(chunkID).contains(senderID)) {
+                perceivedRepDeg.get(chunkID).add(senderID);
+                System.out.println("SAVING DB");
+                //dumpPerceived();
+                Peer.saveDBToDisk();
+            }
+
+        }
+        else {
+            perceivedRepDeg.put(chunkID, new ArrayList<>());
+            perceivedRepDeg.get(chunkID).add(senderID);
+        }
+
+
+    }
+
+    public void dumpPerceived() {
+        System.out.println("DUMPING PERCEIVED!");
+        for (FileChunkID name: perceivedRepDeg.keySet()){
+
+            String key =name.toString();
+            String value = perceivedRepDeg.get(name).toString();
+            System.out.println(key + " " + value);
+
+        }
+
+    }
+
+    public void decreasePerceivedRepDeg(FileChunkID chunkID, int senderID){
+
+
+
+
+
+        if(perceivedRepDeg.containsKey(chunkID)) {
+
+            if (perceivedRepDeg.get(chunkID).contains(senderID)) {
+                System.out.println("DECRESING FROM: " + perceivedRepDeg.get(chunkID).size());
+                perceivedRepDeg.get(chunkID).remove(senderID);
+                if(perceivedRepDeg.get(chunkID).size()==0)
+                    perceivedRepDeg.remove(chunkID);
+
+                System.out.println("DECRESING TO: " + perceivedRepDeg.get(chunkID).size());
+                System.out.println("SAVING DB");
+                Peer.saveDBToDisk();
+            }
+
+        }
+    }
+
+    public int getPerceivedRepDeg(FileChunkID chunkID){
+
+        //dumpPerceived();
+
+        if(perceivedRepDeg.containsKey(chunkID))
+            return perceivedRepDeg.get(chunkID).size();
+        else
+            return -1;
+    }
+
+    public Integer getDesiredRepDeg(FileChunkID chunkID){
+        if(desiredRepDeg.containsKey(chunkID))
+            return desiredRepDeg.get(chunkID);
+        else
+            return -1;
+    }
+
+
+    public synchronized FileChunkID getHighestPerceivedRepDegChunk() {
+        FileChunkID best = null;
+        for (FileChunkID chunkID : perceivedRepDeg.keySet()) {
+            if (best == null || perceivedRepDeg.get(chunkID).size() > perceivedRepDeg.get(best).size())
+                best = chunkID;
+
+        }
+
+        return best;
+    }
+
+
+}
\ No newline at end of file
Index: project/README.md
===================================================================
--- project/README.md	(nonexistent)
+++ project/README.md	(revision 1)
@@ -0,0 +1,61 @@
+# SDIS-P1
+
+
+## From the src/ folder:
+
+### Compile the Peer:
+javac main/java/peer/Peer.java
+
+
+### Compile the TestApp:
+javac main/java/testapp/TestApp.java
+
+### Run RMI:
+rmiregistry &
+
+
+### Run Peer that launches RMI:
+java main/java/peer/Peer <protocol_version> <peer_id> <RMI_Access_Point> <MC_IP>:<MP_PORT> <MDB_IP>:<MDP_PORT> <MDR_IP>:<MDR_PORT>
+
+ex:
+java main/java/peer/Peer 1.0 0 192.168.0.1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
+
+### Run normal peers:
+java main/java/peer/Peer <protocol_version> <peer_id> <MC_IP>:<MP_PORT> <MDB_IP>:<MDP_PORT> <MDR_IP>:<MDR_PORT>
+
+ex:
+java main/java/peer/Peer 1.0 1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
+
+
+
+### Run a TestApp:
+
+## Backup:
+java main/java/testapp/TestApp <rmi_peer_ip>/obj BACKUP <file_path> <replication_degree>
+
+ex:
+java main/java/testapp/TestApp 127.0.0.1/obj BACKUP /Users/zemiguel/IdeaProjects/SDIS-P1/src/files/test1.pdf 1
+
+## Restore:
+java main/java/testapp/TestApp <rmi_peer_ip>/obj RESTORE <file_name> <replication_degree>
+
+ex:
+java main/java/testapp/TestApp 127.0.0.1/obj RESTORE test1.pdf
+
+## Delete:
+java main/java/testapp/TestApp <rmi_peer_ip>/obj DELETE <file_name>
+
+ex:
+java main/java/testapp/TestApp 127.0.0.1/obj DELETE test1.pdf
+
+## Reclaim:
+java main/java/testapp/TestApp <rmi_peer_ip>/obj RECLAIM <amount>
+
+ex:
+java main/java/testapp/TestApp 127.0.0.1/obj RECLAIM 0
+
+## State:
+java main/java/testapp/TestApp <rmi_peer_ip>/obj STATE
+
+ex:
+java main/java/testapp/TestApp 127.0.0.1/obj STATE
Index: project/src/main/java/file/FileChunk.java
===================================================================
--- project/src/main/java/file/FileChunk.java	(nonexistent)
+++ project/src/main/java/file/FileChunk.java	(revision 1)
@@ -0,0 +1,43 @@
+package main.java.file;
+
+import java.io.Serializable;
+
+public class FileChunk implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final int CHUNK_MAX_SIZE = 64000;
+    private int replicationDegree;
+    private int chunkNo;
+    private FileID fileID;
+    private byte[] chunkData;
+
+    public FileChunk(int replicationDegree, int chunkNo, FileID fileID, byte[] chunkData) {
+        this.replicationDegree = replicationDegree;
+        this.chunkNo = chunkNo;
+        this.fileID = fileID;
+        this.chunkData = chunkData;
+    }
+
+    public int getChunkNo() {
+        return chunkNo;
+    }
+
+    public FileID getFileID() {
+        return fileID;
+    }
+
+    public byte[] getChunkData() {
+        return chunkData;
+    }
+
+    public int getReplicationDegree() {
+        return replicationDegree;
+    }
+
+    @Override
+    public String toString() {
+        return new String(chunkData);
+    }
+}
+
Index: project/src/main/java/file/FileChunkID.java
===================================================================
--- project/src/main/java/file/FileChunkID.java	(nonexistent)
+++ project/src/main/java/file/FileChunkID.java	(revision 1)
@@ -0,0 +1,58 @@
+package main.java.file;
+
+
+import java.io.Serializable;
+
+public class FileChunkID implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+
+    private String fileID;
+    private int chunkNumber;
+
+    public FileChunkID(String fileID, int chunkNumber) {
+        this.fileID = fileID;
+        this.chunkNumber = chunkNumber;
+    }
+
+    public String getFileID() {
+        return fileID;
+    }
+
+    public int getChunkNumber() {
+        return chunkNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + fileID.hashCode();
+        result = 31 * result + chunkNumber;
+
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+
+        if (this == obj)
+            return true;
+
+        if (obj == null)
+            return false;
+
+
+        FileChunkID other = (FileChunkID) obj;
+
+        if (this.chunkNumber != other.chunkNumber)
+            return false;
+
+        return this.fileID.equals(other.fileID);
+    }
+
+    @Override
+    public String toString() {
+        return fileID + "-" + chunkNumber;
+    }
+}
\ No newline at end of file
Index: project/src/main/java/file/FileID.java
===================================================================
--- project/src/main/java/file/FileID.java	(nonexistent)
+++ project/src/main/java/file/FileID.java	(revision 1)
@@ -0,0 +1,54 @@
+package main.java.file;
+
+import java.io.Serializable;
+
+public class FileID implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private String fileID;
+    private int numChunks;
+    private int desiredRepDeg;
+
+
+    public FileID(String fileID, int desiredRepDeg) {
+        this.fileID = fileID;
+        this.numChunks = -1;
+        this.desiredRepDeg = desiredRepDeg;
+
+    }
+
+    @Override
+    public String toString() {
+        String[] fileIDSplitted = fileID.split("/");
+        return fileIDSplitted[fileIDSplitted.length - 1];
+    }
+    @Override
+    public boolean equals(Object o) {
+
+
+        if (o == this) {
+            return true;
+        }
+
+        if (!(o instanceof FileID)) {
+            return false;
+        }
+
+        FileID c = (FileID) o;
+
+        return fileID.equals(c.fileID);
+    }
+
+    public void setNumChunks(int numChunks) {
+        this.numChunks = numChunks;
+    }
+
+    public int getNumChunks() {
+        return numChunks;
+    }
+
+    public int getDesiredRepDeg() {
+        return desiredRepDeg;
+    }
+}
\ No newline at end of file
Index: project/src/main/java/listeners/Broker.java
===================================================================
--- project/src/main/java/listeners/Broker.java	(nonexistent)
+++ project/src/main/java/listeners/Broker.java	(revision 1)
@@ -0,0 +1,174 @@
+package main.java.listeners;
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.peer.Peer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.DatagramPacket;
+
+import static main.java.utils.Utilities.*;
+import static main.java.utils.Constants.*;
+
+
+public class Broker {
+
+
+    public static void sendSTORED(FileChunkID chunkID) {
+        byte message[] = messageConstructor("STORED", null, chunkID, null);
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+            Broker.sendToMC(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+
+    }
+
+    public static void sendDELETE(FileID fileID) {
+        byte message[] = messageConstructor("DELETE", null, null, fileID);
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+            Broker.sendToMC(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+
+
+    public static void sendPUTCHUNK(FileChunk chunk) throws InterruptedException {
+        byte message[] = messageConstructor("PUTCHUNK", chunk, null, null);
+
+
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+
+
+
+            Broker.sendToMDB(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+    }
+
+    public static void sendREMOVED(FileChunkID chunkID){
+
+        byte message[] = messageConstructor("REMOVED", null, chunkID, null);
+
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+            Broker.sendToMC(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+
+    }
+
+    public static void sendGETCHUNK(FileChunkID chunkID) {
+        byte message[] = messageConstructor("GETCHUNK", null, chunkID, null);
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+            Broker.sendToMC(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+    public static void sendCHUNK(FileChunk chunk, FileChunkID chunkID) {
+        byte message[] = messageConstructor("CHUNK", chunk, chunkID, null);
+
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            outputStream.write(message);
+
+            byte messageToSend[] = outputStream.toByteArray();
+
+            Broker.sendToMDR(messageToSend);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+    private static void sendToMDB(byte[] backupMessage) throws InterruptedException {
+
+
+        DatagramPacket messagePacket = new DatagramPacket(backupMessage, backupMessage.length, Peer.getMDBListener().address, Peer.getMDBListener().port);
+
+        try {
+            Peer.getMDBListener().send(messagePacket);
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+    private static void sendToMC(byte[] backupMessage) {
+
+
+        DatagramPacket messagePacket = new DatagramPacket(backupMessage, backupMessage.length, Peer.getMCListener().address, Peer.getMCListener().port);
+
+
+        try {
+            Peer.getMCListener().send(messagePacket);
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void sendToMDR(byte[] restoreMessage) {
+        DatagramPacket messagePacket = new DatagramPacket(restoreMessage, restoreMessage.length, Peer.getMDRListener().address, Peer.getMDRListener().port);
+
+        try {
+            Peer.getMDRListener().send(messagePacket);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
Index: project/src/main/java/listeners/Listener.java
===================================================================
--- project/src/main/java/listeners/Listener.java	(nonexistent)
+++ project/src/main/java/listeners/Listener.java	(revision 1)
@@ -0,0 +1,202 @@
+package main.java.listeners;
+
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.peer.Peer;
+import main.java.service.PacketHandler;
+import main.java.utils.Constants;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static main.java.utils.Constants.*;
+
+
+public class Listener implements Runnable {
+
+    private MulticastSocket multicastSocket;
+    public InetAddress address;
+    public int port;
+
+    public volatile ConcurrentHashMap<String, ArrayList<FileChunk>> chunksReceived;
+
+    private ConcurrentHashMap<FileChunkID, ArrayList<String>> storedChunks;
+
+    private ConcurrentHashMap<FileChunkID, ArrayList<String>> putChunks;
+
+
+
+    public Listener(InetAddress addr, int prt) {
+        address = addr;
+        port = prt;
+        storedChunks = new ConcurrentHashMap<>();
+        putChunks = new ConcurrentHashMap<>();
+        chunksReceived = new ConcurrentHashMap<>();
+
+    }
+
+
+
+    @Override
+    public void run() {
+
+        try {
+            multicastSocket = new MulticastSocket(port);
+            multicastSocket.setTimeToLive(1);
+            multicastSocket.joinGroup(address);
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+
+        boolean end = false;
+        while(!end) {
+
+            byte[] buffer = new byte[PACKET_MAX_SIZE];
+            Thread t;
+            try {
+
+
+                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
+                multicastSocket.receive(packet);
+
+
+                t = new Thread(new PacketHandler(packet));
+                t.start();
+
+                try {
+                    t.join();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+
+
+
+
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        multicastSocket.close();
+
+
+
+    }
+
+
+    public void startCountingStoreds(FileChunkID fileChunkID) {
+
+        if (!storedChunks.containsKey(fileChunkID)) {
+            //System.out.println("Starting to count:" + chunkID.toString());
+            storedChunks.put(fileChunkID, new ArrayList<>());
+        }
+
+    }
+
+    public void clearCount(FileChunkID fileChunkID) {
+        storedChunks.get(fileChunkID).clear();
+    }
+
+    public int getCount(FileChunkID fileChunkID) {
+        return storedChunks.get(fileChunkID).size();
+    }
+
+    public void dumpHashmap() {
+        for (FileChunkID name: storedChunks.keySet()){
+
+            String key =name.toString();
+            String value = storedChunks.get(name).toString();
+            System.out.println(key + " " + value);
+
+        }
+
+    }
+
+    public void stopCounting(FileChunkID fileChunkID) {
+        storedChunks.remove(fileChunkID);
+    }
+
+    public void send(DatagramPacket messagePacket) throws IOException {
+        multicastSocket.send(messagePacket);
+    }
+
+    public synchronized void countStored(FileChunkID chunkID, String senderID) {
+        if (storedChunks.containsKey(chunkID))
+            if (!storedChunks.get(chunkID).contains(senderID)) {
+                storedChunks.get(chunkID).add(senderID);
+                Peer.getDb().increasePerceivedRepDeg(chunkID, Integer.parseInt(senderID));
+            }
+            else
+                System.out.println("Already counted this peer");
+
+    }
+    public synchronized void startCountingPutChunks(FileChunkID chunkID) {
+        if (!putChunks.containsKey(chunkID))
+            putChunks.put(chunkID, new ArrayList<>());
+    }
+
+    public synchronized void countPutChunk(FileChunkID chunkID, String senderID) {
+
+        if (putChunks.containsKey(chunkID))
+            if (!putChunks.get(chunkID).contains(senderID))
+                putChunks.get(chunkID).add(senderID);
+
+    }
+
+    public synchronized int getCountPutChunks(FileChunkID chunkID) {
+        return putChunks.get(chunkID).size();
+    }
+
+    public synchronized void stopSavingPutChunks(FileChunkID chunkID) {
+        putChunks.remove(chunkID);
+    }
+
+
+    public boolean isCounting(FileChunkID fileChunkID) {
+        if(storedChunks.containsKey(fileChunkID))
+            return true;
+        else {
+            return false;
+        }
+    }
+
+
+
+    /*
+        MDR
+     */
+    public synchronized void queueChunk(FileChunk chunk) {
+        //System.out.println("CHUNK TO MERGE: " + chunk.getFileID().toString());
+        ArrayList<FileChunk> fileChunks = chunksReceived.get(chunk.getFileID().toString());
+        fileChunks.add(chunk);
+        //System.out.println("File Chunks: " + fileChunks);
+
+        //printChunksReceived();
+        notifyAll();
+    }
+
+    public synchronized ArrayList<FileChunk> retrieveChunk(String fileID) {
+        ArrayList<FileChunk> receivedChunks = chunksReceived.get(fileID);
+
+        return receivedChunks;
+    }
+
+    public void printChunksReceived() {
+        for (String name: chunksReceived.keySet()){
+
+            String value = chunksReceived.get(name).toString();
+            System.out.println(name + ": " + value);
+        }
+    }
+
+
+}
Index: project/src/main/java/peer/Peer.java
===================================================================
--- project/src/main/java/peer/Peer.java	(nonexistent)
+++ project/src/main/java/peer/Peer.java	(revision 1)
@@ -0,0 +1,433 @@
+package main.java.peer;
+
+
+import main.java.database.Database;
+import main.java.file.FileID;
+import main.java.listeners.Listener;
+import main.java.protocols.Backup;
+import main.java.protocols.Delete;
+import main.java.protocols.Reclaim;
+import main.java.protocols.Restore;
+import main.java.service.RMI;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+
+import java.io.*;
+import java.net.*;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static main.java.utils.Utilities.getLocalAddress;
+import static main.java.utils.Constants.*;
+
+public class Peer implements RMI {
+
+
+
+
+    private static Listener MCChannel;  //MC CHANNEL
+    private static Listener MDBChannel; //BACKUP CHANNEL
+    private static Listener MDRChannel; //RESTORE CHANNEL
+
+    private static String rmiRemoteObject;
+
+    private static int ID;
+    private static float protocolVersion;
+
+    private static InetAddress MCAddress;
+    private static InetAddress MDBAddress;
+    private static InetAddress MDRAddress;
+
+    private static int MCPort;
+    private static int MDBPort;
+    private static int MDRPort;
+
+    private static InetAddress ip;
+
+    private static volatile Database db;
+    private static Disk disk;
+
+    public static boolean restoring;
+
+    /*
+        javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ peer/Peer.java
+        DENTRO DO /src/: rmiregistry &
+        usage:
+        protocol version,the server id, service access point, MC, MDB, MDR
+        rmi init example:
+        java -Djava.net.preferIPv4Stack=true -Djava.rmi.server.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/
+        main/java/service/ main/java/peer/Peer 1.0 0 192.168.0.1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
+        1.0, 0, 192.168.0.1, 224.0.0.0:8000, 224.0.0.0:8001, 224.0.0.0:8002
+        normal peer example:
+        java peer.Peer 1.0 1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
+    */
+    public static void main(String[] args) throws IOException, ClassNotFoundException {
+
+        if(!parseArgs(args)) {
+            System.out.println("Bad arguments");
+            System.out.println("USAGE (RMI): java peer.Peer <protocol_version> <service_access_point> <MCADDR>:<MCPORT> " +
+                    "<MDBADDR>:<MDBPORT> <MDRADDR>:<MDRPORT>");
+            System.out.println("USAGE (NON-RMI): java peer.Peer <protocol_version> <MCADDR>:<MCPORT> " +
+                    "<MDBADDR>:<MDBPORT> <MDRADDR>:<MDRPORT>");
+            return;
+        }
+
+
+        ip = getLocalAddress();
+
+        MCChannel = new Listener(MCAddress, MCPort);
+        MDBChannel = new Listener(MDBAddress, MDBPort);
+        MDRChannel = new Listener(MDRAddress, MDRPort);
+
+        loadDisk();
+        loadDatabase();
+
+        db.printDatabase();
+        disk.printDisk();
+        restoring = false;
+
+
+        System.out.println("Start Listening on MC Channel...");
+        new Thread(MCChannel).start();
+        System.out.println("Start Listening on MDB Channel...");
+        new Thread(MDBChannel).start();
+        System.out.println("Start Listening on MDR Channel...");
+        new Thread(MDRChannel).start();
+
+        return;
+
+
+
+
+
+    }
+
+    public static void saveDBToDisk() {
+
+        File dir = new File("peer"+getID()+"/database/");
+
+        if(!dir.exists()){
+            System.out.println("creating directory: " + dir.getName());
+
+            try{
+                dir.mkdirs();
+            }
+            catch(SecurityException se){
+                se.printStackTrace();
+            }
+
+        }
+
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream("peer"+getID()+"/database/dbs.data");
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            System.out.println("Database does not exist!");
+            createDB();
+            System.out.println("New DB created and saved to disk...");
+        }
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(fos);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        try {
+            assert oos != null;
+            oos.writeObject(db);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        try {
+            oos.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    private static void createDB() {
+        db = new Database();
+        saveDBToDisk();
+    }
+
+    private static void loadDatabase() {
+        System.out.println("Loading database...");
+        try {
+            FileInputStream fileInputStream = new FileInputStream("peer"+getID()+"/database/dbs.data");
+
+            ObjectInputStream objectInputStream = new ObjectInputStream(
+                    fileInputStream);
+            db = (Database) objectInputStream.readObject();
+            objectInputStream.close();
+        } catch (FileNotFoundException e) {
+            System.out.println("Database not found");
+
+            createDB();
+        } catch (IOException | ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    private static boolean parseArgs(String[] args) {
+
+        //args == 6 -> INITIALIZE RMI
+        if(args.length == 6) {
+
+            protocolVersion = Float.parseFloat(args[0]);
+            ID = Integer.parseInt(args[1]);
+            rmiRemoteObject = args[2];
+            try {
+
+                MCAddress = InetAddress.getByName(args[3].split(":")[0]);
+                MDBAddress = InetAddress.getByName(args[4].split(":")[0]);
+                MDRAddress = InetAddress.getByName(args[5].split(":")[0]);
+
+                MCPort = Integer.parseInt(args[3].split(":")[1]);
+                MDBPort = Integer.parseInt(args[4].split(":")[1]);
+                MDRPort = Integer.parseInt(args[5].split(":")[1]);
+
+
+
+            }
+            catch (UnknownHostException e){
+                System.out.println("Address not found");
+                return false;
+            }
+
+            System.out.println("\t LAUNCHING RMI");
+            launchRMI();
+
+        } else if (args.length == 5) {//normal peer
+
+            protocolVersion = Float.parseFloat(args[0]);
+            ID = Integer.parseInt(args[1]);
+
+            try {
+
+                MCAddress = InetAddress.getByName(args[2].split(":")[0]);
+                MDBAddress = InetAddress.getByName(args[3].split(":")[0]);
+                MDRAddress = InetAddress.getByName(args[4].split(":")[0]);
+
+                MCPort = Integer.parseInt(args[2].split(":")[1]);
+                MDBPort = Integer.parseInt(args[3].split(":")[1]);
+                MDRPort = Integer.parseInt(args[4].split(":")[1]);
+
+
+
+            }
+            catch (UnknownHostException e){
+                System.out.println("Address not found");
+                return false;
+            }
+
+
+        }
+        else return false;
+
+
+        return true;
+    }
+
+    private static void launchRMI() {
+
+        //System.setProperty("java.rmi.server.hostname", "localhost");
+
+        //System.setProperty("rmi.server.codebase", "file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/bin/");
+        try {
+
+
+            RMI peer = new Peer();
+
+            RMI stub = (RMI) UnicastRemoteObject.exportObject(peer, 0);
+
+            Registry registry = LocateRegistry.getRegistry();
+            registry.rebind("obj", stub);
+
+        } catch (Exception e) {
+            System.err.println("Server exception: " + e.toString());
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+
+
+    public static Database getDb() {
+        return db;
+
+    }
+
+    public static Listener getMCListener() {
+        return MCChannel;
+    }
+
+    public static Listener getMDBListener() {
+        return MDBChannel;
+    }
+
+    public static Listener getMDRListener() {
+        return MDRChannel;
+    }
+
+    public static void saveDisk(){
+        FileOutputStream fos = null;
+        File dir = new File("peer"+getID()+"/disk/");
+
+        if(!dir.exists()){
+            System.out.println("creating directory: " + dir.getName());
+
+            try{
+                dir.mkdirs();
+            }
+            catch(SecurityException se){
+                se.printStackTrace();
+            }
+
+        }
+
+        try {
+            fos = new FileOutputStream("peer"+getID()+"/disk/disk.data");
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+            System.out.println("Disk does not exist!");
+            createDisk();
+            System.out.println("New Disk created and saved to disk...");
+        }
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(fos);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        try {
+            oos.writeObject(disk);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        try {
+            oos.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void loadDisk() throws ClassNotFoundException, IOException {
+
+        System.out.println("Loading disk...");
+
+        try {
+            FileInputStream fileInputStream = new FileInputStream("peer"+getID()+"/disk/disk.data");
+
+            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
+            disk = (Disk) objectInputStream.readObject();
+            objectInputStream.close();
+        } catch (FileNotFoundException e) {
+            System.out.println("Disk not found");
+            createDisk();
+        } catch (IOException | ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void createDisk(){
+        disk = new Disk();
+        saveDisk();
+    }
+
+    @Override
+    public void backup(File file, int replicationDegree) throws RemoteException {
+
+        Thread t = new Thread(new Backup(file, replicationDegree));
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+
+    }
+
+    @Override
+    public void delete(String filePath) throws RemoteException {
+        Thread t = new Thread(new Delete(new FileID(filePath, 0)));
+
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException e){
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void restore(File file) throws RemoteException {
+
+        Thread t = new Thread(new Restore(file));
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+
+    }
+
+    @Override
+    public String state() throws RemoteException {
+
+        db.printDatabase();
+        disk.printDisk();
+        return null;
+    }
+
+    @Override
+    public void reclaim(int amount) throws RemoteException {
+
+        Thread t = new Thread(new Reclaim(amount));
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+
+
+    }
+
+
+
+
+    public static int getID() {
+        return ID;
+    }
+
+    public static InetAddress getAddress() {
+        return ip;
+    }
+
+    public static float getProtocolVersion() {
+        return protocolVersion;
+    }
+
+    public static Disk getDisk(){ return disk; }
+
+
+}
\ No newline at end of file
Index: project/src/main/java/protocols/Backup.java
===================================================================
--- project/src/main/java/protocols/Backup.java	(nonexistent)
+++ project/src/main/java/protocols/Backup.java	(revision 1)
@@ -0,0 +1,137 @@
+package main.java.protocols;
+
+import main.java.file.*;
+import main.java.peer.Peer;
+
+
+import java.io.*;
+import java.util.Arrays;
+
+import static main.java.utils.Constants.*;
+import static main.java.utils.Utilities.*;
+
+public class Backup implements Runnable{
+
+    private File file;
+    private int repDeg;
+    private String encryptedID;
+    private FileID fileID;
+    private int fileParts;
+    //private static Message message;
+
+
+    public Backup(File file, int replicationDegree) {
+
+        this.file = file;
+        this.repDeg = replicationDegree;
+
+    }
+
+    /**
+     * @param file File to split into chunks and later backed up
+     * @return Number of chunks that the file was split into
+     * @throws IOException exception to be thrown in case of an invalid file.
+     */
+    public int createChunks(File file) throws IOException {
+
+
+        byte[] fileData = loadFileData(file);
+
+        fileParts = fileData.length / CHUNK_MAX_SIZE;
+
+
+        String fileName = file.getName();
+
+        ByteArrayInputStream streamBuffer = new ByteArrayInputStream(fileData);
+        byte[] data = new byte[CHUNK_MAX_SIZE];
+
+
+        for(int i = 0; i <= fileParts; i++) {
+            FileChunkID id = new FileChunkID(file.getName(), i);
+
+
+            byte[] chunkData;
+
+            /*
+                Size of last chunk is always shorter than CHUNK_MAX_SIZE
+                If the file size is a multiple of CHUNK_MAX_SIZE, the last chunk has size 0.
+             */
+
+            if(i == fileParts - 1 && file.length() % CHUNK_MAX_SIZE == 0) {
+                chunkData = new byte[0];
+            } else {
+                int bytesRead = streamBuffer.read(data, 0, data.length);
+                chunkData = Arrays.copyOfRange(data, 0, bytesRead);
+            }
+
+
+
+            fileID = new FileID(sha256(file.getName()), repDeg);
+
+            fileID.setNumChunks(fileParts + 1);
+
+
+            FileChunk chunk = new FileChunk(repDeg, i, fileID, chunkData);
+
+            Thread t = new Thread(new BackupChunk(chunk));
+            t.start();
+
+            try {
+                t.join();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        Peer.saveDBToDisk();
+
+
+        return fileParts;
+
+    }
+
+
+    @Override
+    public void run() {
+        //String fileIDString;
+        //fileIDString = file.getName() + file.getPath() + file.lastModified();
+        //encryptedID = sha256(fileIDString);
+        try {
+            createChunks(file);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+
+        Peer.getDb().insertFile(fileID);
+
+
+
+
+
+
+    }
+
+    public static byte[] loadFileData(File file) throws FileNotFoundException {
+        FileInputStream inputStream = new FileInputStream(file);
+        byte[] fileData = new byte[(int) file.length()];
+
+        try {
+            inputStream.read(fileData);
+            inputStream.close();
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+
+        return fileData;
+    }
+
+
+
+
+
+
+}
Index: project/src/main/java/protocols/BackupChunk.java
===================================================================
--- project/src/main/java/protocols/BackupChunk.java	(nonexistent)
+++ project/src/main/java/protocols/BackupChunk.java	(revision 1)
@@ -0,0 +1,86 @@
+package main.java.protocols;
+
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.listeners.Broker;
+import main.java.peer.Peer;
+
+import java.io.IOException;
+
+public class BackupChunk implements Runnable {
+
+    public FileChunk chunk;
+    public boolean repDegReached;
+
+    public BackupChunk(FileChunk chunk) {
+        this.chunk = chunk;
+        repDegReached = false;
+    }
+
+    @Override
+    public void run() {
+        int attempt = 0;
+
+        long timeToRetry = 500;
+        FileID fID = new FileID(chunk.getFileID().toString(),chunk.getReplicationDegree());
+
+
+        FileChunkID fileChunkID = new FileChunkID(fID.toString(), chunk.getChunkNo());
+
+
+
+        while(!repDegReached) {
+
+            Peer.getDb().addNewRepDegCounter(new FileChunkID(fID.toString(), chunk.getChunkNo()), chunk.getReplicationDegree());
+
+            timeToRetry = timeToRetry *2;
+
+            if(Peer.getMCListener().isCounting(fileChunkID))
+                Peer.getMCListener().clearCount(fileChunkID);
+
+            try {
+                Broker.sendPUTCHUNK(chunk);
+                Peer.getMCListener().startCountingStoreds(fileChunkID);
+                System.out.println("Listening for STOREDs for " + timeToRetry + "ms");
+                Thread.sleep(timeToRetry);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+
+            int perceivedRepDeg = Peer.getMCListener().getCount(fileChunkID);
+
+            System.out.println("Replication degree count: " + perceivedRepDeg + "/" + chunk.getReplicationDegree());
+
+
+            if(perceivedRepDeg < chunk.getReplicationDegree()) {
+                attempt++;
+
+                if(attempt > 4) {
+                    System.out.println("The Desired Replication Degree wasn't reached in 5 tries, skipping chunk...");
+                    repDegReached = true;
+                    return;
+
+
+                }
+                else {
+                    System.out.println("The Desired Replication Degree was not reached, trying again...");
+
+                }
+
+            } else {
+                System.out.println("Desired Replication Degree accomplished!");
+                repDegReached = true;
+            }
+
+        }
+
+
+
+
+
+        Peer.getMCListener().stopCounting(fileChunkID);
+    }
+}
\ No newline at end of file
Index: project/src/main/java/protocols/Delete.java
===================================================================
--- project/src/main/java/protocols/Delete.java	(nonexistent)
+++ project/src/main/java/protocols/Delete.java	(revision 1)
@@ -0,0 +1,22 @@
+package main.java.protocols;
+
+import main.java.file.FileID;
+import main.java.listeners.Broker;
+import main.java.peer.Peer;
+
+public class Delete implements Runnable {
+
+    private FileID fileID;
+
+    public Delete(FileID fId){
+        fileID = fId;
+    }
+
+    @Override
+    public void run() {
+        Broker.sendDELETE(fileID);
+
+        Peer.getDb().removeFile(fileID);
+    }
+
+}
Index: project/src/main/java/protocols/Reclaim.java
===================================================================
--- project/src/main/java/protocols/Reclaim.java	(nonexistent)
+++ project/src/main/java/protocols/Reclaim.java	(revision 1)
@@ -0,0 +1,59 @@
+package main.java.protocols;
+
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.listeners.Broker;
+import main.java.peer.Peer;
+
+import java.io.File;
+
+import static main.java.utils.Constants.MAX_WAITING_TIME;
+
+public class Reclaim implements Runnable {
+
+    private int amount;
+
+    public Reclaim(int amount){
+        this.amount = amount;
+
+    }
+    @Override
+    public void run() {
+
+        Peer.getDisk().setCapacity(amount);
+
+        while (Peer.getDisk().getFreeSpace() < 0){
+            FileChunkID chunkID = Peer.getDb().getHighestPerceivedRepDegChunk();
+
+            System.out.println("CHUNKID: " + chunkID.toString());
+            if(chunkID != null){
+                Broker.sendREMOVED(chunkID);
+
+
+                File f = new File("peer"+Peer.getID()+"/Backup/"+
+                        chunkID.getFileID().split("\\.")[0]+ "/"+chunkID.toString());
+
+               // File f = new File("/debug/"+chunkID.toString());
+
+
+
+                Peer.getDisk().removeFile(f.length());
+                Peer.getDb().decreasePerceivedRepDeg(chunkID, Peer.getID());
+                /* Peer.getDb().removeFile(new FileID(chunkID.getFileID(), -1)); */
+
+                try{
+                    //System.out.println("\tWaiting...");
+                    Thread.sleep((long)(Math.random() * MAX_WAITING_TIME));
+                } catch (InterruptedException ie){
+                    ie.printStackTrace();
+                }
+
+                f.delete();
+
+
+            }
+
+        }
+
+    }
+}
Index: project/src/main/java/protocols/Restore.java
===================================================================
--- project/src/main/java/protocols/Restore.java	(nonexistent)
+++ project/src/main/java/protocols/Restore.java	(revision 1)
@@ -0,0 +1,158 @@
+package main.java.protocols;
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.listeners.Broker;
+import main.java.peer.Peer;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import static main.java.utils.Constants.*;
+import static main.java.utils.Utilities.*;
+
+public class Restore implements Runnable {
+
+    private File file;
+
+
+    public Restore(File file) {
+        this.file = file;
+    }
+
+    @Override
+    public void run() {
+        byte[] fileData = new byte[0];
+
+
+        String finalName = file.getName();
+
+        FileID fID = new FileID(sha256(file.getName()), -1);
+        String filename = fID.toString();
+
+
+        Peer.restoring = true;
+
+
+
+        System.out.println("\t Restoring file: " + filename + "\n");
+        Peer.getMDRListener().chunksReceived.put(filename, new ArrayList<>());
+        ArrayList<FileChunk> chunks = new ArrayList<>();
+
+        ArrayList<FileChunk> fileChunks;
+
+        int fileParts = Peer.getDb().getNumChunksOfFile(fID);
+
+
+
+
+        for (int i = 0; i < fileParts; i++) {
+
+            FileChunkID chunkID = new FileChunkID(filename, i);
+
+
+            //send get chunk
+            Broker.sendGETCHUNK(chunkID);
+
+            //receive chunk
+            fileChunks = Peer.getMDRListener().chunksReceived.get(filename);
+
+
+            FileChunk chunkAUX = fileChunks.isEmpty() ? null : Peer.getMDRListener().chunksReceived.get(filename).remove(0);
+
+            while (chunkAUX == null) {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+
+                chunkAUX = fileChunks.isEmpty() ? null : Peer.getMDRListener().chunksReceived.get(filename).remove(0);
+            }
+
+            chunks.add(chunkAUX);
+
+
+            //System.out.println("DATABASE : ");
+            //Peer.getDb().printDatabase();
+
+        }
+
+
+
+
+        for (int i = 0; i < fileParts; i++) {
+            FileChunk chunkTmp = null;
+            ArrayList<FileChunk> chunkAUX = null;
+            for(FileChunk chunk : chunks) {
+                if(chunk.getChunkNo() == i) {
+                    chunkTmp = chunk;
+                    break;
+                }
+            }
+
+            if(chunkTmp == null) {
+                System.out.println("Missing chunk file!!");
+            } else {
+                try {
+                    fileData = concatBytes(fileData, chunkTmp.getChunkData());
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    }
+
+            }
+
+            File dir = new File("peer"+Peer.getID()+"/restored/");
+
+            if(!dir.exists()) {
+                dir.mkdirs();
+            }
+
+            FileOutputStream out = null;
+            try {
+                out = new FileOutputStream("peer"+Peer.getID()+"/restored/" + finalName);
+
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+
+            }
+            try {
+                assert out != null;
+                out.write(fileData);
+
+            } catch (IOException e) {
+                e.printStackTrace();
+
+            }
+            try {
+                out.close();
+
+            } catch (IOException e) {
+                e.printStackTrace();
+
+            }
+
+
+        }
+
+
+        Peer.restoring = false;
+
+
+
+
+    }
+
+    private static byte[] concatBytes(byte[] a, byte[] b) throws IOException {
+
+
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
+        outputStream.write( a );
+        outputStream.write( b );
+
+        byte result[] = outputStream.toByteArray( );
+
+        return result;
+    }
+}
Index: project/src/main/java/service/PacketHandler.java
===================================================================
--- project/src/main/java/service/PacketHandler.java	(nonexistent)
+++ project/src/main/java/service/PacketHandler.java	(revision 1)
@@ -0,0 +1,476 @@
+package main.java.service;
+
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.listeners.Broker;
+import main.java.peer.Peer;
+import main.java.protocols.Backup;
+import main.java.protocols.BackupChunk;
+import main.java.utils.Constants.*;
+
+import java.io.*;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static main.java.utils.Constants.*;
+import static main.java.utils.Utilities.getLocalAddress;
+import static main.java.utils.Utilities.sha256;
+
+public class PacketHandler implements Runnable {
+
+    private DatagramPacket packetToHandle;
+    private String packet_header;
+    private byte[] packet_body;
+    private String subprotocol;
+    private FileID fileID;
+    private float protocolVersion;
+    private int replicationDegree;
+    private int chunkNo;
+    private int senderID;
+    private int bodyStartingIndex;
+    private String[] header_splitted;
+
+    private InetAddress senderIP;
+
+    private boolean listeningChunk;
+    private boolean receivedChunk;
+    private boolean badMessage;
+
+
+    public PacketHandler(DatagramPacket packetToHandle) {
+        this.packetToHandle = packetToHandle;
+        packet_header = null;
+        packet_body = null;
+        header_splitted = null;
+        senderIP = packetToHandle.getAddress();
+        listeningChunk = false;
+        receivedChunk = false;
+        badMessage = false;
+    }
+
+    @Override
+    public void run() {
+
+        parseSubProtocol();
+        //parseHeader();
+
+
+        if(Integer.parseInt(header_splitted[2]) != Peer.getID()){
+
+            switch (subprotocol) {
+                case PUTCHUNK:
+                    parsePUTCHUNK();
+                    PUTCHUNKHandler();
+                    break;
+                case STORED:
+                    parseSTORED();
+                    STOREDHandler();
+                    break;
+                case GETCHUNK:
+                    parseGETCHUNK();
+                    GETCHUNKHandler();
+                    break;
+                case CHUNK:
+                    parseCHUNK();
+                    CHUNKHandler();
+                    break;
+                case DELETE:
+                    parseDELETE();
+                    DELETEHandler();
+                    break;
+                case REMOVED:
+                    parseREMOVED();
+                    REMOVEDHandler();
+                    break;
+
+                default: System.out.println("Unknown protocol. Ignoring message... " + subprotocol);
+                    badMessage = true;
+                    break;
+
+            }
+
+            if(!badMessage)
+                System.out.println("\t Sender ID: " + packetToHandle.getAddress() + " \n" +
+                        "\t PEER ID : " + senderID + "\n");
+
+
+        }
+
+
+
+    }
+
+    private void parseSubProtocol() {
+
+        ByteArrayInputStream stream = new ByteArrayInputStream(packetToHandle.getData());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+        try {
+            packet_header = reader.readLine();
+            header_splitted = packet_header.split(" ");
+            subprotocol = header_splitted[0];
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    private void parseREMOVED() {
+
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(sha256(header_splitted[3]), -1);
+        chunkNo = Integer.parseInt(header_splitted[4]);
+
+
+
+    }
+
+    private void REMOVEDHandler() {
+
+
+        if(Peer.getDb().isFileStored(fileID)){
+            FileChunkID chunkID = new FileChunkID(fileID.toString(), chunkNo);
+            Peer.getDb().decreasePerceivedRepDeg(chunkID , senderID);
+
+            System.out.println("HANDLER REMOVED: "+ Peer.getDb().getPerceivedRepDeg(chunkID) + "/" +
+                    Peer.getDb().getDesiredRepDeg(chunkID));
+
+
+            if(Peer.getDb().getPerceivedRepDeg(chunkID) < Peer.getDb().getDesiredRepDeg(chunkID)){
+                System.out.println("\tPerceived rep degree dropped below desired rep degree.");
+
+                Peer.getMCListener().startCountingPutChunks(chunkID);
+
+                try{
+                    System.out.println("\tWaiting for Putchunk messages...");
+                    Thread.sleep((long)(Math.random() * MAX_WAITING_TIME));
+                } catch (InterruptedException ie){
+                    ie.printStackTrace();
+                }
+
+                int putChunksReceived = Peer.getMCListener().getCountPutChunks(chunkID);
+
+                Peer.getMCListener().stopSavingPutChunks(chunkID);
+
+                if (putChunksReceived == 0){
+
+                    try {
+                        File cFile = new File("peer"+Peer.getID()+"/Backup/"+
+                                fileID.toString().split("\\.")[0]+ "/"+chunkID.toString());
+
+
+
+
+                        byte[] data = Backup.loadFileData(cFile);
+
+                        //FileChunk(int replicationDegree, int chunkNo, FileID fileID, byte[] chunkData)
+                        fileID = new FileID(fileID.toString(), Peer.getDb().getDesiredRepDeg(chunkID));
+                        FileChunk fChunk = new FileChunk(Peer.getDb().getDesiredRepDeg(chunkID), chunkNo,
+                                fileID, data);
+
+                        new Thread(new BackupChunk(fChunk)).start();
+                    } catch (FileNotFoundException e) {
+                        e.printStackTrace();
+                    }
+
+
+                }
+
+
+            }
+
+        }
+
+
+
+
+
+
+    }
+
+    private void parseDELETE() {
+
+
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(sha256(header_splitted[3]), -1);
+
+    }
+
+    private void DELETEHandler() {
+
+
+
+        final File folder = new File("peer"+Peer.getID()+"/Backup/"+fileID.toString()+
+                "/");
+
+        System.out.println("FOLDER: " + folder.getPath());
+
+        final File[] files = folder.listFiles( new FilenameFilter() {
+            @Override
+            public boolean accept( final File dir,
+                                   final String name ) {
+                return name.matches( fileID.toString() + "-.*" );
+            }
+        } );
+        for ( final File file : files ) {
+            if ( !file.delete() ) {
+                System.err.println( "Can't remove " + file.getAbsolutePath() );
+            }
+            else {
+                Peer.getDisk().removeFile(file.length());
+                Peer.getDb().removeChunkInfo(new FileChunkID(fileID.toString(),
+                        Integer.parseInt(file.getName().split("-")[1])));
+
+                Peer.getDb().removeFile(fileID);
+            }
+        }
+
+
+
+
+
+    }
+
+    private void parseCHUNK() {
+
+        //CHUNK <Version> <SenderId> <FileId> <ChunkNo> <CRLF><CRLF><Body>
+        if(listeningChunk)
+            receivedChunk=true;
+
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(header_splitted[3], -1);
+        chunkNo = Integer.parseInt(header_splitted[4]);
+        parseBody();
+
+    }
+
+    private void CHUNKHandler() {
+
+
+        if(Peer.restoring){
+            FileChunk chunk = new FileChunk(-1, chunkNo, fileID, packet_body);
+
+
+            Peer.getMDRListener().queueChunk(chunk);
+            //System.out.println("\t CHUNKS RECEIVED : ");
+            //Peer.getMDRListener().printChunksReceived();
+        }
+
+
+    }
+
+    private void parseGETCHUNK() {
+
+        /* GETCHUNK <Version> <SenderId> <FileId> <ChunkNo> <CRLF><CRLF> */
+
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(header_splitted[3], -1);
+        chunkNo = Integer.parseInt(header_splitted[4]);
+
+
+    }
+
+    private void GETCHUNKHandler() {
+
+
+        FileChunkID chunkID = new FileChunkID(fileID.toString(), chunkNo);
+        FileInputStream is = null;
+        File chunkFile = new File("peer"+Peer.getID()+"/Backup/"+fileID.toString().split("\\.")[0]+ "/" + chunkID);
+        try {
+            is  = new FileInputStream(chunkFile);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        byte[] chunkData = new byte[(int) chunkFile.length()];
+
+        try {
+            assert is != null;
+            is.read(chunkData);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        try {
+            is.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+        FileChunk chunk = new FileChunk(-1, chunkID.getChunkNumber(), fileID, chunkData);
+
+        /*
+
+        To avoid flooding the host with CHUNK messages, each peer shall wait for a random time
+        uniformly distributed between 0 and 400 ms, before sending the CHUNK message. If it receives
+        a CHUNK message before that time expires, it will not send the CHUNK message.
+
+         */
+
+        try {
+            listeningChunk = true;
+            Thread.sleep((long)(Math.random() * MAX_WAITING_TIME));
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        if(!receivedChunk)
+            Broker.sendCHUNK(chunk, chunkID);
+
+        receivedChunk = false;
+        listeningChunk = false;
+
+
+    }
+
+    private void parseSTORED() {
+        //STORED <Version> <SenderId> <FileId> <ChunkNo> <CRLF><CRLF>
+
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(header_splitted[3], -1);
+        chunkNo = Integer.parseInt(header_splitted[4]);
+
+    }
+
+    private void STOREDHandler() {
+
+        FileChunkID fcID = new FileChunkID(fileID.toString(), chunkNo);
+        Peer.getMCListener().countStored(fcID, String.valueOf(senderID));
+        Peer.getDb().increasePerceivedRepDeg(new FileChunkID(fileID.toString(), chunkNo), senderID);
+
+    }
+
+
+    private void parsePUTCHUNK() {
+
+
+        replicationDegree = Integer.parseInt(header_splitted[5]);
+        protocolVersion = Float.parseFloat(header_splitted[1]);
+        senderID = Integer.parseInt(header_splitted[2]);
+        fileID = new FileID(header_splitted[3], replicationDegree);
+        chunkNo = Integer.parseInt(header_splitted[4]);
+        parseBody();
+
+    }
+
+    private void PUTCHUNKHandler() {
+
+
+
+        File dir = new File("peer"+Peer.getID()+"/Backup/"+fileID.toString().split("\\.")[0]+ "/");
+
+        if(!dir.exists()){
+
+            System.out.println("creating directory: " + dir.getName());
+
+            try{
+                dir.mkdirs();
+            }
+            catch(SecurityException se){
+                se.printStackTrace();
+            }
+
+        }
+
+
+
+        FileChunkID chunkID = new FileChunkID(fileID.toString(), chunkNo);
+        System.out.println("\t FILEID : " + fileID.toString() + "\n"
+                + "\t CHUNK NO : " + chunkNo+ "\n");
+        System.out.println("\t PUT CHUNK: " + fileID.toString() + " with Replication Degree : " + replicationDegree  + " in " + dir.getPath());
+        System.out.println("\t Chunk ID: " + chunkID.toString());
+
+
+
+
+
+        File chunkfile = new File(dir.getPath()+"/"+ chunkID.toString());
+
+
+
+
+
+        Peer.getMDBListener().countPutChunk(chunkID, String.valueOf(senderID));
+
+        if(chunkfile.exists()) {
+            System.out.println("\n\t I already have this chunk, sending STORED...");
+            Broker.sendSTORED(chunkID);
+        } else {
+
+            if (Peer.getDisk().saveFile(packet_body.length)){
+
+                try {
+                    FileOutputStream out = new FileOutputStream(dir.getPath()+"/" + chunkID.toString());
+                    System.out.println("\n\t Saving Chunk...\n");
+                    out.write(packet_body);
+                    out.close();
+
+                    Peer.getDb().insertFile(fileID);
+                    Peer.getDb().insertChunkInfo(fileID, replicationDegree, chunkNo, Peer.getID());
+
+                    try{
+                        System.out.println("\tSending STORED response...");
+                        Thread.sleep((long)(Math.random() * MAX_WAITING_TIME));
+                    } catch (InterruptedException ie){
+                        ie.printStackTrace();
+                    }
+
+                    Broker.sendSTORED(chunkID);
+
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+            }
+
+
+        }
+
+
+
+
+
+    }
+
+    private void parseBody() {
+
+        ByteArrayInputStream stream = new ByteArrayInputStream(packetToHandle.getData());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+        String line = null;
+        int totalHeaderLinesLength = 0;
+        int totalLines = 0;
+
+        do {
+            try {
+                line = reader.readLine();
+                totalHeaderLinesLength += line.length();
+                totalLines++;
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } while (!(line != null && line.isEmpty()));
+
+        bodyStartingIndex = totalHeaderLinesLength + (totalLines * CRLF.getBytes().length);
+
+        packet_body = Arrays.copyOfRange(packetToHandle.getData(), bodyStartingIndex,
+                packetToHandle.getLength());
+
+        //System.out.println("STARTING INDEX:"  + bodyStartingIndex);
+    }
+
+
+
+
+
+}
Index: project/src/main/java/service/RMI.java
===================================================================
--- project/src/main/java/service/RMI.java	(nonexistent)
+++ project/src/main/java/service/RMI.java	(revision 1)
@@ -0,0 +1,19 @@
+package main.java.service;
+
+import java.io.File;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RMI extends Remote {
+
+    void backup(File file, int replicationDegree) throws RemoteException;
+
+    void delete(String filePath) throws RemoteException;
+
+    void restore(File file) throws RemoteException;
+
+    String state() throws RemoteException;
+
+    void reclaim(int amount) throws RemoteException;
+
+}
\ No newline at end of file
Index: project/src/main/java/testapp/TestApp.java
===================================================================
--- project/src/main/java/testapp/TestApp.java	(nonexistent)
+++ project/src/main/java/testapp/TestApp.java	(revision 1)
@@ -0,0 +1,200 @@
+package main.java.testapp;
+
+import main.java.service.RMI;
+
+
+import java.io.File;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+import static main.java.utils.Utilities.validateAccessPoint;
+
+//TODO: RENAME TO TestApp
+public class TestApp {
+
+
+    public static String AP;
+    public static String subProtocol;
+    public static String filePath;
+    public static int replicationDegree;
+    private static String rmiObjectAddress;
+    private static String rmiObjectName;
+    private static int amount;
+
+    private static RMI peer;
+
+    /*
+    java DBS <peer_ap> <sub_protocol> <opnd_1> <opnd_2>
+    java DBS 1923 BACKUP test1.pdf 3
+     */
+
+    /*
+    If you choose to use RMI in the communication between the test application and the peer,
+    you should use as access point the name of the remote object providing the "testing" service.
+     */
+
+    public static void main(String[] args) {
+
+
+
+        parseAccessPoint(args);
+        parseSubProtocol(args);
+
+        try {
+            Registry registry = LocateRegistry.getRegistry(rmiObjectAddress);
+
+            peer = (RMI)registry.lookup(rmiObjectName);
+
+
+        } catch (RemoteException | NotBoundException e) {
+            System.out.println("Invalid RMI object name");
+            return;
+        }
+
+
+
+        if(subProtocol.equals("BACKUP")) {
+            System.out.println("Initializing Backup...");
+            parseBackupArguments(args);
+
+            try {
+                File file = new File(filePath);
+                peer.backup(file, replicationDegree);
+                System.out.println("\t File Path: " + filePath + "\n");
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        } else if(subProtocol.equals("RESTORE")) {
+            System.out.println("Initializing Restore...");
+            parseRestoreArguments(args);
+
+            try {
+                File file = new File(filePath);
+                peer.restore(file);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        } else if(subProtocol.equals("DELETE")) {
+            System.out.println("Initializing DELETE");
+            parseDeleteArguments(args);
+            try {
+                peer.delete(filePath);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+
+        }
+        else if(subProtocol.equals("RECLAIM")) {
+            System.out.println("Initializing Reclaim...");
+            parseReclaimArguments(args);
+
+            try {
+                peer.reclaim(amount);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+        else if(subProtocol.equals("STATE")){
+            System.out.println("Retrieving testapp state...");
+
+            try {
+                peer.state();
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+
+
+
+
+
+
+
+    }
+
+    private static void parseDeleteArguments(String[] args) {
+
+        filePath = args[2];
+        System.out.println("DELETE FILE PATH: " + filePath);
+
+    }
+
+    private static void parseSubProtocol(String[] args) {
+        subProtocol = args[1];
+    }
+
+    private static boolean parseBackupArguments(String[] args) {
+
+        if(args.length != 4) {
+            System.out.println("\t Usage: peer <accessPoint> BACKUP <filePath> <replicationDegree> \n");
+            return false;
+        } else {
+            filePath = args[2];
+            replicationDegree = Integer.parseInt(args[3]);
+        }
+
+        System.out.println("\t \t \t TestApp Arguments\n\n" +
+                "\t Access Point: " + AP + "\n" +
+                "\t Sub Protocol : " + subProtocol + "\n" +
+                "\t File Path: " + filePath + "\n" + "\n" +
+                "\t Replication Degree: " + replicationDegree + "\n");
+
+        return true;
+
+    }
+
+    private static boolean parseRestoreArguments(String[] args) {
+        if(args.length != 3) {
+            System.out.println("\t Usage: peer <accessPoint> RESTORE <filePath> <replicationDegree> \n");
+            return false;
+        } else {
+            filePath = args[2];
+
+        }
+
+        System.out.println("\t \t \t \t TestApp Arguments\n\n" +
+                "\t Access Point: " + AP + "\n" +
+                "\t Sub Protocol : " + subProtocol + "\n" +
+                "\t File Path: " + filePath + "\n" + "\n");
+
+        return true;
+    }
+
+    private static boolean parseReclaimArguments(String[] args){
+        if(args.length != 3) {
+            System.out.println("\t Usage: \n" + "\t On src/main/java run: \n" + "\t \t java Peer.peer <access_point> " +
+                    "RECLAIM <amount>  \n");
+            return false;
+        } else {
+            amount = Integer.parseInt(args[2]);
+
+        }
+
+        System.out.println("\t \t \t \t TestApp Arguments\n\n" +
+                "\t Access Point: " + AP + "\n" +
+                "\t Sub Protocol : " + subProtocol + "\n" +
+                "\t Amount: " + amount + "\n" + "\n" );
+
+        return true;
+    }
+
+    public static void parseAccessPoint(String[] args) {
+
+        AP = args[0];
+
+
+        if(validateAccessPoint(AP)) {
+            String[] splittedAP = AP.split("/");
+            rmiObjectName = splittedAP[1];
+            rmiObjectAddress = splittedAP[0];
+        } else {
+            System.out.println("\t Wrong Access Point format! \n" + "\t Usage: IP/remoteObjectName");
+        }
+
+
+    }
+
+
+}
Index: project/src/main/java/utils/Constants.java
===================================================================
--- project/src/main/java/utils/Constants.java	(nonexistent)
+++ project/src/main/java/utils/Constants.java	(revision 1)
@@ -0,0 +1,24 @@
+package main.java.utils;
+
+import main.java.peer.Peer;
+
+public class Constants {
+
+    public static final int CHUNK_MAX_SIZE = 64000;
+    public static final String PUTCHUNK = "PUTCHUNK";
+    public static final String STORED = "STORED";
+    public static final String REMOVED = "REMOVED";
+    public static final String GETCHUNK = "GETCHUNK";
+    public static final String DELETE = "DELETE";
+    public static final String CHUNK = "CHUNK";
+    public static final String PROTOCOL_VERSION= "1.0";
+    public static final String MESSAGE_SEPARATOR = " ";
+    private final static char CR  = (char) 0x0D;
+    private final static char LF  = (char) 0x0A;
+    public final static String CRLF  = "" + CR + LF;
+    public static final String ENCODING = "ISO-8859-1";
+    public static final int MAX_WAITING_TIME = 400;
+    public static final int DISK_SIZE = 2560;
+    public static final int PACKET_MAX_SIZE = 65000;
+
+}
\ No newline at end of file
Index: project/src/main/java/utils/Utilities.java
===================================================================
--- project/src/main/java/utils/Utilities.java	(nonexistent)
+++ project/src/main/java/utils/Utilities.java	(revision 1)
@@ -0,0 +1,162 @@
+package main.java.utils;
+
+
+import main.java.file.FileChunk;
+import main.java.file.FileChunkID;
+import main.java.file.FileID;
+import main.java.peer.Peer;
+
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.*;
+import java.security.MessageDigest;
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+
+import static main.java.utils.Constants.*;
+
+public class Utilities {
+
+
+    public static String sha256(String base) {
+        try{
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            byte[] hash = digest.digest(base.getBytes("UTF-8"));
+            StringBuffer hexString = new StringBuffer();
+
+            for (int i = 0; i < hash.length; i++) {
+                String hex = Integer.toHexString(0xff & hash[i]);
+                if(hex.length() == 1) hexString.append('0');
+                hexString.append(hex);
+            }
+
+            return hexString.toString();
+
+        } catch(Exception ex){
+            throw new RuntimeException(ex);
+        }
+    }
+
+
+
+
+    public static InetAddress getLocalAddress() throws IOException
+    {
+        MulticastSocket socket = new MulticastSocket();
+        socket.setTimeToLive(0);
+
+        InetAddress addr = InetAddress.getByName("225.0.0.0");
+        socket.joinGroup(addr);
+
+        byte[] bytes = new byte[0];
+        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, addr,
+                socket.getLocalPort());
+
+        socket.send(packet);
+        socket.receive(packet);
+
+        socket.close();
+
+        return packet.getAddress();
+    }
+
+    public static boolean validateAccessPoint(String peerAP) {
+
+        Pattern pattern;
+        pattern = Pattern.compile("^([0-9]{1,3}\\.){3}[0-9]{1,3}(\\/([A-Z]|[a-z])+)?$");
+
+        if(!pattern.matcher(peerAP).matches()) {
+            System.out.println("Invalid Access Point!");
+            return false;
+        }
+        return true;
+    }
+
+
+
+    //TODO: CONFIRM MESSAGES ARE AS SPECIFIED IN DOCUMENT <- IMPORTANT FOR INTEROPERABILITY
+    public static byte[] messageConstructor(String protocol, FileChunk chunk, FileChunkID chunkID, FileID fileID) {
+        String message = "default";
+
+        byte[] msgToSend = new byte[0];
+
+        //TODO: chunkgetChunkData() only works for txt. message could be byte[]?
+
+        switch (protocol) {
+            case "PUTCHUNK":
+                message = PUTCHUNK + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += chunk.getFileID() + MESSAGE_SEPARATOR;
+                message += chunk.getChunkNo() + MESSAGE_SEPARATOR;
+                message += chunk.getReplicationDegree() + MESSAGE_SEPARATOR;
+                message += CRLF + CRLF;
+                msgToSend = concatBytes(message.getBytes(), chunk.getChunkData());
+                break;
+            case "STORED":
+                message = STORED + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += chunkID.getFileID() + MESSAGE_SEPARATOR;
+                message += chunkID.getChunkNumber() + MESSAGE_SEPARATOR;
+                message += CRLF + CRLF;
+                msgToSend = message.getBytes();
+                break;
+            case "REMOVED":
+                message = REMOVED + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += chunkID.getFileID() + MESSAGE_SEPARATOR;
+                message += chunkID.getChunkNumber() + MESSAGE_SEPARATOR;
+                message += CRLF + CRLF;
+                msgToSend = message.getBytes();
+                break;
+            case "DELETE":
+                message = DELETE + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += fileID.toString() + MESSAGE_SEPARATOR;
+                message += CRLF+CRLF;
+                msgToSend = message.getBytes();
+                break;
+            case "GETCHUNK":
+                message = GETCHUNK + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += chunkID.getFileID() + MESSAGE_SEPARATOR;
+                message += chunkID.getChunkNumber() + MESSAGE_SEPARATOR;
+                message += CRLF + CRLF;
+                msgToSend = message.getBytes();
+                break;
+            case "CHUNK":
+                message = CHUNK + MESSAGE_SEPARATOR;
+                message += Peer.getProtocolVersion() + MESSAGE_SEPARATOR;
+                message += Peer.getID() + MESSAGE_SEPARATOR;
+                message += chunk.getFileID().toString() + MESSAGE_SEPARATOR;
+                message += chunkID.getChunkNumber() + MESSAGE_SEPARATOR;
+                message += CRLF + CRLF;
+                msgToSend = concatBytes(message.getBytes(), chunk.getChunkData());
+                break;
+
+        }
+        return msgToSend;
+    }
+
+
+    public static byte[] concatBytes(byte[] a, byte[] b) {
+        int aLen = a.length;
+        int bLen = b.length;
+
+        byte[] c = new byte[aLen + bLen];
+
+        System.arraycopy(a, 0, c, 0, aLen);
+        System.arraycopy(b, 0, c, aLen, bLen);
+
+        return c;
+    }
+
+}
Index: project/src/backup.sh
===================================================================
--- project/src/backup.sh	(nonexistent)
+++ project/src/backup.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj BACKUP /Users/zemiguel/IdeaProjects/SDIS-P1/src/files/test1.pdf 1
\ No newline at end of file
Index: project/src/backup2.sh
===================================================================
--- project/src/backup2.sh	(nonexistent)
+++ project/src/backup2.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj BACKUP /Users/zemiguel/IdeaProjects/SDIS-P1/src/files/test2.txt 1
\ No newline at end of file
Index: project/src/delete.sh
===================================================================
--- project/src/delete.sh	(nonexistent)
+++ project/src/delete.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj DELETE test1.pdf
\ No newline at end of file
Index: project/src/peer.sh
===================================================================
--- project/src/peer.sh	(nonexistent)
+++ project/src/peer.sh	(revision 1)
@@ -0,0 +1 @@
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.server.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/peer/Peer 1.0 1 224.0.0.0:8000 224.0.0.0:8001 224.0.0.0:8002
\ No newline at end of file
Index: project/src/reclaim.sh
===================================================================
--- project/src/reclaim.sh	(nonexistent)
+++ project/src/reclaim.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj RECLAIM 0
\ No newline at end of file
Index: project/src/reset.sh
===================================================================
--- project/src/reset.sh	(nonexistent)
+++ project/src/reset.sh	(revision 1)
@@ -0,0 +1,3 @@
+find . -type f -name '*.class' -delete
+
+rm -r peer[0-9]
\ No newline at end of file
Index: project/src/restore2.sh
===================================================================
--- project/src/restore2.sh	(nonexistent)
+++ project/src/restore2.sh	(revision 1)
@@ -0,0 +1,3 @@
+javac -cp /Users/zemiguel/IdeaProjects/SDIS-P1/src/ main/java/testapp/TestApp.java
+
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj RESTORE test2.txt
\ No newline at end of file
Index: project/src/state.sh
===================================================================
--- project/src/state.sh	(nonexistent)
+++ project/src/state.sh	(revision 1)
@@ -0,0 +1 @@
+java -Djava.net.preferIPv4Stack=true -Djava.rmi.serv.codebase=file:/Users/zemiguel/IdeaProjects/SDIS-P1/src/main/java/service/ main/java/testapp/TestApp 127.0.0.1/obj STATE
\ No newline at end of file
