Index: service/FileChunker.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/FileChunker.class
===================================================================
--- service/FileChunker.class	(nonexistent)
+++ service/FileChunker.class	(revision 1)

Property changes on: service/FileChunker.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/Peer.java
===================================================================
--- service/Peer.java	(nonexistent)
+++ service/Peer.java	(revision 1)
@@ -0,0 +1,410 @@
+package service;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.rmi.registry.Registry;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import channels.Message;
+import channels.MessageHeader;
+import protocols.BackupChunkProtocol;
+import protocols.RestoreChunksProtocol;
+
+public class Peer implements RMI {
+
+	ScheduledThreadPoolExecutor exec;
+	private static Cloud cloud;
+
+	public Peer() {}
+
+	public static void main(String[] args) {
+
+		try 
+		{
+			Peer peer = new Peer();
+			RMI rmi = (RMI) UnicastRemoteObject.exportObject(peer, 0);
+			String acess_point = args[2];
+			Registry registry = LocateRegistry.getRegistry();
+			registry.bind(acess_point, rmi);
+			System.err.println("Peer ready");
+		} 
+		catch (Exception e) {
+			System.err.println("Peer exception: " + e.toString());
+			return;
+		}
+
+		if(args.length != 9) {
+			printHelp();
+			return;
+		}
+
+		try {
+			Cloud aux = new Cloud(args[0], args[1]);
+			cloud = aux.getPeerTools(args[0], args[1]);
+			cloud.createControlRoom(args[3], args[4]);
+			cloud.createBackupChannel(args[5], args[6]);
+			cloud.createRestoreChannel(args[7], args[8]);
+
+			String path = Constants.DATABASE_DIR;
+			Path p = Paths.get(path);
+			if (Files.exists(p)) 
+			{
+				try {
+					cloud.loadFromDisk();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+
+			}
+
+			//			System.out.println(cloud.getStoredChunks().size());
+
+		} catch (NumberFormatException | IOException e) {
+			System.out.println(e.getMessage());
+			return;
+		}
+
+		if(Cloud.checkOrCreateDirectory(Constants.CHUNKS_DIR + args[0] + "/") == -1) {
+			System.out.println("Couldn't create peer chunks dir!");
+		}
+
+		cloud.activateChannels();
+		System.out.println("Channels activated");
+	}
+
+	// the name of each multicast channel consists of the ip multicast address and
+	// port, passed as cmd line arguments, followed by protocol version, server id
+	// and service access point
+	private static void printHelp() {
+		System.out.println("java service.Peer SERVER_id VERSION ACCESS_POINT MC_ip MC_port MDB_ip MDB_port MDR_ip MDR_port");
+	}
+
+	public void saveState() {
+		try {
+			cloud.saveToDisk();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+
+	public synchronized String backup(String file_path, int replication_degree) 
+	{  	
+		FileChunker fc;
+		Message m;
+		MessageHeader h;
+		int numberOfChunks;
+		ExecutorService WORKER_THREAD_POOL;
+		List<Callable<Object>> callables = new ArrayList<Callable<Object>>();
+		List<Future<Object>> futures = null;
+		ArrayList<Chunk> chunkHolder = null;
+		String[] headerElements = new String[Constants.PUTCHUNK_N_ARGS];
+		Chunk aux;
+		int numberOfSteps;
+
+		System.out.println("BACKUP request received");
+
+		try {
+			fc = new FileChunker(file_path, replication_degree);
+			//		cloud.createDirectoryFile(fc);
+		} 
+		catch (IOException e) {
+			System.out.println("ERROR chunking file -> " + e.getMessage());
+			return "ERROR chunking file -> " + e.getMessage();
+		}
+
+		chunkHolder = fc.getChunks();
+		numberOfChunks = chunkHolder.size();
+
+		cloud.registerNumberOfFileChunks(fc.getFileID(), numberOfChunks);
+
+		System.out.println("File splited into " + numberOfChunks + " chunks.");
+
+		headerElements[0] = Constants.PUTCHUNK;
+		headerElements[1] = cloud.getProtocolVersion();
+		headerElements[2] = Integer.toString(cloud.getID());
+
+		numberOfSteps = numberOfChunks % Constants.MAX_NUMBER_OF_THREADS == 0 ? (int)(numberOfChunks / Constants.MAX_NUMBER_OF_THREADS):(int)(numberOfChunks / Constants.MAX_NUMBER_OF_THREADS) + 1;
+
+		for(int step = 0, first, last; step < numberOfSteps; step++, futures.clear(), callables.clear()) {
+			first = step * Constants.MAX_NUMBER_OF_THREADS;
+			last = numberOfChunks - first < Constants.MAX_NUMBER_OF_THREADS ? numberOfChunks : first + Constants.MAX_NUMBER_OF_THREADS;
+
+			System.out.println("Sending from " + first + " to " + last);
+
+			WORKER_THREAD_POOL = Executors.newFixedThreadPool(last-first);
+
+			for(int chunkNo = first; chunkNo < last; chunkNo++) {
+				aux = chunkHolder.get(chunkNo);
+				headerElements[3] = fc.getFileID();
+				headerElements[4] = aux.getChunkNumber();
+				headerElements[5] = Integer.toString(replication_degree);
+				try {
+					h = new MessageHeader(headerElements.clone());
+
+					m = new Message(h, aux.getChunkContent());
+
+					callables.add(new BackupChunkProtocol(cloud, aux, m ,replication_degree));
+				} 
+				catch(IllegalArgumentException e) {
+					System.out.println(e.getMessage());
+					return "Backup failed preparing chunks";
+				}
+			}
+
+			try {
+				futures = WORKER_THREAD_POOL.invokeAll(callables);
+
+				WORKER_THREAD_POOL.shutdown();
+
+				if(WORKER_THREAD_POOL.awaitTermination(35, TimeUnit.SECONDS)) {
+					for(Future<Object> p: futures) {
+						if(!(boolean)p.get()) {
+							System.out.println("One of the chunks could not be stored.");
+
+							delete(file_path); //order successfully stored chunks to be deleted
+
+							return "Backup failed preparing chunks";
+						}
+					}
+				}
+			}
+			catch (InterruptedException | ExecutionException e) {
+				return "Error in peer: " + e.getMessage();
+			}	 
+		}
+
+		System.out.println("backup peer has " + cloud.getNumberOfStoredChunks());
+
+		saveState();
+
+		return "Backup successfull.";
+	}
+
+	public synchronized String restore(String file_path)
+	{
+		System.out.println("RESTORE request received");
+
+		FileChunker fc;
+		List<Callable<Object>> callables = new ArrayList<Callable<Object>>();
+		List<Future<Object>> futures = null;
+		int numberOfChunks;
+		MessageHeader h;
+		int numberOfSteps;
+		ExecutorService WORKER_THREAD_POOL;
+		String[] headerElements = new String[Constants.GETCHUNK_N_ARGS];
+
+		headerElements[0] = Constants.GETCHUNK;
+		headerElements[1] = cloud.getProtocolVersion();
+		headerElements[2] = Integer.toString(cloud.getID());
+
+		try
+		{
+			fc = new FileChunker(file_path);
+		}
+		catch (IOException e) 
+		{
+			System.out.println("ERROR restoring file -> " + e.getMessage());
+			return "ERROR restoring file -> " + e.getMessage();
+		}
+
+		headerElements[3] = fc.getFileID();
+
+		String file_id = fc.getFileID();
+
+		numberOfChunks = cloud.getNumberOfChunks(file_id);
+
+		if(numberOfChunks < 0) {
+			return file_path + " wasn't backep up by this peer.";
+		}
+
+		numberOfSteps = numberOfChunks % Constants.MAX_NUMBER_OF_THREADS == 0 ? (int)(numberOfChunks / Constants.MAX_NUMBER_OF_THREADS):(int)(numberOfChunks / Constants.MAX_NUMBER_OF_THREADS) + 1;      
+
+		for(int step = 0, first, last; step < numberOfSteps; step++, futures.clear(), callables.clear()) {
+			first = step * Constants.MAX_NUMBER_OF_THREADS;
+			last = numberOfChunks - first < Constants.MAX_NUMBER_OF_THREADS ? numberOfChunks : first + Constants.MAX_NUMBER_OF_THREADS;
+
+			System.out.println("Sending from " + first + " to " + last);
+
+			WORKER_THREAD_POOL = Executors.newFixedThreadPool(last-first);
+
+			for(int chunkNo = first; chunkNo < last; chunkNo++) 
+			{
+				headerElements[4] = Integer.toString(chunkNo);
+
+				try 
+				{
+					h = new MessageHeader(headerElements.clone());
+
+					cloud.registerFileChunkToRestore(file_id, headerElements[4]);
+
+					callables.add(new RestoreChunksProtocol(cloud, h));
+
+				} 
+				catch(IllegalArgumentException e) {
+					System.out.println(e.getMessage());
+					return "Restore failed preparing chunks";
+				}
+			}
+
+			try {
+				futures = WORKER_THREAD_POOL.invokeAll(callables);
+
+				WORKER_THREAD_POOL.shutdown();
+
+				if(WORKER_THREAD_POOL.awaitTermination(35, TimeUnit.SECONDS)) {
+					for(Future<Object> p: futures) {
+						if(!(boolean)p.get()) {
+							System.out.println("One of the chunks could not be restored.");
+
+							return "Restore failed preparing chunks";
+						}
+					}
+				}
+			}
+			catch (InterruptedException | ExecutionException e) {
+				return "Error in peer: " + e.getMessage();
+			}	 
+
+		}
+
+		System.out.println("Colected all file parts. Building file.");
+
+		try {
+			cloud.buildFile(file_id, numberOfChunks, Paths.get(file_path).getFileName().toString());
+		} 
+		catch (IOException e) {
+			System.out.println("ERROR builidng file -> " + e.getMessage());
+		}
+
+		saveState();
+
+		return "Restored file successfully.";
+	}
+
+	public synchronized String delete(String file_path)
+	{    
+		FileChunker fc;
+		MessageHeader h;
+		String[] headerElements = new String[Constants.DELETE_N_ARGS];
+
+		System.out.println("DELETE request.");
+
+		try {
+			fc = new FileChunker(file_path);
+			headerElements[0] = Constants.DELETE;
+			headerElements[1] = cloud.getProtocolVersion();
+			headerElements[2] = Integer.toString(cloud.getID());
+			headerElements[3] = fc.getFileID();  
+			h = new MessageHeader(headerElements.clone());
+			if(cloud.checkIfPeerStoredFile(headerElements[3])) {
+				for(int i = 0; i < 3; i++) {
+					cloud.controlRoom.sendHeader(h);
+					Thread.sleep(1000);
+				}	  
+			}
+			else {
+				return "File was not uploaded here. Delete failed.";
+			}
+
+		}
+		catch(Exception e) {
+			System.out.println("ERROR deleting file -> " + e.getMessage());
+			return "ERROR deleting file -> " + e.getMessage();
+		}
+
+		saveState();
+
+		return "Delete successfull.";
+	}
+
+	public synchronized String reclaim(long disk_space)
+	{
+		if(disk_space < 0) {
+			return "Space_disk can't be lower than 0!";
+		}
+
+		System.out.println("Space reclaim request to " + disk_space + " bytes.");
+
+		if(disk_space >= cloud.getChunkSpace()) {
+			cloud.setAvailableSpace(disk_space);
+		}
+		else {
+			cloud.freeUpSpace(disk_space);
+		}
+
+		saveState();
+
+		return "Space reclaim successfull";
+	}
+
+	public synchronized String state()
+	{
+		System.out.println("STATE OPERATION RECEIVED");
+
+		String answer = "\n\nPeer " + cloud.getID() + " state: \n";
+
+		answer += "\tBacked-up files:\n";
+
+		String pathName;
+		int repDegree, nChunks;
+		Chunk c;
+
+		System.out.println("Number of files = " + cloud.getBackedUpFileNames().size());
+
+		for(String key : cloud.getBackedUpFileNames()) {
+
+			pathName = cloud.getPathNameFromFileId(key);
+			repDegree = cloud.getBackedUpChunkNecessaryRepDegree(key);
+
+			if(pathName != null && repDegree > -1) {
+				answer += "\t\tFILE PATHNAME: " + pathName + "    FILE ID: " + key + "    DESIRED REPPLICATION DEGREE:" + repDegree + "\n";
+			}
+			else {
+				System.out.println("Error in key: " + key + " pathname " + pathName + " degree " + repDegree);
+				continue;
+			}
+
+			nChunks = cloud.getNumberOfChunks(key);
+
+			if(nChunks < 0) {
+				System.out.println("Error getting number of chunks in key: " + key + " pathname " + pathName + " degree " + repDegree);
+				continue;
+			}
+
+			answer += "\t\t\tChunks:\n";
+
+			for(int i = 0; i < nChunks; i++) {
+				answer += "\t\t\t\tchk" + i + "   REP: DEGREE " + cloud.getBackepUpChunkPerceivedRepDegree(key, i) + "\n";
+			}
+
+			answer += "\n";
+		}
+
+		answer += "\tStored chunks:\n";
+
+		for(String key : cloud.getStoredChunks().keySet()) {
+			c = cloud.getStoredChunks().get(key);
+
+			if(c.getFilePathName() == null) { //only backep up files have a non-null file path name
+				answer += "\t\t" +c.getChunkId() + ":" + c.getChunkNumber() + "   " + "SIZE: " + c.getChunkSize() / 1024 + " KBytes    " + " PERCEIVED REP. DEGREE: " + c.getActualReplicationDegree() + "\n";
+			}
+		}
+
+		answer += "\n\n\t Total peer disk space: " + cloud.getTotalSpace() / 1024 + " KBytes   Space used by chunks : " + cloud.getChunkSpace() / 1024 + " KBytes   Available space: " + cloud.getAvailableSpace() / 1024 + " KBytes.\n";
+
+		return answer;
+	}
+}
Index: service/.project
===================================================================
--- service/.project	(nonexistent)
+++ service/.project	(revision 1)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>service</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: channels/BackupChannel.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/BackupChannel.class
===================================================================
--- channels/BackupChannel.class	(nonexistent)
+++ channels/BackupChannel.class	(revision 1)

Property changes on: channels/BackupChannel.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/Chunk.java
===================================================================
--- service/Chunk.java	(nonexistent)
+++ service/Chunk.java	(revision 1)
@@ -0,0 +1,140 @@
+package service;
+
+import java.util.ArrayList;
+
+public class Chunk implements java.io.Serializable {
+
+	private int chunkNr;
+    private String fileID;
+    private String filePathName = null;
+    private int size;
+
+    private int actual_replication_degree = 0;
+    private int desired_replication_degree;
+
+    private byte[] chunkContent;
+
+//    each chunk is identified by the pair <fileID, chunkNo>
+    public Chunk(int n, byte[] c, String s, int desiredRepDegree) throws Exception
+    {
+      this.chunkNr = n;
+      this.chunkContent = getFiltredData(c);
+
+      if(chunkContent.length > Constants.MAX_CHUNK_SIZE) {
+    	  throw new Exception("Invalid chunk size -> " + chunkContent.length);
+      }
+      
+      this.size = chunkContent.length;
+      
+      desired_replication_degree = desiredRepDegree;
+      
+      fileID = s;
+    }
+    
+    private byte[] getFiltredData(byte[] c) {
+    	ArrayList<Byte> aux = new ArrayList<Byte>();
+    	boolean toAdd = false;
+    	
+		for(byte b : c) {
+			aux.add(b);
+		}
+		
+		byte[] toReturn = new byte[aux.size()];
+
+		for(int i = aux.size() - 1, k = 0; i >= 0 ; i--) {
+			if(aux.get(i) == 0) {
+				if(!toAdd) {
+					continue;
+				}
+				else {
+					toReturn[k] = aux.get(i);
+					k++;
+				}
+			}
+			else {
+				toReturn[k] = aux.get(i);
+				toAdd = true;
+				k++;
+			}	
+		}
+		
+		return toReturn;
+	}
+
+	public int getReplicationDegree() {
+    	return desired_replication_degree;
+    }
+
+    public void setActualReplicationDegree(int rd)
+    {
+      this.actual_replication_degree = rd;
+    }
+
+
+    public String getChunkNumber()
+    {
+      return Integer.toString(this.chunkNr);
+    }
+
+
+    public String getChunkId()
+    {
+      return this.fileID;
+    }
+
+
+    public int getChunkSize()
+    {
+      return this.size;
+    }
+
+
+    public int getActualReplicationDegree()
+    {
+      return this.actual_replication_degree;
+    }
+
+
+    public byte[] getChunkContent()
+    {
+      return this.chunkContent;
+    }
+    
+    public int compareTo(Chunk c)
+    {
+         return(this.chunkNr - c.chunkNr);
+    }
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (!(obj instanceof Chunk)) {
+			return false;
+		}
+		Chunk other = (Chunk) obj;
+		if (chunkNr != other.chunkNr) {
+			return false;
+		}
+		if (fileID == null) {
+			if (other.fileID != null) {
+				return false;
+			}
+		} else if (!fileID.equals(other.fileID)) {
+			return false;
+		}
+		return true;
+	}
+
+	public String getFilePathName() {
+		return filePathName;
+	}
+
+	public void setFilePathName(String filePathName) {
+		this.filePathName = filePathName;
+	}
+}
Index: service/Client.java
===================================================================
--- service/Client.java	(nonexistent)
+++ service/Client.java	(revision 1)
@@ -0,0 +1,150 @@
+package service;
+
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+public class Client {
+
+    private Client() {}
+
+    public static void main(String[] args) { 
+
+    	System.out.println("CLIENT");
+    	try 
+        {      	
+        	int argc = args.length;
+
+            if(argc < 2 || argc > 4)
+            {
+              print_usage();
+              return;
+            }
+        	
+            
+            String aux = args[0];
+            
+            String[] acess_point = aux.split(":");
+            
+            String ip = acess_point[0];
+            String id = acess_point[1];
+            
+            String sub_protocol = args[1];
+            
+            Registry registry = LocateRegistry.getRegistry(ip);
+            RMI rmi = (RMI) registry.lookup(id);
+            
+            String file_path;
+
+            String response;
+            
+            switch(sub_protocol)
+            {
+              case "BACKUP":
+                if(argc != 4)
+                {
+                	print_error_ops(sub_protocol);
+                }
+                
+                
+                file_path = args[2];
+                int replication_degree = Integer.parseInt(args[3]);
+               
+                
+                response = rmi.backup(file_path, replication_degree);
+                System.out.println("response: " + response);
+                break;
+
+              case "RESTORE":
+                if(argc != 3)
+                {
+                	print_error_ops(sub_protocol);
+                }
+
+                file_path = args[2];
+                response = rmi.restore(file_path);
+                System.out.println("response: " + response);
+                break;
+
+              case "DELETE":
+                if(argc != 3)
+                {
+                	print_error_ops(sub_protocol);
+                }
+
+                
+                file_path = args[2];
+                response = rmi.delete(file_path);
+                System.out.println("response: " + response);
+                break;
+
+              case "RECLAIM":
+                if(argc != 3)
+                {
+                  print_error_ops(sub_protocol);
+                }
+
+                long disk_space = Long.parseLong(args[2]);
+                response = rmi.reclaim(disk_space);
+                System.out.println("response: " + response);
+                break;
+
+              case "STATE":
+                if(argc != 2)
+                {
+                	print_error_ops(sub_protocol);
+                }
+
+                response = rmi.state();
+                System.out.println("response: " + response);
+                break;
+            }
+
+        } catch (Exception e) {
+            System.err.println("Client exception: " + e.toString()); 
+        }
+    }
+    
+    public static void print_usage()
+    {
+      System.out.println("Usage: :\n");
+      System.out.println("  java Client <peer_ap> <operation> <opnd_1> <opnd_2> \n");
+      System.out.println("  - <peer_ap> -> peer's acess point\n");
+      System.out.println("  - <operation> -> Subprotocol ops: BACKUP, RESTORE, DELETE, RECLAIM;  Enhancements: ops with ENH in the end. State: STATE \n");
+      System.out.println("  - <opnd_1> -> path name of the file or in case of op RECLAIM the max amout of disk space\n");
+      System.out.println("  - <opnd_2>* -> integer that specifies the desired replication degree\n");
+    }
+    
+    public static void print_error_ops(String error)
+    {
+      switch(error)
+      {
+        case "BACKUP":
+          System.out.println("ERROR on BACKUP format. Must be:\n");
+          System.out.println("> BACKUP <file_path> <replication_degree>");
+          break;
+
+        case "RESTORE":
+          System.out.println("ERROR on RESTORE format. Must be:\n");
+          System.out.println("> RESTORE <file_path>");
+          break;
+
+        case "DELETE":
+          System.out.println("ERROR on DELETE format. Must be:\n");
+          System.out.println("> DELETE <file_path>");
+          break;
+
+        case "RECLAIM":
+          System.out.println("ERROR on RECLAIM format. Must be:\n");
+          System.out.println("> RECLAIM <disk_space>");
+          break;
+
+        case "STATE":
+          System.out.println("ERROR on STATE format. Must be:\n");
+          System.out.println("> RECLAIM <disk_space>");
+          break;
+
+         default:
+           System.out.println("ERROR. Unkown op");
+      }
+    }
+}
Index: service/Cloud.java
===================================================================
--- service/Cloud.java	(nonexistent)
+++ service/Cloud.java	(revision 1)
@@ -0,0 +1,846 @@
+package service;
+
+import channels.BackupChannel;
+import channels.ControlChannel;
+import channels.Message;
+import channels.MessageHeader;
+import channels.RestoreChannel;
+import protocols.SendChunkMessageProtocol;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentHashMap.KeySetView;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+//https://www.geeksforgeeks.org/serialization-in-java/
+
+public class Cloud implements java.io.Serializable
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * stores received V and backedUp chunks
+	 * key = <fileID>
+	 */
+	private ConcurrentHashMap<String, Chunk> storedChunks;
+
+	/**
+	 * stores the number of chunks taht a file that was backed up in peer was splited into
+	 * key = <fileID>
+	 */
+	private ConcurrentHashMap<String, Integer> numberOfFileChunks;
+
+	/**
+	 * registers the peers that have stored chunks sent by this peer
+	 * key = <fileID>:<ChunkNo>
+	 */
+	private ConcurrentHashMap<String, HashSet<Integer>> colaborativePeers;
+
+	/**
+	 * stores the number of times a chunk was stored
+	 * key = <fileID>:<ChunkNo>
+	 */
+	private ConcurrentHashMap<String, Integer> numberOfChunksConfirmations;
+
+	/**
+	 * key = <fileID>:<ChunkNo>
+	 */
+	private ConcurrentHashMap<String, Integer> chunksToRestore;
+
+	/**
+	 * Holds chucks restored by restore protocol
+	 * key = <fileID>:<ChunkNo>
+	 */
+	private ConcurrentHashMap<String, Chunk> recoveredChunks;
+
+	/**
+	 * stores the number of chunks a file was splited into
+	 * key = <fileID>
+	 */
+	private ConcurrentLinkedQueue<String> numberOfFileChunksToBeRestored;
+
+	private ArrayList<FileChunker> data;
+	private int serverID;
+	private String protocol_version;
+	public ControlChannel controlRoom;
+	public BackupChannel backupCh;
+	public RestoreChannel restoreCh;
+
+	long spaceRemaining;
+
+
+	public Cloud(String id, String version)
+	{
+		numberOfFileChunks = new ConcurrentHashMap<String, Integer>();
+		storedChunks = new ConcurrentHashMap<String, Chunk>();
+		colaborativePeers = new ConcurrentHashMap<String, HashSet<Integer>>();
+		numberOfChunksConfirmations = new ConcurrentHashMap<String, Integer>();
+		this.data = new ArrayList<>();
+
+		this.numberOfFileChunksToBeRestored = new ConcurrentLinkedQueue<String>();
+		this.recoveredChunks = new  ConcurrentHashMap<String, Chunk>();
+
+		this.spaceRemaining = 1073741824; //1GB
+
+		serverID = Integer.parseInt(id);
+		protocol_version = version;
+	}
+
+	public synchronized boolean sendBackupMessage(Message m) {
+		return backupCh.sendMessage(m);
+	}
+
+	public synchronized boolean sendRestoreMessage(Message m) {
+		return restoreCh.sendMessage(m);
+	}
+
+	public Cloud getPeerTools(String id, String version) {
+		return new Cloud(id, version);
+	}
+
+	public int getChunkCurrentRepDegree(String fileId, String chunkNo) {
+		String key = fileId + ':' + chunkNo;
+
+		if(numberOfChunksConfirmations.containsKey(key)) {
+			return numberOfChunksConfirmations.get(key);
+		}
+
+		return -1;
+	}
+
+	public byte[] reverseList(byte[] a) {
+		ArrayList<Byte> aux = new ArrayList<Byte>(a.length);
+
+		for(byte b : a) {
+			aux.add(0, b);
+		}
+
+		byte[] toReturn = new byte[a.length];
+
+		int i = 0;
+
+		for(byte b : aux) {
+			toReturn[i] = b;
+			i++;
+		}
+
+		return toReturn;
+	}
+
+	public boolean buildFile(String file_id, int maxchunks, String fileName) throws IOException
+	{
+		String restore_path = Constants.CHUNKS_DIR + this.serverID + "/restored/";
+		String key;
+		Chunk aux;
+		int total = 0;
+
+		checkOrCreateDirectory(restore_path);
+
+		File restoredFile = new File(restore_path + fileName);
+
+		restoredFile.createNewFile();
+
+		FileOutputStream fos =  new FileOutputStream(restoredFile);
+
+		for(int i = 0; i < maxchunks; i++)
+		{
+			key = file_id + ':' + Integer.toString(i);
+			if(recoveredChunks.containsKey(key)) {
+				aux = recoveredChunks.get(key);
+
+				if(aux != null) {
+					fos.write(reverseList(aux.getChunkContent()));
+					total += aux.getChunkContent().length;
+				}
+				else {
+					System.out.println("NULL chunk file might be corrupted");
+				}	
+			}
+		}
+
+		try
+		{
+			fos.close();
+		}
+		catch(IOException e) {
+			recoveredChunks.clear();
+			System.out.println(e.getMessage());
+		}
+		
+		recoveredChunks.clear();
+
+		System.out.println("Restored file " + fileName + " with " + total + " bytes.");
+
+		return false;
+	}
+
+	public ArrayList<FileChunker> getData()
+	{
+		return this.data;
+	}
+
+	public ConcurrentHashMap<String, Integer> getChunksToRestore()
+	{
+		return this.chunksToRestore;
+	}
+
+	void addFile(FileChunker fc)
+	{
+		this.data.add(fc);
+	}
+
+
+	public void addChunk(Chunk c) {
+		String key = c.getChunkId() + ":" + c.getChunkNumber();
+		storedChunks.put(key, c);
+	}
+
+
+	public void deleteChunks(FileChunker fc, ArrayList<FileChunker> fcs) 
+	{
+
+		String file_id = fc.getFileID();
+
+		String path = Constants.CHUNKS_DIR + this.serverID + "/backup/" + file_id + '/';
+
+		File f = new File(path);
+
+		String[] entries = f.list();
+
+		for(String s: entries)
+		{
+			File currentFile = new File(f.getPath(), s);
+			currentFile.delete();
+		}
+
+		f = new File(path);
+		f.delete();
+	}
+
+	public boolean storeRestoreChunk(Message m) {
+		Chunk toStore = null;
+		byte[] chunkData;
+
+		try {
+			chunkData = m.getChunkContent();
+			toStore = new Chunk(Integer.parseInt(m.getHeader().getChunkNumber()), chunkData, m.getHeader().getFileId(), Integer.parseInt(m.getHeader().getDegree()));
+		} catch (Exception e) {
+			System.out.println("Error in chunk: " + e.getMessage());
+			return false;
+		}
+
+		addToRecoveredChunks(toStore);
+
+		return true;
+	}
+
+	public boolean storeChunk(Message m) {
+
+		Chunk toStore = null;
+		String filename, path;
+		FileOutputStream out = null;
+		File dir = null;
+		byte[] chunkData;
+
+		try {
+			chunkData = m.getChunkContent();
+			toStore = new Chunk(Integer.parseInt(m.getHeader().getChunkNumber()), chunkData, m.getHeader().getFileId(), Integer.parseInt(m.getHeader().getDegree()));
+		} catch (Exception e) {
+			System.out.println("Error in chunk: " + e.getMessage());
+			return false;
+		}
+
+		if(toStore.getChunkContent().length > spaceRemaining) {
+			System.out.println("There is no available space");
+			return false;
+		}
+
+		this.addChunk(toStore);
+		spaceRemaining -= toStore.getChunkSize();
+
+		path = Constants.CHUNKS_DIR + this.serverID + "/backup/" + toStore.getChunkId() + "/";
+		filename = getFileName(Integer.parseInt(toStore.getChunkNumber()));
+
+		if(!doesDirExists(path)) {	
+			dir = new File(path);
+			if(!dir.mkdirs()) {
+				return false;
+			}
+		}
+
+		try {
+			dir = new File(path + filename);
+			dir.createNewFile();
+			out = new FileOutputStream(dir);
+			out.write(toStore.getChunkContent());
+			out.close();
+		} catch (IOException e) {
+			System.out.println("Error writing file to disk: " + e.getMessage());
+			return false;
+		}
+
+		return true;
+	}
+
+	public boolean checkIfChunkWasRestored(String fileid, String chunknr)
+	{
+		return this.recoveredChunks.containsKey(fileid + ':' + chunknr);	
+	}
+
+	public void addToRecoveredChunks(Chunk c) {
+		String key = c.getChunkId() + ":" + c.getChunkNumber();
+		if(!recoveredChunks.containsKey(key)) {
+			recoveredChunks.put(key, c);
+		}
+	}
+
+	public void registerFileChunkToRestore(String file_id, String chunknr)
+	{
+
+		if(!this.numberOfFileChunksToBeRestored.contains(file_id + ':' + chunknr))
+		{
+			this.numberOfFileChunksToBeRestored.add(file_id + ':' + chunknr);
+		}
+	}
+
+	public boolean isFileChunkRegistered(String fileId, String chunknr) {
+		return this.numberOfFileChunksToBeRestored.contains(fileId + ':' + chunknr);
+	}
+
+	public void createDirectoryFile(FileChunker fc) throws IOException
+	{
+		String file_id = fc.getFileID();
+
+		String path = Constants.CHUNKS_DIR + this.serverID + "/backup/" + file_id + '/';
+
+		if(checkOrCreateDirectory(path) == -1)
+		{
+			System.out.println("Couldn't create peer backup chunks dir for that file!");
+		}
+
+		for(int i = 0; i < fc.getChunks().size(); i++)
+		{
+			int nr = i + 1;
+			String filename = "chk" + Integer.toString(nr);
+			File dir = new File(path + filename);	
+
+
+			dir.createNewFile();
+
+			try(FileOutputStream out = new FileOutputStream(dir))
+			{
+				out.write(fc.getChunks().get(i).getChunkContent());
+			}
+		}
+	}
+
+	private static String getFileName(int chunkNumber) {
+		return "chk" + Integer.toString(chunkNumber);
+	}
+
+	private static boolean doesDirExists(String path) {
+		File f = new File(path);
+
+		return f.exists();
+	}
+
+	public boolean checkIfPeerStoredFile(String filename) {
+		return numberOfFileChunks.containsKey(filename);
+	}
+
+	public static int checkOrCreateDirectory(String dirName) 
+	{
+
+		File f = new File(dirName);
+
+		int result = -1;
+
+		if(!f.exists())
+		{
+			//			System.out.println("Creating directory " + f.getName());
+
+			try
+			{
+				f.mkdirs();
+				result = 1;
+			}
+			catch(SecurityException se)
+			{
+				//				 System.err.println("Dir exception: " + se.toString());
+				se.printStackTrace(); 
+				result = -1;
+			}
+
+			if(result == 1)
+			{
+				//				System.out.println("DIR " + f.getName() + " created");
+			}
+		}
+		else if(f.exists())
+		{
+			//			System.out.println("Directory already exists, proceeding");
+			result = 2;
+		}
+		else
+		{
+			result = -1;
+		}
+
+		return result;
+	}
+
+	public void createControlRoom(String ip, String port) throws IOException {
+		controlRoom = new ControlChannel(ip, port, this);
+	}
+
+	public void createBackupChannel(String ip, String port) throws IOException {
+		backupCh = new BackupChannel(ip, port, this);
+	}
+
+	public void createRestoreChannel(String ip, String port) throws IOException {
+		restoreCh = new RestoreChannel(ip, port, this);
+	}
+
+	public void activateChannels() {
+		controlRoom.start();
+		backupCh.start();
+		restoreCh.start();
+	}
+
+
+	public int getID() {
+		return serverID;
+	}
+
+	public boolean storeChunk(Chunk c)
+	{
+		if(!this.storedChunks.containsKey(c.getChunkId() + ":" + c.getChunkNumber())) {
+			addChunk(c);
+			spaceRemaining -= c.getChunkSize();
+		}
+
+		return true;
+	}
+
+	public String getProtocolVersion() {
+		return protocol_version;
+	}
+
+	public ConcurrentHashMap<String, Chunk> getStoredChunks()
+	{
+		return this.storedChunks;
+	}
+
+
+	public synchronized void registerNumberOfFileChunks(String fileID, Integer NFileChunks) {
+		System.out.println(fileID + " registered.");
+		numberOfFileChunks.put(fileID, NFileChunks);
+	}
+
+	public synchronized boolean insertAwaitingStoreFile(MessageHeader h) {
+
+		String key = h.getFileId() + ':' + h.getChunkNumber();
+		if(numberOfFileChunks.containsKey(h.getFileId())) {
+			colaborativePeers.put(key, new HashSet<Integer>());
+			numberOfChunksConfirmations.put(key, 0);
+			return true;
+		}
+		else {
+			System.out.println("Cloud hasn't got " + h.getFileId() + " registered.");
+			return false;
+		}
+	}
+
+	public boolean wasTheRepDegreeMatched(Chunk c) {	
+		return c.getReplicationDegree() <= numberOfChunksConfirmations.get(c.getChunkId() + ':' + c.getChunkNumber());
+	}
+
+
+
+	public synchronized boolean intrepertStoredMessage(MessageHeader h) {
+		String key = h.getFileId() + ':' + h.getChunkNumber();
+
+		if(numberOfFileChunks.containsKey(h.getFileId())) {
+			if(colaborativePeers.get(key).add(Integer.parseInt(h.getSenderId()))) {
+				numberOfChunksConfirmations.put(key, numberOfChunksConfirmations.get(key) + 1);
+			}
+			return true;
+		}
+		else {
+			if(storedChunks.containsKey(key)) {
+				if(!colaborativePeers.containsKey(key)) {
+					colaborativePeers.put(key, new HashSet<Integer>());
+					colaborativePeers.get(key).add(getID());
+				}			
+				colaborativePeers.get(key).add(Integer.parseInt(h.getSenderId()));
+				storedChunks.get(key).setActualReplicationDegree(colaborativePeers.get(key).size());
+
+				return true;
+			}
+			return false;
+		}
+	}
+
+	public synchronized boolean deleteFile(MessageHeader h) {
+		String fileId = h.getFileId();
+		String path = Constants.CHUNKS_DIR + this.serverID + "/backup/" + fileId + "/";
+		File dir = new File(path);
+		boolean somethingDeleted = false;
+
+		if(dir.isDirectory()) {
+			for(File f : dir.listFiles()) {
+				f.delete();
+				somethingDeleted = true;
+			}
+
+			if(!dir.delete()) {
+				System.out.println("Couldn't erase " + h.getFileId() + " from file system");
+				return false;
+			}
+		}
+
+		for(String key : storedChunks.keySet()) {
+			Chunk c = storedChunks.get(key);
+			if(c.getChunkId().equals(fileId)) {
+				storedChunks.remove(key);
+				spaceRemaining += c.getChunkSize();
+				somethingDeleted = true;
+			}
+		}
+
+		if(somethingDeleted) {
+			System.out.println(h.getFileId() + " erased from file system.");
+		}
+
+		return true;
+	}
+
+	public KeySetView<String, Integer> getBackedUpFileNames() {
+		return numberOfFileChunks.keySet();
+	}
+
+	public String getPathNameFromFileId(String fileId) {
+		if(storedChunks.containsKey(fileId + ":" + "0")) {
+			return storedChunks.get(fileId + ":" + "0").getFilePathName();
+		}
+
+		return null;
+	}
+
+	public int getBackedUpChunkNecessaryRepDegree(String fileId) {
+		if(storedChunks.containsKey(fileId + ":" + "0")) {
+			return storedChunks.get(fileId + ":" + "0").getReplicationDegree();
+		}
+
+		return -1;
+	}
+
+	public int getBackepUpChunkPerceivedRepDegree(String fileId, int id) {
+		if(colaborativePeers.containsKey(fileId + ":" + Integer.toString(id))) {
+			return colaborativePeers.get(fileId + ":" + Integer.toString(id)).size();
+		}
+
+		return 0;
+	}
+
+	public long getAvailableSpace() {
+		return this.spaceRemaining;
+	}
+
+	public int getNumberOfStoredChunks() {
+		return storedChunks.size();
+	}
+
+	public long getChunkSpace() {
+		long total = 0;
+
+		for(String key : storedChunks.keySet()) {
+			Chunk c = storedChunks.get(key);
+			total += c.getChunkSize();
+		}
+
+		return total;
+	}
+
+	public long getTotalSpace() {		
+		return this.spaceRemaining + getChunkSpace();
+	}
+
+	public void setAvailableSpace(long s) {
+		this.spaceRemaining = s-getChunkSpace();
+	}
+
+	public void sendRemovedMessage(String fileId, String chunkNumber) {
+		String[] headerElements = new String[Constants.REMOVED_N_ARGS];
+		MessageHeader h;
+		headerElements[0] = Constants.REMOVED;
+		headerElements[1] = getProtocolVersion();
+		headerElements[2] = Integer.toString(getID());
+		headerElements[3] = fileId;
+		headerElements[4] = chunkNumber; 
+
+		h = new MessageHeader(headerElements);
+
+		controlRoom.sendHeader(h);
+	}
+
+	public void sendHeader(MessageHeader mh)
+	{
+		controlRoom.sendHeader(mh);
+	}
+
+	public boolean freeUpSpace(long desiredSpace) {
+
+		String fileId, chunkNo, path;
+		File file;
+
+		for(String key : storedChunks.keySet()) {
+			Chunk c = storedChunks.get(key);
+			if(getChunkSpace() <= desiredSpace) {
+				break;
+			}
+
+			fileId = c.getChunkId();
+			chunkNo = c.getChunkNumber();
+
+			path = Constants.CHUNKS_DIR + this.serverID + "/backup/" + fileId + "/chk" + chunkNo;
+
+			file = new File(path);
+
+			if(file.delete()) {
+				sendRemovedMessage(fileId, chunkNo);
+				storedChunks.remove(key);
+			}
+		}
+
+		setAvailableSpace(desiredSpace);
+
+		return true;
+	}
+
+	public boolean intrepertRemovedMessage(MessageHeader h) {
+		String fileId, chunkNo, senderId, key;
+
+		fileId = h.getFileId();
+		chunkNo = h.getChunkNumber();
+		senderId = h.getSenderId();
+		key = fileId + ":" + chunkNo;
+
+		if(checkIfPeerStoredFile(fileId)) {
+			if(numberOfChunksConfirmations.containsKey(key)) {
+				numberOfChunksConfirmations.put(key, numberOfChunksConfirmations.get(key) - 1);
+			}
+			if(this.colaborativePeers.containsKey(key)) {
+				this.colaborativePeers.get(key).remove(Integer.parseInt(senderId));
+			}			
+			if(storedChunks.containsKey(key)) {
+				return storedChunks.get(key).getReplicationDegree() < numberOfChunksConfirmations.get(key);
+			}
+		}
+
+		return true;
+	}
+
+	public int getNumberOfChunks(String file_id)
+	{
+		if(numberOfFileChunks.containsKey(file_id)) {
+			return numberOfFileChunks.get(file_id);
+		}
+
+		return -1;
+	}
+
+	public Chunk getFileChunkRepDegree(String fileId, String chunkNo) {
+
+		if(storedChunks.containsKey(fileId + ":" + chunkNo)) {
+			return storedChunks.get(fileId + ":" + chunkNo);
+		}
+
+		return null;
+	}
+
+	public void intrepertGetChunkMessage(MessageHeader header) {
+
+		String key = header.getFileId() + ":" + header.getChunkNumber();
+		Chunk toSend;
+		SendChunkMessageProtocol p;
+
+		if(storedChunks.containsKey(key)) {
+			System.out.println("Going to send chunk no " + header.getChunkNumber());
+			toSend = storedChunks.get(key);
+
+			p = new SendChunkMessageProtocol(toSend, this);
+
+			p.run();
+		}
+		else {
+			System.out.println("I dont have that chunk");
+		}
+	}
+
+
+	@SuppressWarnings({ "unchecked" })
+	public void loadFromDisk() throws IOException, Exception
+	{	
+		String path = Constants.DATABASE_DIR + "peer" + this.serverID + '/';
+
+
+		String path1 = path + "storedChunks.ser";
+		Path p = Paths.get(path1);
+		if (Files.exists(p)) {
+			FileInputStream fis = new FileInputStream(path1);
+			ObjectInputStream ois = new ObjectInputStream(fis);
+			this.setStoredChunks( (ConcurrentHashMap<String, Chunk>)ois.readObject());
+			fis.close();
+			ois.close();
+		}
+
+
+
+
+		String path2 = path + "numberOfFileChunks.ser";
+		Path p2 = Paths.get(path2);
+		if (Files.exists(p2)) {
+			FileInputStream fis2 = new FileInputStream(path2);
+			ObjectInputStream ois2 = new ObjectInputStream(fis2);
+			this.setNumberOfFileChunks((ConcurrentHashMap<String, Integer>)ois2.readObject());
+			fis2.close();
+			ois2.close();
+		}
+
+
+		String path3 = path + "colaborativePeers.ser";
+		Path p3 = Paths.get(path3);
+		if (Files.exists(p3)) {
+			FileInputStream fis3 = new FileInputStream(path3);
+			ObjectInputStream ois3 = new ObjectInputStream(fis3);
+			this.setColaborativePeers((ConcurrentHashMap<String, HashSet<Integer>>)ois3.readObject());
+			fis3.close();
+			ois3.close();
+		}
+
+
+		String path4 = path + "numberOfChunksConfirmations.ser";
+		Path p4 = Paths.get(path4);
+		if (Files.exists(p4)) {
+			FileInputStream fis4 = new FileInputStream(path4);
+			ObjectInputStream ois4 = new ObjectInputStream(fis4);
+			this.setNumberOfChunksConfirmations((ConcurrentHashMap<String, Integer>)ois4.readObject());
+			fis4.close();
+			ois4.close();
+		}
+
+
+		String path5 = path + "chunksToRestore.ser";
+		Path p5 = Paths.get(path5);
+		if (Files.exists(p5)) {
+			FileInputStream fis5 = new FileInputStream(path5);
+			ObjectInputStream ois5 = new ObjectInputStream(fis5);
+			this.setChunksToRestore((ConcurrentHashMap<String, Integer>)ois5.readObject());
+			fis5.close();
+			ois5.close();
+		}
+
+		String path6 = path + "recoveredChunks.ser";
+		Path p6 = Paths.get(path6);
+		if (Files.exists(p6)) {
+			FileInputStream fis6 = new FileInputStream(path6);
+			ObjectInputStream ois6 = new ObjectInputStream(fis6);
+			this.setRecoveredChunks((ConcurrentHashMap<String, Chunk>)ois6.readObject());
+			fis6.close();
+			ois6.close();
+		}
+
+		String path7 = path + "numberOfFileChunksToBeRestored.ser";
+		Path p7 = Paths.get(path7);
+		if (Files.exists(p7)) {
+			FileInputStream fis7 = new FileInputStream(path7);
+			ObjectInputStream ois7 = new ObjectInputStream(fis7);
+			this.setNumberOfFileChunksToBeRestored((ConcurrentLinkedQueue<String>)ois7.readObject());
+			fis7.close();
+			ois7.close();
+		}
+	}
+
+	@SuppressWarnings("resource")
+	public synchronized void saveToDisk() throws IOException
+	{
+		String path = Constants.DATABASE_DIR + "peer" + this.serverID + '/';
+
+		checkOrCreateDirectory(path);
+
+		String path1 = path + "storedChunks.ser";
+		FileOutputStream fos1 = new FileOutputStream(path1);
+		ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
+		oos1.writeObject(storedChunks);
+
+		String path2 = path + "numberOfFileChunks.ser";
+		FileOutputStream fos2 = new FileOutputStream(path2);
+		ObjectOutputStream oos2 = new ObjectOutputStream(fos2);
+		oos2.writeObject(numberOfFileChunks);
+
+		String path3 = path + "colaborativePeers.ser";
+		FileOutputStream fos3 = new FileOutputStream(path3);
+		ObjectOutputStream oos3 = new ObjectOutputStream(fos3);
+		oos3.writeObject(colaborativePeers);
+
+		String path4 = path + "numberOfChunksConfirmations.ser";
+		FileOutputStream fos4 = new FileOutputStream(path4);
+		ObjectOutputStream oos4 = new ObjectOutputStream(fos4);
+		oos4.writeObject(numberOfChunksConfirmations);
+
+		String path5 = path + "chunksToRestore.ser";
+		FileOutputStream fos5 = new FileOutputStream(path5);
+		ObjectOutputStream oos5 = new ObjectOutputStream(fos5);
+		oos5.writeObject(chunksToRestore);
+
+		String path6 = path + "recoveredChunks.ser";
+		FileOutputStream fos6 = new FileOutputStream(path6);
+		ObjectOutputStream oos6 = new ObjectOutputStream(fos6);
+		oos6.writeObject(recoveredChunks);
+
+		String path7 = path + "numberOfFileChunksToBeRestored.ser";
+		FileOutputStream fos7 = new FileOutputStream(path7);
+		ObjectOutputStream oos7 = new ObjectOutputStream(fos7);
+		oos7.writeObject(numberOfFileChunksToBeRestored);
+	}
+
+	public void setStoredChunks(ConcurrentHashMap<String, Chunk> sc)
+	{
+		storedChunks = sc;
+	}
+
+	public void setNumberOfFileChunks(ConcurrentHashMap<String, Integer> si)
+	{
+		numberOfFileChunks = si;
+	}
+
+	public void setColaborativePeers(ConcurrentHashMap<String, HashSet<Integer>> sh)
+	{
+		colaborativePeers = sh;
+	}
+
+	public void setNumberOfChunksConfirmations(ConcurrentHashMap<String, Integer> si)
+	{
+		numberOfChunksConfirmations = si;
+	}
+
+	public void setChunksToRestore(ConcurrentHashMap<String, Integer> si)
+	{
+		chunksToRestore = si;
+	}
+
+	public void setRecoveredChunks(ConcurrentHashMap<String, Chunk> sc)
+	{
+		recoveredChunks = sc;
+	}
+
+	public void setNumberOfFileChunksToBeRestored(ConcurrentLinkedQueue<String> s)
+	{
+		numberOfFileChunksToBeRestored = s;
+	}
+}
Index: service/Constants.java
===================================================================
--- service/Constants.java	(nonexistent)
+++ service/Constants.java	(revision 1)
@@ -0,0 +1,34 @@
+package service;
+
+public class Constants {
+	public static final int MAX_CHUNK_SIZE = 64 * 1000;
+	public static final int MAX_MESSAGE_SIZE = MAX_CHUNK_SIZE + 1024;
+	public static final String CHUNKS_DIR = "../Project 1 -- Distributed Backup Service/peers/peer";
+	public static final String DATABASE_DIR = "../Project 1 -- Distributed Backup Service/database/";
+	public static final int MAX_NUMBER_OF_THREADS = 8;
+	public static final String NORMAL_VERSION = "1.0";
+	
+	// Available header types
+	public static final String PUTCHUNK = "PUTCHUNK";
+	public static final String STORED = "STORED";
+	public static final String GETCHUNK = "GETCHUNK";
+	public static final String CHUNK = "CHUNK";
+	public static final String DELETE = "DELETE";
+	public static final String REMOVED = "REMOVED";
+	
+	
+	// headers necessary arguments
+	
+	public static final int PUTCHUNK_N_ARGS = 6;
+	public static final int STORED_N_ARGS = 5;
+	public static final int GETCHUNK_N_ARGS = 5;
+	public static final int CHUNK_N_ARGS = 5;
+	public static final int DELETE_N_ARGS = 4;
+	public static final int REMOVED_N_ARGS = 5;
+	
+	//'0xD''0xA'
+	public static final byte CR = 0xD;
+	public static final byte LF = 0xA;
+	
+	public static final int MAX_PUTCHUNK_MESSAGES = 5;
+}
Index: service/Peer.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/Peer.class
===================================================================
--- service/Peer.class	(nonexistent)
+++ service/Peer.class	(revision 1)

Property changes on: service/Peer.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/RMI.java
===================================================================
--- service/RMI.java	(nonexistent)
+++ service/RMI.java	(revision 1)
@@ -0,0 +1,13 @@
+package service;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RMI extends Remote
+{
+  String backup(String file_path, int replication_degree) throws RemoteException;
+  String restore(String file_path) throws RemoteException;
+  String delete(String file_path) throws RemoteException;
+  String reclaim(long disk_space) throws RemoteException;
+  String state() throws RemoteException;
+}
Index: Project 1 -- Distributed Backup Service.iml
===================================================================
--- Project 1 -- Distributed Backup Service.iml	(nonexistent)
+++ Project 1 -- Distributed Backup Service.iml	(revision 1)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
Index: channels/BackupChannel.java
===================================================================
--- channels/BackupChannel.java	(nonexistent)
+++ channels/BackupChannel.java	(revision 1)
@@ -0,0 +1,99 @@
+package channels;
+
+import java.io.IOException;
+
+import protocols.SendSoredMessageProtocol;
+import service.Cloud;
+import service.Constants;
+
+public class BackupChannel extends Channel {
+	private Message receivedMessage;
+
+	public BackupChannel(String addr, String p, Cloud f) throws IOException {
+		super(addr, p, f.getID(), f);
+	}
+	
+	public void printLog(String msg) {
+		System.out.println("MDB " + serverId + msg);
+	}
+	
+//	This message is used to ensure that the chunk is backed up with the desired replication degree as follows. The initiator-peer collects the confirmation messages during a time interval of one second. If the number of confirmation messages it received up to the end of that interval is lower than the desired replication degree, it retransmits the backup message on the MDB channel, and doubles the time interval for receiving confirmation messages. This procedure is repeated up to a maximum number of five times, i.e. the initiator will send at most 5 PUTCHUNK messages per chunk.
+//
+//	Hint: Because UDP is not reliable, a peer that has stored a chunk must reply with a STORED message to every PUTCHUNK message it receives. Furthermore, the initiator-peer needs to keep track of which peers have responded.
+//
+//	A peer should also count the number of confirmation messages for each of the chunks it has stored and keep that count in non-volatile memory. This information can be useful if the peer runs out of disk space: in that event, the peer may try to free some space by evicting chunks whose actual replication degree is higher than the desired replication degree.
+	
+	@Override
+	public void run () {
+		printLog(": UP");
+		int messageSenderId = -1;
+		String messageType;
+		
+		try {
+			joinChannelGroup();
+		} catch (IOException e1) {
+			e1.printStackTrace();
+		}
+		
+		do {
+			try {
+				
+				
+				receivedMessage = captureData();
+				
+				if(receivedMessage == null) {
+					printLog(" passing message.");
+					continue;
+				}
+				
+				messageType = receivedMessage.getHeader().getMessageType();
+				
+				messageSenderId = Integer.parseInt(receivedMessage.getHeader().getSenderId());
+			
+				if(messageType.equals(Constants.PUTCHUNK) && messageSenderId != getServerId()) {
+					//printLog(" received: " + receivedMessage.getHeader().toString());
+
+					if(father.storeChunk(receivedMessage)) {
+						SendSoredMessageProtocol mp = new SendSoredMessageProtocol(buildMessage(), father);
+						
+						mp.start();
+					}
+					else {
+						printLog(" ERROR: couldn't store chunk");
+					}
+				}
+				else {
+					//printLog(" ERROR: Invalid message header received -> " + receivedMessage.getHeader().toString());
+				}
+				
+//				leaveGroupChannel();			
+			}
+			catch (IOException e) {
+				printLog(" ERROR: " + "IOException -> " + e.getMessage());
+			}
+		} while(true);
+	}
+	
+	public MessageHeader buildMessage() {
+		MessageHeader response;
+		
+		response = buildResponseMessage();
+
+		return response;
+	}
+	
+	public MessageHeader buildResponseMessage() {
+		MessageHeader response;
+	    String[] headerElements = new String[Constants.STORED_N_ARGS];
+	    
+	    headerElements[0] = Constants.STORED;
+	    headerElements[1] = father.getProtocolVersion();
+	    headerElements[2] = Integer.toString(father.getID());
+	    headerElements[3] = receivedMessage.getHeader().getFileId();
+	    headerElements[4] = receivedMessage.getHeader().getChunkNumber();
+	    
+	    response = new MessageHeader(headerElements);
+		
+		return response;
+	}
+}
Index: channels/Channel.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/Channel.class
===================================================================
--- channels/Channel.class	(nonexistent)
+++ channels/Channel.class	(revision 1)

Property changes on: channels/Channel.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: channels/Channel.java
===================================================================
--- channels/Channel.java	(nonexistent)
+++ channels/Channel.java	(revision 1)
@@ -0,0 +1,128 @@
+package channels;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.util.Random;
+
+import service.Cloud;
+import service.Constants;
+
+abstract class Channel extends Thread {
+
+	protected MulticastSocket socket;
+	protected InetAddress address;
+	protected int port;
+	protected DatagramPacket rcv;
+	protected DatagramPacket send;
+	protected int serverId;
+	protected Cloud father;
+	
+	protected Channel(String addr, String p, int sId, Cloud f) throws IOException {
+		
+		address = InetAddress.getByName(addr);
+		port = Integer.parseInt(p);
+		socket = new MulticastSocket(port);
+		serverId = sId;
+		father = f; 
+	}
+	
+	protected MulticastSocket getSocket() {
+		return socket;
+	}
+
+	protected void setSocket(MulticastSocket socket) {
+		this.socket = socket;
+	}
+
+	protected InetAddress getAddress() {
+		return address;
+	}
+
+	protected void setAddress(InetAddress address) {
+		this.address = address;
+	}
+
+	protected int getPort() {
+		return port;
+	}
+
+	protected void setPort(int port) {
+		this.port = port;
+	}
+	
+	protected int getServerId() {
+		return serverId;
+	}
+	
+	protected void resetMyPacket(byte[] data) {
+		rcv = new DatagramPacket(data, Constants.MAX_MESSAGE_SIZE);
+	}
+	
+	public void waitRandomTime() throws InterruptedException {
+		Random r = new Random();
+		int low = 1;
+		int high = 401;
+		int result = r.nextInt(high-low) + low;
+		
+		Thread.sleep(result);
+	}
+	
+	protected Message captureData() throws IOException {
+		byte[] data = new byte[Constants.MAX_MESSAGE_SIZE];
+		
+		Message m;
+		
+		resetMyPacket(data);
+		
+		socket.receive(rcv);
+		
+		byte[] filtredData = new byte[Constants.MAX_MESSAGE_SIZE];
+		
+		System.arraycopy(rcv.getData(), rcv.getOffset(), filtredData, 0, rcv.getLength());
+		
+		try {
+			m = new Message(filtredData, rcv.getLength());
+		} catch(IllegalArgumentException e) {
+			System.out.println("Error receiving message: " + e.getMessage());
+			return null;
+		}
+		
+		return m;
+	}
+	
+	public synchronized boolean sendMessage(Message m) {
+		send = new DatagramPacket(m.toBytes(), m.toBytes().length, address, port);
+		
+		try {
+			socket.send(send);
+	    }
+		catch (IOException e) {
+			return false;
+	    }
+		
+	    return true;
+	}
+	
+	public synchronized boolean sendHeader(MessageHeader h) {
+		send = new DatagramPacket(h.headerToBytes(), h.headerToBytes().length, father.controlRoom.address, father.controlRoom.port);
+		
+		try {
+			socket.send(send);
+	    }
+		catch (IOException e) {
+			return false;
+	    }
+		
+	    return true;
+	}
+	
+	protected void joinChannelGroup() throws IOException {
+		socket.joinGroup(address);
+	}
+	
+	protected void leaveGroupChannel() throws IOException {
+		socket.leaveGroup(address);
+	}
+}
Index: channels/ControlChannel.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/ControlChannel.class
===================================================================
--- channels/ControlChannel.class	(nonexistent)
+++ channels/ControlChannel.class	(revision 1)

Property changes on: channels/ControlChannel.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: channels/ControlChannel.java
===================================================================
--- channels/ControlChannel.java	(nonexistent)
+++ channels/ControlChannel.java	(revision 1)
@@ -0,0 +1,124 @@
+/**
+ * 
+ */
+package channels;
+
+import java.io.IOException;
+import protocols.recoverChunkRepDegreeProtocol;
+import service.Chunk;
+import service.Cloud;
+import service.Constants;
+
+/**
+ * @author dnc18
+ *
+ */
+public class ControlChannel extends Channel {
+	private Message receivedMessage;
+	
+	public ControlChannel(String addr, String p, Cloud servicePeerRef) throws IOException {
+		super(addr, p, servicePeerRef.getID(), servicePeerRef);
+	}
+	
+	public void printLog(String msg) {
+		System.out.println("MC " + serverId + msg);
+	}
+	
+	@Override
+	public void run() {
+		recoverChunkRepDegreeProtocol rcrdp = null;
+		Message m;
+		
+		printLog(": UP");
+		
+		try {
+			joinChannelGroup();
+		} catch (IOException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+		
+		do {
+			try {
+				
+				
+				receivedMessage = captureData();
+				
+				if(receivedMessage == null) {
+					continue;
+				}
+				
+				if(getServerId() != Integer.parseInt(receivedMessage.getHeader().getSenderId())) {
+//					printLog(" received: " + receivedMessage.getHeader().toString());
+					switch(receivedMessage.getHeader().getMessageType()) {
+						case Constants.STORED:
+							father.intrepertStoredMessage(receivedMessage.getHeader());
+							break;
+						case Constants.GETCHUNK:
+							father.intrepertGetChunkMessage(receivedMessage.getHeader());
+							break;
+						case Constants.DELETE:
+//							printLog(" before delete: " + father.getAvailableSpace() + " avaliable bytes and " + father.getNumberOfStoredChunks() + " stored chunks.");
+							father.deleteFile(receivedMessage.getHeader());
+//							printLog(" adter delete: " + father.getAvailableSpace() + " avaliable bytes and " + father.getNumberOfStoredChunks() + " stored chunks.");
+							break;
+						case Constants.REMOVED:
+							if(father.intrepertStoredMessage(receivedMessage.getHeader())) {
+								printLog(": chunk replication degree disrespected, sending again.");
+								m = buildReplicationRestoreMessage(receivedMessage.getHeader().getFileId(), receivedMessage.getHeader().getChunkNumber());
+								if(m != null) {
+									rcrdp = new recoverChunkRepDegreeProtocol(father, m);
+									System.out.println("Calling");
+									rcrdp.run();
+								}
+								else {
+									System.out.println("couldn t create backup protocol thread");
+								}
+							}
+							break;
+						default: // MC, MDB, MDR
+							//printLog(" ERROR: Invalid message header received -> " + receivedMessage.getHeader().getMessageType());
+							continue;
+					}
+				}
+				
+//				leaveGroupChannel();
+			}
+			catch (Exception e) {
+				printLog(" ERROR: " + e.getMessage());
+				e.printStackTrace();
+				break;
+			}
+		} while(true);
+	}
+	
+	private Message buildReplicationRestoreMessage(String fileId, String chunkNo) {
+		Message m = null;
+		MessageHeader h = null;
+		String[] headerElements = new String[Constants.PUTCHUNK_N_ARGS];
+		Chunk toSave = father.getFileChunkRepDegree(fileId, chunkNo);
+		
+		
+		if(toSave == null) {
+			return null;
+		}
+		
+		try {
+			headerElements[0] = Constants.PUTCHUNK;
+		    headerElements[1] = father.getProtocolVersion();
+		    headerElements[2] = Integer.toString(father.getID());
+		    headerElements[3] = fileId;
+	   	  	headerElements[4] = chunkNo;
+	   	  	headerElements[5] = Integer.toString(toSave.getReplicationDegree());
+	   	  	
+	   	  	h = new MessageHeader(headerElements.clone());
+	   	  	m = new Message(h, toSave.getChunkContent());
+		}
+	   	catch(IllegalArgumentException e) {
+	   		System.out.println("Error creating backup protocol -> " + e.getMessage());
+	   		return null;
+	   	}
+		
+		return m;
+	}
+}
Index: channels/Message.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/Message.class
===================================================================
--- channels/Message.class	(nonexistent)
+++ channels/Message.class	(revision 1)

Property changes on: channels/Message.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: channels/Message.java
===================================================================
--- channels/Message.java	(nonexistent)
+++ channels/Message.java	(revision 1)
@@ -0,0 +1,109 @@
+package channels;
+
+import java.util.ArrayList;
+
+import service.Constants;
+
+public class Message {
+	
+	private MessageHeader h;
+	private byte[] chunkData;
+	int totalLength = 0;
+
+	public Message(byte[] msg) {
+		int separation = headerLength(msg);
+		
+		if(separation < 0) {
+			throw new IllegalArgumentException("Couldn't find separation.");
+		}
+		
+		h = new MessageHeader(msg, separation);
+		
+		if(!separateChunkData(msg, separation)) {
+			throw new IllegalArgumentException("Invalid message, chuk max size exceeded");
+		}
+	}
+	
+	public Message(byte[] msg, int length) {
+		totalLength = length;
+		int separation = headerLength(msg);
+		
+		if(separation < 0) {
+			throw new IllegalArgumentException("Couldn't find separation.");
+		}
+		
+		h = new MessageHeader(msg, separation);
+		
+		if(!separateChunkData(msg, separation)) {
+			throw new IllegalArgumentException("Invalid message, chuk max size exceeded");
+		}
+	}
+	
+	public void setTotalLength(int l) {
+		totalLength = l;
+	}
+	
+	public Message(MessageHeader h, byte[] chunk) {
+		if(chunk.length > Constants.MAX_CHUNK_SIZE) {
+			throw new IllegalArgumentException("Invalid message, chuk max size exceeded");
+		}
+		
+		this.h = h;
+		
+		chunkData = chunk;
+	}
+	
+	private int headerLength(byte[] msg) {
+		int i = 0;
+		
+		for(i = 0; i < msg.length-4; i++) {
+			if(msg[i] == Constants.CR && msg[i+1] == Constants.LF && msg[i+2] == Constants.CR && msg[i+3] == Constants.LF) {
+				return i;
+			}
+		}
+		
+		return -1;
+	}
+	
+	private boolean separateChunkData(byte[] msg, int separation) {
+		
+		int chunkLength = totalLength <= 0 ? msg.length - separation - 4: totalLength - separation - 4;
+		
+		if(chunkLength > Constants.MAX_CHUNK_SIZE) {
+			System.out.println("looks to big");
+			return false;
+		}
+		
+		chunkData = new byte[chunkLength];
+		
+		System.arraycopy(msg, separation + 4, chunkData, 0, chunkLength);
+		
+		return true;
+	}
+	
+	public MessageHeader getHeader() {
+		return h;
+	}
+	
+	public byte[] getChunkContent() {
+		return chunkData;
+	}
+	
+	public byte[] toBytes() {
+		ArrayList<Byte> messageToBeSent = h.toBytes();
+		byte[] toReturn = new byte[messageToBeSent.size() + chunkData.length];
+		int index = 0;
+		
+		for(Byte b : messageToBeSent) {
+			toReturn[index] = b;
+			index++;
+		}
+		for(Byte b : chunkData) {
+			toReturn[index] = b;
+			index++;
+		}
+		
+		return toReturn;
+	}
+	
+}
Index: peersStates.sh
===================================================================
--- peersStates.sh	(nonexistent)
+++ peersStates.sh	(revision 1)
@@ -0,0 +1,4 @@
+java -jar ./out/Client.jar localhost:Peer1 STATE
+java -jar ./out/Client.jar localhost:Peer2 STATE
+java -jar ./out/Client.jar localhost:Peer3 STATE
+java -jar ./out/Client.jar localhost:Peer4 STATE
\ No newline at end of file

Property changes on: peersStates.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: startClientBackups.sh
===================================================================
--- startClientBackups.sh	(nonexistent)
+++ startClientBackups.sh	(revision 1)
@@ -0,0 +1,4 @@
+java -jar ./out/Client.jar localhost:Peer1 BACKUP ./J\ Cole.mp3 3
+java -jar ./out/Client.jar localhost:Peer2 BACKUP ./100MB.bin 1
+java -jar ./out/Client.jar localhost:Peer3 BACKUP ./loboFixe.jpg 3
+java -jar ./out/Client.jar localhost:Peer4 BACKUP ./teste.txt 2

Property changes on: startClientBackups.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: channels/MessageHeader.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/MessageHeader.class
===================================================================
--- channels/MessageHeader.class	(nonexistent)
+++ channels/MessageHeader.class	(revision 1)

Property changes on: channels/MessageHeader.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: channels/MessageHeader.java
===================================================================
--- channels/MessageHeader.java	(nonexistent)
+++ channels/MessageHeader.java	(revision 1)
@@ -0,0 +1,257 @@
+package channels;
+
+import java.util.ArrayList;
+
+import service.Constants;
+
+public class MessageHeader {
+	private String messageType;
+	private String version;
+	private String senderId;
+	private String fileId;
+	private String chunkNumber;
+	private String degree;
+	
+	public MessageHeader(byte[] msg, int length) {
+		
+		byte[] header = getHeaderPart(msg, length);
+		
+		String headerString = new String(header);
+		
+		String[] headerElements = headerString.split("\\s+");
+		
+		if(!analyseHeader(headerElements)) {
+			throw new IllegalArgumentException("Minimum header arguments not matched");
+		}
+	}
+	
+	public MessageHeader(String[] headerComponents) {
+		if(!analyseHeader(headerComponents)) {
+			throw new IllegalArgumentException("Minimum header arguments not matched");
+		}
+	}
+	
+	@Override
+	public String toString() {
+		if(messageType.equals(Constants.PUTCHUNK)) {
+			return messageType + " " + version + " " + senderId + " " + fileId + " " + chunkNumber + " " + degree;
+		}
+		else {
+			return messageType + " " + version + " " + senderId + " " + fileId + " " + chunkNumber;
+		}
+	}
+	
+	public String getMessageType() {
+		return messageType;
+	}
+
+	private void setMessageType(String messageType) {
+		this.messageType = messageType;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+
+	private void setVersion(String version) {
+		this.version = version;
+	}
+
+	public String getSenderId() {
+		return senderId;
+	}
+
+	private boolean setSenderId(String senderId) {	
+		try {
+			Integer.parseInt(senderId);
+			this.senderId = senderId;
+		} catch(Exception e) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public String getFileId() {
+		return fileId;
+	}
+
+	private void setFileId(String fileId) {
+		this.fileId = fileId;
+	}
+
+	public String getChunkNumber() {
+		return chunkNumber;
+	}
+
+	private void setChunkNumber(String chunkNumber) {
+		this.chunkNumber = chunkNumber;
+	}
+
+	public String getDegree() {
+		if(degree == null) {
+			return "1";
+		}
+		return degree;
+	}
+
+	private void setDegree(String degree) {
+		this.degree = degree;
+	}
+
+	private byte[] getHeaderPart(byte[] msg, int length) {
+		byte[] headerPart = new byte[length];
+		
+		for(int i = 0; i < length; i++) {
+			headerPart[i] = msg[i];
+		}
+		
+		return headerPart;
+	}
+	
+	public ArrayList<Byte> toBytes() {
+		ArrayList<Byte> headerBytes = new ArrayList<Byte>();
+		Byte space = new String(" ").getBytes()[0];
+		
+		for(Byte b : getMessageType().getBytes()) {
+			headerBytes.add(b);
+		}
+		
+		headerBytes.add(space);
+		
+		for(Byte b : getVersion().getBytes()) {
+			headerBytes.add(b);
+		}
+		
+		headerBytes.add(space);
+		
+		for(Byte b : getSenderId().getBytes()) {
+			headerBytes.add(b);
+		}
+		
+		headerBytes.add(space);
+		
+		for(Byte b : getFileId().getBytes()) {
+			headerBytes.add(b);
+		}
+		
+		headerBytes.add(space);
+		
+		if(!messageType.equals(Constants.DELETE)) {
+			for(Byte b : getChunkNumber().getBytes()) {
+				headerBytes.add(b);
+			}
+		}
+		
+		headerBytes.add(space);
+		
+		if(messageType.equals(Constants.PUTCHUNK)) {
+			for(Byte b : getDegree().getBytes()) {
+				headerBytes.add(b);
+			}
+		}
+		headerBytes.add(space);
+		
+		headerBytes.add(Constants.CR);
+		headerBytes.add(Constants.LF);
+		headerBytes.add(Constants.CR);
+		headerBytes.add(Constants.LF);
+		
+		return headerBytes;
+	}
+	
+	public byte[] headerToBytes() {
+		ArrayList<Byte> headerBytes = toBytes();
+		byte[] toReturn = new byte[headerBytes.size()];
+		int i = 0;
+		
+		for(Byte b: headerBytes) {
+			toReturn[i] = b;
+			i++;
+		}
+		
+		return toReturn;
+	}
+	
+	private boolean analyseHeader(String[] h) {
+		
+		switch(h[0]) {
+			case Constants.PUTCHUNK:
+				if(h.length != Constants.PUTCHUNK_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				setChunkNumber(h[4]);
+				setDegree(h[5]);
+				break;
+			case Constants.STORED:
+				if(h.length != Constants.STORED_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				setChunkNumber(h[4]);
+				break;
+			case Constants.GETCHUNK:
+				if(h.length != Constants.GETCHUNK_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				setChunkNumber(h[4]);
+				break;
+			case Constants.CHUNK:
+				if(h.length != Constants.CHUNK_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				setChunkNumber(h[4]);
+				break;
+			case Constants.DELETE:
+				if(h.length != Constants.DELETE_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				break;
+			case Constants.REMOVED:
+				if(h.length != Constants.REMOVED_N_ARGS) {
+					return false;
+				}
+				setMessageType(h[0]);
+				setVersion(h[1]);
+				if(!setSenderId(h[2])) {
+					return false;
+				}
+				setFileId(h[3]);
+				setChunkNumber(h[4]);
+				break;
+			default:
+				return false;
+		}
+		
+		return true;
+	}
+}
Index: channels/RestoreChannel.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: channels/RestoreChannel.class
===================================================================
--- channels/RestoreChannel.class	(nonexistent)
+++ channels/RestoreChannel.class	(revision 1)

Property changes on: channels/RestoreChannel.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: channels/RestoreChannel.java
===================================================================
--- channels/RestoreChannel.java	(nonexistent)
+++ channels/RestoreChannel.java	(revision 1)
@@ -0,0 +1,100 @@
+package channels;
+
+import java.io.IOException;
+
+import protocols.SendSoredMessageProtocol;
+import service.Cloud;
+import service.Constants;
+
+public class RestoreChannel extends Channel{
+
+	private Message receivedMessage;
+
+	public RestoreChannel(String addr, String p, Cloud f) throws IOException {
+		super(addr, p, f.getID(), f);
+	}
+	
+	public void printLog(String msg) {
+		System.out.println("MDR " + serverId + msg);
+	}
+	
+	@Override 
+	public void run() 
+	{
+		printLog(": UP");
+		int messageSenderId = -1;
+		String messageType;
+		
+		try {
+			joinChannelGroup();
+		} catch (IOException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+		
+		do 
+		{
+			try
+			{
+				receivedMessage = captureData();
+				
+				if(receivedMessage == null) {
+					printLog(" passing message.");
+					continue;
+				}
+				
+				messageType = receivedMessage.getHeader().getMessageType();
+				
+				messageSenderId = Integer.parseInt(receivedMessage.getHeader().getSenderId());
+			
+				if(messageType.equals(Constants.CHUNK) && messageSenderId != getServerId()) {			
+					if(father.isFileChunkRegistered(receivedMessage.getHeader().getFileId(), receivedMessage.getHeader().getChunkNumber())) {
+						//printLog(" received: " + receivedMessage.getHeader().toString());
+						if(father.storeRestoreChunk(receivedMessage)) {
+							SendSoredMessageProtocol mp = new SendSoredMessageProtocol(buildMessage(), father);
+							
+							mp.start();
+						}
+						else {
+							printLog(" ERROR: couldn't store chunk");
+						}
+					}
+				}
+				else {
+					//printLog(" ERROR: Invalid message header received -> " + receivedMessage.getHeader().toString());
+				}
+				
+//				leaveGroupChannel();		
+			}
+			catch(IOException e) 
+			{
+				printLog(" ERROR: " + "IOException -> " + e.getMessage());
+			}
+		}
+		while(true);
+	}
+	
+	public MessageHeader buildMessage() {
+		MessageHeader response;
+		
+		response = buildResponseMessage();
+
+		return response;
+	}
+	
+	public MessageHeader buildResponseMessage() {
+		MessageHeader response;
+	    String[] headerElements = new String[Constants.STORED_N_ARGS];
+	    
+	    headerElements[0] = Constants.CHUNK;
+	    headerElements[1] = father.getProtocolVersion();
+	    headerElements[2] = Integer.toString(father.getID());
+	    headerElements[3] = receivedMessage.getHeader().getFileId();
+	    headerElements[4] = receivedMessage.getHeader().getChunkNumber();
+	    
+	    response = new MessageHeader(headerElements);
+		
+		return response;
+	}
+	
+}
Index: out/Client.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: out/Client.jar
===================================================================
--- out/Client.jar	(nonexistent)
+++ out/Client.jar	(revision 1)

Property changes on: out/Client.jar
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: out/Peer.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: out/Peer.jar
===================================================================
--- out/Peer.jar	(nonexistent)
+++ out/Peer.jar	(revision 1)

Property changes on: out/Peer.jar
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/BackupChunkProtocol.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: protocols/BackupChunkProtocol.class
===================================================================
--- protocols/BackupChunkProtocol.class	(nonexistent)
+++ protocols/BackupChunkProtocol.class	(revision 1)

Property changes on: protocols/BackupChunkProtocol.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/BackupChunkProtocol.java
===================================================================
--- protocols/BackupChunkProtocol.java	(nonexistent)
+++ protocols/BackupChunkProtocol.java	(revision 1)
@@ -0,0 +1,50 @@
+package protocols;
+
+import java.util.concurrent.Callable;
+
+import channels.Message;
+import service.Chunk;
+import service.Cloud;
+import service.Constants;
+
+public class BackupChunkProtocol implements Callable<Object> {
+	Cloud storage;
+	Chunk objective;
+	Message builtChunkMessage;
+	int repDegree;
+	
+	public BackupChunkProtocol(Cloud c, Chunk chunksToBeStored, Message m, int replication_degree) {
+		storage = c;
+		objective = chunksToBeStored;
+		repDegree = replication_degree;
+		builtChunkMessage = m;
+	}
+
+	@Override
+	public Object call() throws Exception {
+		
+		int numberOfTries = 0;
+		int multiplier = 1;
+		
+		if(storage.insertAwaitingStoreFile(builtChunkMessage.getHeader())) {
+			while(numberOfTries < Constants.MAX_PUTCHUNK_MESSAGES) {
+				storage.sendBackupMessage(builtChunkMessage);
+//				System.out.println("Trying " + multiplier + " seconds");
+				
+				Thread.sleep(1000 * multiplier);
+				
+				if(storage.wasTheRepDegreeMatched(objective)) {
+					storage.addChunk(objective);
+					System.out.println(objective.getChunkNumber() + " replication degree matched!");
+					return true;
+				}
+				
+				multiplier *= 2;
+				numberOfTries++;
+			}
+		}
+		
+		System.out.println("ERROR: " + objective.getChunkId() + " replication degree not matched!");
+		return false;
+	}
+}
Index: protocols/RestoreChunksProtocol.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: protocols/RestoreChunksProtocol.class
===================================================================
--- protocols/RestoreChunksProtocol.class	(nonexistent)
+++ protocols/RestoreChunksProtocol.class	(revision 1)

Property changes on: protocols/RestoreChunksProtocol.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/RestoreChunksProtocol.java
===================================================================
--- protocols/RestoreChunksProtocol.java	(nonexistent)
+++ protocols/RestoreChunksProtocol.java	(revision 1)
@@ -0,0 +1,46 @@
+package protocols;
+
+import java.util.concurrent.Callable;
+
+import channels.MessageHeader;
+import service.Cloud;
+import service.Constants;
+
+
+public class RestoreChunksProtocol implements Callable<Object>{
+	Cloud cloud;
+	MessageHeader message_header;
+	
+	public RestoreChunksProtocol(Cloud c, MessageHeader mh) 
+	{	
+		this.cloud = c;
+		this.message_header = mh;
+	}
+	
+	@Override
+	public Object call() throws Exception {
+		
+		int numberOfTries = 0;
+		int multiplier = 1;
+		
+			while(numberOfTries < Constants.MAX_PUTCHUNK_MESSAGES) {
+				
+				cloud.sendHeader(this.message_header);
+//				System.out.println("Trying " + multiplier + " seconds");
+				
+				Thread.sleep(1000 * multiplier);
+				
+				if(cloud.checkIfChunkWasRestored(this.message_header.getFileId(), this.message_header.getChunkNumber()))
+				{
+					System.out.println(this.message_header.getChunkNumber() + " restored");
+					return true;
+				}
+				
+				multiplier *= 2;
+				numberOfTries++;
+			}
+		
+		System.out.println("ERROR: " + this.message_header.getChunkNumber() + " could not be restored!");
+		return false;
+	}
+}
Index: protocols/SendChunkMessageProtocol.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: protocols/SendChunkMessageProtocol.class
===================================================================
--- protocols/SendChunkMessageProtocol.class	(nonexistent)
+++ protocols/SendChunkMessageProtocol.class	(revision 1)

Property changes on: protocols/SendChunkMessageProtocol.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/SendChunkMessageProtocol.java
===================================================================
--- protocols/SendChunkMessageProtocol.java	(nonexistent)
+++ protocols/SendChunkMessageProtocol.java	(revision 1)
@@ -0,0 +1,54 @@
+package protocols;
+
+import java.util.Random;
+
+import channels.Message;
+import channels.MessageHeader;
+import service.Chunk;
+import service.Cloud;
+import service.Constants;
+
+public class SendChunkMessageProtocol extends Thread {
+
+	Cloud father;
+	Chunk toSend;
+	
+	public SendChunkMessageProtocol (Chunk c, Cloud f) {
+		father = f;
+		toSend = c;
+	}
+	
+	@Override
+	public void run() {
+		String[] headerElements = new String[Constants.CHUNK_N_ARGS];
+		Message m;
+		MessageHeader h;
+		
+		headerElements[0] = Constants.CHUNK;
+  	  	headerElements[1] = father.getProtocolVersion();
+  	  	headerElements[2] = Integer.toString(father.getID());
+  	  	headerElements[3] = toSend.getChunkId();
+  	  	headerElements[4] = toSend.getChunkNumber();
+  	  	
+  	  	try {
+  	  		h = new MessageHeader(headerElements.clone());
+  	  		
+  	  		m = new Message(h, toSend.getChunkContent());
+			
+  	  		waitRandomTime();
+			
+			father.restoreCh.sendMessage(m);
+		} catch (InterruptedException e) {
+			System.out.println("Error in send message protocol -> " + e.getMessage());
+		}
+	}
+	
+	public void waitRandomTime() throws InterruptedException {
+		Random r = new Random();
+		int low = 1;
+		int high = 401;
+		int result = r.nextInt(high-low) + low;
+		
+		Thread.sleep(result);
+	}
+}
Index: protocols/SendSoredMessageProtocol.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: protocols/SendSoredMessageProtocol.class
===================================================================
--- protocols/SendSoredMessageProtocol.class	(nonexistent)
+++ protocols/SendSoredMessageProtocol.class	(revision 1)

Property changes on: protocols/SendSoredMessageProtocol.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/SendSoredMessageProtocol.java
===================================================================
--- protocols/SendSoredMessageProtocol.java	(nonexistent)
+++ protocols/SendSoredMessageProtocol.java	(revision 1)
@@ -0,0 +1,24 @@
+package protocols;
+
+import channels.MessageHeader;
+import service.Cloud;
+
+public class SendSoredMessageProtocol extends Thread {
+	MessageHeader messageToSend;
+	Cloud cloud;
+	
+	public SendSoredMessageProtocol(MessageHeader toSend, Cloud c) {
+		messageToSend = toSend;
+		cloud = c;
+	}
+	
+	@Override
+	public void run() {
+		try {
+			cloud.controlRoom.waitRandomTime();
+			cloud.controlRoom.sendHeader(messageToSend);
+		} catch (InterruptedException e) {
+			System.out.println(e.getMessage());
+		}	
+	}
+}
Index: protocols/recoverChunkRepDegreeProtocol.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: protocols/recoverChunkRepDegreeProtocol.class
===================================================================
--- protocols/recoverChunkRepDegreeProtocol.class	(nonexistent)
+++ protocols/recoverChunkRepDegreeProtocol.class	(revision 1)

Property changes on: protocols/recoverChunkRepDegreeProtocol.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: protocols/recoverChunkRepDegreeProtocol.java
===================================================================
--- protocols/recoverChunkRepDegreeProtocol.java	(nonexistent)
+++ protocols/recoverChunkRepDegreeProtocol.java	(revision 1)
@@ -0,0 +1,56 @@
+package protocols;
+
+import java.util.Random;
+
+import channels.Message;
+import service.Cloud;
+import service.Constants;
+
+public class recoverChunkRepDegreeProtocol extends Thread {
+	Cloud father;
+	Message toSend;
+	String fileId;
+	String chunkNo;
+
+	public recoverChunkRepDegreeProtocol(Cloud c, Message m) {
+		father = c;
+		toSend = m;
+		fileId = m.getHeader().getFileId();
+		chunkNo = m.getHeader().getChunkNumber();
+	}
+	
+	@Override
+	public void run () {
+		int numberOfTries = 0, multiplier = 1;
+		
+		try {
+			waitRandomTime();
+			while(numberOfTries < Constants.MAX_PUTCHUNK_MESSAGES) {
+				father.sendBackupMessage(toSend);
+				
+				Thread.sleep(1000 * multiplier);
+				
+				if(father.getChunkCurrentRepDegree(fileId, chunkNo) >= Integer.parseInt(toSend.getHeader().getDegree())) {
+					System.out.println("replication degree for " + chunkNo + " restored.");
+					return;
+				}
+				
+				multiplier *= 2;
+				numberOfTries++;
+			}			
+		}
+		catch(InterruptedException e) {
+			System.out.println(e.getMessage());
+			return;
+		}
+	}
+	
+	public void waitRandomTime() throws InterruptedException {
+		Random r = new Random();
+		int low = 1;
+		int high = 401;
+		int result = r.nextInt(high-low) + low;
+		
+		Thread.sleep(result);
+	}
+}
Index: service/.classpath
===================================================================
--- service/.classpath	(nonexistent)
+++ service/.classpath	(revision 1)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: service/Chunk.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/Chunk.class
===================================================================
--- service/Chunk.class	(nonexistent)
+++ service/Chunk.class	(revision 1)

Property changes on: service/Chunk.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/Client.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/Client.class
===================================================================
--- service/Client.class	(nonexistent)
+++ service/Client.class	(revision 1)

Property changes on: service/Client.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/Cloud.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/Cloud.class
===================================================================
--- service/Cloud.class	(nonexistent)
+++ service/Cloud.class	(revision 1)

Property changes on: service/Cloud.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/Constants.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/Constants.class
===================================================================
--- service/Constants.class	(nonexistent)
+++ service/Constants.class	(revision 1)

Property changes on: service/Constants.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: service/FileChunker.java
===================================================================
--- service/FileChunker.java	(nonexistent)
+++ service/FileChunker.java	(revision 1)
@@ -0,0 +1,123 @@
+package service;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The porpous of this class is to load a file and split it to chunks so it they can be shared
+ *
+ */
+public class FileChunker implements java.io.Serializable {
+	
+	private String name;
+	private String id;
+	private ArrayList<Chunk> fileChunks = new ArrayList<Chunk>();
+	private String fileDate;
+	private Path fileLocation;
+	private int replication_degree;
+	
+	public FileChunker(String path, int replication_degree) throws IOException {
+		
+		this.fileLocation = Paths.get(path);
+		byte[] fileData = Files.readAllBytes(fileLocation);
+		int fileDataLength = fileData.length;
+		byte[] aux;
+		int to = 0;
+		Chunk auxChunk = null;
+		
+		name = fileLocation.getFileName().toString();
+		fileDate = new SimpleDateFormat("yyyy/MM/dd").format(new File(fileLocation.toString()).lastModified());
+		id = SHA256(name + fileDate);
+		
+		System.out.println(name + " has " + fileDataLength + " bytes.");
+		
+		for(int chunkID = 0, from = 0; from < fileDataLength; chunkID++, from += (to-from)) {
+			
+			to = fileDataLength - from > Constants.MAX_CHUNK_SIZE ? from + Constants.MAX_CHUNK_SIZE : fileData.length;
+			
+			try {
+				aux = Arrays.copyOfRange(fileData, from, to);
+				
+				//System.out.println("Chunk no " + chunkID + " with " + aux.length + " created with bytes from " + from + " to " + to);
+				
+				auxChunk = new Chunk(chunkID, aux, id, replication_degree);
+				
+				auxChunk.setFilePathName(name);
+				
+				fileChunks.add(auxChunk); // para podermos distinguir entre diferentes versões 
+				
+			} catch(Exception e) {
+				System.out.println("Invalid chunk size, might be corrupted");
+			}
+		}
+	}
+	
+	public FileChunker(String path) throws IOException 
+	{
+		this.fileLocation = Paths.get(path);
+		
+		name = fileLocation.getFileName().toString();
+		fileDate = new SimpleDateFormat("yyyy/MM/dd").format(new File(fileLocation.toString()).lastModified());
+		id = SHA256(name + fileDate);
+	}
+	
+	public ArrayList<Chunk> getChunks() {
+		return fileChunks;
+	}
+	
+	public String getFileID() {
+		return id;
+	}
+	
+	public String getFileName()
+	{
+		return this.name;
+	}
+	
+	public int getFileSize() {
+		int size = 0;
+		
+		for(Chunk c : fileChunks) {
+			size += c.getChunkSize();
+		}
+		
+		return size;
+	}
+	
+	public Path getPath()
+	{
+		return this.fileLocation;
+	}
+	
+	public int getReplicationDegree()
+	{
+		return this.replication_degree;
+	}
+	
+	private 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 (byte aHash : hash) {
+                String hex = Integer.toHexString(0xff & aHash);
+                if (hex.length() == 1) {
+                	hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+
+            return hexString.toString();
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
Index: service/RMI.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: service/RMI.class
===================================================================
--- service/RMI.class	(nonexistent)
+++ service/RMI.class	(revision 1)

Property changes on: service/RMI.class
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: J Cole.mp3
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: J Cole.mp3
===================================================================
--- J Cole.mp3	(nonexistent)
+++ J Cole.mp3	(revision 1)

Property changes on: J Cole.mp3
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: loboFixe.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: loboFixe.jpg
===================================================================
--- loboFixe.jpg	(nonexistent)
+++ loboFixe.jpg	(revision 1)

Property changes on: loboFixe.jpg
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: spawnPeers.sh
===================================================================
--- spawnPeers.sh	(nonexistent)
+++ spawnPeers.sh	(revision 1)
@@ -0,0 +1,4 @@
+java -jar ./out/Peer.jar 1 1.0 Peer1 224.0.0.1 2241 224.0.0.2 2242 224.0.0.3 2243 &
+java -jar ./out/Peer.jar 2 1.0 Peer2 224.0.0.1 2241 224.0.0.2 2242 224.0.0.3 2243 &
+java -jar ./out/Peer.jar 3 1.0 Peer3 224.0.0.1 2241 224.0.0.2 2242 224.0.0.3 2243 &
+java -jar ./out/Peer.jar 4 1.0 Peer4 224.0.0.1 2241 224.0.0.2 2242 224.0.0.3 2243

Property changes on: spawnPeers.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: 100MB.bin
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: 100MB.bin
===================================================================
--- 100MB.bin	(nonexistent)
+++ 100MB.bin	(revision 1)

Property changes on: 100MB.bin
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: file.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: file.png
===================================================================
--- file.png	(nonexistent)
+++ file.png	(revision 1)

Property changes on: file.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: restore.sh
===================================================================
--- restore.sh	(nonexistent)
+++ restore.sh	(revision 1)
@@ -0,0 +1 @@
+java -jar ./out/Client.jar localhost:Peer1 RESTORE ./J\ Cole.mp3
\ No newline at end of file

Property changes on: restore.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: teste.txt
===================================================================
--- teste.txt	(nonexistent)
+++ teste.txt	(revision 1)
@@ -0,0 +1 @@
+O bernardo é burro.
\ No newline at end of file
