diff --git a/src/ClientMain.java b/src/ClientMain.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5b72c5a6bcc582b036d77801cf31db7e2a994df
--- /dev/null
+++ b/src/ClientMain.java
@@ -0,0 +1,123 @@
+import java.io.File;
+import java.io.IOException;
+import java.util.Random;
+
+public class ClientMain {
+	
+	public static void main(String[] args) throws Exception{
+		
+		final int cport = Integer.parseInt(args[0]);
+		int timeout = Integer.parseInt(args[1]);
+			
+		File downloadFolder = new File("to_store");
+		if (!downloadFolder.exists())
+			if (!downloadFolder.mkdir()) throw new RuntimeException("Cannot create download folder (folder absolute path: " + downloadFolder.getAbsolutePath() + ")");
+		
+		File uploadFolder = new File("to_store");
+		if (!uploadFolder.exists())
+			throw new RuntimeException("to_store folder does not exist");
+		
+		// testClient(cport, timeout, downloadFolder);
+		
+		// example to launch a number of concurrent clients, each doing the same operations
+		for (int i = 0; i < 10; i++) {
+			new Thread() {
+				public void run() {
+					test2Client(cport, timeout, downloadFolder, uploadFolder);
+				}
+			}.start();
+		}
+	}
+	
+	public static void test2Client(int cport, int timeout, File downloadFolder, File uploadFolder) {
+		Client client = null;
+		
+		try {
+			client = new Client(cport, timeout, Logger.LoggingType.ON_FILE_AND_TERMINAL);
+			client.connect();
+			Random random = new Random(System.currentTimeMillis() * System.nanoTime());
+			
+			File fileList[] = uploadFolder.listFiles();
+			for (int i=0; i<fileList.length/2; i++) {
+				File fileToStore = fileList[random.nextInt(fileList.length)];
+				try {					
+					client.store(fileToStore);
+				} catch (Exception e) {
+					System.out.println("Error storing file " + fileToStore);
+					e.printStackTrace();
+				}
+			}
+			
+			String list[] = null;
+			try { list = list(client); } catch(IOException e) { e.printStackTrace(); }
+			
+			for (int i = 0; i < list.length/4; i++) {
+				String fileToRemove = list[random.nextInt(list.length)];
+				try {
+					client.remove(fileToRemove);
+				} catch (Exception e) {
+					System.out.println("Error remove file " + fileToRemove);
+					e.printStackTrace();
+				}
+			}
+			
+			try { list = list(client); } catch(IOException e) { e.printStackTrace(); }
+			
+		} catch(IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (client != null)
+				try { client.disconnect(); } catch(Exception e) { e.printStackTrace(); }
+		}
+	}
+	
+	public static void testClient(int cport, int timeout, File downloadFolder) {
+		Client client = null;
+		
+		try {
+			
+			client = new Client(cport, timeout, Logger.LoggingType.ON_FILE_AND_TERMINAL);
+		
+			try { client.connect(); } catch(IOException e) { e.printStackTrace(); return; }
+			
+			try { list(client); } catch(IOException e) { e.printStackTrace(); }
+			
+			try { client.store(new File("Clipboard01.pdf")); } catch(IOException e) { e.printStackTrace(); }
+			
+			try { client.store(new File("Clipboard01.pdf")); } catch(IOException e) { e.printStackTrace(); }
+
+			try { client.store(new File("Clipboard01.jpg")); } catch(IOException e) { e.printStackTrace(); }
+			
+			String list[] = null;
+			try { list = list(client); } catch(IOException e) { e.printStackTrace(); }
+			
+			if (list != null)
+				for (String filename : list)
+					try { client.load(filename, downloadFolder); } catch(IOException e) { e.printStackTrace(); }
+			
+			/*if (list != null)
+				for (String filename : list)
+					try { client.remove(filename); } catch(IOException e) { e.printStackTrace(); }
+			try { client.remove(list[0]); } catch(IOException e) { e.printStackTrace(); }
+			
+			try { list(client); } catch(IOException e) { e.printStackTrace(); }*/
+			
+		} finally {
+			if (client != null)
+				try { client.disconnect(); } catch(Exception e) { e.printStackTrace(); }
+		}
+	}
+
+	public static String[] list(Client client) throws IOException, NotEnoughDstoresException {
+		System.out.println("Retrieving list of files...");
+		String list[] = client.list();
+		
+		System.out.println("Ok, " + list.length + " files:");
+		int i = 0; 
+		for (String filename : list)
+			System.out.println("[" + i++ + "] " + filename);
+		
+		return list;
+	}
+	
+}
diff --git a/src/Controller.java b/src/Controller.java
deleted file mode 100644
index d5cfe8443615f5fe45b9956666316eb1c654c26d..0000000000000000000000000000000000000000
--- a/src/Controller.java
+++ /dev/null
@@ -1,211 +0,0 @@
-import java.io.*;
-import java.net.*;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.zip.InflaterInputStream;
-
-public class Controller {
-
-    // orchestrates client requests
-    // maintains an index with the allocation of files to Dstores
-    // waits for Dstores to join the datastore (rebalance operation)
-    // it doesn't serve any client request until at least R Dstores have joined the system
-
-    static File file = new File("output.txt");
-    static int R;
-    // list of all dstores
-    static List<Socket> ports = new ArrayList<>();
-    // file, list of dstores
-    static HashMap<String, List<Socket>> index = new HashMap<>();
-    // file, status (e.g. in progress)
-    static HashMap<String, String> status = new HashMap<>();
-
-    public static void main (String[] args) {
-
-        // port to listen on
-        final int cport = Integer.parseInt(args[0]);
-        // replication factor => number of Dstores to join
-        R = Integer.parseInt(args[1]);
-        // timeout in milliseconds
-        int timeout = Integer.parseInt(args[2]);
-        // how long to wait to start the next rebalance operation
-        int rebalance_period = Integer.parseInt(args[3]);
-
-        System.out.println("Started");
-
-        createLogFile();
-
-        try {
-            ServerSocket socket = new ServerSocket(cport);
-            for(;;) {
-                try {
-                    while(R > 0) {
-                        // accept Dstores
-                        System.out.println("waiting for Dstore to join");
-                        Socket client = socket.accept(); // establish a connection between client and server
-                        System.out.println(R);
-                        ports.add(client);
-                        R = R - 1;
-                    }
-
-                    // create a Thread for each client
-                    Socket client = socket.accept();
-                    Thread t = new Thread() {
-                        @Override
-                        public void run() {
-                            try {
-                                handleClient(client);
-                            } catch (Exception e) {
-                                e.printStackTrace();
-                            }
-                        }
-                    };
-                    t.start();
-                } catch (Exception e1) {
-                    System.out.println(e1);
-                }
-            }
-
-        } catch (IOException e) {
-            System.out.println("error" + e);
-        }
-
-    }
-
-    private static void handleDstores(Socket clientSocket) throws IOException, InterruptedException {
-        OutputStream outputStream = clientSocket.getOutputStream();
-        InputStream inputStream = clientSocket.getInputStream();
-
-        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-        String line;
-        while((line = reader.readLine()) != null) {
-            String[] tokens = line.split(" ");
-            String cmd = tokens[0];
-            if(cmd.equals("ACK")) {
-
-            } else if (cmd.equals("STORE_ACK")) {
-                System.out.println("STORE_ACK");
-
-            } else if (cmd.equals("REMOVE_ACK")) {
-
-            } else if (cmd.equals("QUIT")) {
-                clientSocket.close();
-                break;
-            } else {
-                String msg = "unknown command " + line;
-                // outputStream.write(msg.getBytes());
-                log(msg);
-            }
-
-            String msg = "You typed " + line;
-            log(msg);
-        }
-
-        if(!clientSocket.isClosed()) {
-            clientSocket.close();
-        }
-    }
-
-    private static void handleClient(Socket clientSocket) throws IOException {
-        OutputStream outputStream = clientSocket.getOutputStream();
-        InputStream inputStream = clientSocket.getInputStream();
-        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-        String line;
-
-        while((line = reader.readLine()) != null) {
-            String[] tokens = line.split(" ");
-            String cmd = tokens[0];
-
-            if (cmd.equals("STORE")) {
-
-                String filename = tokens[1];
-                int filesize = Integer.parseInt(tokens[2]);
-                status.put(filename, "store in progress");
-                log("store in progress");
-
-                String msg = "";
-
-                for(Socket socket : ports) {
-                    msg = socket.getPort() + msg + " ";
-                }
-
-                // test and see if this actually gets all the ports
-                System.out.println("STORE_TO " + msg);
-
-                // send this message to the client so the client knows where to store the file
-                outputStream.write(("STORE_TO " + msg).getBytes());
-                outputStream.flush();
-
-                final int[] num = {0};
-
-                for(Socket socket : ports) {
-                    Thread t = new Thread() {
-                        @Override
-                        public void run() {
-                            try {
-                                InputStream inputStream = socket.getInputStream();
-                                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-                                String line = reader.readLine();
-                                String[] ts = line.split(" ");
-                                String cmd = ts[0];
-                                if(cmd == "STORE_ACK") {
-                                    num[0]++;
-                                }
-                            } catch (Exception e) {
-                                e.printStackTrace();
-                            }
-                        }
-                    };
-                    t.start();
-                }
-
-                status.put(filename, "store complete");
-                outputStream.write("STORE_COMPLETE".getBytes());
-
-            } else if (cmd.equals("LOAD")) {
-
-            } else if (cmd.equals("REMOVE")) {
-
-            } else if (cmd.equals("LIST")) {
-                String msg = "";
-                for(String filename : status.keySet()) {
-                    msg = filename + " " + msg;
-                }
-
-                System.out.println(msg);
-                outputStream.write(("LIST " + msg).getBytes());
-
-            } else {
-                String msg = "unknown command " + line;
-                log(msg);
-            }
-        }
-
-        clientSocket.close();
-    }
-
-    private static void createLogFile() {
-        try {
-            if(file.createNewFile()) {
-                System.out.println("File created");
-            } else {
-                System.out.println("File already exists");
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static void log(String message) {
-        try {
-            FileWriter writer = new FileWriter(file, true);
-            writer.write(java.time.LocalDate.now() + ", " +
-                    java.time.LocalTime.now() + ", CONTROLLER: " + message + '\n');
-            writer.close();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/Dstore.java b/src/Dstore.java
deleted file mode 100644
index 0de5ae7537f4cf24a09cb6f24a531993b9c94be0..0000000000000000000000000000000000000000
--- a/src/Dstore.java
+++ /dev/null
@@ -1,159 +0,0 @@
-import java.io.*;
-import java.net.*;
-
-public class Dstore {
-
-    static File file = new File("output.txt");
-
-    public static void main (String[] args) {
-
-        // port to listen on
-        final int port = Integer.parseInt(args[0]);
-        // controller's port to talk to
-        final int cport = Integer.parseInt(args[1]);
-        // timeout in milliseconds
-        int timeout = Integer.parseInt(args[2]);
-        // where to store the data locally
-        String file_folder = args[3];
-
-        InetAddress ip;
-        createLogFile();
-
-        try {
-
-            ip = InetAddress.getLocalHost();
-            Socket socket = new Socket();
-            socket.connect(new InetSocketAddress(ip, cport));
-
-            ServerSocket s = new ServerSocket(port);
-            Socket client = s.accept();
-
-            handleClient(client, socket, file_folder);
-
-        } catch (Exception e) {
-            System.out.println("error " + e);
-        }
-    }
-
-    private static void handleClient(Socket clientSocket, Socket controllerSocket, String file_folder) throws IOException, InterruptedException {
-        OutputStream outputStream = clientSocket.getOutputStream();
-        InputStream inputStream = clientSocket.getInputStream();
-
-        OutputStream cOutputStream = controllerSocket.getOutputStream();
-        InputStream cInputStream = controllerSocket.getInputStream();
-
-        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-        String line;
-
-        while((line = reader.readLine()) != null) {
-            String[] tokens = line.split(" ");
-            String cmd = tokens[0];
-
-            if (cmd.equals("STORE")) {
-
-                // ACK the command from the client
-                outputStream.write("ACK\n".getBytes());
-
-                // read the filename, filesize and create the data bytes to be read
-                String filename = tokens[1];
-                int filesize = Integer.parseInt(tokens[2]);
-
-                // read data and store the file
-                byte[] data = new byte[filesize];
-                inputStream.readNBytes(data, 0, filesize);
-                FileOutputStream out = new FileOutputStream(file_folder + "/" + filename);
-                out.write(data);
-                out.flush();
-                out.close();
-
-                cOutputStream.write(("STORE_ACK " + filename).getBytes());
-
-            } else if (cmd.equals("LOAD_DATA")) {
-                String filename = tokens[1];
-                File file = new File(file_folder + "/" + filename);
-                if(file.exists()) {
-                    FileInputStream in = new FileInputStream(file_folder + "/" + filename);
-                    int n;
-                    while((n = in.read())!= -1) {
-                        outputStream.write(n);
-                    }
-                    in.close();
-                } else {
-                    clientSocket.close();
-                }
-
-            } else if (cmd.equals("QUIT")) {
-                clientSocket.close();
-                break;
-            } else {
-                String msg = "unknown command " + line;
-                // outputStream.write(msg.getBytes());
-                log(msg);
-            }
-        }
-
-        if(!clientSocket.isClosed() || !controllerSocket.isClosed()) {
-            clientSocket.close();
-            controllerSocket.close();
-        }
-    }
-
-    private static void handleController(Socket controllerSocket, String file_folder) throws IOException {
-        OutputStream outputStream = controllerSocket.getOutputStream();
-        InputStream inputStream = controllerSocket.getInputStream();
-        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-        String line;
-
-        while((line = reader.readLine()) != null) {
-            String[] tokens = line.split(" ");
-            String cmd = tokens[0];
-
-            if (cmd.equals("REMOVE")) {
-
-                String filename = tokens[1];
-                File file = new File(file_folder + "/" + filename);
-                if(file.exists()) {
-                    file.delete();
-                    outputStream.write(("REMOVE_ACK " + filename).getBytes());
-                } else {
-                    outputStream.write(("ERROR_FILE_DOES_NOT_EXIST " + filename).getBytes());
-                }
-
-            } else if (cmd.equals("QUIT")) {
-                controllerSocket.close();
-                break;
-            } else {
-                String msg = "unknown command " + line;
-                // outputStream.write(msg.getBytes());
-                log(msg);
-            }
-        }
-
-        if(!controllerSocket.isClosed()) {
-            controllerSocket.close();
-        }
-    }
-
-    private static void createLogFile() {
-        try {
-            if(file.createNewFile()) {
-                System.out.println("File created");
-            } else {
-                System.out.println("File already exists");
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static void log(String message) {
-        try {
-            FileWriter writer = new FileWriter(file, true);
-            writer.write(java.time.LocalDate.now() + ", " +
-                    java.time.LocalTime.now() + ", DSTORE: " + message + '\n');
-            writer.close();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/client-1.0.2.jar b/src/client-1.0.2.jar
new file mode 100644
index 0000000000000000000000000000000000000000..e10e391d3ba673d02192327bed3383618c2e4249
Binary files /dev/null and b/src/client-1.0.2.jar differ